Commit graph

213 commits

Author SHA1 Message Date
Thomas Reitz
6368596973 docs: Experten Profile wieder in Scope — eigenständiger Menüpunkt, RBAC expertprofile, vollständiges Schema 2026-03-15 19:24:19 +01:00
Thomas Reitz
37899d0399 docs: Briefing erweitert — Experten-Profil explizit ausgeschlossen, O365-Sync und Integrationen ergänzt 2026-03-15 19:20:01 +01:00
Thomas Reitz
17a26e5e69 docs: Platform Developer Briefing Phase 2 — vollständige Implementierungsanleitung 2026-03-15 19:12:33 +01:00
Thomas Reitz
8e235c584e fix: PostgreSQL data migration, Redis auth, Vault-Loading in Playbooks
- PostgreSQL: initdb durch rsync-Ansatz ersetzt (Ubuntu/Debian kompatibel)
  Data-Dir wird via rsync vom Default-Cluster nach /data/postgresql migriert
- PostgreSQL: de_DE.UTF-8 Locale-Generierung als ersten Task hinzugefügt
- Redis: redis-cli ping mit Passwort-Auth (no_log: true)
- Playbooks: vars_files: ../vault.yml in dbs01/aps01/web01 ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 16:34:45 +01:00
Thomas Reitz
d212a7623f fix(infra): Deutsche Locale in common-Role installieren
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:40:52 +01:00
Thomas Reitz
4955c03570 fix(infra): Handler aus tasks/ in separate handlers/main.yml verschoben
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:36:48 +01:00
Thomas Reitz
7339ae000b feat(infra): vault.yml mit generierten Passwörtern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:35:21 +01:00
Thomas Reitz
39e5367916 chore(infra): Zabbix vorerst deaktiviert
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:30:43 +01:00
Thomas Reitz
36196457ea feat(infra): vollständige Ansible-Struktur Phase 1
- Alle Ansible-Rollen erstellt: common, disk_setup, docker, postgresql,
  pgbouncer, redis, nginx, zabbix_agent
