import type { ChecklistItem, OnboardingCounters } from "../shared/types"; /** * Pure computation of the getting-started checklist (#351). * * Six rows in a fixed order. The `profile` row is always done (the checklist * is only reachable from an open profile). The `budget` row is locked when the * edition does not include Budget; a locked row is excluded from `total` and * from `complete`, and links to the Users settings (license card) instead. */ export interface ChecklistOptions { /** An import folder has been chosen. */ importFolderSet: boolean; /** The Budget feature is part of the current edition. */ budgetAllowed: boolean; } export interface Checklist { items: ChecklistItem[]; /** Number of unlocked rows that are done. */ done: number; /** Number of unlocked rows (6, or 5 when Budget is locked). */ total: number; /** True when every unlocked row is done. */ complete: boolean; } export function computeChecklist( counters: OnboardingCounters, options: ChecklistOptions ): Checklist { const budgetLocked = !options.budgetAllowed; const items: ChecklistItem[] = [ // Always done: the checklist only exists inside an open profile. The link // is never rendered for a done row (#352); it points at profile management. { key: "profile", done: true, locked: false, to: "/settings/users" }, { key: "importFolder", done: options.importFolderSet, locked: false, to: "/import" }, { key: "source", done: counters.sources >= 1, locked: false, to: "/import" }, { key: "import", done: counters.imports >= 1, locked: false, to: "/import" }, { key: "manualCategory", done: counters.manualCategorized >= 1, locked: false, to: "/transactions", }, { key: "budget", done: counters.budgetEntries >= 1, locked: budgetLocked, to: budgetLocked ? "/settings/users" : "/budget", }, ]; const unlocked = items.filter((item) => !item.locked); const done = unlocked.filter((item) => item.done).length; const total = unlocked.length; return { items, done, total, complete: done === total }; }