From 4c2e04e78b28577f82a380e974661aa9e62f1363 Mon Sep 17 00:00:00 2001 From: Erik Dunin Date: Fri, 30 Oct 2026 08:23:02 +0100 Subject: [PATCH] Kleine Verbesserungen und Teams, Outlook Integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Outlook: Kalender-Abo je Konto (eigene Aufgaben, Meilensteine, Abwesenheiten als .ics), einzurichten unter Einstellungen - Teams: Nachricht in einen Kanal bei neuen Aufgaben, mit Link direkt auf die Aufgabe; einzurichten unter Admin → Teams - Sitzungen nach Bedarf laden: beim Start nur die aktuelle und drei davor, im Verlauf „Weitere laden“ in Fünferschritten - Termin aus Mail: Wahl Aufgabe/Meilenstein, Notiz auch bei Meilensteinen, nur aktiv mit eingerichteter KI - Kalender zeigt die KW, auch statt der Browser-Datumsfelder - Pfeile bei den Abwesenheiten, kleinere Server-Korrekturen Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 143 +++++++- public/App.html | 70 +++- public/js/abwesenheit/abwesenheit.js | 4 +- public/js/admin/admin.js | 108 +++++- public/js/auth.js | 2 + public/js/date-picker.js | 30 +- public/js/globals.js | 3 + public/js/plan/plan-info.js | 25 +- public/js/plan/plan-mail-eintrag.js | 79 +++-- public/js/plan/plan-modals.js | 29 +- public/js/plan/plan-state.js | 42 +++ public/js/settings/settings.js | 75 +++++ public/js/store.js | 118 ++++++- public/js/utils.js | 20 +- public/js/weeks.js | 5 +- public/js/wochenbericht/wochenbericht.js | 24 +- public/plan-styles.css | 22 +- public/styles.css | 39 ++- server.js | 401 ++++++++++++++++++++++- 19 files changed, 1100 insertions(+), 139 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cdb5f38..d5fa3a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1422,8 +1422,9 @@ step into a project explicit. Everything lives in `public/js/auftrag.js`: `renderPlanKommentarVerlauf()` in `plan-info.js`): je Gewerk nur, wenn sich der Text gegenüber der Sitzung davor geändert hat; gleicher Text in mehreren Gewerken einer Sitzung (Altbestand aus der Umstellung auf - Kommentare je Gewerk) wird EIN Eintrag „HLS, ARCH“. Drei sichtbar, Rest - über „Alle N anzeigen“. Keine neue Datenhaltung — jede Sitzung hält ihre + Kommentare je Gewerk) wird EIN Eintrag „HLS, ARCH“. Gezeigt wird, was + geladen ist, darunter „Weitere laden“ (siehe „Sitzungen nach Bedarf + laden“). Keine neue Datenhaltung — jede Sitzung hält ihre Kopie des Projekts. Stile: Block „Kommentar der Sitzung mit Verlauf“ am Ende von `plan-styles.css`. **Eigene Kachel „Protokolle“** (Eriks Wunsch; links unter „Termine und @@ -1791,8 +1792,11 @@ step into a project explicit. Everything lives in `public/js/auftrag.js`: Eriks Wunsch: Mailtext per Drag & Drop (markierter Text oder ganze `.eml`-Datei) oder Strg+V (außerhalb von Eingabefeldern) auf die Bereiche **Terminplan** oder **Aufgaben** eines Projekts; eine KI macht daraus -Einträge. **Nur Aufgaben, keine Meilensteine** — Eriks Vorgabe: Der -Terminplan ist nur die grafische Darstellung der Aufgabenliste. +Einträge — **Aufgabe oder Meilenstein**: Die KI schlägt die Art vor +(`art` in `KI_MAIL_SCHEMA`), in der Auswahlliste `#mailAuswahlModal` (immer, +auch bei nur einem Vorschlag) steht je Zeile der Umschalter +„Aufgabe | Meilenstein“ (Eriks Wunsch). Danach öffnet sich je Eintrag das +passende Panel. - Beim Hineinziehen erscheint die Fläche `#mailAblage` (fest, `pointer-events: none`, Zähler für dragenter/-leave). Nicht in gesperrten Sitzungen. Kein Konflikt mit dem Balken-Ziehen (das läuft über Maus-Ereignisse). @@ -1805,15 +1809,25 @@ Terminplan ist nur die grafische Darstellung der Aufgabenliste. „nächsten Freitag“ stimmt. Ohne KI/bei Fehler: ein Eintrag mit dem Betreff als Titel. **Speichert nichts.** `.msg` (Outlook) wird abgewiesen mit dem Hinweis, Text zu markieren. +- **Nur mit eingerichteter KI aktiv** (Eriks Wunsch): `mailKiStatusLaden()` + holt nach der Anmeldung (`afterAuthenticated()`) und nach jeder Änderung + der KI-Zugangsdaten (`putMyAiKey()`) die Anbieter mit Schlüssel; ohne + einen gibt es weder Ablagefläche noch abgefangenes Strg+V. - Anbieter: zuletzt erfolgreich genutzter (`localStorage projuebersicht-mail-ki`), sonst der erste mit Schlüssel. -- Jeder Vorschlag öffnet das normale Aufgaben-Panel vorausgefüllt - (`openTodoModal(null, prefill)`, neu: `title`, `aufwand`, `notiz`, - `mailHinweis`, `nachSchliessen`); bei mehreren erst eine Auswahl - (`#mailAuswahlModal`), dann nacheinander. Ohne Speichern entsteht nichts. -- **Notiz je Aufgabe** (`t.notiz`, `plan_todos.notiz`, Migration - `migrateAddPlanTodoNotiz()`, in `IMPORT_OPTIONAL_COLS`): Feld „Notiz“ im - Aufgaben-Panel für jede Aufgabe; aus einer Mail mit Absender, Datum, +- Umschalter „Aufgabe | Meilenstein“: eigene Stile `.segmented.mail-auswahl-art` + und Klasse `mail-art-knopf` (sonst greift unter Glas + `html.glas button:not([class])` mit Rand und Glanzkante, und der gewählte + Knopf war von `var(--surface)` kaum zu unterscheiden). +- Jeder gewählte Vorschlag öffnet nacheinander das normale Panel + vorausgefüllt: `openTodoModal(null, prefill)` (`title`, `start`/`end`, + `assignees`, `aufwand`, `notiz`, `mailHinweis`, `nachSchliessen`) bzw. + `openMilestoneModal(null, prefill)` (`name`, `date` = Ende des Vorschlags, + `notiz`, `mailHinweis`, `nachSchliessen`). Ohne Speichern entsteht nichts. +- **Notiz je Aufgabe und Meilenstein** (`t.notiz`/`m.notiz`, + `plan_todos.notiz`/`plan_milestones.notiz`, Migration + `migrateAddPlanTodoNotiz()`, in `IMPORT_OPTIONAL_COLS`): Feld „Notiz“ in + beiden Panels; aus einer Mail mit Absender, Datum, Betreff, Bezugssatz und Mailtext. In der Aufgabenliste ein Symbol mit Tooltip. Stile: Block „Termin aus Mail“ am Ende von `plan-styles.css`. @@ -3458,8 +3472,8 @@ nur, wenn sich der Kommentar DES GEWÄHLTEN GEWERKS gegenüber der Sitzung davor geändert hat; Sitzungen dazwischen als grauer Vermerk („KW 40 bis KW 43 ohne neuen Kommentar“); geänderte Leistungsphasen seit dem vorigen Eintrag als Chips („LPH 5: 60 → 75 %“); Verfasser und Datum; „übernehmen“ -(`berichtVerlaufUebernehmen()` → `takeCommentFromHistory()`); drei -sichtbar, Rest über „Alle N anzeigen“ (`berichtVerlaufUmschalten()` zeichnet +(`berichtVerlaufUebernehmen()` → `takeCommentFromHistory()`); gezeigt wird, +was geladen ist, „Weitere laden“ holt ältere (`berichtVerlaufNeu()` zeichnet nur den Abschnitt neu, ein angefangener Kommentar bleibt). Die dichte Karte bekommt dafür `opts.ohneVerlauf` (`renderBoardDrawer()`), damit der Kachel-Streifen dort nicht zusätzlich erscheint. Projektliste und @@ -4168,6 +4182,94 @@ across Gewerke or users. Erik's own words: "macht so keinen Sinn mehr." no special-casing needed. A redirect `GET /Test.html`-style old link isn't needed here since nothing ever pointed at this tab before it existed. +## Kalender-Abo für Outlook (iCalendar) + +Eriks Wunsch: die eigenen Termine in Outlook sehen, nur zum Lesen. +Einstellungen → Mein Konto → Karte „Kalender in Outlook“ +(`renderSettingsKalender()`, `js/settings/settings.js`): Adresse erzeugen, +kopieren, „In Outlook öffnen“ (`webcal:`-Link), Anleitung, „Neue Adresse“ +(alte wird ungültig), „Abschalten“. +- **Server:** `users.kalender_token` (Migration in `migrateUsersDepartment()`, + leer = aus). `GET`/`POST /mein-kalender` (requireAuth, nur eigenes Konto; + POST `{aus:true}` schaltet ab, sonst neue Adresse). **`GET + /kalender/.ics` ist ohne Sitzung erreichbar** — der Token (24 Byte + zufällig) IST die Anmeldung, weil Outlook sich nicht einloggen kann. + Unbekannt, deaktiviertes Konto oder Rolle ohne „Anwendung nutzen“ → 404. +- **Inhalt** (`kalenderEreignisse()`, rein und testbar): aus der jüngsten + echten Sitzung die eigenen offenen Aufgaben mit Termin (ohne erledigte und + ohne Phasenbalken), Meilensteine der Projekte, denen man zugeordnet ist + oder in denen man eine eigene Aufgabe hat, dazu die eigenen + Abwesenheiten. Alles ganztägig (`VALUE=DATE`, DTEND = Tag danach), als + „frei“ (Abwesenheit „abwesend“). Notizen gehen in die Beschreibung. + `kalenderText()` maskiert nach RFC 5545 und faltet Zeilen auf 75 Byte. +- **Adresse** (`kalenderUrl()`): der Host, über den der Browser die App + aufruft — wer über `localhost` öffnet, bekommt eine `localhost`-Adresse + (eine automatische Ersetzung durch die Geräte-IP hat Erik verworfen, weil + sie im Docker-Container nicht verlässlich ist). +- **Grenze:** Neues Outlook / Outlook im Browser holen das Abo über + Microsoft-Server aus dem Internet ab — dafür muss der Server von außen + erreichbar sein. Das klassische Outlook am PC holt es im Firmennetz direkt. + +## Teams: Nachricht bei neuen Aufgaben + +Eriks Wunsch: Wird jemandem eine Aufgabe zugewiesen, erscheint in einem +Teams-Kanal eine Karte mit Link direkt auf die Aufgabe. +- **Einrichten:** In Teams im Kanal *Workflows* → „Beim Empfang einer + Webhook-Anfrage in einem Kanal posten“; die erzeugte https-Adresse unter + **Admin → Teams** eintragen (`loadAdminTeams()`, `js/admin/admin.js`, + Recht **`a.teams`**, neu im Katalog „Admin-Bereich“), „Testnachricht + senden“. Anleitung steht aufklappbar in der Karte. +- **Speicher:** Tabelle `integrationen` (schluessel/wert; `teams_webhook`, + `teams_neue_aufgaben`), bewusst NICHT `app_settings` (das darf jedes + Konto lesen) und nicht in `IMPORT_TABLES`. Die Adresse geht nie an den + Browser zurück — `GET /admin/teams` liefert nur `konfiguriert`/`host`. + `PUT /admin/teams {webhook?, neueAufgaben?}` (nur https), `POST + /admin/teams/test` (Fehler als `{ok:false}` mit HTTP 200). +- **Auslöser:** `neueZuweisungenMelden()` (aus `/save-week` und + `/save-projects`, jetzt mit `req`) sammelt je Aufgabe die NEU Zuständigen + — auch Personen ohne Konto, nicht wer sich selbst zuweist, keine + Phasenbalken, nie die Sandbox — und `teamsNeueAufgaben()` schickt je + Aufgabe EINE Adaptive Card (`teamsKarte()`: Titel, Projekt, Zuständig, + Fällig/Zeitraum, Aufwand, Zugewiesen von, Knopf „Aufgabe öffnen“). Im + Hintergrund; Fehler nur im Log, das Speichern scheitert nie daran. +- **Link** (`aufgabenLink()`): `/Projektplan/?weekLabel=…&projNr=… + &aufgabe=#aufgaben`, Host aus der speichernden Anfrage (= Adresse + der App im Büro; funktioniert nur im Büronetz/VPN). Im Browser liest + `plan-state.js` `aufgabe` beim Laden (`startAufgabe`): öffnet die + JÜNGSTE Sitzung, falls der Link eine ältere nennt (Aufgaben-ids bleiben + beim Sitzungswechsel gleich), springt nach „Aufgaben“ und öffnet das + Aufgaben-Panel; Adresszeile wird nachgezogen. +- Braucht Internetzugang nach außen (Microsoft-Server). + +## Sitzungen nach Bedarf laden (`js/store.js`) + +Eriks Wunsch: `/load-all` wuchs jede Woche (alle Sitzungen, ~17 KB je +Woche). Jetzt lädt der Browser beim Start nur die jüngsten +`SITZUNGEN_START` (4: aktuelle + drei davor) über `GET /load-all?wochen=4`; +dieselbe Antwort trägt `wochenListe` = ALLE echten Sitzungen schlank +(`{id, label, date, locked, updatedAt}`, `echteSitzungenSortiert()`), im +Browser `db.wochenListe`. **`db.weeks` hält damit nur die GELADENEN +Sitzungen** — wer über `db.weeks` iteriert, sieht nicht alle. +- **Verlauf** (Wochenbericht „Verlauf der Vorwochen“, Kommentar-Verlauf im + Projekt-Dashboard, Verlauf-Kacheln der Projektliste): zeigt, was geladen + ist; darunter `weitereLadenKnopf()` → `weitereSitzungenLaden()` → + `GET /weeks-aelter?vor=<älteste geladene>&anzahl=5` holt je + `SITZUNGEN_NACHLADEN` (5) ältere. Die früheren Knöpfe „Alle N anzeigen“ + sind entfallen. Der Wochenbericht zeichnet danach nur den Verlauf neu + (`berichtVerlaufNeu()`), damit ein angefangener Kommentar bleibt; der + Kachel-Verlauf merkt sich, ob er aufgeklappt war (`verlaufOffen`). +- **Nicht geladene Sitzungen bei Bedarf**: `sitzungSicherLaden(label)` (über + `GET /week`). Genutzt von Admin → Sitzungen (Liste aus `adminSitzungen()` + = alle; Entsperren/Öffnen/Löschen/Sperren nehmen jetzt das Label) und von + `openPlanView()` bei einem Link auf ein Projekt einer älteren Sitzung. +- Live: Ein Ereignis zu einer nicht geladenen Sitzung zieht nur deren Eintrag + in `db.wochenListe` nach, statt alles neu zu laden. `deleteWeekById()` + zählt für „mindestens eine Sitzung“ die `wochenListe`. +- `/load-all` ohne `wochen` liefert weiterhin alles (ältere Browser-Stände). + `loadWeeks()` nimmt dafür auch eine Liste von Labels (`IN (…)`). +- Gemessen: 98 KB statt 158 KB bei 9 Sitzungen; bleibt künftig etwa gleich, + statt mit jeder Woche zu wachsen. + ## Gleichzeitiges Arbeiten (Zusammenführen statt Verwerfen, `js/store.js`) Eine Sitzung wird weiterhin als Ganzes über `POST /save-week` mit @@ -4324,6 +4426,17 @@ Rechner. kein `backdrop-filter` — eigenes Design, Kiosk-Rechner) und `prefers-reduced-transparency` (deckende Flächen). +## Datums-Kalender mit KW (`js/date-picker.js`) + +Eriks Wunsch: Jeder Kalender beim Anlegen/Bearbeiten zeigt die +Kalenderwoche. `renderRangePicker()` hat eine erste Spalte „KW“ +(`getWeekNumber()`, `.range-picker-kw`, Popup 316px breit). Native +`` (Feiertage, Abwesenheiten in der Benutzerverwaltung, +„Aufgabe aus Kommentar“, Assistent, TODOs aus Protokoll) öffnen über einen +globalen Klick-Abfänger denselben Kalender statt des Browser-Kalenders; der +Wert wird ins Feld geschrieben und `input`/`change` ausgelöst. Ausnahme per +Attribut `data-nativ`. + ## Dunkelmodus nachgeschärft (Kalender, Sitzungskommentar) Eriks Wahl „A“ aus einem Kontrast-Vergleich; Block „DUNKELMODUS @@ -4460,8 +4573,8 @@ Speicherüberlauf. Vier Ebenen, jede fängt ab, was die darüber verpasst: (`wlan-energiesparen-aus.service`), `wlan-waechter.timer` jede Minute — ohne Gateway ab 2 min neu verbinden, nach 10 min `systemctl reboot`. - **Prüfen:** `Docker/pi-pruefen.sh` (Speicher, Neustarts, OOM, Strom). -- Offen (Schritt 4 des Plans): `/load-all` liefert weiter ALLE Sitzungen — - wächst wöchentlich auf Server und Browser. +- **Sitzungen nach Bedarf** (Schritt 4, umgesetzt 30.10.2026): siehe + „Sitzungen nach Bedarf laden“ unten. ## Docker diff --git a/public/App.html b/public/App.html index 80fd275..b86cf3b 100644 --- a/public/App.html +++ b/public/App.html @@ -38,8 +38,8 @@ - - + + + +
+

Kalender in Outlook

+

Ihre offenen Aufgaben, die Meilensteine Ihrer Projekte und Ihre + Abwesenheiten als Kalender in Outlook — nur zum Ansehen, geändert wird weiter hier. + Outlook aktualisiert ihn selbst (je nach Outlook stündlich bis alle paar Stunden).

+
+
+

KI-Zugangsdaten

Für die automatische Punkt-Erkennung beim Protokoll-Import @@ -665,6 +676,28 @@

+ + +
+

Notiz

+ + +
@@ -1912,21 +1950,21 @@
- - + + - + - - - + + + - + - + - - + + @@ -1957,21 +1995,21 @@ (das seine Vorwochen-Helfer nutzt), und alle zusammen muessen nach js/gantt.js kommen, dessen buildGanttHtml() plan-build-gantt.js umhuellt. --> - + - + - + - +