@burdenoff/microfe-timecampus 2026.703.6 → 2026.707.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 (66) hide show
  1. package/dist/generated/wspace-operations.js +1023 -1514
  2. package/dist/generated/wspace-operations.js.map +1 -1
  3. package/dist/generated/wspace-types.js +12 -18
  4. package/dist/generated/wspace-types.js.map +1 -1
  5. package/dist/index.js +4 -4
  6. package/dist/timecampus/TimeCampusRoot.js +130 -198
  7. package/dist/timecampus/TimeCampusRoot.js.map +1 -1
  8. package/dist/timecampus/TimeCampusRoutes.js +96 -164
  9. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  10. package/dist/timecampus/components/index.js +2 -2
  11. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +7 -7
  12. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +6 -6
  13. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +8 -8
  14. package/dist/timecampus/pages/geofencing/GeofencingPage.js +6 -6
  15. package/dist/timecampus/pages/lifetime/FiniteLifePage.js +91 -91
  16. package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
  17. package/dist/timecampus/pages/lifetime/LifeBalancePage.js +18 -18
  18. package/dist/timecampus/pages/lifetime/LifeTimelinePage.js +7 -7
  19. package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +1 -1
  20. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +6 -6
  21. package/dist/timecampus/pages/projects/ProjectDetailPage.js +7 -7
  22. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +212 -212
  23. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  24. package/dist/timecampus/pages/team/TeamMembersPage.js +4 -4
  25. package/dist/timecampus/pages/time/TimeTrackingPage.js +143 -143
  26. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +1 -1
  28. package/dist/timecampus/pages/timeline/TimelinePage.js +1 -1
  29. package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
  30. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +87 -93
  31. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  32. package/package.json +1 -1
  33. package/dist/timecampus/pages/applets/AppletStorePage.js +0 -167
  34. package/dist/timecampus/pages/applets/AppletStorePage.js.map +0 -1
  35. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +0 -162
  36. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +0 -1
  37. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +0 -133
  38. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +0 -1
  39. package/dist/timecampus/pages/channels/ChannelsPage.js +0 -418
  40. package/dist/timecampus/pages/channels/ChannelsPage.js.map +0 -1
  41. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +0 -142
  42. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +0 -1
  43. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +0 -119
  44. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +0 -1
  45. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +0 -110
  46. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +0 -1
  47. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +0 -83
  48. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +0 -1
  49. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +0 -198
  50. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +0 -1
  51. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +0 -124
  52. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +0 -1
  53. package/dist/timecampus/pages/templates/TemplateApplicationPage.js +0 -88
  54. package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +0 -1
  55. package/dist/timecampus/pages/templates/TemplateDetailPage.js +0 -135
  56. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +0 -1
  57. package/dist/timecampus/pages/templates/TemplatesPage.js +0 -191
  58. package/dist/timecampus/pages/templates/TemplatesPage.js.map +0 -1
  59. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +0 -196
  60. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +0 -1
  61. package/dist/timecampus/pages/widgets/WidgetsPage.js +0 -297
  62. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +0 -1
  63. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +0 -133
  64. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +0 -1
  65. package/dist/timecampus/pages/workflows/WorkflowsPage.js +0 -311
  66. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +0 -1
