Commit graph

42 commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
Thomas Reitz
0e6565e210 feat(crm): Vertraege-Modul Frontend — ContractsCard vollstaendig implementiert
- ContractsCard.tsx: CRUD-Card ersetzt Platzhalter (Liste, Create, Edit, Delete)
- types.ts: CreateContractPayload, UpdateContractPayload, ContractsQueryParams ergaenzt
- api.ts: contractsApi mit 4 Methoden (nested /crm/companies/:id/contracts)
- hooks.ts: crmKeys.contracts + useContracts, useCreate/Update/DeleteContract
- CompanyDetailPage.tsx: contractCount-Prop entfernt (Card laedt via API)
- docs/INSIGHT-CRM.md + Summarize.md aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 21:32:51 +01:00
Thomas Reitz
27507f1372 feat(crm): move Import-Wizard into CRM-Settings tab
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>
2026-03-12 21:00:19 +01:00
Thomas Reitz
dda672d41e fix(crm): align Phase 2.2–2.4 frontend to actual backend contracts
- Import: convert mapping Record→Array, entityType PERSON→contact
- Enrichment: handle suggestions as Record<field,{current,suggested,source}>
- Types: add availableTargetFields to ImportPreviewResponse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 20:55:47 +01:00
Thomas Reitz
219863d538 feat(crm): remove Lexware section from ContactDetailPage
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>
2026-03-12 20:49:36 +01:00
Thomas Reitz
b746fc987d fix(crm): Kontaktdaten-Card zeigt alle Allgemein-Felder konstant
E-Mail, Telefon, Mobil, LinkedIn, Unternehmen, Position, Abteilung
werden immer angezeigt — leere Felder mit '—' Platzhalter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 20:45:01 +01:00
Thomas Reitz
bff4419c27 fix(crm): ContactDetailPage Layout-Anpassungen
- 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>
2026-03-12 20:39:18 +01:00
Thomas Reitz
ec9f3ea364 feat(crm): UI/UX Redesign – Kontakt Eingabemaske & Detailansicht
- Neue Drawer-Komponente (right-side slide-in, sticky footer, Portal)
- ContactFormModal: Modal → Drawer, Tab 1 Allgemein / Tab 2 Details & Adresse
- Checkbox "Adresse vom Unternehmen übernehmen" (blendet Adressfelder aus)
- Typ-Feld entfernt (Kontakte immer PERSON)
- ContactDetailPage: Subtitle "Position @ Unternehmen" im Header
- Kontaktdaten-Card: 2 Sub-Spalten, kein Firma-Duplikat, Mobil tel:-Link, LinkedIn-Icon
- Neue Card "Notizen & Tags" (Badges + Notizen-Text + Custom Fields)
- Aktivitäten-Spalte auf minmax(380px, 40%) verbreitert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 20:20:30 +01:00
Thomas Reitz
fdab2d5bcb feat(crm): Phase 2.2-2.4 frontend — Forecast, Import, Enrichment
Phase 2.3 Forecast:
- probability field on PipelineStage (types, edit UI, add-stage form)
- ForecastPage with pipeline filter, period selector, summary cards, table
- forecastApi + useForecast hook
- /crm/forecast route + "Prognose" nav link

Phase 2.2 CSV/Excel Import:
- 3-step wizard ImportPage (Upload → Mapping → Result)
- Entity type selection, auto-mapping, duplicate strategy, preview table
- importApi (preview + execute) + useImportPreview/useImportExecute hooks
- /crm/import route + "Import" nav link

Phase 2.4 Data Enrichment:
- "Anreichern" button on CompanyDetailPage with suggestions modal
- Per-field accept with PATCH update
- enrichmentApi (enrich, getConfig, setConfig) + hooks
- NorthDataConfig in CRM Settings "Integrationen" tab
- API-Key management UI

All changes pass TypeScript strict mode (npx tsc --noEmit).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:37:54 +01:00
Thomas Reitz
aaedf68085 feat(crm): Phase 2.1 Custom Fields — backend + frontend integration
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>
2026-03-12 18:22:57 +01:00
Thomas Reitz
48df3c3144 feat(crm): Phase 1 backend schema expansion + frontend integration
Backend (CRM-Expert Phase 1):
- New enums: ContactSource, EntityStatus, CompanySize, OwnerRole,
  LostReason, EmailType, PhoneType
- Contact: add linkedinUrl, birthday, source, department, status
- Company: add vatId, taxId, tradeRegisterNumber, registerCourt,
  companySize, deliveryAddress, dataEnrichedAt/Source, status
- Deal: add lostReason + lostReasonText (required when status=LOST)
- Multi-value emails/phones tables (contact_emails, contact_phones)
- Owner m:n model (contact_owners, company_owners, deal_owners)
- Redis Pub/Sub CRM events (crm.contact.created, crm.deal.won, etc.)
- Activity due_soon scheduler (cron every 15 min)
- SQL migration with data migration for existing records