- ansible.cfg mit Pipeline-Optimierung
- hosts.yml mit echten IPs (DBS01=.20, APS01=.21, WEB01=.22)
- group_vars für alle Server (dbs, aps, web)
- Zabbix-Server auf sentinel.xinion.de gesetzt
- vault.yml.example als Vorlage für Secrets
- site.yml nutzt import_playbook (DBS01→APS01→WEB01)
- BRIEFING.md für alle 4 Repos angelegt (Platform, Apps, Infra, Shared)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:23:29 +01:00
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
c96ccb5fcc chore: Prisma-Migration 20260315_master_data
Ergänzt fehlende Migration für Stammdaten-Tabellen (departments,
locations, cost_centers, job_titles, skill_categories) — Tabellen
wurden bereits via SQL-Push auf dem Server erstellt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:45:23 +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
0f5d01df2a docs: Stand.md aktualisiert (Button-Primärfarbe + Commit-Count)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 09:24:11 +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
0a5a37d169 docs: Stand.md aktualisiert — vollständiger Feature-Überblick Stand 2026-03-15
Alle implementierten Features dokumentiert: Auth, M365 Integration, Dashboard,
CRM Phase 1-2.5 inkl. Kanban + Berechtigungsmodell, Expertenprofil, Profilzugriff,
Admin-Bereich, Login-Branding, API-Endpunkte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 09:08:36 +01:00
Thomas Reitz
3adca2de65 fix: VisibilityLevel Enum-Referenzen durch String-Literale ersetzen
Prisma Enums existieren als Runtime-Objekt nur nach prisma generate,
aber TypeScript im watch-mode erwartet importierbare Werte. Verwende
stattdessen direkte String-Vergleiche ('ALL', 'TEAM', 'OWN').

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:28:56 +01:00
Thomas Reitz
b484f4380f fix: VisibilityLevel als lokalen String-Type statt Prisma-Enum verwenden
Prisma-Enums sind zur Laufzeit nicht als Objekt verfuegbar wenn ts-node
den Import vor der Client-Generierung auflöst. Ersetzt durch eigenen
Type-Export aus build-visibility-filter.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:24:18 +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
c987ce87c0 fix: Timeline-Linie auf Fortsetzungsseite bei Seitenüberlauf zeichnen
Wenn ein Projekteintrag auf eine neue Seite überläuft (Tasks zu lang),
wird jetzt trotzdem eine pendingLine vom Seitenanfang der neuen Seite
bis zum Gap vor dem nächsten Eintrag vorgemerkt. Dadurch entsteht eine
durchgehende Timeline-Linie auf der Fortsetzungsseite, die den
überlaufenden Inhalt visuell mit dem nächsten Eintrag verbindet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 20:16:42 +01:00
Thomas Reitz
a7cf59ae20 fix: Timeline-Linie am Anfang der nächsten Iteration zeichnen (Deferred-Draw)
Vorherige Ansätze berechneten die Ziel-Header-Höhe am Ende des Eintrags
neu (fehleranfällig durch doppelte Font-State-Operationen). Neuer Ansatz:
Linie für Entry i wird am ANFANG von Entry i+1 gezeichnet, BEVOR der
Seitenumbruch-Check läuft — mit demselben headerH der bereits berechnet
wurde. Eine einzige Bedingung entscheidet konsistent ob Linie gezeichnet
wird UND ob ein Seitenumbruch folgt, ohne Redundanz oder State-Probleme.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 15:39:10 +01:00
Thomas Reitz
fb57f5a4dc fix: exakte Header-Höhe für Timeline-Linie prüfen statt fester 40px-Schwelle
Berechnet die tatsächliche Mindesthöhe des nächsten Projekteintrags
(Datum + Rolle + Firma) identisch zur Seitenumbruch-Logik. Verhindert
hängende Linien wenn der nächste Header > 40px hoch ist und eine neue
Seite benötigt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 15:02:24 +01:00
Thomas Reitz
bec770c6ba fix: verhindere hängende Timeline-Linie bei Seitenumbruch im PDF-Export
Wenn ein Projekteintrag nahe am Seitenende endet und der nächste
Eintrag auf einer neuen Seite beginnt, wird keine Verbindungslinie
mehr gezeichnet. Vorher entstand ein hängender Strich ohne Ziel-Punkt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 14:54:07 +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
1608f4e936 fix: PDF export — tab chars garbled, unconditional bullets, encrypted PDF attachments
Root causes identified via DB hex-dump and server logs:

1. Tab character in budget lines: DB stores e.g. `**Budget Verantwortung:**\t750.000 EUR`
   (byte 0x09 between `:**` and the amount). PDFKit can't render \t in WinAnsiEncoding,
   producing garbage output like `"sSãUU`. Fix: `.replace(/\t/g, ' ')` in cleaned text.

2. Unconditional bullet: `\u2022 ${sanitize(hasBullet ? cleaned : cleaned)}` always
   prepended `•` — the ternary was a no-op. Fix: only add `•` when hasBullet is true;
   `**...**` header lines now render as Helvetica-Bold without a bullet.

3. ITIL4 Foundation Cert.pdf is owner-password-encrypted. pdf-lib threw
   "Input document is encrypted" → cert was silently skipped.
   Fix: `PdfLib.load(attBuffer, { ignoreEncryption: true })`.

Applies to both PDF and DOCX export paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 13:56:57 +01:00
Thomas Reitz
98e7f48ce2 fix: make GET /settings/branding public to break login loading loop
LoginPage calls /settings/branding to load branding config (logo, colors).
Without @Public(), the JWT guard returns 401, which triggered the axios
response interceptor to attempt a silent refresh, fail, and call
window.location.href = '/login' — creating an infinite reload loop on
the login page itself.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:44:50 +01:00
Thomas Reitz
ad3a580d0b fix: resolve login loading loop caused by Vite HMR reconnects + rate limiting
- Add @SkipThrottle() to POST /auth/refresh so repeated silent-refresh calls
  from page reloads no longer exhaust the rate limit (HTTP 429)
- Configure Vite HMR explicitly with host/clientPort/protocol=wss so the
  WebSocket connects correctly through Traefik instead of reconnecting every ~1s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:35:18 +01:00
