diff --git a/CLAUDE.md b/CLAUDE.md index 8804ea7..5a2adc2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,7 +27,7 @@ httpdocs/ │ ├── Repository/ │ │ ├── Central/ │ │ └── Tenant/ -│ ├── Service/ # TenantContext, RegistrationService, etc. +│ ├── Service/ # TenantContext, RegistrationService, LexofficeService, etc. │ ├── Doctrine/ # TenantConnectionMiddleware │ ├── EventSubscriber/ # TenantRequestSubscriber, ArchivedUserSubscriber │ ├── Security/ # ArchivedUserChecker, AccessDeniedHandler @@ -42,7 +42,7 @@ httpdocs/ ├── assets/ │ ├── app.js # Webpack-Entry für Timetracking │ ├── styles/ # SCSS (main.scss als Entry) -│ └── scripts/ # JS-Module (calendar, entries, crud, stopwatch, team, account, report) +│ └── scripts/ # JS-Module (calendar, entries, crud, stopwatch, searchable-select, team, account, report) ├── migrations/ │ ├── central/ # Doctrine-Migrations für Central-DB │ └── tenant/ # Doctrine-Migrations für Tenant-DB @@ -136,10 +136,13 @@ Gemeinsame Hilfsfunktionen in `assets/scripts/utils.js`: - `removeWithAnimation(el, className)` / `animateIn(el, className)` — Animierte DOM-Operationen - Konstanten: `ANIMATION_MS`, `FADE_MS`, `MINUTES_PER_DAY` +### JS: searchable-select.js + +Wiederverwendbares durchsuchbares Dropdown-Modul (`SearchableSelect`-Klasse) mit Gruppen, Keyboard-Navigation, Filter und `onSelect`-Callback. Wird von `stopwatch.js` (Projekt/Service-Auswahl) und `crud.js` (Lexoffice-Kontaktauswahl) importiert. + ### JS: stopwatch.js -Stoppuhr-Modul mit zwei Klassen: -- `SearchableSelect` — Wiederverwendbares durchsuchbares Dropdown mit Gruppen, Keyboard-Navigation und Filter +Stoppuhr-Modul: - `StopwatchManager` — Timer-Steuerung: Start/Stop/Resume, Tick-Display (Sekunden-Auflösung), DOM-Integration mit `window.entryManager`, Tab-Title-Update ### Globale `[hidden]`-Regel @@ -162,7 +165,7 @@ Komplexe Logik die in mehreren Templates vorkommt wird in `templates/_macros/hel | `member` | Eigene Einträge + alle fremden sehen (kein Team-Zugriff) | | `tracker` | Nur eigene Einträge | -Superadmin = Account-Ersteller, kann Kontoinhaber übertragen und `primaryColor` setzen. +Superadmin = Account-Ersteller, kann Kontoinhaber übertragen, `primaryColor` und `lexofficeApiKey` setzen. ## Services-Injection (services.yaml) @@ -185,7 +188,7 @@ Live-Timer zum Tracken von Zeiteinträgen. UI in der Navigation (Desktop + Hambu ### Architektur - **Backend**: Timer-API im `TimeTrackingController` (`/api/timer/*`) -- **Frontend**: `assets/scripts/stopwatch.js` (importiert in `app.js`), enthält `SearchableSelect`-Klasse (durchsuchbare Dropdowns) und `StopwatchManager` +- **Frontend**: `assets/scripts/stopwatch.js` (importiert in `app.js`), nutzt `SearchableSelect` aus `searchable-select.js` - **Styles**: `assets/styles/components/_stopwatch.scss` - **Template**: Popover in `_sections/nav.html.twig`, Play-Button in `timetracking/_entry_row.html.twig` - **Icon**: `_atoms/icon-stopwatch.html.twig` @@ -209,6 +212,38 @@ Live-Timer zum Tracken von Zeiteinträgen. UI in der Navigation (Desktop + Hambu - LocalStorage (`tt_timer_state`): persistiert Timer-State über Page-Reloads, wird mit Server-State abgeglichen - LocalStorage (`tt_last_project_id`, `tt_last_service_id`): merkt letzte Auswahl für Quick-Start +## Lexoffice-Integration (Lexware Office) + +Optionale Verknüpfung von Kunden mit Lexware Office Kontakten. Nur aktiv wenn `Account.lexofficeApiKey` gesetzt ist. + +### Datenmodell + +- `Account.lexofficeApiKey` (Central-DB): API-Key für Lexware Office, gespeichert pro Account +- `Client.lexofficeContactId` (Tenant-DB): UUID des verknüpften Lexware-Kontakts (nullable) + +### Architektur + +- **Service**: `LexofficeService` — HTTP-Client für Lexware Office API (`https://api.lexware.io/v1`), paginierter Kontakt-Abruf mit Rate-Limit-Retry +- **Controller**: `LexofficeController` — API-Endpunkte für Kontaktlisten und Einzelkontakte +- **Client-Controller**: `lexoffice-refresh`-Endpunkt zum Aktualisieren des Kundennamens aus Lexware +- **Frontend**: Lexoffice-Logik in `crud.js`, nutzt `SearchableSelect` für Kontakt-Dropdown +- **Account-Settings**: API-Key-Verwaltung im Account-Tab (Eingabe/Ändern mit Maskierung) + +### API-Endpunkte + +| Route | Method | Beschreibung | +|------------------------------------------|--------|-------------------------------------------| +| `/api/lexoffice/contacts` | GET | Alle Kunden-Kontakte aus Lexware Office | +| `/api/lexoffice/contacts/{contactId}` | GET | Einzelnen Kontakt abrufen | +| `/api/clients/{id}/lexoffice-refresh` | PATCH | Kundenname aus Lexware aktualisieren | + +### Verhalten + +- Beim Verknüpfen wird der Kundenname aus Lexware übernommen, das Name-Feld wird disabled +- Bereits verknüpfte Kontakte werden in der Dropdown-Liste ausgeblendet (Duplikat-Schutz) +- Reload-Button pro Zeile zum Aktualisieren des Namens aus Lexware +- Kontakte werden clientseitig gecacht (einmal geladen pro Page-Session) + ## TenantConnectionMiddleware Registriert via Service-Tag in `services.yaml` (nicht via `doctrine.yaml` — DoctrineBundle 3.x unterstützt `middlewares`-Config-Key nicht):