feat: 2026.1.0-alpha.16 – dashboard banner refactor, period auto-activation, text & docs

Dashboard (dashboard/src/dashboard.tsx, settings.tsx, apiAcademicPeriods.ts):
- Refactor loadHolidayStatus to useCallback with stable empty-deps reference;
  removes location.pathname dependency that caused overlapping API calls at mount
  and left the banner unresolved via request-sequence cancellation
- Add key prop derived from severity:text to Syncfusion MessageComponent to force
  remount on state change, fixing stale banner that ignored React prop/children updates
- Correct German transliterated text to proper Umlauts throughout visible UI strings
  (fuer -> für, oe -> ö, ae -> ä etc. across dashboard and settings views)

Backend (server/init_academic_periods.py):
- Refactor to idempotent two-phase flow: seed default periods only when table is
  empty; on every run activate exactly the non-archived period covering date.today()
- Enforces single-active invariant by deactivating all periods before promoting match
- Emits explicit warning when no period covers current date instead of doing nothing

Deployment (docker-compose.prod.yml):
- Add init_academic_periods.py to server startup chain after migrations and defaults;
  eliminates manual post-deploy step to set an active academic period

Release docs:
- program-info.json: bump to 2026.1.0-alpha.16; fix JSON parse error caused by
  typographic curly quotes in the new changelog entry
- TECH-CHANGELOG.md: detailed alpha.16 section with root-cause motivation for both
  dashboard refactoring decisions (unstable callback ref + Syncfusion stale render)
- DEV-CHANGELOG.md: document dashboard refactor, Syncfusion key fix, Umlaut changes,
  and program-info JSON regression and fix
- README.md: add Latest Release Highlights section for alpha.16
- .github/copilot-instructions.md: sync file map, prod bootstrap note, backend and
  frontend pattern additions for academic period init and Syncfusion remount pattern
This commit is contained in:
2026-04-02 14:16:53 +00:00
parent 06411edfab
commit 4d652f0554
10 changed files with 1054 additions and 888 deletions

View File

@@ -5,6 +5,29 @@
This changelog documents technical and developer-relevant changes included in public releases. For development workspace changes, see DEV-CHANGELOG.md. Not all changes here are reflected in the user-facing changelog (`program-info.json`), and not all UI/feature changes are repeated here. Some changes (e.g., backend refactoring, API adjustments, infrastructure, developer tooling, or internal logic) may only appear in TECH-CHANGELOG.md. For UI/feature changes, see `dashboard/public/program-info.json`.
## 2026.1.0-alpha.16 (2026-04-02)
- 🐛 **Dashboard holiday banner refactoring and state fix (`dashboard/src/dashboard.tsx`)**:
- **Motivation — unstable fetch function:** `loadHolidayStatus` had `location.pathname` in its `useCallback` dependency array, causing a new function reference to be created on every navigation event. The `useEffect` depending on that reference then re-fired, producing overlapping API calls at mount that cancelled each other via the request-sequence guard, leaving the banner unresolved.
- **Refactoring:** Removed `location.pathname` from `useCallback` deps (it was unused inside the function body). The callback now has an empty dependency array, making its reference stable across the component lifetime. The `useEffect` is keyed only to the stable callback reference — no spurious re-fires.
- **Motivation — Syncfusion stale render:** Syncfusion's `MessageComponent` caches its rendered DOM internally and does not reactively update when React passes new children or props. Even after React state changed, the component displayed whatever text was rendered on first mount.
- **Fix:** Added a `key` prop derived from `${severity}:${text}` to `MessageComponent`. React unmounts and remounts the component whenever the key changes, bypassing Syncfusion's internal caching and ensuring the correct message is always visible.
- **Result:** Active-period name and holiday overlap details now render correctly on hard refresh, initial load, and route transitions without additional API calls.
- 🗓️ **Academic period bootstrap hardening (`server/init_academic_periods.py`)**:
- Refactored initialization into idempotent flow:
- seed default periods only when table is empty,
- on every run, activate exactly the non-archived period covering `date.today()`.
- Enforces single-active behavior by deactivating all previously active periods before setting the period for today.
- Emits explicit warning if no period covers current date (all remain inactive), improving operational diagnostics.
- 🚀 **Production startup alignment (`docker-compose.prod.yml`)**:
- Server startup command now runs `python /app/server/init_academic_periods.py` after migrations and default settings bootstrap.
- Removes manual post-deploy step to set an active academic period.
- 🌐 **Dashboard UX/text refinement (`dashboard/src/dashboard.tsx`)**:
- Converted user-facing transliterated German strings to proper Umlauts in the dashboard (for example: "für", "prüfen", "Ferienüberschneidungen", "Vorfälle", "Ausfälle").
Notes for integrators:
- On production boot, the active period is now derived from current date coverage automatically.
- If customer calendars do not include today, startup logs a warning and dashboard banner will still guide admins to configure periods.
## 2026.1.0-alpha.15 (2026-03-31)
- 🔌 **TV Power Intent Phase 1 (server-side)**:
- Scheduler now publishes retained QoS1 group-level intents to `infoscreen/groups/{group_id}/power/intent`.