- Neue Funktion blLineAt() auf Modul-Ebene: parst Zeile an Cursor-Position
(Einrueckung, Bullet/Nummeriert, Zahl, Zeileninhalt) ohne Closure-Probleme
- Neuer Toolbar-Button "1. Liste": toggled nummerierte Liste (1./2./3.);
wandelt Bullet→Nummeriert und Nummeriert→Bullet automatisch um
- Tab-Taste: fuegt 2 Leerzeichen am Zeilenanfang ein (Einrueckung)
- Shift+Tab: entfernt bis zu 2 Leerzeichen (Ausrueckung)
- Enter in nummerierter Liste: setzt naechste Zeile mit N+1 fort
- Enter auf leerem Listenelement: beendet die Liste (Bullet + Nummeriert)
- Enter beruecksichtigt Einrueckung bei Bullets
- CSS: bulletToolbarSep (Trennlinie) + bulletToolbarHint (Keyboard-Hinweis)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend @IsIn erlaubte nur CEFR-Codes (C1/C2/B2…), Frontend schickte
aber deutsche Bezeichnungen (Verhandlungssicher/Fließend/Gut).
Alle 4 Frontend-Level + CEFR-Codes für Rückwärtskompatibilität aufgenommen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- max-width 960px auf Kontakt-Detailseite
- M365-Sektion umbenannt zu "Outlook Daten", default eingeklappt
- Aufgaben-Tab entfernt (nur noch E-Mails + Kalender)
- "In Outlook speichern"-Button: pusht/aktualisiert Kontakt in Outlook-Kontakte via MS Graph POST/PATCH /me/contacts
- Kontaktdaten: Typ, Status immer sichtbar, Bundesland (state) in Adresse
- Backend: GraphService exportiert, pushContactToOutlook-Methode, POST /crm/contacts/:id/push-to-outlook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ProjectRequestFormModal: Eigenständiges Formular mit Projektdetails oben
(Beschreibung, Auslastung/Start, Laufzeit/Vorort-Anteil, Stundensätze)
und Standard-Vorgangsdaten darunter (Titel, Pipeline/Stage, Kontakt, etc.)
Auto-Select bei genau einem isProjectType-Typ; Warnung wenn nicht konfiguriert
- DealsPage: Neuer outlined Button "Neue Projektanfrage" neben "Neuer Vorgang"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Suche startet mit Organisator-Name vorbelegt → CRM zeigt sofort
passende Kontakte
- Klickbare Teilnehmer-Chips ermöglichen schnellen Wechsel zwischen
den Event-Attendees als Suchbegriff
- Aktiver Chip wird farblich hervorgehoben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neues CalendarEventModal: Klick auf Termin in Agenda-/Wochenansicht
öffnet Popup mit Betreff, Datum/Uhrzeit, Ort, Organisator, Teilnehmern
und Online-Meeting-Link
- CRM-Aktivität: Kontaktsuche direkt im Modal; Termin als MEETING-
Aktivität beim gewählten CRM-Kontakt speichern
- "Im Outlook öffnen"-Link im Modal-Footer
- Zeitzonen-Fix: MS Graph liefert UTC-Zeiten ohne 'Z'-Suffix →
toDate() hängt 'Z' an → alle Termine jetzt in korrekter Ortszeit
- DayAgenda + AgendaView + WeekView: <a>-Tags durch klickbare <div>
(role=button) ersetzt; onEventClick-Prop weitergereicht
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AppLayout: appsOpen initial false (Anwendungen immer eingeklappt beim Laden)
- DashboardPage: useLocation + useEffect auf location.key → setzt activeTab
auf 'home' bei jedem Navigations-Klick auf Dashboard in der Sidebar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Analoge SVG-Uhr (AnalogClock.tsx) aktualisiert jede Sekunde via setInterval
- useWeather: 3-Tage-Prognose via Open-Meteo daily-Parameter (weather_code, tempMax, tempMin)
- Dashboard Home-Tab: 3-Spalten-Layout (Uhr+Wetter links | Aufgaben+Mails mitte | Messe+Agenda rechts)
- Spruch des Tages rechts im Header (deterministisch nach Tagesdatum, 35 dt. Zitate)
- WeatherWidget aus dem Header in die linke Spalte verschoben
- Kompaktes Aufgaben-Widget: Top 8 offene Aufgaben (CRM + O365), direkt erledigbar
- Kompaktes E-Mail-Widget: Posteingang der letzten 3 Tage, direkter Öffnen-Link
- „Alle →" Buttons schalten auf den jeweiligen Tab um
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Felder im Benutzerprofil (analog Microsoft 365 /me):
- Stellenbezeichnung (jobTitle), Abteilung (department)
- Firma (companyName), Standort (officeLocation)
Changes:
- Core: Prisma-Migration + neue Felder in User-Model, UpdateUserDto,
findById/update/updateProfile
- CRM: M365UserProfile-Interface + getM365Profile um neue Felder erweitert;
neue Methode getM365Photo() lädt 96x96 JPEG als Base64 Data-URL;
neuer Endpoint GET /crm/office365/photo
- Frontend: AuthContext User-Interface, M365UserProfile-Typ, office365Api.getM365Photo()
ProfilePage: Neues Formular-Fieldset "Organisation" mit 4 Feldern;
manueller Sync-Button übernimmt auch Profilbild (immer überschreiben);
useO365ProfileSync: Auto-Sync lädt Foto nur wenn noch kein INSIGHT-Avatar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neuer Hook useO365ProfileSync: läuft einmalig pro Browser-Session,
überschreibt INSIGHT-Profil-Felder mit O365-Daten (firstName, lastName,
phone, mobile, city, street, postalCode) — kein UI-Feedback, kein Fehler
bricht die UX auf.
- AppLayout ruft useO365ProfileSync() auf, sodass die Synchronisation
beim Laden der App (nach Login) automatisch startet.
- ProfilePage: "↓ O365 übernehmen"-Button überschreibt jetzt alle Felder
wo O365 Daten hat (nicht mehr nur leere Felder) — konsistent mit dem
Auto-Sync-Verhalten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
E-Mail Tab:
- Klick auf E-Mail öffnet Detail-Modal (Lesefenster wie Outlook)
- Modal zeigt: Betreff, Absender, Datum, Anhang-Info, Body-Vorschau
- CRM-Bereich: gefundener Kontakt mit "Im CRM öffnen" + "Als Aktivität"
speichern; kein Kontakt → "Kontakt anlegen" navigiert zu /crm/contacts
- "In Outlook öffnen" Link im Footer des Modals
Kalender Tab:
- WeekView: nur Arbeitstage Mo–Fr (5-Spalten-Grid statt 7)
- Neue Ansicht "Agenda": 14-Tage-Listenansicht (eigener Toggle-Button)
- Tages-Agenda nur bei Monat- und Wochenansicht sichtbar (nicht Agenda-View)
- Home-Tab: Tages-Agenda des heutigen Tages als Widget rechts
(nur sichtbar wenn M365 verbunden)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Graph API: getCalendarEventsForRange() für beliebigen Datumsbereich,
GET /crm/office365/calendar/range?startDate=&endDate= Endpoint
(vor bestehenden calendar-Route definiert um Routing-Konflikt zu vermeiden)
- Graph API: wellKnownName aus mailFolders $select entfernt (400-Fehler auf
Exchange-Tenants die das OData-Property nicht unterstützen)
- Frontend: DashboardCalendarTab mit MonthView (6×7 Grid), WeekView (7 Spalten)
und DayAgenda (rechts 1/3), Navigation vor/zurück + Heute-Button,
deterministisches Event-Coloring, Klick öffnet Termin in Outlook Online
- Frontend: DashboardEmailTab Ordner-Sortierung auf Display-Name-Basis
(wellKnownName optional, isInboxFolder() erkennt Posteingang/Inbox)
- Frontend: M365MailFolder.wellKnownName als optional markiert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vorher: activeFolderId war null bis Ordnerliste geladen → E-Mail-Query nie gestartet
Jetzt: Default-Ordner-ID = 'inbox' (Graph API Well-Known-Name) → sofortiger Abruf
Zusätzlich: Fehler-/Leer-Zustand in der Ordner-Sidebar anzeigen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tab-Leiste auf Dashboard-Seite. Home zeigt bisherigen Inhalt,
restliche Tabs als Platzhalter (Inhalt folgt).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Traefik leitet x-forwarded-proto nicht korrekt weiter, sodass der
Controller http:// statt https:// generierte — Azure lehnt nicht-HTTPS
Redirect-URIs für nicht-localhost ab (AADSTS50011).
Protokoll wird jetzt aus der konfigurierten SSO-Redirect-URI abgeleitet
(immer HTTPS), der Host bleibt dynamisch (IP oder DNS).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MSAL-node v5 erzeugt bei getAuthCodeUrl mit reinen Graph-API-Scopes
(ohne openid) einen fehlerhaften Authorize-URL → AADSTS900561.
getIntegrationAuthUrl und handleIntegrationCallback verwenden jetzt
direkte fetch-Aufrufe (analog zu refreshIntegrationToken) ohne MSAL,
was den Fehler umgeht und denselben Standard-OAuth2-Flow garantiert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problem: Redirect-URI wurde falsch aus SSO-URI abgeleitet, und unterstützte
nur eine feste URL statt sowohl IP als auch DNS-Name.
Lösung:
- initM365Integration: Host aus x-forwarded-host/host Header lesen,
korrekte Redirect-URI bauen (proto://host/api/v1/auth/integrations/...)
- Redis-State speichert jetzt {userId, redirectUri} als JSON
- handleIntegrationCallback: gespeicherte redirectUri aus State verwenden
- getIntegrationAuthUrl/handleIntegrationCallback: optionaler redirectUri-Parameter
- Fallback-Derivation: base URL aus SSO-URI + fester Integrations-Pfad
Beide URIs müssen in Azure registriert sein:
- http://172.20.10.59/api/v1/auth/integrations/microsoft-365/callback
- http://insight.xinion.lan/api/v1/auth/integrations/microsoft-365/callback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IntegrationsService benötigt EntraIdService (Token-Refresh), der in
AuthModule als Provider registriert aber nicht exportiert war.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neuer "Integrationen"-Tab in ProfilePage mit "Microsoft 365 verbinden"-
Button, OAuth-Callback-Handling (?integration=...&status=...), Trennen-Button
- EmailsTab, CalendarTab, TasksTab fuer ContactDetailPage (via MS Graph Proxy)
- useIntegrations, useDisconnectM365, useContactEmails/Calendar/Tasks Hooks
- integrationsApi + graphApi in crm/api.ts
- M365Email, M365CalendarEvent, M365Task, UserIntegration Types in crm/types.ts
- Tabs nur sichtbar wenn Kontakt eine E-Mail-Adresse hat; ohne Verbindung Connect-Button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Neues GraphModule mit GraphService + GraphController
- GET /crm/contacts/:id/emails → MS Graph Emails (Search nach Kontakt-E-Mail)
- GET /crm/contacts/:id/calendar → Kalender-Termine (naechste 90 Tage)
- GET /crm/contacts/:id/tasks → Microsoft To Do Listen + Aufgaben
- GraphService: JWT an Core-Service weiterleiten, M365-Token holen, Graph aufrufen
- Redis-Cache 5 Minuten fuer alle Graph-Responses
- CORE_SERVICE_URL env var + docker-compose.crm.yml Eintrag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>