| @@ -298,12 +298,12 @@ Serverseitige Logik: `ReportController` ermittelt via `TimeEntryRepository::find | |||||
| ## Statistiken | ## Statistiken | ||||
| Arbeitszeit-Statistik als gestapeltes Balkendiagramm (Chart.js), erreichbar über den Statistik-Button im Report-Header. Tab-Navigation zwischen „Zeiten" und „Statistiken". | |||||
| Arbeitszeit-Statistik als gestapeltes Balkendiagramm (Chart.js) + drei Donut-Charts (Kunden, Projekte, Leistungen), erreichbar über den Statistik-Button im Report-Header. Tab-Navigation zwischen „Zeiten" und „Statistiken". | |||||
| ### Architektur | ### Architektur | ||||
| - **Backend**: `ReportController::statistics()` (Seite) + `ReportController::statisticsData()` (API) | - **Backend**: `ReportController::statistics()` (Seite) + `ReportController::statisticsData()` (API) | ||||
| - **Frontend**: `assets/scripts/statistics.js` (eigener Webpack-Entry), nutzt Chart.js (tree-shaked: nur `BarController`, `BarElement`, `CategoryScale`, `LinearScale`, `Tooltip`) | |||||
| - **Frontend**: `assets/scripts/statistics.js` (eigener Webpack-Entry), nutzt Chart.js (tree-shaked: `BarController`, `BarElement`, `DoughnutController`, `ArcElement`, `CategoryScale`, `LinearScale`, `Tooltip`) | |||||
| - **Styles**: `assets/styles/sections/_statistics.scss` | - **Styles**: `assets/styles/sections/_statistics.scss` | ||||
| - **Template**: `templates/report/statistics.html.twig` | - **Template**: `templates/report/statistics.html.twig` | ||||
| @@ -313,23 +313,30 @@ Arbeitszeit-Statistik als gestapeltes Balkendiagramm (Chart.js), erreichbar übe | |||||
| |--------------------|--------|-------------------------------------------------------| | |--------------------|--------|-------------------------------------------------------| | ||||
| | `/api/statistics` | GET | Aggregierte Zeitdaten: `?range={12months|6months|4weeks}&userId={id}` | | | `/api/statistics` | GET | Aggregierte Zeitdaten: `?range={12months|6months|4weeks}&userId={id}` | | ||||
| Rückgabe enthält zwei Bereiche: | |||||
| - **Balkendiagramm**: `labels`, `billable`, `nonBillable`, `billableRevenue`, `nonBillableRevenue`, `groupBy` | |||||
| - **Verteilung**: `distribution.clients`, `distribution.projects`, `distribution.services` — jeweils Arrays mit `{name, hours, revenue}` | |||||
| ### Daten-Aggregation | ### Daten-Aggregation | ||||
| `TimeEntryRepository::getStatisticsData()` aggregiert Zeiteinträge in Buckets: | |||||
| **Balkendiagramm** — `TimeEntryRepository::getStatisticsData()` aggregiert Zeiteinträge in Buckets: | |||||
| - **12months**: Monats-Buckets (`Y-m`), letztes Jahr | - **12months**: Monats-Buckets (`Y-m`), letztes Jahr | ||||
| - **6months**: Wochen-Buckets (`o-W`), letztes halbes Jahr | - **6months**: Wochen-Buckets (`o-W`), letztes halbes Jahr | ||||
| - **4weeks**: Tages-Buckets (`Y-m-d`), letzte 4 Wochen | - **4weeks**: Tages-Buckets (`Y-m-d`), letzte 4 Wochen | ||||
| Rückgabe: `labels`, `billable`, `nonBillable`, `billableRevenue`, `nonBillableRevenue`, `groupBy`. Billable/Non-Billable Trennung via `Service.billable`. Revenue-Berechnung nutzt die Stundensatz-Kaskade. | |||||
| Billable/Non-Billable Trennung via `Service.billable`. Revenue-Berechnung nutzt die Stundensatz-Kaskade. | |||||
| **Donut-Charts** — `TimeEntryRepository::getDistributionData()` aggregiert für denselben Zeitraum/User nach Kunden, Projekten und Leistungen (jeweils `SUM(duration)` und `SUM(rate * duration / 60)`). Einträge < 5% werden clientseitig zu „Rest" zusammengefasst. | |||||
| ### Frontend-Features | ### Frontend-Features | ||||
| - **Metrik-Umschalter**: Stunden oder Umsatz (clientseitig, kein neuer API-Call) | |||||
| - **Metrik-Umschalter**: Stunden oder Umsatz — gilt für Bar-Chart und Donuts (clientseitig, kein neuer API-Call) | |||||
| - **User-Filter**: Admins/Members können nach einzelnem User filtern oder alle sehen (Account-Name als „Alle") | - **User-Filter**: Admins/Members können nach einzelnem User filtern oder alle sehen (Account-Name als „Alle") | ||||
| - **Tracker**: sieht nur eigene Daten (serverseitig erzwungen) | - **Tracker**: sieht nur eigene Daten (serverseitig erzwungen) | ||||
| - **Tages-Ansicht**: Alternierende Wochen-Bänder als visuelle Hilfe (Custom Chart.js Plugin `weekBands`) | - **Tages-Ansicht**: Alternierende Wochen-Bänder als visuelle Hilfe (Custom Chart.js Plugin `weekBands`) | ||||
| - **Tooltip**: Zeigt formatierte Stunden (`h:mm`) oder Umsatz (`€`) je nach Metrik | - **Tooltip**: Zeigt formatierte Stunden (`h:mm`) oder Umsatz (`€`) je nach Metrik | ||||
| - **Brand-Farbe**: Billable-Balken nutzen `--color-primary`, Non-Billable grau | - **Brand-Farbe**: Billable-Balken nutzen `--color-primary`, Non-Billable grau | ||||
| - **Donut-Charts**: Drei nebeneinander (3-Spalten-Grid, responsive 1-Spalte), eigene Legende mit Farb-Dot, Name, Wert und Prozent. 8-Farben-Palette, „Rest" in Grau | |||||
| ## TenantConnectionMiddleware | ## TenantConnectionMiddleware | ||||