Frontend integration:
- types.ts: all new enums, interfaces, label maps
- api.ts: owner CRUD endpoints (add/remove for contacts/companies/deals)
- hooks.ts: 6 new owner mutation hooks
- ContactFormModal: LinkedIn, birthday, source, department, status fields
- ContactDetailPage: display new fields (LinkedIn, department, birthday,
  source, status badge)
- CompanyDetailPage: display vatId, taxId, trade register, company size,
  delivery address, data enrichment info
- DealFormModal: lost reason dropdown + text (shown when status=LOST)
- DealDetailPage: display lost reason with label
- CompaniesPage: EntityStatus-aware status dots (ACTIVE/INACTIVE/BLOCKED)
- ActivityType: add FOLLOWUP to all label maps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:56:41 +01:00
Thomas Reitz
a85634a906 feat: add trade event (Messe-Timer) feature with admin CRUD and dashboard tiles
Backend: TradeEvent Prisma model, NestJS CRUD module with date validation
and tenant isolation. Frontend: Admin Events page with create/edit/delete
modals, dashboard countdown tiles showing upcoming/ongoing/ended events
with progress bars, and useEventCountdown hook for live timer updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:33:19 +01:00
Thomas Reitz
923e6bc127 feat(frontend): redesign CompanyDetailPage with tabbed layout
Replace 3-column grid with 4-tab layout:
- Tab 1 (Unternehmen): 2-column grid with master data + Lexware status (left), contacts + relationships (right)
- Tab 2 (Aktivitäten): ActivityFeed component at full width
- Tab 3 (Vorgänge): Combined CRM deals + Lexware vouchers table with source badges, filters, and color-coded Lexware rows
- Tab 4 (Verträge): ContractsCard placeholder at full width

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:08:53 +01:00
Thomas Reitz
dbbde6c4fa style(crm): indent and color-highlight contact sub-rows
Sub-rows now have an indigo left border, tinted background and stronger
indent to visually separate them from company rows. Dark mode variants
included. Expanded company row also gets a primary-colored left border.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:15:19 +01:00
Thomas Reitz
72fd57740b feat(crm): expandable contact sub-rows in companies table
Companies with linked contacts now show an expand arrow. Clicking it
lazy-loads and displays contacts as indented sub-rows with name,
position, email, phone, type badge and status. Backend gains companyId
filter on GET /contacts for efficient per-company querying.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:03:26 +01:00
Thomas Reitz
00f6b9842e fix(crm): link imported Ansprechpartner to CRM company via companyId
When importing contact persons from Lexware, if the parent company
is already in the CRM (detected via linkedMap), the companyId is now
passed to createContact so the contact is properly associated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:20:07 +01:00
Thomas Reitz
332a623d6f fix(crm): fix Lexware import duplicate detection — pageSize 500 exceeded backend max 100
The backend DTO validation enforces @Max(100) on pageSize. The ImportTab
was requesting pageSize=500, causing a 400 Bad Request. This left the
linkedMap empty and showed import buttons for already-imported contacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:12:32 +01:00
Thomas Reitz
6e77bf43b0 feat(crm): prevent duplicate Lexware imports — show linked status in import list
Import tab now loads all CRM companies/contacts and cross-references
lexwareContactId to detect already-imported entries. Linked contacts show
a green badge and "Öffnen" link instead of import buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:48:32 +01:00
Thomas Reitz
0ed1e77844 feat(crm): add company detail overhaul with industries, account types, relationship types
- Backend: new CRUD services/controllers for Industries, AccountTypes,
  RelationshipTypes, CompanyRelationships with Prisma schema migration
