@burdenoff/microfe-timecampus 2026.830.2 → 2026.903.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/timecampus/components/index.js +4 -4
  2. package/dist/timecampus/pages/HomePage.d.ts.map +1 -1
  3. package/dist/timecampus/pages/HomePage.js +142 -131
  4. package/dist/timecampus/pages/HomePage.js.map +1 -1
  5. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +7 -7
  6. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  7. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +7 -7
  8. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  9. package/dist/timecampus/pages/dashboard/ReportsPage.js +13 -13
  10. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +7 -7
  11. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +7 -7
  12. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  13. package/dist/timecampus/pages/focus-score/FocusScorePage.js +13 -13
  14. package/dist/timecampus/pages/geofencing/GeofencingPage.js +135 -135
  15. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  16. package/dist/timecampus/pages/invoicing/InvoicingPage.js +7 -7
  17. package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -1
  18. package/dist/timecampus/pages/lifetime/DeadTimeRadarPage.js +8 -8
  19. package/dist/timecampus/pages/lifetime/FiniteLifePage.js +4 -4
  20. package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
  21. package/dist/timecampus/pages/lifetime/LifeBalancePage.js +104 -104
  22. package/dist/timecampus/pages/lifetime/LifeBalancePage.js.map +1 -1
  23. package/dist/timecampus/pages/lifetime/LifeTimelinePage.js +7 -7
  24. package/dist/timecampus/pages/lifetime/LifeTimelinePage.js.map +1 -1
  25. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js +33 -33
  26. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
  27. package/dist/timecampus/pages/lifetime/RecommendationsPage.js +80 -80
  28. package/dist/timecampus/pages/lifetime/RecommendationsPage.js.map +1 -1
  29. package/dist/timecampus/pages/lifetime/TimeWellSpentPage.js +16 -16
  30. package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +11 -11
  31. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +11 -11
  32. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  33. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +14 -14
  34. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  35. package/dist/timecampus/pages/meetings/MeetingsPage.js +12 -12
  36. package/dist/timecampus/pages/projects/ProjectsPage.js +5 -5
  37. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  38. package/dist/timecampus/pages/projects/TasksPage.js +8 -8
  39. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +8 -8
  40. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  41. package/dist/timecampus/pages/team/TeamMembersPage.js +8 -8
  42. package/dist/timecampus/pages/team-activity/TeamActivityPage.js +13 -13
  43. package/dist/timecampus/pages/time/TimeEntriesPage.js +5 -5
  44. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  45. package/dist/timecampus/pages/time/TimeTrackingPage.js +138 -138
  46. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  47. package/dist/timecampus/pages/time/TimerPage.js +14 -14
  48. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  49. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +26 -26
  50. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  51. package/dist/timecampus/pages/timeline/TimelinePage.js +8 -8
  52. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +79 -79
  53. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  54. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FiniteLifePage.js","names":[],"sources":["../../../../src/timecampus/pages/lifetime/FiniteLifePage.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent } from \"react\";\nimport {\n Hourglass,\n Sparkles,\n SlidersHorizontal,\n CalendarDays,\n Lock,\n ArrowRight,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport {\n PageLayout,\n ChartCard,\n EmptyState,\n Modal,\n DonutChart,\n WeeksGrid,\n LoadingSkeleton,\n ToastContainer,\n useToast,\n type DonutSlice,\n} from \"../../components\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useMyLifeInWeeksQuery,\n useMyLifeProfileQuery,\n useUpdateMyLifeProfileMutation,\n type MyLifeInWeeksQuery,\n} from \"@/generated/wspace-operations\";\nimport { lifeDomainLabel, formatSecondsHuman } from \"./domains\";\n\n/** One life-domain roll-up row, typed from the generated query result. */\ntype LifeDomainRollup =\n MyLifeInWeeksQuery[\"myLifeInWeeks\"][\"domainRollups\"][number];\n\n/** Grouped integer (e.g. 1,742). */\nfunction nf(n: number): string {\n return Math.max(0, Math.round(n)).toLocaleString();\n}\n\n/** Whole-week count → years with one decimal (e.g. 33.5). */\nfunction weeksToYears(weeks: number): string {\n return (Math.round((weeks / 52) * 10) / 10).toFixed(1);\n}\n\n/** ISO datetime → the `YYYY-MM-DD` a native date input expects. */\nfunction isoToDateInput(iso: string | null | undefined): string {\n if (!iso) return \"\";\n return iso.slice(0, 10);\n}\n\nconst TODAY_DATE_INPUT = new Date().toISOString().slice(0, 10);\n\n/**\n * FiniteLifePage — the finite-life \"your life in weeks\" memento.\n *\n * A calm, opt-in, entirely private reflection: each cell is one week of a life,\n * anchored to the gentle north star \"What if today was your last?\". Everything\n * it draws comes from the viewer's own (optional) birth date and life\n * expectancy — never a guessed age or an invented mortality date. It is\n * self-scoped (identity travels in the gateway headers, so the queries take no\n * user argument) and honest by construction: with no birth date it still\n * renders an open horizon and simply invites you to add one. It respects the\n * `hideRemainingWeeks` choice so no one is forced to stare down a countdown.\n */\nexport function FiniteLifePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { toasts, addToast, dismissToast } = useToast();\n\n const {\n data: weeksData,\n loading: weeksLoading,\n error: weeksError,\n refetch: refetchWeeks,\n } = useMyLifeInWeeksQuery({ skip: !workspaceId });\n\n const {\n data: profileData,\n loading: profileLoading,\n error: profileError,\n refetch: refetchProfile,\n } = useMyLifeProfileQuery({ skip: !workspaceId });\n\n const [updateProfile, { loading: saving }] = useUpdateMyLifeProfileMutation();\n\n const weeks = weeksData?.myLifeInWeeks;\n const profile = profileData?.myLifeProfile;\n\n // ---- Setup modal (opt-in, dismissible) -----------------------------------\n const [setupOpen, setSetupOpen] = useState(false);\n const [birthDateInput, setBirthDateInput] = useState(\"\");\n const [expectancyInput, setExpectancyInput] = useState(\"\");\n const [hideRemainingInput, setHideRemainingInput] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const openSetup = () => {\n setBirthDateInput(isoToDateInput(profile?.birthDate ?? weeks?.birthDate));\n setExpectancyInput(\n profile?.lifeExpectancyYears != null\n ? String(profile.lifeExpectancyYears)\n : weeks?.lifeExpectancyYears != null\n ? String(weeks.lifeExpectancyYears)\n : \"\",\n );\n setHideRemainingInput(\n profile?.hideRemainingWeeks ?? weeks?.hideRemainingWeeks ?? false,\n );\n setFormError(null);\n setSetupOpen(true);\n };\n\n const handleSave = async (e?: FormEvent) => {\n e?.preventDefault();\n setFormError(null);\n\n if (!birthDateInput) {\n setFormError(\"Please add your birth date to continue.\");\n return;\n }\n if (birthDateInput > TODAY_DATE_INPUT) {\n setFormError(\"Your birth date can't be in the future.\");\n return;\n }\n\n let expectancy: number | null = null;\n if (expectancyInput.trim() !== \"\") {\n const parsed = Number(expectancyInput);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > 130) {\n setFormError(\n \"Life expectancy should be a whole number of years (1–130).\",\n );\n return;\n }\n expectancy = parsed;\n }\n\n try {\n await updateProfile({\n variables: {\n input: {\n birthDate: new Date(\n `${birthDateInput}T00:00:00.000Z`,\n ).toISOString(),\n lifeExpectancyYears: expectancy,\n hideRemainingWeeks: hideRemainingInput,\n finiteLifeOptIn: true,\n },\n },\n });\n await Promise.all([refetchWeeks(), refetchProfile()]);\n setSetupOpen(false);\n addToast({\n variant: \"success\",\n title: \"Saved — this is just for you\",\n description: \"You can change or turn this off anytime.\",\n });\n } catch {\n addToast({\n variant: \"error\",\n title: \"Couldn't save your settings\",\n description: \"Please try again in a moment.\",\n });\n }\n };\n\n // ---- Derived, honest values ----------------------------------------------\n const hasBirthDate = !!weeks?.birthDate;\n const hideRemaining =\n weeks?.hideRemainingWeeks ?? profile?.hideRemainingWeeks ?? false;\n\n const weeksLived = weeks?.weeksLived ?? null;\n const expectancyYears = weeks?.lifeExpectancyYears ?? null;\n // Only reveal the full mortal horizon when the user hasn't asked to hide it.\n const gridTotalWeeks = hideRemaining\n ? null\n : (weeks?.expectedTotalWeeks ?? null);\n const weeksRemaining =\n hideRemaining || weeks?.weeksRemaining == null\n ? null\n : weeks.weeksRemaining;\n\n // Reflection: where the recent (backend-defined) window actually went.\n const domainSlices: DonutSlice[] = useMemo(() => {\n const rollups: LifeDomainRollup[] = weeks?.domainRollups ?? [];\n return rollups\n .filter((r) => r.totalSeconds > 0)\n .map((r) => ({\n name: lifeDomainLabel(r.domain),\n value: r.totalSeconds,\n }));\n }, [weeks?.domainRollups]);\n const rollupTotalSeconds = domainSlices.reduce((sum, s) => sum + s.value, 0);\n\n const loading = weeksLoading || profileLoading;\n const hardError = (weeksError || profileError) && !weeks && !profile;\n\n const setupAction = (\n <button\n type=\"button\"\n onClick={openSetup}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <SlidersHorizontal className=\"h-4 w-4\" />\n Life settings\n </button>\n );\n\n if (loading && !weeks && !profile) {\n return (\n <PageLayout\n title=\"Your Life in Weeks\"\n description=\"A calm, private view of the weeks you've lived — and the ones still ahead.\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Your Life in Weeks\"\n description=\"A calm, private view of the weeks you've lived — and the ones still ahead.\"\n purpose=\"Each square is a single week of your life. It's a gentle memento — not a countdown — meant only for you, to help this week feel a little more intentional.\"\n actions={setupAction}\n >\n {hardError ? (\n <div className=\"rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n We couldn't load your life view right now. Please try again in a\n moment.\n </div>\n ) : (\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* The grid — the memento itself */}\n <div className=\"lg:col-span-2\">\n <ChartCard\n title=\"Your life in weeks\"\n description={\n hasBirthDate\n ? \"Each row is a year; each square, a week you have lived or have yet to live.\"\n : \"Add your birth date to fill in the weeks you've already lived.\"\n }\n >\n <WeeksGrid\n weeksLived={weeksLived}\n totalWeeks={gridTotalWeeks}\n ariaLabel={\n hasBirthDate\n ? `Your life in weeks: ${nf(weeksLived ?? 0)} weeks lived so far`\n : \"Your life in weeks — birth date not set yet\"\n }\n />\n <div className=\"mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] bg-action-primary-bg\" />\n Weeks lived\n </span>\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] border-2 border-action-primary-bg bg-bg-surface\" />\n This week\n </span>\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] border border-border-seam\" />\n Ahead\n </span>\n </div>\n </ChartCard>\n </div>\n\n {/* The calm side card */}\n <GlassCard\n treatment=\"glass\"\n glow\n className=\"flex flex-col p-5 sm:p-6\"\n >\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/10 text-primary\">\n <Hourglass className=\"h-5 w-5\" />\n </div>\n\n {hasBirthDate ? (\n <div className=\"mt-4 space-y-5\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n You have lived\n </p>\n <p className=\"mt-0.5 text-3xl font-bold tabular-nums text-text-primary\">\n {nf(weeksLived ?? 0)}{\" \"}\n <span className=\"text-xl font-semibold text-text-secondary\">\n weeks\n </span>\n </p>\n </div>\n\n {expectancyYears != null && weeksRemaining != null ? (\n <div>\n <p className=\"text-sm text-text-secondary\">\n If you live to {expectancyYears}, about\n </p>\n <p className=\"mt-0.5 text-2xl font-bold tabular-nums text-text-primary\">\n {nf(weeksRemaining)}{\" \"}\n <span className=\"text-lg font-semibold text-text-secondary\">\n remain\n </span>\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {weeksToYears(weeksLived ?? 0)} years lived ·{\" \"}\n {weeksToYears(weeksRemaining)} years ahead\n </p>\n </div>\n ) : (\n <p className=\"text-xs text-text-secondary\">\n {hideRemaining\n ? \"You've chosen to see only the weeks you've lived.\"\n : \"Add a life expectancy in settings to gently sketch the weeks ahead.\"}\n </p>\n )}\n\n <div className=\"rounded-lg border border-border-seam bg-bg-surface/40 p-3\">\n <p className=\"flex items-start gap-2 text-sm text-text-primary\">\n <Sparkles className=\"mt-0.5 h-4 w-4 shrink-0 text-primary\" />\n <span>\n What if today was your last? Make this week count.\n </span>\n </p>\n </div>\n </div>\n ) : (\n <div className=\"mt-4 space-y-4\">\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n See your life in weeks\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Add your birth date and we'll gently fill in the weeks\n you've lived so far — a quiet reminder that time is\n finite, and this week is yours to spend.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={openSetup}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n Add your birth date\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n\n <p className=\"mt-6 flex items-start gap-1.5 text-xs text-text-secondary\">\n <Lock className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <span>\n Only you can see this. Your birth date is never shared, and\n you can turn this off anytime.\n </span>\n </p>\n </GlassCard>\n </div>\n\n {/* Gentle reflection — only when there's real recent data to show */}\n {domainSlices.length > 0 && (\n <ChartCard\n title=\"Where your recent weeks went\"\n description=\"How your logged time settled across life's domains lately — a mirror, not a scorecard.\"\n >\n <DonutChart\n data={domainSlices}\n centerLabel=\"logged lately\"\n centerValue={formatSecondsHuman(rollupTotalSeconds)}\n valueFormat={(v) => formatSecondsHuman(v)}\n emptyLabel=\"Nothing logged in this window yet.\"\n ariaLabel=\"Recent time by life domain\"\n />\n </ChartCard>\n )}\n\n {/* Honest first-run nudge when nothing has been logged either */}\n {hasBirthDate && domainSlices.length === 0 && (\n <EmptyState\n title=\"Your weeks are yours to shape\"\n description=\"As you capture how you spend your time, a quiet reflection of your recent weeks will appear here — only for you.\"\n />\n )}\n </div>\n )}\n\n {/* Opt-in setup */}\n <Modal\n open={setupOpen}\n onClose={() => setSetupOpen(false)}\n title=\"Your life in weeks\"\n description=\"This is private and entirely optional. Add only what feels comfortable.\"\n footer={\n <>\n <button\n type=\"button\"\n onClick={() => setSetupOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n form=\"finite-life-setup\"\n disabled={saving}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {saving ? \"Saving…\" : \"Save\"}\n </button>\n </>\n }\n >\n <form\n id=\"finite-life-setup\"\n onSubmit={handleSave}\n className=\"space-y-5\"\n >\n <div>\n <label\n htmlFor=\"finite-life-birthdate\"\n className=\"flex items-center gap-1.5 text-sm font-medium text-text-primary\"\n >\n <CalendarDays className=\"h-4 w-4 text-text-secondary\" />\n Your birth date\n </label>\n <input\n id=\"finite-life-birthdate\"\n type=\"date\"\n value={birthDateInput}\n max={TODAY_DATE_INPUT}\n onChange={(e) => setBirthDateInput(e.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Used only to count the weeks you've lived. It stays with you.\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"finite-life-expectancy\"\n className=\"text-sm font-medium text-text-primary\"\n >\n Life expectancy{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <input\n id=\"finite-life-expectancy\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n max={130}\n step={1}\n placeholder=\"e.g. 90\"\n value={expectancyInput}\n onChange={(e) => setExpectancyInput(e.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n A gentle horizon for the weeks ahead — never a deadline. Leave it\n blank to simply see the weeks you've lived.\n </p>\n </div>\n\n <label className=\"flex items-start gap-2.5 rounded-lg border border-border-subtle bg-bg-surface/40 p-3\">\n <input\n type=\"checkbox\"\n checked={hideRemainingInput}\n onChange={(e) => setHideRemainingInput(e.target.checked)}\n className=\"mt-0.5 h-4 w-4 shrink-0 rounded border-border-subtle\"\n />\n <span className=\"text-sm text-text-primary\">\n Hide the weeks ahead\n <span className=\"mt-0.5 block text-xs text-text-secondary\">\n Show only the weeks you've lived — no horizon, no countdown.\n </span>\n </span>\n </label>\n\n {formError && (\n <p className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n {formError}\n </p>\n )}\n </form>\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FiniteLifePage.js","names":[],"sources":["../../../../src/timecampus/pages/lifetime/FiniteLifePage.tsx"],"sourcesContent":["import { useMemo, useState, type FormEvent } from \"react\";\nimport {\n Hourglass,\n Sparkles,\n SlidersHorizontal,\n CalendarDays,\n Lock,\n ArrowRight,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport {\n PageLayout,\n ChartCard,\n EmptyState,\n Modal,\n DonutChart,\n WeeksGrid,\n LoadingSkeleton,\n ToastContainer,\n useToast,\n type DonutSlice,\n} from \"../../components\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useMyLifeInWeeksQuery,\n useMyLifeProfileQuery,\n useUpdateMyLifeProfileMutation,\n type MyLifeInWeeksQuery,\n} from \"@/generated/wspace-operations\";\nimport { lifeDomainLabel, formatSecondsHuman } from \"./domains\";\n\n/** One life-domain roll-up row, typed from the generated query result. */\ntype LifeDomainRollup =\n MyLifeInWeeksQuery[\"myLifeInWeeks\"][\"domainRollups\"][number];\n\n/** Grouped integer (e.g. 1,742). */\nfunction nf(n: number): string {\n return Math.max(0, Math.round(n)).toLocaleString();\n}\n\n/** Whole-week count → years with one decimal (e.g. 33.5). */\nfunction weeksToYears(weeks: number): string {\n return (Math.round((weeks / 52) * 10) / 10).toFixed(1);\n}\n\n/** ISO datetime → the `YYYY-MM-DD` a native date input expects. */\nfunction isoToDateInput(iso: string | null | undefined): string {\n if (!iso) return \"\";\n return iso.slice(0, 10);\n}\n\nconst TODAY_DATE_INPUT = new Date().toISOString().slice(0, 10);\n\n/**\n * FiniteLifePage — the finite-life \"your life in weeks\" memento.\n *\n * A calm, opt-in, entirely private reflection: each cell is one week of a life,\n * anchored to the gentle north star \"What if today was your last?\". Everything\n * it draws comes from the viewer's own (optional) birth date and life\n * expectancy — never a guessed age or an invented mortality date. It is\n * self-scoped (identity travels in the gateway headers, so the queries take no\n * user argument) and honest by construction: with no birth date it still\n * renders an open horizon and simply invites you to add one. It respects the\n * `hideRemainingWeeks` choice so no one is forced to stare down a countdown.\n */\nexport function FiniteLifePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { toasts, addToast, dismissToast } = useToast();\n\n const {\n data: weeksData,\n loading: weeksLoading,\n error: weeksError,\n refetch: refetchWeeks,\n } = useMyLifeInWeeksQuery({ skip: !workspaceId });\n\n const {\n data: profileData,\n loading: profileLoading,\n error: profileError,\n refetch: refetchProfile,\n } = useMyLifeProfileQuery({ skip: !workspaceId });\n\n const [updateProfile, { loading: saving }] = useUpdateMyLifeProfileMutation();\n\n const weeks = weeksData?.myLifeInWeeks;\n const profile = profileData?.myLifeProfile;\n\n // ---- Setup modal (opt-in, dismissible) -----------------------------------\n const [setupOpen, setSetupOpen] = useState(false);\n const [birthDateInput, setBirthDateInput] = useState(\"\");\n const [expectancyInput, setExpectancyInput] = useState(\"\");\n const [hideRemainingInput, setHideRemainingInput] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const openSetup = () => {\n setBirthDateInput(isoToDateInput(profile?.birthDate ?? weeks?.birthDate));\n setExpectancyInput(\n profile?.lifeExpectancyYears != null\n ? String(profile.lifeExpectancyYears)\n : weeks?.lifeExpectancyYears != null\n ? String(weeks.lifeExpectancyYears)\n : \"\",\n );\n setHideRemainingInput(\n profile?.hideRemainingWeeks ?? weeks?.hideRemainingWeeks ?? false,\n );\n setFormError(null);\n setSetupOpen(true);\n };\n\n const handleSave = async (e?: FormEvent) => {\n e?.preventDefault();\n setFormError(null);\n\n if (!birthDateInput) {\n setFormError(\"Please add your birth date to continue.\");\n return;\n }\n if (birthDateInput > TODAY_DATE_INPUT) {\n setFormError(\"Your birth date can't be in the future.\");\n return;\n }\n\n let expectancy: number | null = null;\n if (expectancyInput.trim() !== \"\") {\n const parsed = Number(expectancyInput);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > 130) {\n setFormError(\n \"Life expectancy should be a whole number of years (1–130).\",\n );\n return;\n }\n expectancy = parsed;\n }\n\n try {\n await updateProfile({\n variables: {\n input: {\n birthDate: new Date(\n `${birthDateInput}T00:00:00.000Z`,\n ).toISOString(),\n lifeExpectancyYears: expectancy,\n hideRemainingWeeks: hideRemainingInput,\n finiteLifeOptIn: true,\n },\n },\n });\n await Promise.all([refetchWeeks(), refetchProfile()]);\n setSetupOpen(false);\n addToast({\n variant: \"success\",\n title: \"Saved — this is just for you\",\n description: \"You can change or turn this off anytime.\",\n });\n } catch {\n addToast({\n variant: \"error\",\n title: \"Couldn't save your settings\",\n description: \"Please try again in a moment.\",\n });\n }\n };\n\n // ---- Derived, honest values ----------------------------------------------\n const hasBirthDate = !!weeks?.birthDate;\n const hideRemaining =\n weeks?.hideRemainingWeeks ?? profile?.hideRemainingWeeks ?? false;\n\n const weeksLived = weeks?.weeksLived ?? null;\n const expectancyYears = weeks?.lifeExpectancyYears ?? null;\n // Only reveal the full mortal horizon when the user hasn't asked to hide it.\n const gridTotalWeeks = hideRemaining\n ? null\n : (weeks?.expectedTotalWeeks ?? null);\n const weeksRemaining =\n hideRemaining || weeks?.weeksRemaining == null\n ? null\n : weeks.weeksRemaining;\n\n // Reflection: where the recent (backend-defined) window actually went.\n const domainSlices: DonutSlice[] = useMemo(() => {\n const rollups: LifeDomainRollup[] = weeks?.domainRollups ?? [];\n return rollups\n .filter((r) => r.totalSeconds > 0)\n .map((r) => ({\n name: lifeDomainLabel(r.domain),\n value: r.totalSeconds,\n }));\n }, [weeks?.domainRollups]);\n const rollupTotalSeconds = domainSlices.reduce((sum, s) => sum + s.value, 0);\n\n const loading = weeksLoading || profileLoading;\n const hardError = (weeksError || profileError) && !weeks && !profile;\n\n const setupAction = (\n <button\n type=\"button\"\n onClick={openSetup}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <SlidersHorizontal className=\"h-4 w-4\" />\n Life settings\n </button>\n );\n\n if (loading && !weeks && !profile) {\n return (\n <PageLayout\n title=\"Your Life in Weeks\"\n description=\"A calm, private view of the weeks you've lived — and the ones still ahead.\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Your Life in Weeks\"\n description=\"A calm, private view of the weeks you've lived — and the ones still ahead.\"\n purpose=\"Each square is a single week of your life. It's a gentle memento — not a countdown — meant only for you, to help this week feel a little more intentional.\"\n actions={setupAction}\n >\n {hardError ? (\n <div className=\"rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n We couldn't load your life view right now. Please try again in a\n moment.\n </div>\n ) : (\n <div className=\"space-y-6\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* The grid — the memento itself */}\n <div className=\"lg:col-span-2\">\n <ChartCard\n title=\"Your life in weeks\"\n description={\n hasBirthDate\n ? \"Each row is a year; each square, a week you have lived or have yet to live.\"\n : \"Add your birth date to fill in the weeks you've already lived.\"\n }\n >\n <WeeksGrid\n weeksLived={weeksLived}\n totalWeeks={gridTotalWeeks}\n ariaLabel={\n hasBirthDate\n ? `Your life in weeks: ${nf(weeksLived ?? 0)} weeks lived so far`\n : \"Your life in weeks — birth date not set yet\"\n }\n />\n <div className=\"mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] bg-action-primary-bg\" />\n Weeks lived\n </span>\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] border-2 border-action-primary-bg bg-bg-surface\" />\n This week\n </span>\n <span className=\"flex items-center gap-1.5\">\n <span className=\"h-3 w-3 rounded-[3px] border border-border-seam\" />\n Ahead\n </span>\n </div>\n </ChartCard>\n </div>\n\n {/* The calm side card */}\n <GlassCard\n treatment=\"glass\"\n glow\n className=\"flex flex-col p-5 sm:p-6\"\n >\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/10 text-primary\">\n <Hourglass className=\"h-5 w-5\" />\n </div>\n\n {hasBirthDate ? (\n <div className=\"mt-4 space-y-5\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n You have lived\n </p>\n <p className=\"mt-0.5 text-3xl font-bold tabular-nums text-text-primary\">\n {nf(weeksLived ?? 0)}{\" \"}\n <span className=\"text-xl font-semibold text-text-secondary\">\n weeks\n </span>\n </p>\n </div>\n\n {expectancyYears != null && weeksRemaining != null ? (\n <div>\n <p className=\"text-sm text-text-secondary\">\n If you live to {expectancyYears}, about\n </p>\n <p className=\"mt-0.5 text-2xl font-bold tabular-nums text-text-primary\">\n {nf(weeksRemaining)}{\" \"}\n <span className=\"text-lg font-semibold text-text-secondary\">\n remain\n </span>\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {weeksToYears(weeksLived ?? 0)} years lived ·{\" \"}\n {weeksToYears(weeksRemaining)} years ahead\n </p>\n </div>\n ) : (\n <p className=\"text-xs text-text-secondary\">\n {hideRemaining\n ? \"You've chosen to see only the weeks you've lived.\"\n : \"Add a life expectancy in settings to gently sketch the weeks ahead.\"}\n </p>\n )}\n\n <div className=\"rounded-lg border border-border-seam bg-bg-surface/40 p-3\">\n <p className=\"flex items-start gap-2 text-sm text-text-primary\">\n <Sparkles className=\"mt-0.5 h-4 w-4 shrink-0 text-primary\" />\n <span>\n What if today was your last? Make this week count.\n </span>\n </p>\n </div>\n </div>\n ) : (\n <div className=\"mt-4 space-y-4\">\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n See your life in weeks\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Add your birth date and we'll gently fill in the weeks\n you've lived so far — a quiet reminder that time is\n finite, and this week is yours to spend.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={openSetup}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n Add your birth date\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n\n <p className=\"mt-6 flex items-start gap-1.5 text-xs text-text-secondary\">\n <Lock className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <span>\n Only you can see this. Your birth date is never shared, and\n you can turn this off anytime.\n </span>\n </p>\n </GlassCard>\n </div>\n\n {/* Gentle reflection — only when there's real recent data to show */}\n {domainSlices.length > 0 && (\n <ChartCard\n title=\"Where your recent weeks went\"\n description=\"How your logged time settled across life's domains lately — a mirror, not a scorecard.\"\n >\n <DonutChart\n data={domainSlices}\n centerLabel=\"logged lately\"\n centerValue={formatSecondsHuman(rollupTotalSeconds)}\n valueFormat={(v) => formatSecondsHuman(v)}\n emptyLabel=\"Nothing logged in this window yet.\"\n ariaLabel=\"Recent time by life domain\"\n />\n </ChartCard>\n )}\n\n {/* Honest first-run nudge when nothing has been logged either */}\n {hasBirthDate && domainSlices.length === 0 && (\n <EmptyState\n title=\"Your weeks are yours to shape\"\n description=\"As you capture how you spend your time, a quiet reflection of your recent weeks will appear here — only for you.\"\n />\n )}\n </div>\n )}\n\n {/* Opt-in setup */}\n <Modal\n open={setupOpen}\n onClose={() => setSetupOpen(false)}\n title=\"Your life in weeks\"\n description=\"This is private and entirely optional. Add only what feels comfortable.\"\n footer={\n <>\n <button\n type=\"button\"\n onClick={() => setSetupOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n form=\"finite-life-setup\"\n disabled={saving}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {saving ? \"Saving…\" : \"Save\"}\n </button>\n </>\n }\n >\n <form\n id=\"finite-life-setup\"\n onSubmit={handleSave}\n className=\"space-y-5\"\n >\n <div>\n <label\n htmlFor=\"finite-life-birthdate\"\n className=\"flex items-center gap-1.5 text-sm font-medium text-text-primary\"\n >\n <CalendarDays className=\"h-4 w-4 text-text-secondary\" />\n Your birth date\n </label>\n <input\n id=\"finite-life-birthdate\"\n type=\"date\"\n value={birthDateInput}\n max={TODAY_DATE_INPUT}\n onChange={(e) => setBirthDateInput(e.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Used only to count the weeks you've lived. It stays with you.\n </p>\n </div>\n\n <div>\n <label\n htmlFor=\"finite-life-expectancy\"\n className=\"text-sm font-medium text-text-primary\"\n >\n Life expectancy{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <input\n id=\"finite-life-expectancy\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n max={130}\n step={1}\n placeholder=\"e.g. 90\"\n value={expectancyInput}\n onChange={(e) => setExpectancyInput(e.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary\"\n />\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n A gentle horizon for the weeks ahead — never a deadline. Leave it\n blank to simply see the weeks you've lived.\n </p>\n </div>\n\n <label className=\"flex items-start gap-2.5 rounded-lg border border-border-subtle bg-bg-surface/40 p-3\">\n <input\n type=\"checkbox\"\n checked={hideRemainingInput}\n onChange={(e) => setHideRemainingInput(e.target.checked)}\n className=\"mt-0.5 h-4 w-4 shrink-0 rounded border-border-subtle\"\n />\n <span className=\"text-sm text-text-primary\">\n Hide the weeks ahead\n <span className=\"mt-0.5 block text-xs text-text-secondary\">\n Show only the weeks you've lived — no horizon, no countdown.\n </span>\n </span>\n </label>\n\n {formError && (\n <p className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n {formError}\n </p>\n )}\n </form>\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -2,42 +2,42 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { cn as t } from "../../utils/helpers.js";
3
3
  import { StatCard as n } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
5
- import { useMyBalanceTargetsQuery as i, useMyLifeBalanceQuery as a, useSetBalanceTargetMutation as o } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as s } from "../../components/PageLayout.js";
7
- import { StatusBadge as c } from "../../components/StatusBadge.js";
8
- import { EmptyState as l } from "../../components/EmptyState.js";
5
+ import { EmptyState as i } from "../../components/EmptyState.js";
6
+ import { useMyBalanceTargetsQuery as a, useMyLifeBalanceQuery as o, useSetBalanceTargetMutation as s } from "../../../generated/wspace-operations.js";
7
+ import { PageLayout as c } from "../../components/PageLayout.js";
8
+ import { StatusBadge as l } from "../../components/StatusBadge.js";
9
9
  import { Modal as u } from "../../components/Modal.js";
