- Jobtitel aus Profil statt experiences[0].area unter dem Namen
- Platform-Logo aus Redis über Avatar einfügen
- Dominante Akzentfarbe dynamisch aus Logo extrahieren
- Firmen-Fußzeile aus Redis-Settings als DOCX-Footer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getExportData(): jobTitle zu den selektierten User-Feldern hinzugefuegt
- ExportData-Interface: jobTitle: string | null ergaenzt
- PDF: Unter dem Namen wird jetzt data.jobTitle angezeigt statt
profile.experiences[0].area
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- extractDominantColor(): 20x20 Resize via sharp, Alpha gegen Weiss flatten,
alle gesaettigten Pixel (nicht weiss/schwarz/grau, range > 35) mitteln
- Ergebnis wird als accentColor fuer Timeline-Linien, Ueberschriften,
Skill-Chips usw. verwendet
- Fallback auf #009688 wenn kein Logo hinterlegt oder keine Farbe extrahierbar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Footer: doc.page.margins.bottom temporaer auf 0 gesetzt beim Footer-Zeichnen
verhindert PDFKit Auto-Pagination (footerTextY > maxY wuerde sonst
eine leere zweite Seite erzeugen)
- Logo: Platform-Branding-Logo (aus Redis platform_branding_logo) wird oben
in der linken Spalte ueber dem Profilfoto gerendert (fit 180x50px)
- Firmendaten + Branding parallel via Promise.all aus Redis geladen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ZERTIFIZIERUNGEN von rechter Spalte in linke Spalte verschoben
(nach ERFAHRUNG, vor FÄHIGKEITEN)
- Textbreite auf leftColWidth (certContentWidth = leftColWidth - 14) angepasst
- Timeline-Linie dynamisch (certEntryStartY gespeichert, Linie nach Content)
- Alte rechte-Spalte-Sektion entfernt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pdfSectionTitle: Linie zieht sich jetzt ueber die volle Spaltenbreite (width-Parameter)
statt nur bis zum Textende
- Titel 'BERUFSERFAHRUNG' umbenannt in 'BERUFSERFAHRUNG / PROJEKTE'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BERUFSERFAHRUNG: Timeline-Linie wird jetzt NACH dem Content gezeichnet
(entryStartY gespeichert, Linie von entryStartY+8 bis yRight-4)
Damit stimmt die Laenge exakt mit der tatsaechlichen Eintraghoehe ueberein
- Seitenumbruch-Flag (pageBreakOccurred): Linie wird nicht gezeichnet wenn
der Content ueber eine Seite hinausgeht
- FAEHIGKEITEN: aus dem full-width Bereich am Seitenende entfernt und in die
linke Spalte nach ERFAHRUNG verschoben (kleinere Chips: 7pt, 16px, 5px Pad)
- Alte full-width FAEHIGKEITEN-Sektion entfernt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tasks: Bullet-Praefix nur fuer Zeilen mit echtem Aufzaehlungszeichen (kein spurious Bullet bei Plaintext)
- Zertifizierungen: Schriftgroesse reduziert (Titel 10->9pt, Aussteller 9->8pt) und Timeline-Linie gekuerzt
- Anhaenge: Bild-Anhaenge werden als zusaetzliche Seiten ans PDF angehaengt
- ExportData-Interface + getExportData() um attachments[] erweitert
- Gleiche Bullet-Fix-Logik im DOCX-Export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 Korrekturen am Experten-Profil PDF-Export:
1. Dateiname: Vorname_Nachname_CV.pdf/.docx (RFC 5987 Umlaut-sicher)
2. Fettschrift: Zeitraum, Firmenname und Branche in Berufserfahrung
3. Zeichen-Darstellung: Markdown-Marker (**bold**/*italic*/__u__)
werden aus Tasks-Text entfernt, doppelte Bullet-Praefix-Normalisierung
4. Abstaende: Sprachen 14->11px, Erfahrung 14->11px, Gap 8->4px
5. Zertifizierungen in rechte Spalte verschoben (nach Berufserfahrung)
statt als Vollbreite-Sektion am Ende
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>
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>