"use client"; import { useCallback, useEffect, useState } from "react"; export type HeatmapColorScheme = "green-red" | "blue-orange" | "purple-yellow" | "mono"; export interface AppPreferences { /** Hide allocations that belong to COMPLETED or CANCELLED projects. Default: true. */ hideCompletedProjects: boolean; /** * Timeline row rendering style. * "strip" — horizontal colored blocks per allocation (classic Gantt) + load graph * "bar" — stacked vertical bars per day showing hours by project * "heatmap" — strips overlaid with a green→red utilization colour per day column */ timelineDisplayMode: "strip" | "bar" | "heatmap"; /** Color palette used for heatmap overlays and bar-mode project view bars. */ heatmapColorScheme: HeatmapColorScheme; /** Show open demand / placeholder entries by default when loading the timeline. Default: true. */ showDemandProjects: boolean; /** Blink overbooked days (>8h) as a warning on the timeline. Default: false. */ blinkOverbookedDays: boolean; } const STORAGE_KEY = "capakraken_prefs"; const CHANGE_EVENT = "capakraken-prefs-changed"; const DEFAULT: AppPreferences = { hideCompletedProjects: true, timelineDisplayMode: "strip", heatmapColorScheme: "green-red", showDemandProjects: true, blinkOverbookedDays: false, }; export function readAppPreferences(): AppPreferences { if (typeof window === "undefined") return DEFAULT; try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return DEFAULT; return { ...DEFAULT, ...(JSON.parse(raw) as Partial) }; } catch { return DEFAULT; } } function saveAppPreferences(prefs: AppPreferences) { localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs)); // Broadcast to all hook instances in the same tab window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: prefs })); } export function useAppPreferences() { const [prefs, setPrefs] = useState(DEFAULT); useEffect(() => { // Sync from storage on mount setPrefs(readAppPreferences()); // Keep in sync when any hook instance saves a change function handleChange(e: Event) { setPrefs((e as CustomEvent).detail); } window.addEventListener(CHANGE_EVENT, handleChange); return () => window.removeEventListener(CHANGE_EVENT, handleChange); }, []); const setHideCompletedProjects = useCallback((value: boolean) => { setPrefs((prev) => { const next = { ...prev, hideCompletedProjects: value }; saveAppPreferences(next); return next; }); }, []); const setTimelineDisplayMode = useCallback((value: AppPreferences["timelineDisplayMode"]) => { setPrefs((prev) => { const next = { ...prev, timelineDisplayMode: value }; saveAppPreferences(next); return next; }); }, []); const setHeatmapColorScheme = useCallback((value: HeatmapColorScheme) => { setPrefs((prev) => { const next = { ...prev, heatmapColorScheme: value }; saveAppPreferences(next); return next; }); }, []); const setShowDemandProjects = useCallback((value: boolean) => { setPrefs((prev) => { const next = { ...prev, showDemandProjects: value }; saveAppPreferences(next); return next; }); }, []); const setBlinkOverbookedDays = useCallback((value: boolean) => { setPrefs((prev) => { const next = { ...prev, blinkOverbookedDays: value }; saveAppPreferences(next); return next; }); }, []); return { prefs, setHideCompletedProjects, setTimelineDisplayMode, setHeatmapColorScheme, setShowDemandProjects, setBlinkOverbookedDays }; }