117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import {
|
||
firstOpenDay,
|
||
getFriday,
|
||
getMonday,
|
||
getWeekNumber,
|
||
getYearLabel,
|
||
weekNeedsSchool,
|
||
} from "./calendar";
|
||
import { START_WEEK_NUMBER, TRAINEE } from "../data/holidays-bw";
|
||
import { WEEK_THEMES } from "../data/suggestions";
|
||
|
||
export type WeekEntry = {
|
||
number: number;
|
||
yearLabel: string;
|
||
from: string;
|
||
to: string;
|
||
/** Flat weekly activity bullets (like original hefte) */
|
||
activities: string[];
|
||
frameworkRefs: string[];
|
||
weekTheme: string;
|
||
schoolTopics: string[];
|
||
done: boolean;
|
||
};
|
||
|
||
export type AppState = {
|
||
/** Monday of the current open week */
|
||
currentWeekMonday: string;
|
||
weeks: Record<string, WeekEntry>;
|
||
usedSuggestionIds: number[];
|
||
};
|
||
|
||
/** Stable localStorage key – bump suffix only when AppState shape breaks compatibility. */
|
||
export const STORAGE_KEY = "berichtsheft-generator-v2";
|
||
/** Legacy key cleared on reset so old drafts do not linger. */
|
||
export const STORAGE_KEY_LEGACY = "berichtsheft-generator-v1";
|
||
|
||
const DEFAULT_FRAMEWORK = ["1.1", "1.2", "1.4", "2.1", "2.2", "2.3", "2.5", "4.4"];
|
||
|
||
export function pickTheme(exclude?: string): string {
|
||
const pool = WEEK_THEMES.filter((t) => t !== exclude);
|
||
const src = pool.length ? pool : WEEK_THEMES;
|
||
return src[Math.floor(Math.random() * src.length)] || "Büroabläufe";
|
||
}
|
||
|
||
export function createEmptyWeek(monday: string): WeekEntry {
|
||
return {
|
||
number: getWeekNumber(monday) || START_WEEK_NUMBER,
|
||
yearLabel: getYearLabel(monday),
|
||
from: monday,
|
||
to: getFriday(monday),
|
||
activities: [],
|
||
frameworkRefs: [...DEFAULT_FRAMEWORK],
|
||
weekTheme: pickTheme(),
|
||
schoolTopics: weekNeedsSchool(monday) ? [] : ["Ferien"],
|
||
done: false,
|
||
};
|
||
}
|
||
|
||
export function defaultState(): AppState {
|
||
const monday = getMonday(firstOpenDay());
|
||
return {
|
||
currentWeekMonday: monday,
|
||
weeks: { [monday]: createEmptyWeek(monday) },
|
||
usedSuggestionIds: [],
|
||
};
|
||
}
|
||
|
||
export function loadState(): AppState {
|
||
try {
|
||
const raw = localStorage.getItem(STORAGE_KEY);
|
||
if (!raw) return defaultState();
|
||
const parsed = JSON.parse(raw) as AppState;
|
||
if (!parsed.currentWeekMonday) return defaultState();
|
||
return {
|
||
...defaultState(),
|
||
...parsed,
|
||
weeks: parsed.weeks ?? {},
|
||
usedSuggestionIds: parsed.usedSuggestionIds ?? [],
|
||
};
|
||
} catch {
|
||
return defaultState();
|
||
}
|
||
}
|
||
|
||
export function saveState(state: AppState): void {
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
||
}
|
||
|
||
export function ensureWeek(state: AppState, monday: string): WeekEntry {
|
||
if (!state.weeks[monday]) {
|
||
state.weeks[monday] = createEmptyWeek(monday);
|
||
}
|
||
return state.weeks[monday];
|
||
}
|
||
|
||
export function getTrainee() {
|
||
return TRAINEE;
|
||
}
|
||
|
||
export function nextWeekMonday(monday: string): string {
|
||
const d = new Date(monday + "T12:00:00");
|
||
d.setDate(d.getDate() + 7);
|
||
const y = d.getFullYear();
|
||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||
const day = String(d.getDate()).padStart(2, "0");
|
||
return `${y}-${m}-${day}`;
|
||
}
|
||
|
||
export function prevWeekMonday(monday: string): string {
|
||
const d = new Date(monday + "T12:00:00");
|
||
d.setDate(d.getDate() - 7);
|
||
const y = d.getFullYear();
|
||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||
const day = String(d.getDate()).padStart(2, "0");
|
||
return `${y}-${m}-${day}`;
|
||
}
|