@@ -6,9 +6,9 @@ import { useApproveExpenseMutation as a, useCreateExpenseMutation as ee, useDele
6
6
  import { PageLayout as o } from "../../components/PageLayout.js";
7
7
  import { StatusBadge as se } from "../../components/StatusBadge.js";
8
8
  import { EmptyState as ce } from "../../components/EmptyState.js";
9
- import { ChartCard as s } from "../../components/ChartCard.js";
9
+ import { Modal as s } from "../../components/Modal.js";
10
+ import { ChartCard as c } from "../../components/ChartCard.js";
10
11
  import { ToastContainer as le, useToast as ue } from "../../components/Toast.js";
11
- import { Modal as c } from "../../components/Modal.js";
12
12
  import { DonutChart as de } from "../../components/charts/DonutChart.js";
13
13
  import { RadialGauge as fe } from "../../components/charts/RadialGauge.js";
14
14
  import "../../components/index.js";
@@ -310,7 +310,7 @@ function C() {
310
310
  }) : /* @__PURE__ */ h(p, { children: [
311
311
  /* @__PURE__ */ h("div", {
312
312
  className: "grid grid-cols-1 gap-4 lg:grid-cols-3",
313
- children: [/* @__PURE__ */ m(s, {
313
+ children: [/* @__PURE__ */ m(c, {
314
314
  title: "Spend by category",
315
315
  description: "Total tracked spend grouped by category",
316
316
  className: "lg:col-span-2",
@@ -323,7 +323,7 @@ function C() {
323
323
  emptyLabel: "No categorised spend yet.",
324
324
  ariaLabel: "Spend by category"
325
325
  })
326
- }), /* @__PURE__ */ m(s, {
326
+ }), /* @__PURE__ */ m(c, {
327
327
  title: "Approval progress",
328
328
  description: "Approved vs. awaiting review",
329
329
  children: /* @__PURE__ */ m(fe, {
@@ -441,7 +441,7 @@ function C() {
441
441
  })
442
442
  })
443
443
  ] }),
444
- /* @__PURE__ */ m(c, {
444
+ /* @__PURE__ */ m(s, {
445
445
  open: qe,
446
446
  onClose: Q,
447
447
  title: I ? "Edit Expense" : "Add Expense",
@@ -570,7 +570,7 @@ function C() {
570
570
  ]
571
571
  })
572
572
  }),
573
- /* @__PURE__ */ m(c, {
573
+ /* @__PURE__ */ m(s, {
574
574
  open: W !== null,
575
575
  onClose: () => G(null),
576
576
  title: "Delete expense?",
@@ -591,7 +591,7 @@ function C() {
591
591
  children: "This permanently removes the expense. This action cannot be undone."
592
592
  })
593
593
  }),
594
- /* @__PURE__ */ h(c, {
594
+ /* @__PURE__ */ h(s, {
595
595
  open: K !== null,
596
596
  onClose: () => q(null),
597
597
  title: "Reject expense",
@@ -618,7 +618,7 @@ function C() {
618
618
  className: b
619
619
  })]
620
620
  }),
621
- /* @__PURE__ */ m(c, {
621
+ /* @__PURE__ */ m(s, {
622
622
  open: H !== null,
623
623
  onClose: () => U(null),
624
624
  title: "Expense review",
@@ -4,8 +4,8 @@ import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
4
4
  import { useCreateGeofenceMutation as i, useDeleteGeofenceMutation as a, useListGeofencesQuery as o, useToggleGeofenceMutation as s, useUpdateGeofenceMutation as c } from "../../../generated/wspace-operations.js";
5
5
  import { PageLayout as l } from "../../components/PageLayout.js";
6
6
  import { EmptyState as u } from "../../components/EmptyState.js";
7
- import { ChartCard as d } from "../../components/ChartCard.js";
8
- import { Modal as f } from "../../components/Modal.js";
7
+ import { Modal as d } from "../../components/Modal.js";
8
+ import { ChartCard as f } from "../../components/ChartCard.js";
9
9
  import { BarsChart as ee } from "../../components/charts/BarsChart.js";
10
10
  import { DonutChart as p } from "../../components/charts/DonutChart.js";
11
11
  import "../../components/index.js";
@@ -175,7 +175,7 @@ function k() {
175
175
  }),
176
176
  /* @__PURE__ */ C("div", {
177
177
  className: "mt-6 grid grid-cols-1 gap-4 lg:grid-cols-2",
178
- children: [/* @__PURE__ */ S(d, {
178
+ children: [/* @__PURE__ */ S(f, {
179
179
  title: "Trigger configuration",
180
180
  description: "How your zones fire",
181
181
  children: /* @__PURE__ */ S(p, {
@@ -185,7 +185,7 @@ function k() {
185
185
  ariaLabel: "Geofence trigger configuration mix",
186
186
  emptyLabel: "No zones to summarize yet"
187
187
  })
188
- }), /* @__PURE__ */ S(d, {
188
+ }), /* @__PURE__ */ S(f, {
189
189
  title: "Activity by zone",
190
190
  description: "Recorded enter/exit events per zone",
191
191
  children: /* @__PURE__ */ S(ee, {
@@ -351,7 +351,7 @@ function k() {
351
351
  })
352
352
  })
353
353
  ] }),
354
- /* @__PURE__ */ C(f, {
354
+ /* @__PURE__ */ C(d, {
355
355
  open: R,
356
356
  onClose: () => z(!1),
357
357
  title: B ? "Edit Geofence" : "Create Geofence",
@@ -487,7 +487,7 @@ function k() {
487
487
  ]
488
488
  })]
489
489
  }),
490
- /* @__PURE__ */ S(f, {
490
+ /* @__PURE__ */ S(d, {
491
491
  open: W !== null,
492
492
  onClose: () => G(null),
493
493
  title: "Delete Geofence",
@@ -1,33 +1,33 @@
1
1
  import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
- import { useMyLifeInWeeksQuery as ee, useMyLifeProfileQuery as te, useUpdateMyLifeProfileMutation as ne } from "../../../generated/wspace-operations.js";
4
- import { PageLayout as n } from "../../components/PageLayout.js";
5
- import { EmptyState as r } from "../../components/EmptyState.js";
6
- import { ChartCard as i } from "../../components/ChartCard.js";
7
- import { ToastContainer as a, useToast as o } from "../../components/Toast.js";
8
- import { Modal as s } from "../../components/Modal.js";
9
- import { DonutChart as re } from "../../components/charts/DonutChart.js";
10
- import { WeeksGrid as c } from "../../components/charts/WeeksGrid.js";
3
+ import { useMyLifeInWeeksQuery as n, useMyLifeProfileQuery as ee, useUpdateMyLifeProfileMutation as r } from "../../../generated/wspace-operations.js";
4
+ import { PageLayout as i } from "../../components/PageLayout.js";
5
+ import { EmptyState as te } from "../../components/EmptyState.js";
6
+ import { Modal as ne } from "../../components/Modal.js";
7
+ import { ChartCard as a } from "../../components/ChartCard.js";
8
+ import { ToastContainer as re, useToast as ie } from "../../components/Toast.js";
9
+ import { DonutChart as ae } from "../../components/charts/DonutChart.js";
10
+ import { WeeksGrid as o } from "../../components/charts/WeeksGrid.js";
11
11
  import "../../components/index.js";
12
- import { formatSecondsHuman as l, lifeDomainLabel as ie } from "./domains.js";
13
- import { useMemo as ae, useState as u } from "react";
14
- import { ArrowRight as oe, CalendarDays as se, Hourglass as d, Lock as f, SlidersHorizontal as p, Sparkles as m } from "lucide-react";
15
- import { Fragment as ce, jsx as h, jsxs as g } from "react/jsx-runtime";
16
- import { GlassCard as _ } from "@burdenoff/fe-libs/ui";
12
+ import { formatSecondsHuman as s, lifeDomainLabel as c } from "./domains.js";
13
+ import { useMemo as l, useState as u } from "react";
14
+ import { ArrowRight as d, CalendarDays as oe, Hourglass as f, Lock as p, SlidersHorizontal as m, Sparkles as h } from "lucide-react";
15
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
16
+ import { GlassCard as se } from "@burdenoff/fe-libs/ui";
17
17
  //#region src/timecampus/pages/lifetime/FiniteLifePage.tsx
18
- function v(e) {
18
+ function y(e) {
19
19
  return Math.max(0, Math.round(e)).toLocaleString();
20
20
  }
21
- function y(e) {
21
+ function b(e) {
22
22
  return (Math.round(e / 52 * 10) / 10).toFixed(1);
23
23
  }
24
- function b(e) {
24
+ function ce(e) {
25
25
  return e ? e.slice(0, 10) : "";
26
26
  }
27
27
  var x = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
28
28
  function S() {
29
- let { workspaceId: S = "" } = e(), { toasts: le, addToast: C, dismissToast: w } = o(), { data: T, loading: E, error: D, refetch: O } = ee({ skip: !S }), { data: k, loading: A, error: j, refetch: M } = te({ skip: !S }), [N, { loading: P }] = ne(), F = T?.myLifeInWeeks, I = k?.myLifeProfile, [L, R] = u(!1), [z, B] = u(""), [V, H] = u(""), [U, W] = u(!1), [G, K] = u(null), q = () => {
30
- B(b(I?.birthDate ?? F?.birthDate)), H(I?.lifeExpectancyYears == null ? F?.lifeExpectancyYears == null ? "" : String(F.lifeExpectancyYears) : String(I.lifeExpectancyYears)), W(I?.hideRemainingWeeks ?? F?.hideRemainingWeeks ?? !1), K(null), R(!0);
29
+ let { workspaceId: S = "" } = e(), { toasts: le, addToast: C, dismissToast: w } = ie(), { data: T, loading: E, error: D, refetch: O } = n({ skip: !S }), { data: k, loading: A, error: j, refetch: M } = ee({ skip: !S }), [N, { loading: P }] = r(), F = T?.myLifeInWeeks, I = k?.myLifeProfile, [L, R] = u(!1), [z, B] = u(""), [V, H] = u(""), [U, W] = u(!1), [G, K] = u(null), q = () => {
30
+ B(ce(I?.birthDate ?? F?.birthDate)), H(I?.lifeExpectancyYears == null ? F?.lifeExpectancyYears == null ? "" : String(F.lifeExpectancyYears) : String(I.lifeExpectancyYears)), W(I?.hideRemainingWeeks ?? F?.hideRemainingWeeks ?? !1), K(null), R(!0);
31
31
  }, ue = async (e) => {
32
32
  if (e?.preventDefault(), K(null), !z) {
33
33
  K("Please add your birth date to continue.");
@@ -64,88 +64,88 @@ function S() {
64
64
  description: "Please try again in a moment."
65
65
  });
66
66
  }
67
- }, J = !!F?.birthDate, Y = F?.hideRemainingWeeks ?? I?.hideRemainingWeeks ?? !1, X = F?.weeksLived ?? null, Z = F?.lifeExpectancyYears ?? null, de = Y ? null : F?.expectedTotalWeeks ?? null, Q = Y || F?.weeksRemaining == null ? null : F.weeksRemaining, $ = ae(() => (F?.domainRollups ?? []).filter((e) => e.totalSeconds > 0).map((e) => ({
68
- name: ie(e.domain),
67
+ }, J = !!F?.birthDate, Y = F?.hideRemainingWeeks ?? I?.hideRemainingWeeks ?? !1, X = F?.weeksLived ?? null, Z = F?.lifeExpectancyYears ?? null, de = Y ? null : F?.expectedTotalWeeks ?? null, Q = Y || F?.weeksRemaining == null ? null : F.weeksRemaining, $ = l(() => (F?.domainRollups ?? []).filter((e) => e.totalSeconds > 0).map((e) => ({
68
+ name: c(e.domain),
69
69
  value: e.totalSeconds
70
70
  })), [F?.domainRollups]), fe = $.reduce((e, t) => e + t.value, 0);
71
- return (E || A) && !F && !I ? /* @__PURE__ */ h(n, {
71
+ return (E || A) && !F && !I ? /* @__PURE__ */ _(i, {
72
72
  title: "Your Life in Weeks",
73
73
  description: "A calm, private view of the weeks you've lived — and the ones still ahead.",
74
- children: /* @__PURE__ */ h(t, { variant: "detail" })
75
- }) : /* @__PURE__ */ g(n, {
74
+ children: /* @__PURE__ */ _(t, { variant: "detail" })
75
+ }) : /* @__PURE__ */ v(i, {
76
76
  title: "Your Life in Weeks",
77
77
  description: "A calm, private view of the weeks you've lived — and the ones still ahead.",
78
78
  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.",
79
- actions: /* @__PURE__ */ g("button", {
79
+ actions: /* @__PURE__ */ v("button", {
80
80
  type: "button",
81
81
  onClick: q,
82
82
  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",
83
- children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), "Life settings"]
83
+ children: [/* @__PURE__ */ _(m, { className: "h-4 w-4" }), "Life settings"]
84
84
  }),
85
85
  children: [
86
- (D || j) && !F && !I ? /* @__PURE__ */ h("div", {
86
+ (D || j) && !F && !I ? /* @__PURE__ */ _("div", {
87
87
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
88
88
  children: "We couldn't load your life view right now. Please try again in a moment."
89
- }) : /* @__PURE__ */ g("div", {
89
+ }) : /* @__PURE__ */ v("div", {
90
90
  className: "space-y-6",
91
91
  children: [
92
- /* @__PURE__ */ g("div", {
92
+ /* @__PURE__ */ v("div", {
93
93
  className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
94
- children: [/* @__PURE__ */ h("div", {
94
+ children: [/* @__PURE__ */ _("div", {
95
95
  className: "lg:col-span-2",
96
- children: /* @__PURE__ */ g(i, {
96
+ children: /* @__PURE__ */ v(a, {
97
97
  title: "Your life in weeks",
98
98
  description: J ? "Each row is a year; each square, a week you have lived or have yet to live." : "Add your birth date to fill in the weeks you've already lived.",
99
- children: [/* @__PURE__ */ h(c, {
99
+ children: [/* @__PURE__ */ _(o, {
100
100
  weeksLived: X,
101
101
  totalWeeks: de,
102
- ariaLabel: J ? `Your life in weeks: ${v(X ?? 0)} weeks lived so far` : "Your life in weeks — birth date not set yet"
103
- }), /* @__PURE__ */ g("div", {
102
+ ariaLabel: J ? `Your life in weeks: ${y(X ?? 0)} weeks lived so far` : "Your life in weeks — birth date not set yet"
103
+ }), /* @__PURE__ */ v("div", {
104
104
  className: "mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 text-xs text-text-secondary",
105
105
  children: [
106
- /* @__PURE__ */ g("span", {
106
+ /* @__PURE__ */ v("span", {
107
107
  className: "flex items-center gap-1.5",
108
- children: [/* @__PURE__ */ h("span", { className: "h-3 w-3 rounded-[3px] bg-action-primary-bg" }), "Weeks lived"]
108
+ children: [/* @__PURE__ */ _("span", { className: "h-3 w-3 rounded-[3px] bg-action-primary-bg" }), "Weeks lived"]
109
109
  }),
110
- /* @__PURE__ */ g("span", {
110
+ /* @__PURE__ */ v("span", {
111
111
  className: "flex items-center gap-1.5",
112
- children: [/* @__PURE__ */ h("span", { className: "h-3 w-3 rounded-[3px] border-2 border-action-primary-bg bg-bg-surface" }), "This week"]
112
+ children: [/* @__PURE__ */ _("span", { className: "h-3 w-3 rounded-[3px] border-2 border-action-primary-bg bg-bg-surface" }), "This week"]
113
113
  }),
114
- /* @__PURE__ */ g("span", {
114
+ /* @__PURE__ */ v("span", {
115
115
  className: "flex items-center gap-1.5",
116
- children: [/* @__PURE__ */ h("span", { className: "h-3 w-3 rounded-[3px] border border-border-seam" }), "Ahead"]
116
+ children: [/* @__PURE__ */ _("span", { className: "h-3 w-3 rounded-[3px] border border-border-seam" }), "Ahead"]
117
117
  })
118
118
  ]
119
119
  })]
120
120
  })
121
- }), /* @__PURE__ */ g(_, {
121
+ }), /* @__PURE__ */ v(se, {
122
122
  treatment: "glass",
123
123
  glow: !0,
124
124
  className: "flex flex-col p-5 sm:p-6",
125
125
  children: [
126
- /* @__PURE__ */ h("div", {
126
+ /* @__PURE__ */ _("div", {
127
127
  className: "flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/10 text-primary",
128
- children: /* @__PURE__ */ h(d, { className: "h-5 w-5" })
128
+ children: /* @__PURE__ */ _(f, { className: "h-5 w-5" })
129
129
  }),
130
- J ? /* @__PURE__ */ g("div", {
130
+ J ? /* @__PURE__ */ v("div", {
131
131
  className: "mt-4 space-y-5",
132
132
  children: [
133
- /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
133
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
134
134
  className: "text-sm text-text-secondary",
135
135
  children: "You have lived"
136
- }), /* @__PURE__ */ g("p", {
136
+ }), /* @__PURE__ */ v("p", {
137
137
  className: "mt-0.5 text-3xl font-bold tabular-nums text-text-primary",
138
138
  children: [
139
- v(X ?? 0),
139
+ y(X ?? 0),
140
140
  " ",
141
- /* @__PURE__ */ h("span", {
141
+ /* @__PURE__ */ _("span", {
142
142
  className: "text-xl font-semibold text-text-secondary",
143
143
  children: "weeks"
144
144
  })
145
145
  ]
146
146
  })] }),
147
- Z != null && Q != null ? /* @__PURE__ */ g("div", { children: [
148
- /* @__PURE__ */ g("p", {
147
+ Z != null && Q != null ? /* @__PURE__ */ v("div", { children: [
148
+ /* @__PURE__ */ v("p", {
149
149
  className: "text-sm text-text-secondary",
150
150
  children: [
151
151
  "If you live to ",
@@ -153,108 +153,108 @@ function S() {
153
153
  ", about"
154
154
  ]
155
155
  }),
156
- /* @__PURE__ */ g("p", {
156
+ /* @__PURE__ */ v("p", {
157
157
  className: "mt-0.5 text-2xl font-bold tabular-nums text-text-primary",
158
158
  children: [
159
- v(Q),
159
+ y(Q),
160
160
  " ",
161
- /* @__PURE__ */ h("span", {
161
+ /* @__PURE__ */ _("span", {
162
162
  className: "text-lg font-semibold text-text-secondary",
163
163
  children: "remain"
164
164
  })
165
165
  ]
166
166
  }),
167
- /* @__PURE__ */ g("p", {
167
+ /* @__PURE__ */ v("p", {
168
168
  className: "mt-1 text-xs text-text-secondary",
169
169
  children: [
170
- y(X ?? 0),
170
+ b(X ?? 0),
171
171
  " years lived ·",
172
172
  " ",
173
- y(Q),
173
+ b(Q),
174
174
  " years ahead"
175
175
  ]
176
176
  })
177
- ] }) : /* @__PURE__ */ h("p", {
177
+ ] }) : /* @__PURE__ */ _("p", {
178
178
  className: "text-xs text-text-secondary",
179
179
  children: Y ? "You've chosen to see only the weeks you've lived." : "Add a life expectancy in settings to gently sketch the weeks ahead."
180
180
  }),
181
- /* @__PURE__ */ h("div", {
181
+ /* @__PURE__ */ _("div", {
182
182
  className: "rounded-lg border border-border-seam bg-bg-surface/40 p-3",
183
- children: /* @__PURE__ */ g("p", {
183
+ children: /* @__PURE__ */ v("p", {
184
184
  className: "flex items-start gap-2 text-sm text-text-primary",
185
- children: [/* @__PURE__ */ h(m, { className: "mt-0.5 h-4 w-4 shrink-0 text-primary" }), /* @__PURE__ */ h("span", { children: "What if today was your last? Make this week count." })]
185
+ children: [/* @__PURE__ */ _(h, { className: "mt-0.5 h-4 w-4 shrink-0 text-primary" }), /* @__PURE__ */ _("span", { children: "What if today was your last? Make this week count." })]
186
186
  })
187
187
  })
188
188
  ]
189
- }) : /* @__PURE__ */ g("div", {
189
+ }) : /* @__PURE__ */ v("div", {
190
190
  className: "mt-4 space-y-4",
191
- children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h3", {
191
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h3", {
192
192
  className: "text-base font-semibold text-text-primary",
193
193
  children: "See your life in weeks"
194
- }), /* @__PURE__ */ h("p", {
194
+ }), /* @__PURE__ */ _("p", {
195
195
  className: "mt-1 text-sm text-text-secondary",
196
196
  children: "Add your birth date and we'll gently fill in the weeks you've lived so far — a quiet reminder that time is finite, and this week is yours to spend."
197
- })] }), /* @__PURE__ */ g("button", {
197
+ })] }), /* @__PURE__ */ v("button", {
198
198
  type: "button",
199
199
  onClick: q,
200
200
  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",
201
- children: ["Add your birth date", /* @__PURE__ */ h(oe, { className: "h-4 w-4" })]
201
+ children: ["Add your birth date", /* @__PURE__ */ _(d, { className: "h-4 w-4" })]
202
202
  })]
203
203
  }),
204
- /* @__PURE__ */ g("p", {
204
+ /* @__PURE__ */ v("p", {
205
205
  className: "mt-6 flex items-start gap-1.5 text-xs text-text-secondary",
206
- children: [/* @__PURE__ */ h(f, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ h("span", { children: "Only you can see this. Your birth date is never shared, and you can turn this off anytime." })]
206
+ children: [/* @__PURE__ */ _(p, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ _("span", { children: "Only you can see this. Your birth date is never shared, and you can turn this off anytime." })]
207
207
  })
208
208
  ]
209
209
  })]
210
210
  }),
211
- $.length > 0 && /* @__PURE__ */ h(i, {
211
+ $.length > 0 && /* @__PURE__ */ _(a, {
212
212
  title: "Where your recent weeks went",
213
213
  description: "How your logged time settled across life's domains lately — a mirror, not a scorecard.",
214
- children: /* @__PURE__ */ h(re, {
214
+ children: /* @__PURE__ */ _(ae, {
215
215
  data: $,
216
216
  centerLabel: "logged lately",
217
- centerValue: l(fe),
218
- valueFormat: (e) => l(e),
217
+ centerValue: s(fe),
218
+ valueFormat: (e) => s(e),
219
219
  emptyLabel: "Nothing logged in this window yet.",
220
220
  ariaLabel: "Recent time by life domain"
221
221
  })
222
222
  }),
223
- J && $.length === 0 && /* @__PURE__ */ h(r, {
223
+ J && $.length === 0 && /* @__PURE__ */ _(te, {
224
224
  title: "Your weeks are yours to shape",
225
225
  description: "As you capture how you spend your time, a quiet reflection of your recent weeks will appear here — only for you."
226
226
  })
227
227
  ]
228
228
  }),
229
- /* @__PURE__ */ h(s, {
229
+ /* @__PURE__ */ _(ne, {
230
230
  open: L,
231
231
  onClose: () => R(!1),
232
232
  title: "Your life in weeks",
233
233
  description: "This is private and entirely optional. Add only what feels comfortable.",
234
- footer: /* @__PURE__ */ g(ce, { children: [/* @__PURE__ */ h("button", {
234
+ footer: /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
235
235
  type: "button",
236
236
  onClick: () => R(!1),
237
237
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
238
238
  children: "Cancel"
239
- }), /* @__PURE__ */ h("button", {
239
+ }), /* @__PURE__ */ _("button", {
240
240
  type: "submit",
241
241
  form: "finite-life-setup",
242
242
  disabled: P,
243
243
  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",
244
244
  children: P ? "Saving…" : "Save"
245
245
  })] }),
246
- children: /* @__PURE__ */ g("form", {
246
+ children: /* @__PURE__ */ v("form", {
247
247
  id: "finite-life-setup",
248
248
  onSubmit: ue,
249
249
  className: "space-y-5",
250
250
  children: [
251
- /* @__PURE__ */ g("div", { children: [
252
- /* @__PURE__ */ g("label", {
251
+ /* @__PURE__ */ v("div", { children: [
252
+ /* @__PURE__ */ v("label", {
253
253
  htmlFor: "finite-life-birthdate",
254
254
  className: "flex items-center gap-1.5 text-sm font-medium text-text-primary",
255
- children: [/* @__PURE__ */ h(se, { className: "h-4 w-4 text-text-secondary" }), "Your birth date"]
255
+ children: [/* @__PURE__ */ _(oe, { className: "h-4 w-4 text-text-secondary" }), "Your birth date"]
256
256
  }),
257
- /* @__PURE__ */ h("input", {
257
+ /* @__PURE__ */ _("input", {
258
258
  id: "finite-life-birthdate",
259
259
  type: "date",
260
260
  value: z,
@@ -262,25 +262,25 @@ function S() {
262
262
  onChange: (e) => B(e.target.value),
263
263
  className: "mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary"
264
264
  }),
265
- /* @__PURE__ */ h("p", {
265
+ /* @__PURE__ */ _("p", {
266
266
  className: "mt-1.5 text-xs text-text-secondary",
267
267
  children: "Used only to count the weeks you've lived. It stays with you."
268
268
  })
269
269
  ] }),
270
- /* @__PURE__ */ g("div", { children: [
271
- /* @__PURE__ */ g("label", {
270
+ /* @__PURE__ */ v("div", { children: [
271
+ /* @__PURE__ */ v("label", {
272
272
  htmlFor: "finite-life-expectancy",
273
273
  className: "text-sm font-medium text-text-primary",
274
274
  children: [
275
275
  "Life expectancy",
276
276
  " ",
277
- /* @__PURE__ */ h("span", {
277
+ /* @__PURE__ */ _("span", {
278
278
  className: "font-normal text-text-secondary",
279
279
  children: "(optional)"
280
280
  })
281
281
  ]
282
282
  }),
283
- /* @__PURE__ */ h("input", {
283
+ /* @__PURE__ */ _("input", {
284
284
  id: "finite-life-expectancy",
285
285
  type: "number",
286
286
  inputMode: "numeric",
@@ -292,34 +292,34 @@ function S() {
292
292
  onChange: (e) => H(e.target.value),
293
293
  className: "mt-2 w-full rounded-lg border border-border-subtle bg-bg-sunken px-3 py-2 text-sm text-text-primary"
294
294
  }),
295
- /* @__PURE__ */ h("p", {
295
+ /* @__PURE__ */ _("p", {
296
296
  className: "mt-1.5 text-xs text-text-secondary",
297
297
  children: "A gentle horizon for the weeks ahead — never a deadline. Leave it blank to simply see the weeks you've lived."
298
298
  })
299
299
  ] }),
300
- /* @__PURE__ */ g("label", {
300
+ /* @__PURE__ */ v("label", {
301
301
  className: "flex items-start gap-2.5 rounded-lg border border-border-subtle bg-bg-surface/40 p-3",
302
- children: [/* @__PURE__ */ h("input", {
302
+ children: [/* @__PURE__ */ _("input", {
303
303
  type: "checkbox",
304
304
  checked: U,
305
305
  onChange: (e) => W(e.target.checked),
306
306
  className: "mt-0.5 h-4 w-4 shrink-0 rounded border-border-subtle"
307
- }), /* @__PURE__ */ g("span", {
307
+ }), /* @__PURE__ */ v("span", {
308
308
  className: "text-sm text-text-primary",
309
- children: ["Hide the weeks ahead", /* @__PURE__ */ h("span", {
309
+ children: ["Hide the weeks ahead", /* @__PURE__ */ _("span", {
310
310
  className: "mt-0.5 block text-xs text-text-secondary",
311
311
  children: "Show only the weeks you've lived — no horizon, no countdown."
312
312
  })]
313
313
  })]
314
314
  }),
315
- G && /* @__PURE__ */ h("p", {
315
+ G && /* @__PURE__ */ _("p", {
316
316
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text",
317
317
  children: G
318
318
  })
319
319
  ]
320
320
  })
321
321
  }),
322
- /* @__PURE__ */ h(a, {
322
+ /* @__PURE__ */ _(re, {
323
323
  toasts: le,
324
324
  onDismiss: w
325
325
  })
@@ -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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"}