10
10
  import { ChartCard as d } from "../../components/ChartCard.js";
11
11
  import { ToastContainer as f, useToast as p } from "../../components/Toast.js";
12
12
  import { ProgressMeter as ee } from "../../components/charts/ProgressMeter.js";
13
- import { BalanceWheel as m } from "../../components/charts/BalanceWheel.js";
13
+ import { BalanceWheel as te } from "../../components/charts/BalanceWheel.js";
14
14
  import "../../components/index.js";
15
- import { ALL_LIFE_DOMAINS as h, formatSecondsHuman as g, isoDaysAgo as te, isoNow as _, lifeDomainLabel as v, secondsToHours as y } from "./domains.js";
15
+ import { ALL_LIFE_DOMAINS as m, formatSecondsHuman as h, isoDaysAgo as g, isoNow as _, lifeDomainLabel as v, secondsToHours as y } from "./domains.js";
16
16
  import { useEffect as b, useMemo as x, useState as S } from "react";
17
- import { Check as C, Clock as w, Gauge as T, Scale as ne, Sparkles as re, Target as E } from "lucide-react";
18
- import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
19
- import { GlassCard as ie } from "@burdenoff/fe-libs/ui";
17
+ import { Check as C, Clock as w, Gauge as ne, Scale as re, Sparkles as ie, Target as T } from "lucide-react";
18
+ import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
19
+ import { GlassCard as ae } from "@burdenoff/fe-libs/ui";
20
20
  //#region src/timecampus/pages/lifetime/LifeBalancePage.tsx
