Commit graph

106 commits

Author SHA1 Message Date
Thomas Reitz
0c8a23ddc4 feat: Anthropic API-Key über Admin-UI konfigurierbar
- GET /settings/ai-config: gibt { configured: boolean } zurück
- POST /settings/ai-config: speichert Key Base64-kodiert in Redis (PLATFORM_ADMIN)
- HelpService: dynamische Key-Auflösung aus Redis mit 60s In-Memory-Cache
- AdminAiSettingsPage: neue Admin-Seite /admin/ai-settings
- HelpPanel: zeigt Hinweis + Link wenn KI nicht konfiguriert
- Env-Variable ANTHROPIC_API_KEY hat weiterhin Vorrang (backward compat.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:59:30 +01:00
Thomas Reitz
3f919340b5 feat: Stammdaten, CRM Reporting, Hilfesystem (hohe Priorität)
Stammdaten (Kapitel 14):
- 5 neue Prisma-Modelle: Department, Location, CostCenter, JobTitle, SkillCategory
- MasterDataModule (Core Service): vollständiges CRUD + öffentliche Dropdown-Endpoints
- Admin-UI /admin/master-data mit 5 Tabs, Inline-Edit, Farbwahl (Skill-Kategorien)

CRM Reporting (Kapitel 22.9):
- recharts ^2.12.0 installiert
- Deals: GET /deals/stats (Win/Loss-Rate, Umsatz, Trend, Verlustgründe)
- Aktivitäten: GET /activities/stats (nach Typ, Completion-Rate, offene Tasks)
- Reports-Seite /crm/reports: LineChart, PieChart, BarChart mit Zeitraum-Filter

Hilfesystem (Kapitel 16):
- @anthropic-ai/sdk installiert; ANTHROPIC_API_KEY optional in .env
- HelpModule (Core Service): POST /help/chat via Claude Haiku
- HelpTooltip-Komponente: Hover-Tooltip für Formularfelder
- HelpPanel: seitlicher Drawer mit Seitenkontext + KI-Chat
- -Button im Topbar (AppLayout), pageKey aus Route abgeleitet

Migration erforderlich: prisma migrate deploy (core-service)
Deployment: core rebuild, crm rebuild, frontend rebuild

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:39:30 +01:00
Thomas Reitz
69305a0b0b feat: Firmendaten um Geschäftsführer, Amtsgericht, Handelsregister erweitert
- Backend: CompanySettings Interface + GET/POST um managingDirector,
  localCourt, commercialRegister ergänzt (Redis-Storage)
- Frontend AdminCompanyPage: neue Sektion „Rechtliche Angaben" mit 3
  Feldern, Footer-Vorschau zeigt alle Angaben inkl. HR-Nummer
- Export-Service: PDF- und DOCX-Fußzeile enthält jetzt Geschäftsführer
  und Handelsregistereintrag (HRB + Amtsgericht kombiniert)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 09:28:06 +01:00
Thomas Reitz
bfaf718596 feat: Primärfarbe (Button-/Akzentfarbe) im Branding konfigurierbar
- Backend: primaryColor-Feld in GET/POST /settings/branding (Redis, Hex-Validierung)
- Frontend AppLayout: --color-primary/--color-primary-hover/--color-primary-light
  CSS-Variablen dynamisch aus Branding-Einstellungen setzen
- AdminCustomizePage: neuer Card-Block „Button-/Primärfarbe" mit 6 Farbpresets,
  Color-Picker, Hex-Input und Live-Vorschau (Solid + Outline Button)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 09:14:41 +01:00
Thomas Reitz
de4af77c5c feat: CRM Berechtigungsmodell — konfigurierbares Sichtbarkeitsmodell (OWN/TEAM/ALL)
Implementiert pro-Entity Sichtbarkeitssteuerung für Companies, Contacts, Deals
und Activities mit Rollen-basierter Zugriffskontrolle (ADMIN sieht alles,
TEAM_LEAD mindestens Team-Sicht, READONLY nur Lesezugriff).

- JWT Payload um tenantRole + department erweitert (Core + CRM)
- Team-Members-Endpoint im Core Service (GET /users/team-members)
- VisibilityModule mit Redis-Cache (CRM Service)
- ReadonlyGuard als globaler Guard (CRM Service)
- buildVisibilityFilter Utility für Prisma WHERE-Filterung
- Admin-Einstellungsseite /admin/crm-settings (Frontend)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:20:53 +01:00
Thomas Reitz
1bba4abac3 fix: Buttons in ExpertProfile nicht mehr vollbreit (align-self: flex-start)
Buttons in sectionHeader (flex-direction: column) streckten sich auf
volle Containerbreite. align-self: flex-start begrenzt die Breite auf
min-width + Textinhalt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 14:34:24 +01:00
Thomas Reitz
da4a036e8a fix: PDF-Grünton dunkler + Buttons gleichbreit
- darkenColor() Funktion: extrahierte Logo-Farbe um 30% abdunkeln
  (gilt für PDF und DOCX Export) → kräftigerer, druckfreundlicher Ton
- ExpertProfileTab: min-width: 130px für btnPrimary und btnSecondary
  → alle Aktions-Buttons haben einheitliche Mindestbreite

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 14:29:00 +01:00
Thomas Reitz
dabd36349e fix: PDF Leerseite, Button-Größen und Primärfarbe
- PDF-Export: doc.flushPages() vor doc.end() verhindert leere
  Schlussseite (PDFKit bufferPages-Bug nach Footer-Loop)
- ExpertProfileTab: height: 32px für btnPrimary/Secondary/Danger
  sowie chipInput- und headerForm-Inputs → einheitliche Höhe
- Primärfarbe #1a56db → #1040bb (dunkler, besser zum Logo passend)
- LoginPage CSS-Fallback-Gradient ebenfalls auf neue Primärfarbe

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 14:07:20 +01:00
Thomas Reitz
d08bedecf7 fix(frontend): Section-Header Titel über Eingabefelder + kompaktere Darstellung
- sectionHeader auf flex-column umgestellt: Titel immer oberhalb der
  Eingabe-Felder statt nebeneinander (Skills / Sprachen / Erfahrung)
- Section-Padding reduziert (1.5rem → 1rem/1.125rem) für kompaktere 3-Spalten
- chipInput und headerForm: flex: 1 + min-width: 0 für sauberes Stretching
- Max-Width-Beschränkungen entfernt damit Inputs die volle Spaltenbreite nutzen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:16:53 +01:00
Thomas Reitz
90a0388b22 feat(core+frontend): Anhänge im Export + neues 3-Spalten-Layout im Profil-Reiter
- PDF-Export: alle Anhänge als zusätzliche Seiten (Bilder als Vorschau, andere mit Hinweis)
- DOCX-Export: Bild-Anhänge als zusätzliche Sections (je eine Seite pro Bild)
- ExpertProfileTab: Skills/Sprachen/Erfahrungen nebeneinander (3 Spalten)
- ExpertProfileTab: Zertifizierungen und Profilanlagen nebeneinander (2 Spalten)
- threeColumnRow CSS-Klasse hinzugefügt (responsive auf 1 Spalte bei <900px)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:59:04 +01:00
Thomas Reitz
c8b25321e7 feat(core+frontend): Profilzugriff-Gruppen für Admin mit delegierten Berechtigungen
- 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>
2026-03-14 10:47:36 +01:00
Thomas Reitz
46ced98bf4 feat(core+frontend): Firmendaten im Admin + PDF-Fusszeile
Backend:
- GET/POST /settings/company (Redis-Key platform_company_settings)
  Felder: name, street, postalCode, city, phone, email, website
- ProfileExportService: RedisService injiziert, laedt Firmendaten vor PDF-Erzeugung
- PDF-Footer: Trennlinie + kompakte Zeile mit allen Firmendaten auf jeder Seite
  (bufferPages=true, switchToPage-Loop vor doc.end())

Frontend:
- AdminCompanyPage: Formular mit Vorschau der Fusszeile
- AdminLayout: neuer Tab 'Firmendaten'
- App.tsx: Route /admin/company

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 09:07:42 +01:00
Thomas Reitz
c333cbfa4b feat: Login-Screen-Branding im Global Admin (Hintergrund + Logo)
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>
2026-03-13 21:09:32 +01:00
Thomas Reitz
b872b7e708 feat(frontend): bullet-editor B/I/U-Formatierung + Aufgaben-Anzeige in Projektliste
BulletEditor (ProjectModal.tsx):
- blWrapFormat(): wrap/unwrap Selektion mit Marker-Paar (**/*/__),
  leere Selektion → leeres Marker-Paar mit Cursor dazwischen
- Buttons: B (Fett **), I (Kursiv *), U (Unterstrichen __)
- Shortcuts: Strg/Cmd + B/I/U in handleKeyDown

ProjectsSection.tsx:
- renderInline(): regex-basierter Inline-Markdown-Renderer ohne
  dangerouslySetInnerHTML — wandelt **bold**, *italic*, __underline__ um
- RichText-Komponente: rendert Aufgaben-Text mit Bullets, Nummernlisten,
  Einrueckung und Inline-Formatierung
- Projektliste zeigt Aufgaben unterhalb der Taetigkeitszeile an
  (nur wenn vorhanden, mit border-top als optischem Trenner)
- Layout-Anpassung: entryItemExpanded + entryItemRow fuer vertikales Layout

CSS: bulletBtnBold/Italic/Underline, entryItemExpanded/Row,
entryTasks, richText/Line/Bullet/Num/Blank

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 20:47:07 +01:00
Thomas Reitz
4f141b94e5 feat(frontend): bullet-editor – nummerierte Liste + Tab/Shift+Tab Einrueckung
- 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>
2026-03-13 20:40:50 +01:00
Thomas Reitz
a4013d4356 feat(frontend): bullet-editor fuer Projektaufgaben + Popup-Backdrop deaktiviert
- Modal.tsx + Drawer.tsx: onClick={onClose} vom Backdrop entfernt — alle
  Popups schliessen sich jetzt nur noch ueber Speichern/Abbrechen-Buttons
- ProjectModal.tsx: Textarea "Aufgaben" durch BulletEditor-Komponente
  ersetzt (Toolbar-Button toggled Aufzaehlungspunkt, Enter setzt Bullet
  auf naechster Zeile fort, leere Bullet-Zeile + Enter beendet die Liste)
- ExpertProfileTab.module.css: CSS fuer bulletEditor, bulletToolbar,
  bulletBtn, bulletEditorTextarea hinzugefuegt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 20:32:00 +01:00
Thomas Reitz
6c51eb5e83 feat(crm): Kontakt-Detailseite – Breite, Outlook Daten, Outlook-Push
- 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>
2026-03-13 19:57:07 +01:00
Thomas Reitz
b197660ac8 feat(crm): Dediziertes Projektanfrage-Formular + Button in Vorgänge-Liste
- 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>
2026-03-13 18:33:33 +01:00
Thomas Reitz
4c739945f0 feat(crm): Projektanfrage-Vorgangstyp – isProjectType + ProjectRequestDetails
Backend:
- DealType.isProjectType Boolean-Flag (Admin-konfigurierbar in CRM Settings)
- Neue 1:1-Tabelle project_request_details (ON DELETE CASCADE):
  notes, workload, startDate, duration, onsitePercent, rateRemote, rateOnsite
- Migration 20260313_project_request
- ProjectRequestDto mit Validierung (0-100% fuer Auslastung/Vorort-Anteil)
- Deals-Service: nested create + upsert fuer projectRequest

Frontend:
- DealFormModal: Vorgangsart-Dropdown an Anfang verschoben;
  Projektanfrage-Sektion erscheint conditional bei isProjectType=true
  (Beschreibung, Auslastung/Start, Laufzeit/Vorort-Anteil, Stundensaetze)
- CrmSettingsPage: DealTypesConfig mit Projektanfrage-Checkbox + Tabellenspalte
- types.ts: ProjectRequestDetails, CreateProjectRequestPayload, Deal.projectRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:17:40 +01:00
Thomas Reitz
6bfce4af97 feat(crm): Vorgangsart (DealType) konfigurierbares Dropdown + Pipeline-Leerstate
- crm-service: Neues DealType-Model (deal_types Tabelle) mit name, color, sortOrder
  und Relation zu Deal.dealTypeId; Migration 20260313_deal_type
- crm-service: Vollstaendiger CRUD REST-Endpoint /crm/deal-types (TenantGuard)
- crm-service: CreateDealDto um optionales dealTypeId erweitert
- frontend: DealType Interface, API (dealTypesApi), Hooks (useDealTypes/...)
- frontend: CrmSettingsPage > Weitere Einstellungen > DealTypesConfig mit Farbpicker
- frontend: DealFormModal: Vorgangsart-Dropdown + Hinweis bei leerer Pipeline-Liste

Deployment: prisma migrate deploy && prisma generate im crm-service ausfuehren.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:15:02 +01:00
Thomas Reitz
f72ac6cb90 fix(frontend): Kalender-Eintragsfarben vereinheitlichen (einheitliches Primary-Blau statt Hash-Farben)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:56:14 +01:00
Thomas Reitz
8b4a71edb2 fix(frontend): Topbar auf Dashboard ausblenden – Profil nur in Tab-Leiste
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:53:26 +01:00
Thomas Reitz
2af54246c8 feat(frontend): E-Mail-Popup, Aktivitaeten-Zeitstrahl + Profil in Tab-Leiste
- EmailsTab: Outlook-aehnlicher Detail-Popup beim Klick auf E-Mail (Von/An/Datum/Anhang-Meta, Body-Vorschau, In Kontakt speichern als EMAIL-Aktivitaet)
- Neues EmailsTab.module.css fuer kompakte Liste und Modal
- ContactDetailPage: Aktivitaeten-Filterleiste (Typ + Zeitraum Von/Bis)
- ContactDetailPage: Zeitstrahl mit vertikaler Verbindungslinie, farbigen Typ-Badges (Note/Call/Email/Meeting/Task/FollowUp)
- DashboardPage: Profil-Bereich (Theme-Schalter, Avatar, Name, Logout) in Tab-Leiste integriert und leicht farblich abgesetzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:51:10 +01:00
Thomas Reitz
17a81c97ef feat(frontend): Kalender-Modal – Teilnehmer-Chips + Auto-Vorauswahl
- 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>
2026-03-13 14:34:56 +01:00
Thomas Reitz
2d56ab6b3d feat(frontend): Kalender-Event-Modal + Zeitzonen-Fix
- 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>
2026-03-13 14:30:56 +01:00
Thomas Reitz
3d75a7f9de feat(frontend+core): Dashboard Kontakte-Tab (O365) + Admin Logo-/Sidebar-Breite
Dashboard Kontakte-Tab:
- DashboardContactsTab.tsx + CSS: O365-Kontakte als Visitenkarten oder Liste
- Kachelansicht (auto-fill Grid) + Listenansicht (6-spaltig) umschaltbar
- Suchfeld (Name, E-Mail, Firma) mit Live-Filter
- Klick öffnet Detail-Modal mit allen Kontaktdaten (E-Mail/Telefon als Links)
- CRM-Import-Button: mappt M365Contact → CreateContactPayload, importiert direkt
- Nicht verbunden / Laden / Fehler States
- DashboardPage: ComingSoonTab entfernt, DashboardContactsTab eingebunden

Admin Branding — Logo-Breite + Sidebar-Breite:
- settings.controller.ts: logoWidth + sidebarWidth in GET/POST Branding
- AdminCustomizePage: Slider Logo-Breite (40–240px) + Sidebar-Breite (200–360px)
  mit Live-Vorschau (skalierte Mini-Sidebar)
- AppLayout: Logo-maxWidth aus branding.logoWidth; Sidebar width + main marginLeft
  dynamisch aus branding.sidebarWidth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 14:03:45 +01:00
Thomas Reitz
d9c6240a3e fix(frontend): Anwendungen-Gruppe bei Start eingeklappt, Dashboard-Klick resettet auf Home-Tab
- 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>
2026-03-13 13:51:04 +01:00
Thomas Reitz
5f50ba1315 feat(frontend): Profil-Topbar — Avatar, Theme-Schalter und Logout nach oben rechts
Sidebar-Bottom-Bereich (Profil, Abmelden, Theme-Toggle) vollstaendig entfernt
und durch eine sticky Topbar im Hauptinhaltsbereich ersetzt:
- Theme-Schalter: kompakte Icon-Button-Gruppe (☀ hell / ☾ dunkel / ⚙ system)
- Benutzer-Avatar + Name klickbar → /profile
- Logout-Icon-Button (SVG, Tooltip "Abmelden")
Sidebar zeigt nur noch Logo, Navigation und Admin-Link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:46:33 +01:00
Thomas Reitz
0f63c28110 fix: WeatherWidget Ort mittig darunter; +30 Sprüche des Tages (65 gesamt) 2026-03-13 13:38:45 +01:00
Thomas Reitz
b34370345f fix: Analoguhr größer, digitale Zeit entfernt, Datum ausgeschrieben (long) 2026-03-13 13:35:17 +01:00
Thomas Reitz
baa0cafd40 fix: WeatherWidget auf volle Spaltenbreite (gleich wie Forecast-Karte) 2026-03-13 13:34:16 +01:00
Thomas Reitz
ceea82a2ac feat: Dashboard Home-Tab mit Analoguhr, 3-Tage-Prognose, Spruch des Tages + kompakte Widgets
- 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>
2026-03-13 13:30:45 +01:00
Thomas Reitz
2348602fb0 feat: Erweiterte Profilfelder (analog O365) + Profilbild-Sync aus Microsoft 365
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>
2026-03-13 13:08:56 +01:00
Thomas Reitz
138742d385 feat: O365-Profil automatisch beim Login synchronisieren
- 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>
2026-03-13 12:50:25 +01:00
Thomas Reitz
347a6ca418 feat: Profil mit Microsoft 365 Daten anreichern
- GraphService: getM365Profile() lädt givenName, surname, mobilePhone,
  businessPhones, city, streetAddress, postalCode, jobTitle via /me
- Office365Controller: GET /crm/office365/profile Endpunkt
- Frontend types: M365UserProfile Interface
- Frontend api: office365Api.getM365Profile()
- ProfilePage: O365 uebernehmen Button (nur wenn M365 verbunden)
  fuellt leere Felder: Vorname, Nachname, Telefon, Mobil, Ort, Strasse, PLZ
  Bestehende Werte werden NICHT ueberschrieben; Feedback zeigt welche
  Felder uebernommen wurden; User muss Speichern klicken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:38:43 +01:00
Thomas Reitz
403c581e57 fix: Agenda-Breite im Home-Sidebar füllt volle 300px
- DayAgenda: fullWidth-Prop ergänzt (setzt .agendaFull → width: 100%)
- Im Kalender-Tab bleibt die Agenda bei 260px (flex-shrink: 0)
- HomeSidebar übergibt fullWidth, sodass die Agenda den Sidebar
  lückenlos ausfüllt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:32:38 +01:00
Thomas Reitz
653464c89b feat: Kompakter Messe-Ticker im Home-Sidebar mit Detail-Popup
- EventCountdownTiles aus homeMain entfernt
- Neuer CompactMesseTicker im Sidebar: kleine klickbare Zeilen
  mit farbiger linker Linie (blau=bevorstehend, grün=läuft)
- Klick öffnet MesseDetailModal: Name, Status-Chip, Countdown,
  Fortschrittsbalken (bei laufenden Messen), Datum, Ort/Stand,
  Beschreibung, Website-Link
- HomeDayAgendaWidget → HomeSidebar: zeigt Ticker + Tages-Agenda
  in einer Spalte; Sidebar auf 300px verbreitert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:27:07 +01:00
Thomas Reitz
3b15c8ab9b feat: Aufgaben-Tab im Dashboard (O365 + CRM bidirektional)
- GraphService: graphPost/graphPatch Helfer; getAllTasksFlat (inkl.
  Body für CRM-Sync-Marker), createM365Task, completeM365Task
- Office365Controller: GET tasks/flat, POST tasks, PATCH tasks/:listId/:taskId/complete
- ActivitiesService/Controller: GET /crm/activities/open-tasks
  (TASK + FOLLOWUP, nicht erledigt)
- Frontend types: M365TaskFlat + CrmOpenTask Interfaces
- Frontend api/hooks: getTasksFlat, createTask, completeTask,
  getOpenTasks; neue Hooks useOffice365TasksFlat, useCrmOpenTasks,
  usePushTaskToO365, useCompleteO365Task, useCompleteCrmTask
- DashboardTasksTab: vereinheitlichte Aufgabenliste mit Farbcodierung
  (O365 blau, CRM orange, Synced grün), Push-Button, Erledigen-Button
- Bidirektionaler Sync via [INSIGHT_CRM:{activityId}] Marker im O365
  Task Body; Erledigen eines Synced-Tasks aktualisiert beide Systeme
- DashboardPage: Tasks-Tab auf DashboardTasksTab umgestellt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:01:04 +01:00
Thomas Reitz
fbf0b33a1f feat(dashboard): E-Mail Lesefenster + Kalender Umbau
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>
2026-03-13 11:36:30 +01:00
Thomas Reitz
f2ed8d0a93 fix(dashboard): Kalender-Toggle immer sichtbar, E-Mail Breite auf 70% angepasst 2026-03-13 11:20:01 +01:00
Thomas Reitz
53666122d0 fix(dashboard): E-Mail Bereich auf 50% Breite begrenzen, Textumbruch aktivieren 2026-03-13 11:16:28 +01:00
Thomas Reitz
76e8dff577 feat(dashboard): Kalender-Tab mit Monats-/Wochenansicht und Tages-Agenda
- 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>
2026-03-13 10:54:32 +01:00
Thomas Reitz
b6b182a349 fix(dashboard): E-Mail Tab lädt sofort mit well-known Inbox-ID als Fallback
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>
2026-03-13 10:32:04 +01:00
Thomas Reitz
01dc8bb41c feat(dashboard): E-Mail Tab mit Outlook-Postfach, Ordner-Navigation und Aktivitäten-Speicherung
- GraphService: getMailFolders() + getMailsByFolder(folderId, days) Methoden
- Office365Controller: GET /crm/office365/folders und /folders/:id/messages?days=X Endpoints
- ContactsController/Service: GET /crm/contacts/lookup?email=xxx für CRM-Kontakt-Abgleich
- Frontend types: M365MailFolder + CrmContactLookup Interfaces
- Frontend API: office365Api.getMailFolders/getMailsInFolder + contactsApi.lookupByEmail
- Frontend Hooks: useOffice365MailFolders, useOffice365MailsInFolder, useContactByEmail
- DashboardEmailTab: Ordner-Sidebar, Zeitfilter (1/7/14 Tage/alle), E-Mail-Liste
  mit Outlook-Link, CRM-Badge bei bekannten Absendern, Aktivitäten-Modal mit Kommentar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 10:16:05 +01:00
Thomas Reitz
f6dd072f23 feat(dashboard): 5 Tabs — Home, E-Mail, Kalender, Aufgaben, Kontakte
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>
2026-03-13 09:49:12 +01:00
Thomas Reitz
ad9c48bcb6 feat(crm): Office365-Übersichtsseite + Graph API Bugfixes
- Neuer Office365-Menüpunkt mit 4 Tabs (E-Mails, Kalender, Kontakte, Aufgaben)
- CRM-Service: Office365Controller mit globalen Graph-Endpoints
- Fix: $search + $orderby Kombination in Graph API nicht erlaubt
- M365Contact Interface + attendees/hasAttachments Typen ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 06:59:52 +01:00
Thomas Reitz
1ecd7dad82 fix(ms365): OAuth-Connect via API-Call statt direktem Browser-Link
Problem: <a href="/api/v1/auth/integrations/microsoft-365"> sendet keinen
JWT-Authorization-Header (JWT liegt im Memory, nicht als Cookie).

Lösung:
- Backend: initM365Integration gibt JSON {url} zurück statt server-redirect
- Frontend: integrationsApi.connectM365() ruft Endpoint via Axios ab, dann
  window.location.href zur OAuth-URL
- ProfilePage + EmailsTab + CalendarTab + TasksTab: <a href> → <button onClick>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:57:20 +01:00
Thomas Reitz
30c4b208d9 feat(crm): Phase 3 MS365 Frontend — Integrationen + Kontakt-Tabs
- 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>
2026-03-12 22:48:43 +01:00
Thomas Reitz
237e0772e6 feat(crm): Kanban-Board mit Drag & Drop (Phase 3.0)
- Neue KanbanPage (/crm/kanban) mit @dnd-kit/core + @dnd-kit/sortable
- Columns pro Pipeline-Stage mit Deal-Karten
- Cross-Column Drag & Drop — stageId per PATCH /crm/deals/:id aktualisiert
- Optimistisches Update: sofortiges visuelles Feedback, Rollback bei Fehler
- Toggle-Button für abgeschlossene Vorgänge (WON/LOST)
- Pipeline-Selektor, Gesamt-Wert pro Spalte (offene Deals)
- NavLink "Kanban" in AppLayout.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:29:01 +01:00
Thomas Reitz
bfe672ec96 feat(crm): Vertragsdokumente — Datei-Upload Frontend + Backend-Briefing
Frontend (gegen API-Contract, bereit fuer Backend-Integration):
- ContractFile Interface in types.ts
- contractFilesApi (upload, list, download-as-blob, delete) in api.ts
- useContractFiles, useUploadContractFile, useDeleteContractFile in hooks.ts
- ContractsCard: Dokumente-Sektion im Edit-Modal
  - Dateiliste mit Icon (PDF/Word/Excel), Name, Groesse, Download, Loeschen
  - Upload-Button (PDF/DOC/DOCX/XLSX/XLS, max 25 MB)
  - Client-seitige Groessenvalidierung
  - Blob-Download via Axios (Auth-Header werden mitgesendet)

Backend-Briefing in INSIGHT-CRM.md:
- contract_files Tabelle + Prisma-Modell
- Datei-Speicherung (/app/uploads/contracts/{tenantId}/{contractId}/{uuid}-{name})
- 4 Endpoints (POST/GET/GET-download/DELETE)
- Sicherheitshinweise (Path Traversal, Tenant-Isolation, DSGVO)
- Docker-Volume Konfiguration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 21:46:32 +01:00