Thomas Reitz
79ad5e4be3 fix(core): PDF-Anhänge korrekt einbetten via pdf-lib + Zeichenbereinigung verbessert
- pdf-lib installiert und importiert
- PDF-Anhänge werden nicht mehr als Platzhalter-Seite angezeigt, sondern
  alle Seiten des Anhang-PDFs direkt in das Export-PDF eingebettet (pdf-lib merge)
- Bild-Anhänge bleiben weiterhin per PDFKit eingebettet
- sanitizePdfText() erweitert: vollständige Windows-1252 U+0080-U+009F Mapping-Tabelle
  für mis-enkodierte Zeichen (€, Anführungszeichen, Gedankenstriche, TM usw.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:23:03 +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
9d7dcaaaea fix(core): PDF-Export Seitenumbruch und Sonderzeichen repariert
- pageBottom von 800 auf 740 reduziert: verhindert PDFKit-Auto-Seitenumbrüche
  mitten in Projekt-Einträgen (bisher: Datum/Rolle/Firma je auf eigener Seite)
- Vor jedem Projekt-Eintrag Header-Höhe (Datum+Rolle+Firma) vorberechnen und
  Seitenumbruch proaktiv auslösen, bevor der Header gezeichnet wird
- sanitizePdfText() Hilfsmethode: ersetzt €→EUR sowie Zeichen außerhalb Latin-1
  die Helvetica (WinAnsiEncoding) nicht rendern kann (bisher: Zeichensalat)
- sanitizePdfText auf Projekt-Texte und Zertifizierungs-Texte angewendet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 11:08:56 +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
f7736a6fca fix(core): Word-Export — Abstand zwischen linker und rechter Spalte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:16:45 +01:00
Thomas Reitz
1a222edb46 fix(core): Word-Export — Logo-Seitenverhältnis korrekt (tatsächliche Pixel-Dimensionen)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:12:09 +01:00
Thomas Reitz
8fc894c74c fix(core): Word-Export — jobTitle, Logo, Akzentfarbe und Firmenfußzeile
- 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>
2026-03-14 09:43:29 +01:00
Thomas Reitz
196515daa8 fix(core): PDF-Export — Titel aus jobTitle-Feld statt erster Erfahrung
- 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>
2026-03-14 09:35:45 +01:00
Thomas Reitz
382beab9c3 feat(core): PDF-Export — Akzentfarbe dynamisch aus Branding-Logo extrahiert
- 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>
2026-03-14 09:27:41 +01:00
Thomas Reitz
f5d83dc1c3 fix(core): PDF-Export — Footer-Leerseite behoben und Logo ueber Profilfoto
- 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>
2026-03-14 09:20:25 +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
1d4894b637 fix(core): PDF-Export — Zertifizierungen in linke Spalte verschoben
- 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>
2026-03-14 08:49:25 +01:00
Thomas Reitz
f8aed00645 fix(core): PDF-Export — Abschnittslinien bis zum Spaltenende und Titel angepasst
- 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>
2026-03-14 08:46:19 +01:00
Thomas Reitz
ed24c061c4 fix(core): PDF-Export — Timeline-Linien korrekt und Faehigkeiten in linke Spalte
- 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>
2026-03-13 22:08:18 +01:00
Thomas Reitz
41d944312c fix(core): PDF-Export Icons kleiner (12->9px) und besser positioniert
- iconSize 12 -> 9px
- iconTextOffset 20 -> 14px (kompakter)
- Alle Icons gleichmaessig bei y+1 positioniert (sauberes vertikales Alignment mit 8pt Text)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 21:39:33 +01:00
Thomas Reitz
927de0a809 fix(core): PDF-Export Zeitraum groesser und fetter (8pt -> 10pt)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 21:35:38 +01:00
Thomas Reitz
3d486e0541 fix(core): PDF-Export — Bullets, Zertifizierungen, Bild-Anhaenge
- 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>
2026-03-13 21:33:01 +01:00
Thomas Reitz
a37942b37d fix: PDF-Export — Dateiname, Fettschrift, Zeichen, Abstaende, Zertifizierungen rechts
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>
2026-03-13 21:20:43 +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