feat: add time-dependent Europe/Brussels dashboard greeting

The dashboard greeting was a fully static "Goedemorgen..." regardless of
actual time of day. New frontend/src/i18n/greeting.ts::getGreetingPeriod
is a pure, clock-injectable function resolving one of 4 periods (05:00-
11:59 morning, 12:00-17:59 afternoon, 18:00-22:59 evening, 23:00-04:59
night) against Europe/Brussels wall-clock time via
Intl.DateTimeFormat({ timeZone, hourCycle: "h23" }), which is DST-safe
by construction.

useGreetingPeriod.ts wires this into React with a 30s poll so the
greeting rolls over live while the app stays open, no reload required.
Each period now has its own greeting word and accompanying sentence in
all 3 languages (dashboard.json), replacing both the fixed "Goedemorgen"
and the fixed "Here's the fleet" follow-up sentence. Night never says
"Goedenacht" (used as a farewell, not a welcome, in Dutch).
This commit is contained in:
NuklearRabbit
2026-08-04 03:08:45 +02:00
parent d17af1c52a
commit e427313bce
8 changed files with 266 additions and 4 deletions
+4 -1
View File
@@ -7,6 +7,7 @@ import { useAuth } from "../context/AuthContext";
import { useDemoGuide } from "../context/DemoGuideContext";
import { useDemoManifest } from "../context/DemoManifestContext";
import { useLocaleFormat } from "../i18n/format";
import { useGreetingPeriod } from "../i18n/useGreetingPeriod";
import { SeverityBadge, StatusBadge } from "../components/Badge";
import { Icon } from "../components/Icons";
import { ErrorState, IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome";
@@ -31,6 +32,7 @@ function attentionItemTitle(
export function Dashboard() {
const { t } = useTranslation(["dashboard", "common", "integrations"]);
const { formatTime, formatShortDate } = useLocaleFormat();
const greetingPeriod = useGreetingPeriod();
const { user } = useAuth();
const { manifest } = useDemoManifest();
const { openGuide, restart, currentIndex, completed, totalSteps } = useDemoGuide();
@@ -78,10 +80,11 @@ export function Dashboard() {
const latestRun = data.recent_automation[0];
const readyCount = manifest?.scenarios.filter((s) => s.ready).length ?? 0;
const greetingTitle = `${t(`greeting.${greetingPeriod}`)}. ${t(`greetingBody.${greetingPeriod}`)}`;
return (
<div className="page dashboard-page">
<PageHeader eyebrow={t("eyebrow")} title={t("title")} description={t("description")} actions={<Link className="button button-secondary" to="/vehicles"><Icon name="fleet" /> {t("viewFleet")}</Link>} />
<PageHeader eyebrow={t("eyebrow")} title={greetingTitle} description={t("description")} actions={<Link className="button button-secondary" to="/vehicles"><Icon name="fleet" /> {t("viewFleet")}</Link>} />
<section className="demo-start-panel" aria-label={t("demoStart.title")}>
<div>