feat: T4 wave 6 - reminders and morning digest, next steps UI, warnings and notifications UI
- In-app reminders: task due (skips rescheduled tasks, catch-up wording) and a per-user morning digest step in the daily job - Frontend: "Als Nächstes" on the colony page, "Heute" on the dashboard, inspection-due card, season chip, apiary overview fields, cleanup sheet for ended colonies - Frontend: plausibility warnings that stay until dismissed, withdrawal spans in the calendar, notification bell with categories and links, notification settings in the profile - ARCHITECTURE.md: T4a sections 11.1-11.10, assumptions 31-41, handler table and daily job step order Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
ef33e5549b
commit
32c61c8e14
77 files changed
+5309
-142
No files matched your search
@@ -0,0 +1,41 @@
|
||||
import { CalendarRange } from "lucide-react";
|
||||
import type { SeasonSegment } from "@/lib/autopilot/types";
|
||||
|
||||
export const SEGMENT_LABELS: Record<SeasonSegment, string> = {
|
||||
WINTERRUHE: "Winterruhe",
|
||||
FRUEHJAHR: "Frühjahr",
|
||||
SCHWARMZEIT: "Schwarmzeit",
|
||||
SOMMER: "Sommer",
|
||||
SPAETSOMMER: "Spätsommer",
|
||||
HERBST: "Herbst",
|
||||
};
|
||||
|
||||
/** „Schwarmzeit · alle 7 Tage“; without an interval just the segment. */
|
||||
export function seasonChipText(season: {
|
||||
segment: SeasonSegment;
|
||||
intervalDays: number | null;
|
||||
}): string {
|
||||
const label = SEGMENT_LABELS[season.segment];
|
||||
if (season.intervalDays === null) return label;
|
||||
return season.intervalDays === 1
|
||||
? `${label} · täglich`
|
||||
: `${label} · alle ${season.intervalDays} Tage`;
|
||||
}
|
||||
|
||||
interface SeasonChipProps {
|
||||
segment: SeasonSegment;
|
||||
intervalDays?: number | null;
|
||||
/** Names the apiary: „Obstwiese: Schwarmzeit“. */
|
||||
apiaryName?: string;
|
||||
}
|
||||
|
||||
/** The season of an apiary as icon plus words. */
|
||||
export function SeasonChip({ segment, intervalDays = null, apiaryName }: SeasonChipProps) {
|
||||
const text = seasonChipText({ segment, intervalDays });
|
||||
return (
|
||||
<span className="inline-flex min-h-6 items-center gap-1 rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-foreground">
|
||||
<CalendarRange className="size-3.5 shrink-0" aria-hidden />
|
||||
{apiaryName ? `${apiaryName}: ${text}` : text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user