| @@ -27,7 +27,7 @@ httpdocs/ | |||||
| │ ├── Repository/ | │ ├── Repository/ | ||||
| │ │ ├── Central/ | │ │ ├── Central/ | ||||
| │ │ └── Tenant/ | │ │ └── Tenant/ | ||||
| │ ├── Service/ # TenantContext, RegistrationService, etc. | |||||
| │ ├── Service/ # TenantContext, RegistrationService, LexofficeService, etc. | |||||
| │ ├── Doctrine/ # TenantConnectionMiddleware | │ ├── Doctrine/ # TenantConnectionMiddleware | ||||
| │ ├── EventSubscriber/ # TenantRequestSubscriber, ArchivedUserSubscriber | │ ├── EventSubscriber/ # TenantRequestSubscriber, ArchivedUserSubscriber | ||||
| │ ├── Security/ # ArchivedUserChecker, AccessDeniedHandler | │ ├── Security/ # ArchivedUserChecker, AccessDeniedHandler | ||||
| @@ -42,7 +42,7 @@ httpdocs/ | |||||
| ├── assets/ | ├── assets/ | ||||
| │ ├── app.js # Webpack-Entry für Timetracking | │ ├── app.js # Webpack-Entry für Timetracking | ||||
| │ ├── styles/ # SCSS (main.scss als Entry) | │ ├── 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/ | ├── migrations/ | ||||
| │ ├── central/ # Doctrine-Migrations für Central-DB | │ ├── central/ # Doctrine-Migrations für Central-DB | ||||
| │ └── tenant/ # Doctrine-Migrations für Tenant-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 | - `removeWithAnimation(el, className)` / `animateIn(el, className)` — Animierte DOM-Operationen | ||||
| - Konstanten: `ANIMATION_MS`, `FADE_MS`, `MINUTES_PER_DAY` | - 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 | ### 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 | - `StopwatchManager` — Timer-Steuerung: Start/Stop/Resume, Tick-Display (Sekunden-Auflösung), DOM-Integration mit `window.entryManager`, Tab-Title-Update | ||||
| ### Globale `[hidden]`-Regel | ### 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) | | | `member` | Eigene Einträge + alle fremden sehen (kein Team-Zugriff) | | ||||
| | `tracker` | Nur eigene Einträge | | | `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) | ## Services-Injection (services.yaml) | ||||
| @@ -185,7 +188,7 @@ Live-Timer zum Tracken von Zeiteinträgen. UI in der Navigation (Desktop + Hambu | |||||
| ### Architektur | ### Architektur | ||||
| - **Backend**: Timer-API im `TimeTrackingController` (`/api/timer/*`) | - **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` | - **Styles**: `assets/styles/components/_stopwatch.scss` | ||||
| - **Template**: Popover in `_sections/nav.html.twig`, Play-Button in `timetracking/_entry_row.html.twig` | - **Template**: Popover in `_sections/nav.html.twig`, Play-Button in `timetracking/_entry_row.html.twig` | ||||
| - **Icon**: `_atoms/icon-stopwatch.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_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 | - 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 | ## TenantConnectionMiddleware | ||||
| Registriert via Service-Tag in `services.yaml` (nicht via `doctrine.yaml` — DoctrineBundle 3.x unterstützt `middlewares`-Config-Key nicht): | Registriert via Service-Tag in `services.yaml` (nicht via `doctrine.yaml` — DoctrineBundle 3.x unterstützt `middlewares`-Config-Key nicht): | ||||