- Frontend: new hooks, API functions, and types for all config entities
- CompanyDetailPage redesign with ActivityFeed, RelationshipsCard
- CompanyFormModal extended with industry, account type, owner fields
- Activities service now supports companyId filter + includeContacts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:21:57 +01:00
Thomas Reitz
4e5c26cadd feat(frontend): add tabbed layout to CRM Settings page
- Restructure CRM Settings into 3 tabs: Module | Lexoffice Sync | Weitere Einstellungen
- Extract LexwareSyncContent as reusable component from LexwareSyncPage
- Embed Import/Export directly in Settings "Lexoffice Sync" tab
- Move Industries, AccountTypes, RelationshipTypes configs to "Weitere Einstellungen" tab
- Keep standalone /crm/lexware-sync route as fallback
- Add tab bar styles matching existing design pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:07:16 +01:00
Thomas Reitz
55329188f6 feat(frontend): redesign Lexware Import with browsable list + Ansprechpartner
- Replace search-only ImportTab with paginated browsable list of all Lexware contacts
- Add expandable Ansprechpartner (contact persons) section per company
- Individual Ansprechpartner can be imported as CRM contacts
- Add pagination controls for navigating large contact lists
- Search filter still available (min. 3 chars, backend MinLength constraint)
- Clean up useLexwareContacts hook with proper query key

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:21:05 +01:00
Thomas Reitz
4f05026bc8 feat(frontend): add Lexware Office Import/Export admin page
New admin page under /crm/lexware-sync with two tabs:
- Import: Search Lexware contacts and import as CRM company or contact
- Export: List CRM companies/contacts and push to Lexware Office
Accessible via CRM Settings page (admin-only).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:00:42 +01:00
Thomas Reitz
6b847cb9f6 fix(frontend): move hooks before conditional return in Lexware components
React hooks must not be called after conditional returns (Rules of Hooks).
Moves all hook calls above the isModuleEnabled check in LexwareSection
and DealVouchersSection to prevent silent component crashes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:37:30 +01:00
Thomas Reitz
2381409e6d feat(frontend): add Lexware Office integration UI
Implements complete Lexware Office frontend integration:
- Types: LexwareVoucher, DealVoucher, LexwareContact, VoucherType
- API: lexwareContactsApi + lexwareVouchersApi (16 endpoints)
- Hooks: React Query hooks for search, link/unlink, sync, vouchers
- LexwareSection: reusable component for Company/Contact detail pages
  with status badge, sync/push/refresh buttons, and voucher table
- LexwareSearchModal: search Lexware contacts and link to CRM entities
- DealVouchersSection: link/unlink vouchers on Deal detail page
- CRM Settings: Lexware Office toggle (admin-configurable)
- Company/Contact/Deal detail pages extended with Lexware sections

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:20:18 +01:00
Thomas Reitz
411a6bbbcb feat(crm): add CRM Settings page with module visibility toggles
- New CrmSettingsContext with localStorage persistence (later swappable to API)
- CrmSettingsPage: toggle switches per CRM module (Kontakte, Unternehmen,
  Vorgänge, Pipelines), only accessible for PLATFORM_ADMIN/TENANT_ADMIN
- CrmModuleGuard: route protection for disabled modules (redirect to dashboard)
- Sidebar: NavLinks conditionally rendered based on module settings
- "Einstellungen" NavLink in CRM section (admin-only, gear icon)
- CRM section hidden when all modules disabled and user is not admin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:22:16 +01:00
Thomas Reitz
36f571f5c3 feat(crm): add Company module + extend Contact/Deal with company relation
- New Company CRUD: CompaniesPage (list/search/pagination), CompanyFormModal,
  CompanyDetailPage (contacts + deals tables)
- Sidebar: "Unternehmen" NavLink between Kontakte and Vorgänge
- ContactsPage: Company column, ContactFormModal: company search + position field,
  ContactDetailPage: company link + position display
- DealsPage: Company column, DealFormModal: company search dropdown,
  DealDetailPage: company link in info card
- Types/API/Hooks extended with Company entity, cross-entity query invalidation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:10:58 +01:00
Thomas Reitz
0b78160f33 feat(crm): inline stage editing, DealDetail optimization, rename Deals to Vorgänge
- PipelinesPage: Stages können jetzt per Doppelklick oder Stift-Icon
  inline bearbeitet werden (Name, Farbe) via PATCH endpoint
- DealDetailPage: Nutzt jetzt deal.pipeline.stages direkt statt
  separatem usePipeline() API-Call (Backend liefert alle Stages mit)
- UI-Texte: "Deals" → "Vorgänge", "Deal" → "Vorgang" in allen
  user-facing Strings (Sidebar, Seiten, Modals, Fehlermeldungen)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:38:58 +01:00
Thomas Reitz
c739dce161 feat: CRM Frontend-Modul mit Kontakte, Deals, Pipelines und Aktivitäten
Komplette CRM-Frontend-Integration in die bestehende React-GUI:

- Types, API-Client und React Query Hooks für alle CRM-Entitäten
- Kontakte: Liste mit Suche/Filter, Detail mit Aktivitäten-Timeline, Create/Edit Modal
- Deals: Liste mit Pipeline/Stage/Status-Filter, Detail mit Fortschrittsbalken, Create/Edit Modal
- Pipelines: Verwaltungsseite mit klappbaren Cards und Stage-Management
- Aktivitäten: Formular-Modal für Notizen, Anrufe, E-Mails, Meetings, Aufgaben
- CRM-Navigation in Sidebar (aufklappbar, mit Inline-SVG-Icons)
- Routen in App.tsx für alle CRM-Seiten
- Vite-Proxy für lokale CRM-API-Entwicklung

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:13:02 +01:00