- Neue Prisma-Modelle ProfileAccessGroup + ProfileAccessGroupMember mit canView/canExport/canEdit
- Manuelle Migration 20260314_profile_access_groups
- ProfileAccessModule: CRUD-Endpoints für Gruppen und Mitglieder (nur PLATFORM_ADMIN)
- Neue Admin-Endpoints in ExpertProfileService/-Controller für alle Profil-Mutationen
- verifyOwnership mit skipCheck-Parameter für Admin-Bypass
- ExpertProfileTab + alle Section-Komponenten erhalten apiBase-Prop für Wiederverwendung
- AdminProfileAccessPage: Gruppen-Tab (CRUD) + Profile-Tab (alle User mit Aktionen)
- AdminProfileDetailPage: Profil eines beliebigen Users im Admin-Kontext bearbeiten
- Route /admin/profile-access + /admin/profiles/:userId + Nav-Tab Profilzugriff
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Im Bereich Anpassungen (AdminCustomizePage) kann der Platform-Admin
nun den Login-Screen individuell gestalten:
- Hintergrundtyp: Farbverlauf, Einfarbig oder Hintergrundbild
- Farbverlauf: zwei Farbpicker (Von/Bis) mit Hex-Eingabe
- Hintergrundbild: Datei-Upload max. 2MB, Live-Vorschau
- Logo auf Login-Screen: wird automatisch aus dem Sidebar-Logo uebernommen
Backend: settings.controller.ts GET/POST /settings/branding um
loginBgType, loginBgColor1, loginBgColor2, loginBgImage erweitert.
LoginPage laedt Branding per oeffentlichem Endpoint (kein Auth), leitet
containerStyle per useMemo ab und zeigt Logo-Bild statt Hardcode-Text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
- 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>
Import ist kein eigener Nav-Eintrag mehr, sondern ein Tab
in den CRM-Einstellungen. /crm/import redirectet auf /crm/settings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lexware contact linking is not relevant for individual contacts,
only for companies. Removed LexwareSection component and the
two-column topRow grid layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Name (Unternehmen) in Klammern in der Überschrift
- Grüner Punkt → "● Aktiv"/"● Inaktiv" Badge
- Position als Subtitle unterhalb des Namens
- Lexware-Card rechts neben Kontaktdaten (260px)
- Aktivitäten als volle Breite unterhalb der Kontaktdaten
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend (CRM expert): Custom field definitions CRUD, bulk value upsert,
7 endpoints, Prisma schema with CustomFieldDef + CustomFieldValue tables.
Frontend: Types, API, hooks, admin settings page with field management,
CustomFieldsDisplay for detail pages, CustomFieldsForm for edit modals.
Also fix Vite allowedHosts for insight.xinion.lan.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backend: 4 new endpoints in SettingsController (GET/POST/DELETE /settings/ssl, POST /settings/ssl/check-dns)
- Certificate validation via Node.js crypto.X509Certificate (PEM format, expiry, SAN match)
- DNS resolution check via dns.promises.resolve4
- Auto-generates Traefik dynamic config (ssl-domain.yml) with custom domain routing + HTTP->HTTPS redirect
- Frontend: AdminSslPage with DNS name input, cert/key upload, status display
- Docker: Core-service gets access to traefik-certs volume and dynamic config directory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>