21
- var A = [{
21
+ var k = [{
22
22
  label: "7 days",
23
23
  days: 7
24
24
  }, {
25
25
  label: "30 days",
26
26
  days: 30
27
27
  }];
28
- function j(e) {
28
+ function A(e) {
29
29
  return Math.round(e * 100);
30
30
  }
31
- function ae(e) {
31
+ function j(e) {
32
32
  return y(e).toFixed(1);
33
33
  }
34
34
  var M = 168;
35
35
  function N({ open: e, onClose: n, currentTargets: r, onSaved: i, notify: a }) {
36
- let [s, { loading: c }] = o(), [l, d] = S({});
36
+ let [o, { loading: c }] = s(), [l, d] = S({});
37
37
  b(() => {
38
38
  if (!e) return;
39
39
  let t = {};
40
- for (let e of h) {
40
+ for (let e of m) {
41
41
  let n = r[e];
42
42
  t[e] = typeof n == "number" ? String(n) : "";
43
43
  }
@@ -45,33 +45,33 @@ function N({ open: e, onClose: n, currentTargets: r, onSaved: i, notify: a }) {
45
45
  }, [e, r]);
46
46
  let f = x(() => {
47
47
  let e = 0;
48
- for (let t of h) {
48
+ for (let t of m) {
49
49
  let n = l[t], r = n ? Number(n) : NaN;
50
50
  Number.isFinite(r) && r > 0 && (e += r);
51
51
  }
52
52
  return Math.round(e * 10) / 10;
53
53
  }, [l]), p = f > M;
54
- return /* @__PURE__ */ k(u, {
54
+ return /* @__PURE__ */ O(u, {
55
55
  open: e,
56
56
  onClose: n,
57
57
  title: "Set your desired balance",
58
58
  description: "Declare the hours per week you want to spend in each life domain. The wheel compares this to where your time actually goes.",
59
59
  size: "xl",
60
- footer: /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O("button", {
60
+ footer: /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D("button", {
61
61
  onClick: n,
62
62
  disabled: c,
63
63
  className: "rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary disabled:opacity-60",
64
64
  children: "Cancel"
65
- }), /* @__PURE__ */ k("button", {
65
+ }), /* @__PURE__ */ O("button", {
66
66
  onClick: async () => {
67
67
  let e = [];
68
- for (let t of h) {
68
+ for (let t of m) {
69
69
  let n = l[t];
70
70
  if (n === void 0 || n.trim() === "") continue;
71
71
  let i = Number(n);
72
72
  if (!Number.isFinite(i) || i < 0) continue;
73
73
  let a = r[t];
74
- a !== void 0 && Math.abs(a - i) < 1e-9 || e.push(s({ variables: {
74
+ a !== void 0 && Math.abs(a - i) < 1e-9 || e.push(o({ variables: {
75
75
  domain: t,
76
76
  desiredHoursPerWeek: i
77
77
  } }));
@@ -88,19 +88,19 @@ function N({ open: e, onClose: n, currentTargets: r, onSaved: i, notify: a }) {
88
88
  },
89
89
  disabled: c,
90
90
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-60",
91
- children: [/* @__PURE__ */ O(C, { className: "h-4 w-4" }), c ? "Saving…" : "Save targets"]
91
+ children: [/* @__PURE__ */ D(C, { className: "h-4 w-4" }), c ? "Saving…" : "Save targets"]
92
92
  })] }),
93
- children: [/* @__PURE__ */ O("div", {
93
+ children: [/* @__PURE__ */ D("div", {
94
94
  className: "space-y-2",
95
- children: h.map((e) => /* @__PURE__ */ k("div", {
95
+ children: m.map((e) => /* @__PURE__ */ O("div", {
96
96
  className: "flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-sunken/40 px-3 py-2",
97
- children: [/* @__PURE__ */ O("label", {
97
+ children: [/* @__PURE__ */ D("label", {
98
98
  htmlFor: `target-${e}`,
99
99
  className: "min-w-0 flex-1 truncate text-sm text-text-primary",
100
100
  children: v(e)
101
- }), /* @__PURE__ */ k("div", {
101
+ }), /* @__PURE__ */ O("div", {
102
102
  className: "flex shrink-0 items-center gap-2",
103
- children: [/* @__PURE__ */ O("input", {
103
+ children: [/* @__PURE__ */ D("input", {
104
104
  id: `target-${e}`,
105
105
  type: "number",
106
106
  min: 0,
@@ -113,13 +113,13 @@ function N({ open: e, onClose: n, currentTargets: r, onSaved: i, notify: a }) {
113
113
  })),
114
114
  placeholder: "0",
115
115
  className: "w-24 rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-right text-sm tabular-nums text-text-primary outline-none transition-colors focus:border-action-primary-bg"
116
- }), /* @__PURE__ */ O("span", {
116
+ }), /* @__PURE__ */ D("span", {
117
117
  className: "w-12 text-xs text-text-secondary",
118
118
  children: "h / wk"
119
119
  })]
120
120
  })]
121
121
  }, e))
122
- }), /* @__PURE__ */ k("p", {
122
+ }), /* @__PURE__ */ O("p", {
123
123
  className: t("mt-4 text-xs tabular-nums", p ? "text-status-warning-text" : "text-text-secondary"),
124
124
  children: [
125
125
  f.toFixed(1),
@@ -133,38 +133,38 @@ function N({ open: e, onClose: n, currentTargets: r, onSaved: i, notify: a }) {
133
133
  }
134
134
  function P({ gap: e }) {
135
135
  let t = Math.round(Math.abs(e) * 100);
136
- return t < 2 ? /* @__PURE__ */ O(c, {
136
+ return t < 2 ? /* @__PURE__ */ D(l, {
137
137
  variant: "success",
138
138
  label: "On target"
139
- }) : e > 0 ? /* @__PURE__ */ O(c, {
139
+ }) : e > 0 ? /* @__PURE__ */ D(l, {
140
140
  variant: "warning",
141
141
  label: `+${t}pp over`
142
- }) : /* @__PURE__ */ O(c, {
142
+ }) : /* @__PURE__ */ D(l, {
143
143
  variant: "info",
144
144
  label: `${t}pp under`
145
145
  });
146
146
  }
147
147
  function F() {
148
- let { workspaceId: o = "" } = e(), { toasts: c, addToast: u, dismissToast: y } = p(), b = (e, t, n) => u({
148
+ let { workspaceId: s = "" } = e(), { toasts: l, addToast: u, dismissToast: y } = p(), b = (e, t, n) => u({
149
149
  variant: e,
150
150
  title: t,
151
151
  description: n
152
152
  }), [C, M] = S(30), [F, I] = S(!1), L = x(() => ({
153
- from: te(C),
153
+ from: g(C),
154
154
  to: _()
155
- }), [C]), { data: R, loading: z, error: B, refetch: V } = a({
155
+ }), [C]), { data: R, loading: z, error: B, refetch: V } = o({
156
156
  variables: {
157
157
  from: L.from,
158
158
  to: L.to
159
159
  },
160
- skip: !o
161
- }), { data: H, refetch: oe } = i({ skip: !o }), U = R?.myLifeBalance, W = x(() => U?.byDomain ?? [], [U]), G = U?.hasTargets ?? !1, K = x(() => {
160
+ skip: !s
161
+ }), { data: H, refetch: oe } = a({ skip: !s }), U = R?.myLifeBalance, W = x(() => U?.byDomain ?? [], [U]), G = U?.hasTargets ?? !1, K = x(() => {
162
162
  let e = {}, t = H?.myBalanceTargets ?? [];
163
163
  for (let n of t) e[n.domain] = n.desiredHoursPerWeek;
164
164
  return e;
165
165
  }, [H]), se = H?.myBalanceTargets.length ?? 0, q = x(() => {
166
166
  let e = /* @__PURE__ */ new Map();
167
- return h.forEach((t, n) => e.set(t, n)), e;
167
+ return m.forEach((t, n) => e.set(t, n)), e;
168
168
  }, []), ce = x(() => [...W].sort((e, t) => (q.get(e.domain) ?? 99) - (q.get(t.domain) ?? 99)).map((e) => ({
169
169
  domain: e.domain,
170
170
  label: v(e.domain),
@@ -178,111 +178,111 @@ function F() {
178
178
  W,
179
179
  G,
180
180
  K
181
- ]), J = U?.coveredSeconds ?? 0, Y = U?.unaccountedSeconds ?? 0, X = J + Y, Z = X > 0 ? Math.round(J / X * 100) : 0, ue = W.filter((e) => e.actualSeconds > 0).length, de = J > 0 && W.length > 0, fe = A.find((e) => e.days === C)?.label ?? `${C} days`, Q = () => {
181
+ ]), J = U?.coveredSeconds ?? 0, Y = U?.unaccountedSeconds ?? 0, X = J + Y, Z = X > 0 ? Math.round(J / X * 100) : 0, ue = W.filter((e) => e.actualSeconds > 0).length, de = J > 0 && W.length > 0, fe = k.find((e) => e.days === C)?.label ?? `${C} days`, Q = () => {
182
182
  V(), oe();
183
183
  }, $ = {
184
184
  title: "Life Balance",
185
185
  description: "How your finite time actually splits across life",
186
186
  purpose: "Your week is a fixed budget of hours. This wheel shows how that budget actually splits across the domains of your life, overlaid with the balance you said you want — so the gap between the two is impossible to ignore.",
187
- actions: /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O("div", {
187
+ actions: /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D("div", {
188
188
  className: "flex items-center rounded-lg border border-border-subtle bg-bg-sunken/50 p-0.5",
189
- children: A.map((e) => /* @__PURE__ */ O("button", {
189
+ children: k.map((e) => /* @__PURE__ */ D("button", {
190
190
  onClick: () => M(e.days),
191
191
  className: t("rounded-md px-2.5 py-1 text-xs font-medium transition-colors", C === e.days ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary"),
192
192
  children: e.label
193
193
  }, e.days))
194
- }), /* @__PURE__ */ k("button", {
194
+ }), /* @__PURE__ */ O("button", {
195
195
  onClick: () => I(!0),
196
196
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
197
- children: [/* @__PURE__ */ O(E, { className: "h-4 w-4" }), "Set targets"]
197
+ children: [/* @__PURE__ */ D(T, { className: "h-4 w-4" }), "Set targets"]
198
198
  })] })
199
199
  };
200
- return z && !U ? /* @__PURE__ */ k(s, {
200
+ return z && !U ? /* @__PURE__ */ O(c, {
201
201
  ...$,
202
- children: [/* @__PURE__ */ k("div", {
202
+ children: [/* @__PURE__ */ O("div", {
203
203
  className: "space-y-6",
204
- children: [/* @__PURE__ */ O(r, {
204
+ children: [/* @__PURE__ */ D(r, {
205
205
  variant: "card",
206
206
  count: 4
207
- }), /* @__PURE__ */ O(r, {
207
+ }), /* @__PURE__ */ D(r, {
208
208
  variant: "card",
209
209
  count: 2
210
210
  })]
211
- }), /* @__PURE__ */ O(f, {
212
- toasts: c,
211
+ }), /* @__PURE__ */ D(f, {
212
+ toasts: l,
213
213
  onDismiss: y
214
214
  })]
215
- }) : B ? /* @__PURE__ */ k(s, {
215
+ }) : B ? /* @__PURE__ */ O(c, {
216
216
  ...$,
217
- children: [/* @__PURE__ */ O("div", {
217
+ children: [/* @__PURE__ */ D("div", {
218
218
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
219
219
  children: "We couldn't load your life balance right now. Please try again in a moment."
220
- }), /* @__PURE__ */ O(f, {
221
- toasts: c,
220
+ }), /* @__PURE__ */ D(f, {
221
+ toasts: l,
222
222
  onDismiss: y
223
223
  })]
224
- }) : de ? /* @__PURE__ */ k(s, {
224
+ }) : de ? /* @__PURE__ */ O(c, {
225
225
  ...$,
226
226
  children: [
227
- /* @__PURE__ */ k("div", {
227
+ /* @__PURE__ */ O("div", {
228
228
  className: "space-y-6",
229
- children: [/* @__PURE__ */ k("div", {
229
+ children: [/* @__PURE__ */ O("div", {
230
230
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
231
231
  children: [
232
- /* @__PURE__ */ O(n, {
232
+ /* @__PURE__ */ D(n, {
233
233
  featured: !0,
234
234
  title: "Tracked time",
235
- value: g(J),
236
- icon: /* @__PURE__ */ O(w, { className: "h-5 w-5" }),
235
+ value: h(J),
236
+ icon: /* @__PURE__ */ D(w, { className: "h-5 w-5" }),
237
237
  description: `Last ${fe}`
238
238
  }),
239
- /* @__PURE__ */ O(n, {
239
+ /* @__PURE__ */ D(n, {
240
240
  title: "Unaccounted",
241
- value: g(Y),
242
- icon: /* @__PURE__ */ O(T, { className: "h-5 w-5" }),
241
+ value: h(Y),
242
+ icon: /* @__PURE__ */ D(ne, { className: "h-5 w-5" }),
243
243
  description: `${Z}% of time is tracked`
244
244
  }),
245
- /* @__PURE__ */ O(n, {
245
+ /* @__PURE__ */ D(n, {
246
246
  title: "Active domains",
247
247
  value: String(ue),
248
- icon: /* @__PURE__ */ O(ne, { className: "h-5 w-5" }),
249
- description: `of ${h.length} life domains`
248
+ icon: /* @__PURE__ */ D(re, { className: "h-5 w-5" }),
249
+ description: `of ${m.length} life domains`
250
250
  }),
251
- /* @__PURE__ */ O(n, {
251
+ /* @__PURE__ */ D(n, {
252
252
  title: "Targets set",
253
- value: `${se} / ${h.length}`,
254
- icon: /* @__PURE__ */ O(E, { className: "h-5 w-5" }),
253
+ value: `${se} / ${m.length}`,
254
+ icon: /* @__PURE__ */ D(T, { className: "h-5 w-5" }),
255
255
  description: G ? "Desired balance declared" : "Not set yet"
256
256
  })
257
257
  ]
258
- }), /* @__PURE__ */ k("div", {
258
+ }), /* @__PURE__ */ O("div", {
259
259
  className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
260
- children: [/* @__PURE__ */ k(d, {
260
+ children: [/* @__PURE__ */ O(d, {
261
261
  className: "lg:col-span-2",
262
262
  title: "Life Balance Wheel",
263
263
  description: G ? "Solid = where your time actually goes · dashed = the balance you want" : "Where your time actually goes across life domains",
264
- children: [/* @__PURE__ */ O(m, {
264
+ children: [/* @__PURE__ */ D(te, {
265
265
  data: ce,
266
266
  height: 360,
267
267
  emptyLabel: "No tracked time in this range yet.",
268
268
  ariaLabel: "Life balance wheel: actual versus desired share of time by domain"
269
- }), /* @__PURE__ */ k("div", {
269
+ }), /* @__PURE__ */ O("div", {
270
270
  className: "mt-4 border-t border-border-seam pt-4",
271
- children: [/* @__PURE__ */ k("div", {
271
+ children: [/* @__PURE__ */ O("div", {
272
272
  className: "flex items-center justify-between text-xs text-text-secondary",
273
- children: [/* @__PURE__ */ O("span", { children: "Time tracked" }), /* @__PURE__ */ k("span", {
273
+ children: [/* @__PURE__ */ D("span", { children: "Time tracked" }), /* @__PURE__ */ O("span", {
274
274
  className: "tabular-nums text-text-primary",
275
275
  children: [
276
- g(J),
276
+ h(J),
277
277
  " of",
278
278
  " ",
279
- g(X),
279
+ h(X),
280
280
  " (",
281
281
  Z,
282
282
  "%)"
283
283
  ]
284
284
  })]
285
- }), /* @__PURE__ */ O(ee, {
285
+ }), /* @__PURE__ */ D(ee, {
286
286
  className: "mt-2",
287
287
  value: Z,
288
288
  max: 100,
@@ -291,24 +291,24 @@ function F() {
291
291
  label: "Coverage"
292
292
  })]
293
293
  })]
294
- }), /* @__PURE__ */ k(ie, {
294
+ }), /* @__PURE__ */ O(ae, {
295
295
  treatment: "glass",
296
296
  className: "p-5 sm:p-6",
297
297
  children: [
298
- /* @__PURE__ */ O("h2", {
298
+ /* @__PURE__ */ D("h2", {
299
299
  className: "text-xl font-bold leading-tight text-text-primary",
300
300
  children: "Your week vs the life you want"
301
301
  }),
302
- /* @__PURE__ */ O("p", {
302
+ /* @__PURE__ */ D("p", {
303
303
  className: "mt-1 text-xs text-text-secondary",
304
304
  children: "Per-domain — hours tracked vs your desired share"
305
305
  }),
306
- !G && /* @__PURE__ */ k("div", {
306
+ !G && /* @__PURE__ */ O("div", {
307
307
  className: "mt-3 flex items-start gap-2 rounded-lg border border-border-subtle bg-action-primary-bg/10 p-3 text-xs text-text-secondary",
308
- children: [/* @__PURE__ */ O(re, { className: "mt-0.5 h-4 w-4 shrink-0 text-primary" }), /* @__PURE__ */ k("span", { children: [
308
+ children: [/* @__PURE__ */ D(ie, { className: "mt-0.5 h-4 w-4 shrink-0 text-primary" }), /* @__PURE__ */ O("span", { children: [
309
309
  "You haven't declared a desired balance yet, so the wheel is descriptive only.",
310
310
  " ",
311
- /* @__PURE__ */ O("button", {
311
+ /* @__PURE__ */ D("button", {
312
312
  onClick: () => I(!0),
313
313
  className: "font-medium text-primary underline-offset-2 hover:underline",
314
314
  children: "Set your targets"
@@ -317,38 +317,38 @@ function F() {
317
317
  "to see the gap."
318
318
  ] })]
319
319
  }),
320
- /* @__PURE__ */ O("ul", {
320
+ /* @__PURE__ */ D("ul", {
321
321
  className: "mt-4 max-h-[30rem] space-y-2 overflow-y-auto pr-1",
322
322
  children: le.map((e) => {
323
323
  let t = K[e.domain];
324
- return /* @__PURE__ */ k("li", {
324
+ return /* @__PURE__ */ O("li", {
325
325
  className: "flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface/40 px-3 py-2.5",
326
326
  children: [
327
- /* @__PURE__ */ k("div", {
327
+ /* @__PURE__ */ O("div", {
328
328
  className: "min-w-0 flex-1",
329
- children: [/* @__PURE__ */ O("p", {
329
+ children: [/* @__PURE__ */ D("p", {
330
330
  className: "truncate text-sm font-medium text-text-primary",
331
331
  children: v(e.domain)
332
- }), /* @__PURE__ */ k("p", {
332
+ }), /* @__PURE__ */ O("p", {
333
333
  className: "mt-0.5 text-xs text-text-secondary",
334
334
  children: [
335
- j(e.actualShare),
335
+ A(e.actualShare),
336
336
  "% of tracked",
337
- G ? ` · ${j(e.desiredShare)}% desired` : "",
337
+ G ? ` · ${A(e.desiredShare)}% desired` : "",
338
338
  typeof t == "number" ? ` · ${t}h/wk target` : ""
339
339
  ]
340
340
  })]
341
341
  }),
342
- /* @__PURE__ */ O("div", {
342
+ /* @__PURE__ */ D("div", {
343
343
  className: "shrink-0 text-right",
344
- children: /* @__PURE__ */ k("p", {
344
+ children: /* @__PURE__ */ O("p", {
345
345
  className: "text-sm font-semibold tabular-nums text-text-primary",
346
- children: [ae(e.actualSeconds), "h"]
346
+ children: [j(e.actualSeconds), "h"]
347
347
  })
348
348
  }),
349
- G && /* @__PURE__ */ O("div", {
349
+ G && /* @__PURE__ */ D("div", {
350
350
  className: "shrink-0",
351
- children: /* @__PURE__ */ O(P, { gap: e.gap })
351
+ children: /* @__PURE__ */ D(P, { gap: e.gap })
352
352
  })
353
353
  ]
354
354
  }, e.domain);
@@ -358,39 +358,39 @@ function F() {
358
358
  })]
359
359
  })]
360
360
  }),
361
- /* @__PURE__ */ O(N, {
361
+ /* @__PURE__ */ D(N, {
362
362
  open: F,
363
363
  onClose: () => I(!1),
364
364
  currentTargets: K,
365
365
  onSaved: Q,
366
366
  notify: b
367
367
  }),
368
- /* @__PURE__ */ O(f, {
369
- toasts: c,
368
+ /* @__PURE__ */ D(f, {
369
+ toasts: l,
370
370
  onDismiss: y
371
371
  })
372
372
  ]
373
- }) : /* @__PURE__ */ k(s, {
373
+ }) : /* @__PURE__ */ O(c, {
374
374
  ...$,
375
375
  children: [
376
- /* @__PURE__ */ O(l, {
376
+ /* @__PURE__ */ D(i, {
377
377
  title: "No life data yet",
378
378
  description: "Once you start tracking time across your day, your balance wheel will show how your hours split across each life domain. In the meantime, you can declare the balance you're aiming for.",
379
- action: /* @__PURE__ */ k("button", {
379
+ action: /* @__PURE__ */ O("button", {
380
380
  onClick: () => I(!0),
381
381
  className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
382
- children: [/* @__PURE__ */ O(E, { className: "h-4 w-4" }), "Declare what you value"]
382
+ children: [/* @__PURE__ */ D(T, { className: "h-4 w-4" }), "Declare what you value"]
383
383
  })
384
384
  }),
385
- /* @__PURE__ */ O(N, {
385
+ /* @__PURE__ */ D(N, {
386
386
  open: F,
387
387
  onClose: () => I(!1),
388
388
  currentTargets: K,
389
389
  onSaved: Q,
390
390
  notify: b
391
391
  }),
392
- /* @__PURE__ */ O(f, {
393
- toasts: c,
392
+ /* @__PURE__ */ D(f, {
393
+ toasts: l,
394
394
  onDismiss: y
395
395
  })
396
396
  ]