feat(dashboard+api): card-based dashboard, camelCase API, UTC fixes
Dashboard: new Syncfusion card layout, global stats, filters, health bars, active event display, client details, bulk restart, 15s auto-refresh, manual refresh toasts API: standardized responses to camelCase; added serializers.py and updated events endpoints Time: ensured UTC storage; frontend appends 'Z' for parsing and displays local time Docs: updated copilot-instructions.md, README.md, TECH-CHANGELOG.md Program Info: bumped to 2025.1.0-alpha.12 with user-facing changelog BREAKING: external API consumers must migrate field names from PascalCase to camelCase.
This commit is contained in:
66
.github/copilot-instructions.md
vendored
66
.github/copilot-instructions.md
vendored
@@ -42,6 +42,35 @@ Small multi-service digital signage app (Flask API, React dashboard, MQTT schedu
|
||||
|
||||
## Recent changes since last commit
|
||||
|
||||
### Latest (November 2025)
|
||||
|
||||
- **API Naming Convention Standardization (camelCase)**:
|
||||
- Backend: Created `server/serializers.py` with `dict_to_camel_case()` utility for consistent JSON serialization
|
||||
- Events API: `GET /api/events` and `GET /api/events/<id>` now return camelCase fields (`id`, `subject`, `startTime`, `endTime`, `type`, `groupId`, etc.) instead of PascalCase
|
||||
- Frontend: Dashboard and appointments page updated to consume camelCase API responses
|
||||
- Appointments page maintains internal PascalCase for Syncfusion scheduler compatibility with automatic mapping from API responses
|
||||
- **Breaking**: External API consumers must update field names from PascalCase to camelCase
|
||||
|
||||
- **UTC Time Handling**:
|
||||
- Database stores all timestamps in UTC (naive timestamps normalized by backend)
|
||||
- API returns ISO strings without 'Z' suffix: `"2025-11-27T20:03:00"`
|
||||
- Frontend: Dashboard and appointments automatically append 'Z' to parse as UTC and display in user's local timezone
|
||||
- Time formatting functions use `toLocaleTimeString('de-DE')` for German locale display
|
||||
- All time comparisons use UTC; `new Date().toISOString()` sends UTC back to API
|
||||
|
||||
- **Dashboard Enhancements**:
|
||||
- New card-based design for Raumgruppen (room groups) with Syncfusion components
|
||||
- Global statistics summary: total infoscreens, online/offline counts, warning groups
|
||||
- Filter buttons: All, Online, Offline, Warnings with dynamic counts
|
||||
- Active event display per group: shows currently playing content with type icon, title, date, and time
|
||||
- Health visualization with color-coded progress bars per group
|
||||
- Expandable client details with last alive timestamps
|
||||
- Bulk restart functionality for offline clients per group
|
||||
- Manual refresh button with toast notifications
|
||||
- 15-second auto-refresh interval
|
||||
|
||||
### Earlier changes
|
||||
|
||||
- Scheduler: when formatting video events the scheduler now performs a best-effort HEAD probe of the streaming URL and includes basic metadata in the emitted payload (mime_type, size, accept_ranges). Placeholders for richer metadata (duration, resolution, bitrate, qualities, thumbnails, checksum) are included for later population by a background worker.
|
||||
- Streaming endpoint: a range-capable streaming endpoint was added at `/api/eventmedia/stream/<media_id>/<filename>` that supports byte-range requests (206 Partial Content) to enable seeking from clients.
|
||||
- Event model & API: `Event` gained video-related fields (`event_media_id`, `autoplay`, `loop`, `volume`) and the API accepts and persists these when creating/updating video events.
|
||||
@@ -116,7 +145,9 @@ Small multi-service digital signage app (Flask API, React dashboard, MQTT schedu
|
||||
- Vite React app; proxies `/api` and `/screenshots` to API in dev (`vite.config.ts`).
|
||||
- Uses Syncfusion components; Vite config pre-bundles specific packages to avoid alias issues.
|
||||
- Environment: `VITE_API_URL` provided at build/run; in dev compose, proxy handles `/api` so local fetches can use relative `/api/...` paths.
|
||||
- Theming: Syncfusion Material 3 theme is used. All component CSS is imported centrally in `dashboard/src/main.tsx` (base, navigations, buttons, inputs, dropdowns, popups, kanban, grids, schedule, filemanager, notifications, layouts, lists, calendars, splitbuttons, icons). Tailwind CSS has been removed.
|
||||
- **API Response Format**: All API endpoints return camelCase JSON (e.g., `startTime`, `endTime`, `groupId`). Frontend consumes camelCase directly.
|
||||
- **UTC Time Parsing**: API returns ISO strings without 'Z' suffix. Frontend appends 'Z' before parsing to ensure UTC interpretation: `const utcString = dateStr.endsWith('Z') ? dateStr : dateStr + 'Z'; new Date(utcString);`. Display uses `toLocaleTimeString('de-DE')` for German format.
|
||||
- Theming: Syncfusion Material 3 theme is used. All component CSS is imported centrally in `dashboard/src/main.tsx` (base, navigations, buttons, inputs, dropdowns, popups, kanban, grids, schedule, filemanager, notifications, layouts, lists, calendars, splitbuttons, icons). Tailwind CSS has been removed.
|
||||
- Scheduler (appointments page): top bar includes Group and Academic Period selectors (Syncfusion DropDownList). Selecting a period calls `POST /api/academic_periods/active`, moves the calendar to today’s month/day within the period year, and refreshes a right-aligned indicator row showing:
|
||||
- Holidays present in the current view (count)
|
||||
- Period label (display_name or name) with a badge indicating whether any holidays exist in that period (overlap check)
|
||||
@@ -169,6 +200,19 @@ Small multi-service digital signage app (Flask API, React dashboard, MQTT schedu
|
||||
- API clients use relative `/api/...` URLs so Vite dev proxy handles requests without CORS issues. The settings UI calls are centralized in `dashboard/src/apiSystemSettings.ts`.
|
||||
- Nested tabs: implemented as controlled components using `selectedItem` with stateful handlers to prevent sub-tab resets during updates.
|
||||
|
||||
- Dashboard page (`dashboard/src/dashboard.tsx`):
|
||||
- Card-based overview of all Raumgruppen (room groups) with real-time status monitoring
|
||||
- Global statistics: total infoscreens, online/offline counts, warning groups
|
||||
- Filter buttons: All / Online / Offline / Warnings with dynamic counts
|
||||
- Per-group cards show:
|
||||
- Currently active event (title, type, date/time in local timezone)
|
||||
- Health bar with online/offline ratio and color-coded status
|
||||
- Expandable client list with last alive timestamps
|
||||
- Bulk restart button for offline clients
|
||||
- Uses Syncfusion ButtonComponent, ToastComponent, and card CSS classes
|
||||
- Auto-refresh every 15 seconds; manual refresh button available
|
||||
- "Nicht zugeordnet" group always appears last in sorted list
|
||||
|
||||
- User dropdown technical notes:
|
||||
- Dependencies: `@syncfusion/ej2-react-splitbuttons` and `@syncfusion/ej2-splitbuttons` must be installed.
|
||||
- Vite: add both to `optimizeDeps.include` in `vite.config.ts` to avoid import-analysis errors.
|
||||
@@ -201,8 +245,19 @@ Note: Syncfusion usage in the dashboard is already documented above; if a UI for
|
||||
- REFRESH_SECONDS — Optional scheduler republish interval; `0` disables periodic refresh.
|
||||
|
||||
## Conventions & gotchas
|
||||
- **Datetime Handling**:
|
||||
- Always compare datetimes in UTC; some DB values may be naive—normalize before comparing (see `routes/events.py`).
|
||||
- Scheduler enforces UTC comparisons and normalizes naive timestamps. It publishes only currently active events and clears retained topics for groups with no active events. It also queries a future window (default: 7 days) and expands recurring events using RFC 5545 rules. Event exceptions are respected. Logging is concise and conversion lookups are cached.
|
||||
- Database stores timestamps in UTC (naive datetimes are normalized to UTC by backend)
|
||||
- API returns ISO strings **without** 'Z' suffix: `"2025-11-27T20:03:00"`
|
||||
- Frontend **must** append 'Z' before parsing: `const utcStr = dateStr.endsWith('Z') ? dateStr : dateStr + 'Z'; new Date(utcStr);`
|
||||
- Display in local timezone using `toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })`
|
||||
- When sending to API, use `date.toISOString()` which includes 'Z' and is UTC
|
||||
- **JSON Naming Convention**:
|
||||
- Backend uses snake_case internally (Python convention)
|
||||
- API returns camelCase JSON (web standard): `startTime`, `endTime`, `groupId`, etc.
|
||||
- Use `dict_to_camel_case()` from `server/serializers.py` before `jsonify()`
|
||||
- Frontend consumes camelCase directly; Syncfusion scheduler maintains internal PascalCase with field mappings
|
||||
- Scheduler enforces UTC comparisons and normalizes naive timestamps. It publishes only currently active events and clears retained topics for groups with no active events. It also queries a future window (default: 7 days) and expands recurring events using RFC 5545 rules. Event exceptions are respected. Logging is concise and conversion lookups are cached.
|
||||
- Use retained MQTT messages for state that clients must recover after reconnect (events per group, client group_id).
|
||||
- Clients should parse `event_type` and then read the corresponding nested payload (`presentation`, `website`, `video`, etc.). `website` and `webuntis` use the same nested `website` payload with `type: browser` and a `url`. Video events include `autoplay`, `loop`, `volume`, and `muted`.
|
||||
- In-container DB host is `db`; do not use `localhost` inside services.
|
||||
@@ -210,11 +265,12 @@ Note: Syncfusion usage in the dashboard is already documented above; if a UI for
|
||||
- When adding a new route:
|
||||
1) Create a Blueprint in `server/routes/...`,
|
||||
2) Register it in `server/wsgi.py`,
|
||||
3) Manage `Session()` lifecycle, and
|
||||
4) Return JSON-safe values (serialize enums and datetimes).
|
||||
3) Manage `Session()` lifecycle,
|
||||
4) Return JSON-safe values (serialize enums and datetimes), and
|
||||
5) Use `dict_to_camel_case()` for camelCase JSON responses
|
||||
- When extending media types, update `MediaType` and any logic in `eventmedia` and dashboard that depends on it.
|
||||
- Academic periods: Events/media can be optionally associated with periods for educational organization. Only one period should be active at a time (`is_active=True`).
|
||||
- Initialization scripts: legacy DB init scripts were removed; use Alembic and `initialize_database.py` going forward.
|
||||
- Initialization scripts: legacy DB init scripts were removed; use Alembic and `initialize_database.py` going forward.
|
||||
|
||||
### Recurrence & holidays: conventions
|
||||
- Do not pre-expand recurrences on the backend. Always send master events with `RecurrenceRule` + `RecurrenceException`.
|
||||
|
||||
Reference in New Issue
Block a user