@burdenoff/microfe-timecampus 2026.728.1 → 2026.731.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"PersonalCorporatePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"names":[],"mappings":"AA8HA,wBAAgB,qBAAqB,gCA4epC"}
1
+ {"version":3,"file":"PersonalCorporatePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"names":[],"mappings":"AA8HA,wBAAgB,qBAAqB,gCAijBpC"}
@@ -2,15 +2,15 @@ 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 { useGrantSourceConsentMutation as i, useMyContextSharingPolicyQuery as ee, useMyPersonalDataSummaryQuery as te, useMySourceConsentsQuery as ne, useRevokeSourceConsentMutation as re, useUpdateContextSharingPolicyMutation as a } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as o } from "../../components/PageLayout.js";
7
- import { StatusBadge as s } from "../../components/StatusBadge.js";
8
- import { EmptyState as c } from "../../components/EmptyState.js";
9
- import { ToastContainer as ie, useToast as ae } from "../../components/Toast.js";
5
+ import { useGrantSourceConsentMutation as i, useMyContextSharingPolicyQuery as a, useMyPersonalDataSummaryQuery as ee, useMySourceConsentsQuery as o, useRevokeSourceConsentMutation as s, useUpdateContextSharingPolicyMutation as c } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as l } from "../../components/PageLayout.js";
7
+ import { StatusBadge as u } from "../../components/StatusBadge.js";
8
+ import { EmptyState as d } from "../../components/EmptyState.js";
9
+ import { ToastContainer as te, useToast as ne } from "../../components/Toast.js";
10
10
  import "../../components/index.js";
11
- import { formatSecondsHuman as l, isoDaysAgo as u, isoNow as d, lifeDomainLabel as f } from "./domains.js";
12
- import { useMemo as p, useState as m } from "react";
13
- import { ArrowUpRight as oe, Building2 as se, Info as h, Lock as g, Plug as _, Share2 as v, ShieldCheck as ce } from "lucide-react";
11
+ import { formatSecondsHuman as re, isoDaysAgo as ie, isoNow as ae, lifeDomainLabel as oe } from "./domains.js";
12
+ import { useMemo as f, useState as p } from "react";
13
+ import { ArrowUpRight as m, Building2 as h, Info as g, Lock as _, Plug as v, Share2 as se, ShieldCheck as ce } from "lucide-react";
14
14
  import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
15
  import { GlassCard as x } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/timecampus/pages/lifetime/PersonalCorporatePage.tsx
@@ -48,21 +48,21 @@ function w({ checked: e, onChange: n, disabled: r = !1, ariaLabel: i }) {
48
48
  });
49
49
  }
50
50
  function T() {
51
- let { workspaceId: T = "" } = e(), { toasts: E, addToast: D, dismissToast: O } = ae(), k = p(() => ({
52
- from: u(30),
53
- to: d()
54
- }), []), { data: A, loading: j, error: M } = ee({ skip: !T }), { data: N, loading: P, error: F, refetch: I } = te({
51
+ let { workspaceId: T = "" } = e(), { toasts: le, addToast: E, dismissToast: ue } = ne(), D = f(() => ({
52
+ from: ie(30),
53
+ to: ae()
54
+ }), []), { data: O, loading: k, error: A } = a({ skip: !T }), { data: j, loading: M, error: N, refetch: P } = ee({
55
55
  variables: {
56
- from: k.from,
57
- to: k.to
56
+ from: D.from,
57
+ to: D.to
58
58
  },
59
59
  skip: !T
60
- }), { data: L, loading: R, error: z, refetch: B } = ne({ skip: !T }), [V] = a(), [H] = i(), [U] = re(), [W, G] = m(null), [le, K] = m(null), q = A?.myContextSharingPolicy, J = N?.myPersonalDataSummary, Y = L?.mySourceConsents ?? [], X = p(() => [...J?.byDomain ?? []].sort((e, t) => t.totalSeconds - e.totalSeconds), [J]), Z = X[0]?.totalSeconds ?? 0, Q = J?.sharedAggregates ?? [], $ = {
60
+ }), { data: F, loading: I, error: L, refetch: R } = o({ skip: !T }), [z] = c(), [B] = i(), [V] = s(), [H, U] = p(null), [de, W] = p(null), [G, K] = p(null), q = O?.myContextSharingPolicy, J = j?.myPersonalDataSummary, Y = F?.mySourceConsents ?? [], X = f(() => [...J?.byDomain ?? []].sort((e, t) => t.totalSeconds - e.totalSeconds), [J]), Z = X[0]?.totalSeconds ?? 0, Q = J?.sharedAggregates ?? [], $ = {
61
61
  title: "Personal & Corporate",
62
62
  description: "Two contexts, one boundary you control",
63
63
  purpose: "TimeCampus keeps two worlds apart: your private life record, which stays yours, and the workspace you work in. This is where you decide exactly which aggregate work signals — never your personal context — travel up to your employer."
64
64
  };
65
- if (!T || j && !A || P && !N) return /* @__PURE__ */ y(o, {
65
+ if (!T || k && !O || M && !j) return /* @__PURE__ */ y(l, {
66
66
  ...$,
67
67
  children: /* @__PURE__ */ b("div", {
68
68
  className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
@@ -82,65 +82,89 @@ function T() {
82
82
  ]
83
83
  })
84
84
  });
85
- if (M && F && !q && !J) return /* @__PURE__ */ y(o, {
85
+ if (A && N && !q && !J) return /* @__PURE__ */ y(l, {
86
86
  ...$,
87
87
  children: /* @__PURE__ */ y("div", {
88
88
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
89
89
  children: "We couldn't load your privacy settings right now. Please try again in a moment."
90
90
  })
91
91
  });
92
- let ue = async (e, t) => {
92
+ let fe = async (e, t) => {
93
93
  if (!q) return;
94
94
  let n = S.find((t) => t.key === e)?.label ?? "signal";
95
- G(e);
95
+ U(e);
96
96
  try {
97
- let r = await V({ variables: { input: {
97
+ let r = await z({ variables: { input: {
98
98
  sustainableHours: q.sustainableHours,
99
99
  recoveryBand: q.recoveryBand,
100
100
  workLifeBoundary: q.workLifeBoundary,
101
101
  [e]: t
102
102
  } } });
103
103
  if (r.error) throw Error(r.error.message);
104
- await I(), D({
104
+ await P(), E({
105
105
  title: t ? `Now sharing "${n}"` : `Stopped sharing "${n}"`,
106
106
  description: "Your workspace only ever sees this as an aggregate signal.",
107
107
  variant: "success"
108
108
  });
109
109
  } catch {
110
- D({
110
+ E({
111
111
  title: "Couldn't update what you share",
112
112
  description: "Nothing was changed. Please try again.",
113
113
  variant: "error"
114
114
  });
115
115
  } finally {
116
- G(null);
116
+ U(null);
117
117
  }
118
- }, de = async (e, t) => {
118
+ }, pe = async (e, t) => {
119
119
  let n = C(e.source);
120
120
  K(e.source);
121
+ try {
122
+ let r = await B({ variables: {
123
+ source: e.source,
124
+ mode: e.mode,
125
+ onDeviceOnly: t
126
+ } });
127
+ if (r.error) throw Error(r.error.message);
128
+ await R(), E({
129
+ title: t ? `${n} now on-device only` : `${n} on-device-only turned off`,
130
+ description: t ? "Only aggregated app/category/timing data reaches your workspace — no titles, URLs, or other details." : void 0,
131
+ variant: "success"
132
+ });
133
+ } catch {
134
+ E({
135
+ title: "Couldn't update on-device-only",
136
+ description: "Nothing was changed. Please try again.",
137
+ variant: "error"
138
+ });
139
+ } finally {
140
+ K(null);
141
+ }
142
+ }, me = async (e, t) => {
143
+ let n = C(e.source);
144
+ W(e.source);
121
145
  try {
122
146
  if (t) {
123
- let t = await H({ variables: { source: e.source } });
147
+ let t = await B({ variables: { source: e.source } });
124
148
  if (t.error) throw Error(t.error.message);
125
149
  } else {
126
- let t = await U({ variables: { source: e.source } });
150
+ let t = await V({ variables: { source: e.source } });
127
151
  if (t.error) throw Error(t.error.message);
128
152
  }
129
- await B(), D({
153
+ await R(), E({
130
154
  title: t ? `${n} connected` : `${n} disconnected`,
131
155
  variant: "success"
132
156
  });
133
157
  } catch {
134
- D({
158
+ E({
135
159
  title: "Couldn't update this source",
136
160
  description: "Nothing was changed. Please try again.",
137
161
  variant: "error"
138
162
  });
139
163
  } finally {
140
- K(null);
164
+ W(null);
141
165
  }
142
166
  };
143
- return /* @__PURE__ */ b(o, {
167
+ return /* @__PURE__ */ b(l, {
144
168
  ...$,
145
169
  children: [
146
170
  /* @__PURE__ */ b("div", {
@@ -153,7 +177,7 @@ function T() {
153
177
  className: "mb-4 flex items-start gap-3",
154
178
  children: [/* @__PURE__ */ y("div", {
155
179
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
156
- children: /* @__PURE__ */ y(g, { className: "h-5 w-5" })
180
+ children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
157
181
  }), /* @__PURE__ */ b("div", {
158
182
  className: "min-w-0",
159
183
  children: [/* @__PURE__ */ b("h2", {
@@ -171,10 +195,10 @@ function T() {
171
195
  children: "Everything you capture about your life. Locked to your account — it never leaves this side."
172
196
  })]
173
197
  })]
174
- }), F && !J ? /* @__PURE__ */ y("div", {
198
+ }), N && !J ? /* @__PURE__ */ y("div", {
175
199
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
176
200
  children: "Couldn't load your private data summary right now."
177
- }) : !J || J.totalPersonalEvents === 0 ? /* @__PURE__ */ y(c, {
201
+ }) : !J || J.totalPersonalEvents === 0 ? /* @__PURE__ */ y(d, {
178
202
  illustration: "empty-data",
179
203
  title: "Nothing private captured yet",
180
204
  description: "Log your first life entry and it will live here — always private to you."
@@ -183,7 +207,7 @@ function T() {
183
207
  children: [/* @__PURE__ */ y(n, {
184
208
  title: "Private life entries",
185
209
  value: J.totalPersonalEvents.toLocaleString(),
186
- icon: /* @__PURE__ */ y(g, { className: "h-5 w-5" }),
210
+ icon: /* @__PURE__ */ y(_, { className: "h-5 w-5" }),
187
211
  description: "captured in the last 30 days"
188
212
  }), X.length === 0 ? /* @__PURE__ */ y("p", {
189
213
  className: "text-xs text-text-secondary",
@@ -200,11 +224,11 @@ function T() {
200
224
  className: "min-w-0",
201
225
  children: [/* @__PURE__ */ y("p", {
202
226
  className: "truncate text-sm font-medium text-text-primary",
203
- children: f(e.domain)
227
+ children: oe(e.domain)
204
228
  }), /* @__PURE__ */ b("p", {
205
229
  className: "mt-0.5 text-xs text-text-secondary",
206
230
  children: [
207
- l(e.totalSeconds),
231
+ re(e.totalSeconds),
208
232
  " ·",
209
233
  " ",
210
234
  e.eventCount.toLocaleString(),
@@ -212,7 +236,7 @@ function T() {
212
236
  e.eventCount === 1 ? "entry" : "entries"
213
237
  ]
214
238
  })]
215
- }), /* @__PURE__ */ y(g, {
239
+ }), /* @__PURE__ */ y(_, {
216
240
  className: "h-3.5 w-3.5 shrink-0 text-text-muted",
217
241
  "aria-label": "Private to you"
218
242
  })]
@@ -238,7 +262,7 @@ function T() {
238
262
  className: "mb-4 flex items-start gap-3",
239
263
  children: [/* @__PURE__ */ y("div", {
240
264
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-info-bg/40 text-status-info-text",
241
- children: /* @__PURE__ */ y(v, { className: "h-5 w-5" })
265
+ children: /* @__PURE__ */ y(se, { className: "h-5 w-5" })
242
266
  }), /* @__PURE__ */ b("div", {
243
267
  className: "min-w-0",
244
268
  children: [/* @__PURE__ */ y("h2", {
@@ -249,13 +273,13 @@ function T() {
249
273
  children: "Aggregate signals only. Every switch is off until you turn it on."
250
274
  })]
251
275
  })]
252
- }), M && !q ? /* @__PURE__ */ y("div", {
276
+ }), A && !q ? /* @__PURE__ */ y("div", {
253
277
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
254
278
  children: "Couldn't load your sharing settings right now."
255
279
  }) : /* @__PURE__ */ y("div", {
256
280
  className: "space-y-3",
257
281
  children: S.map((e) => {
258
- let n = q ? q[e.key] : !1, r = W === e.key;
282
+ let n = q ? q[e.key] : !1, r = H === e.key;
259
283
  return /* @__PURE__ */ y("div", {
260
284
  className: "rounded-lg border border-border-subtle bg-bg-sunken/40 p-3",
261
285
  children: /* @__PURE__ */ b("div", {
@@ -275,7 +299,7 @@ function T() {
275
299
  checked: n,
276
300
  disabled: r || !q,
277
301
  ariaLabel: `Share "${e.label}" with your workspace`,
278
- onChange: (t) => ue(e.key, t)
302
+ onChange: (t) => fe(e.key, t)
279
303
  }), /* @__PURE__ */ y("span", {
280
304
  className: t("text-[0.65rem] font-medium uppercase tracking-wide", n ? "text-status-success-text" : "text-text-muted"),
281
305
  children: r ? "Saving…" : n ? "On" : "Off"
@@ -307,7 +331,7 @@ function T() {
307
331
  className: "mb-4 flex items-start gap-3",
308
332
  children: [/* @__PURE__ */ y("div", {
309
333
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
310
- children: /* @__PURE__ */ y(se, { className: "h-5 w-5" })
334
+ children: /* @__PURE__ */ y(h, { className: "h-5 w-5" })
311
335
  }), /* @__PURE__ */ b("div", {
312
336
  className: "min-w-0",
313
337
  children: [/* @__PURE__ */ b("h2", {
@@ -330,7 +354,7 @@ function T() {
330
354
  children: [
331
355
  /* @__PURE__ */ b("div", {
332
356
  className: "flex items-start gap-2 rounded-lg border border-status-info-border/40 bg-status-info-bg/10 px-3 py-2.5 text-xs text-status-info-text",
333
- children: [/* @__PURE__ */ y(h, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ y("span", { children: "Your workspace only ever receives aggregate work signals — coarse yes/no bands, never entries, locations, health or timestamps." })]
357
+ children: [/* @__PURE__ */ y(g, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ y("span", { children: "Your workspace only ever receives aggregate work signals — coarse yes/no bands, never entries, locations, health or timestamps." })]
334
358
  }),
335
359
  /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
336
360
  className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-muted",
@@ -350,11 +374,11 @@ function T() {
350
374
  className: "flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-sunken/40 px-3 py-2.5",
351
375
  children: [/* @__PURE__ */ b("span", {
352
376
  className: "flex min-w-0 items-center gap-2 text-sm text-text-primary",
353
- children: [/* @__PURE__ */ y(oe, { className: "h-4 w-4 shrink-0 text-status-success-text" }), /* @__PURE__ */ y("span", {
377
+ children: [/* @__PURE__ */ y(m, { className: "h-4 w-4 shrink-0 text-status-success-text" }), /* @__PURE__ */ y("span", {
354
378
  className: "truncate",
355
379
  children: C(e)
356
380
  })]
357
- }), /* @__PURE__ */ y(s, {
381
+ }), /* @__PURE__ */ y(u, {
358
382
  variant: "success",
359
383
  label: "Shared",
360
384
  dot: !0
@@ -377,7 +401,7 @@ function T() {
377
401
  className: "mb-4 flex items-start gap-3",
378
402
  children: [/* @__PURE__ */ y("div", {
379
403
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
380
- children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
404
+ children: /* @__PURE__ */ y(v, { className: "h-5 w-5" })
381
405
  }), /* @__PURE__ */ b("div", {
382
406
  className: "min-w-0",
383
407
  children: [/* @__PURE__ */ y("h3", {
@@ -388,20 +412,20 @@ function T() {
388
412
  children: "Each capture source only feeds your private record once you consent — and you can withdraw at any moment."
389
413
  })]
390
414
  })]
391
- }), z && Y.length === 0 ? /* @__PURE__ */ y("div", {
415
+ }), L && Y.length === 0 ? /* @__PURE__ */ y("div", {
392
416
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
393
417
  children: "Couldn't load your connected sources right now."
394
- }) : R && Y.length === 0 ? /* @__PURE__ */ y(r, {
418
+ }) : I && Y.length === 0 ? /* @__PURE__ */ y(r, {
395
419
  variant: "list",
396
420
  count: 3
397
- }) : Y.length === 0 ? /* @__PURE__ */ y(c, {
421
+ }) : Y.length === 0 ? /* @__PURE__ */ y(d, {
398
422
  illustration: "empty-data",
399
423
  title: "No sources connected yet",
400
424
  description: "When you connect a capture source — a calendar, location or health signal — you'll grant and revoke exactly what it may feed here."
401
425
  }) : /* @__PURE__ */ y("div", {
402
426
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
403
427
  children: Y.map((e) => {
404
- let n = le === e.source;
428
+ let n = de === e.source;
405
429
  return /* @__PURE__ */ y(x, {
406
430
  treatment: "metric",
407
431
  className: "p-4",
@@ -415,15 +439,20 @@ function T() {
415
439
  }), /* @__PURE__ */ b("div", {
416
440
  className: "mt-1.5 flex flex-wrap items-center gap-1.5",
417
441
  children: [
418
- /* @__PURE__ */ y(s, {
442
+ /* @__PURE__ */ y(u, {
419
443
  variant: "muted",
420
444
  label: C(e.mode)
421
445
  }),
422
- e.onDeviceOnly && /* @__PURE__ */ y(s, {
423
- variant: "info",
424
- label: "On-device only"
446
+ e.granted && /* @__PURE__ */ y("button", {
447
+ type: "button",
448
+ role: "switch",
449
+ "aria-checked": e.onDeviceOnly,
450
+ disabled: G === e.source,
451
+ onClick: () => pe(e, !e.onDeviceOnly),
452
+ className: t("rounded-full border px-2 py-0.5 text-[0.65rem] font-medium uppercase tracking-wide transition-colors", "focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50", e.onDeviceOnly ? "border-status-info-border/60 bg-status-info-bg/40 text-status-info-text" : "border-border-subtle bg-bg-sunken/40 text-text-muted hover:text-text-secondary", G === e.source && "cursor-not-allowed opacity-50"),
453
+ children: G === e.source ? "Saving…" : e.onDeviceOnly ? "On-device only" : "Turn on on-device only"
425
454
  }),
426
- e.retentionDays != null && /* @__PURE__ */ y(s, {
455
+ e.retentionDays != null && /* @__PURE__ */ y(u, {
427
456
  variant: "muted",
428
457
  label: `Kept ${e.retentionDays}d`
429
458
  })
@@ -435,7 +464,7 @@ function T() {
435
464
  checked: e.granted,
436
465
  disabled: n,
437
466
  ariaLabel: `Consent for ${C(e.source)}`,
438
- onChange: (t) => de(e, t)
467
+ onChange: (t) => me(e, t)
439
468
  }), /* @__PURE__ */ y("span", {
440
469
  className: t("text-[0.65rem] font-medium uppercase tracking-wide", e.granted ? "text-status-success-text" : "text-text-muted"),
441
470
  children: n ? "Saving…" : e.granted ? "Granted" : "Revoked"
@@ -446,9 +475,9 @@ function T() {
446
475
  })
447
476
  })]
448
477
  }),
449
- /* @__PURE__ */ y(ie, {
450
- toasts: E,
451
- onDismiss: O
478
+ /* @__PURE__ */ y(te, {
479
+ toasts: le,
480
+ onDismiss: ue
452
481
  })
453
482
  ]
454
483
  });
@@ -1 +1 @@
1
- {"version":3,"file":"PersonalCorporatePage.js","names":[],"sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport {\n Lock,\n Building2,\n Share2,\n ShieldCheck,\n Plug,\n Info,\n ArrowUpRight,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport {\n PageLayout,\n StatCard,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n ToastContainer,\n useToast,\n} from \"../../components\";\nimport { cn } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useMyContextSharingPolicyQuery,\n useMySourceConsentsQuery,\n useMyPersonalDataSummaryQuery,\n useUpdateContextSharingPolicyMutation,\n useGrantSourceConsentMutation,\n useRevokeSourceConsentMutation,\n type MyPersonalDataSummaryQuery,\n type MySourceConsentsQuery,\n} from \"@/generated/wspace-operations\";\nimport {\n lifeDomainLabel,\n formatSecondsHuman,\n isoDaysAgo,\n isoNow,\n} from \"./domains\";\n\ntype PersonalDomainCount =\n MyPersonalDataSummaryQuery[\"myPersonalDataSummary\"][\"byDomain\"][number];\ntype SourceConsent = MySourceConsentsQuery[\"mySourceConsents\"][number];\n\n/** The three aggregate work signals the user can share up to their workspace. */\ntype PolicyKey = \"sustainableHours\" | \"recoveryBand\" | \"workLifeBoundary\";\n\nconst POLICY_TOGGLES: {\n key: PolicyKey;\n label: string;\n description: string;\n}[] = [\n {\n key: \"sustainableHours\",\n label: \"Sustainable hours\",\n description:\n \"A yes/no signal that your working hours sit within a healthy range — never the hours themselves.\",\n },\n {\n key: \"recoveryBand\",\n label: \"Recovery band\",\n description:\n \"Whether you're getting enough recovery between work blocks, as a single band — not what you did to recover.\",\n },\n {\n key: \"workLifeBoundary\",\n label: \"Work-life boundary\",\n description:\n \"Whether work is staying inside the hours you've set aside for it — a boundary flag, nothing more.\",\n },\n];\n\n/** Turn a raw enum/source token like \"SUSTAINABLE_HOURS\" or \"onDevice\" into prose. */\nfunction humanizeToken(raw: string): string {\n const spaced = raw\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim()\n .toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\n/**\n * A small, token-styled toggle switch. Kept local to this page (per brief) —\n * a headless button with `role=\"switch\"` so it stays accessible and themable.\n * No hover movement: the track only changes colour.\n */\nfunction ToggleSwitch({\n checked,\n onChange,\n disabled = false,\n ariaLabel,\n}: {\n checked: boolean;\n onChange: (next: boolean) => void;\n disabled?: boolean;\n ariaLabel: string;\n}) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={ariaLabel}\n disabled={disabled}\n onClick={() => onChange(!checked)}\n className={cn(\n \"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full border transition-colors\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface\",\n checked\n ? \"border-transparent bg-action-primary-bg\"\n : \"border-border-subtle bg-bg-sunken\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <span\n className={cn(\n \"inline-block h-4 w-4 transform rounded-full shadow-sm transition-transform motion-reduce:transition-none\",\n checked\n ? \"translate-x-6 bg-action-primary-text\"\n : \"translate-x-1 bg-bg-surface\",\n )}\n />\n </button>\n );\n}\n\nexport function PersonalCorporatePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { toasts, addToast, dismissToast } = useToast();\n\n // Keep the range identity stable so `isoNow()` doesn't trigger refetch loops.\n const range = useMemo(() => ({ from: isoDaysAgo(30), to: isoNow() }), []);\n\n const {\n data: policyData,\n loading: policyLoading,\n error: policyError,\n } = useMyContextSharingPolicyQuery({ skip: !workspaceId });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n refetch: refetchSummary,\n } = useMyPersonalDataSummaryQuery({\n variables: { from: range.from, to: range.to },\n skip: !workspaceId,\n });\n\n const {\n data: consentsData,\n loading: consentsLoading,\n error: consentsError,\n refetch: refetchConsents,\n } = useMySourceConsentsQuery({ skip: !workspaceId });\n\n const [updatePolicy] = useUpdateContextSharingPolicyMutation();\n const [grantConsent] = useGrantSourceConsentMutation();\n const [revokeConsent] = useRevokeSourceConsentMutation();\n\n const [savingPolicyKey, setSavingPolicyKey] = useState<PolicyKey | null>(\n null,\n );\n const [savingSource, setSavingSource] = useState<string | null>(null);\n\n const policy = policyData?.myContextSharingPolicy;\n const summary = summaryData?.myPersonalDataSummary;\n const consents: SourceConsent[] = consentsData?.mySourceConsents ?? [];\n\n // Biggest slices of private time first — most meaningful at a glance.\n const personalDomains = useMemo<PersonalDomainCount[]>(() => {\n const list = summary?.byDomain ?? [];\n return [...list].sort((a, b) => b.totalSeconds - a.totalSeconds);\n }, [summary]);\n\n const maxDomainSeconds = personalDomains[0]?.totalSeconds ?? 0;\n const sharedAggregates: string[] = summary?.sharedAggregates ?? [];\n\n const pageProps = {\n title: \"Personal & Corporate\",\n description: \"Two contexts, one boundary you control\",\n purpose:\n \"TimeCampus keeps two worlds apart: your private life record, which stays yours, and the workspace you work in. This is where you decide exactly which aggregate work signals — never your personal context — travel up to your employer.\",\n } as const;\n\n const initialLoading =\n !workspaceId ||\n (policyLoading && !policyData) ||\n (summaryLoading && !summaryData);\n\n if (initialLoading) {\n return (\n <PageLayout {...pageProps}>\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <LoadingSkeleton variant=\"card\" count={1} />\n <LoadingSkeleton variant=\"card\" count={1} />\n <LoadingSkeleton variant=\"card\" count={1} />\n </div>\n </PageLayout>\n );\n }\n\n // Both core surfaces failed and we have nothing to show — calm, honest error.\n if (policyError && summaryError && !policy && !summary) {\n return (\n <PageLayout {...pageProps}>\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 privacy settings right now. Please try again in\n a moment.\n </div>\n </PageLayout>\n );\n }\n\n const handlePolicyToggle = async (key: PolicyKey, next: boolean) => {\n if (!policy) return;\n const label = POLICY_TOGGLES.find((t) => t.key === key)?.label ?? \"signal\";\n setSavingPolicyKey(key);\n try {\n const res = await updatePolicy({\n variables: {\n input: {\n sustainableHours: policy.sustainableHours,\n recoveryBand: policy.recoveryBand,\n workLifeBoundary: policy.workLifeBoundary,\n [key]: next,\n },\n },\n });\n if (res.error) throw new Error(res.error.message);\n // Shared-aggregate list can change with the policy — keep Corporate honest.\n await refetchSummary();\n addToast({\n title: next ? `Now sharing \"${label}\"` : `Stopped sharing \"${label}\"`,\n description:\n \"Your workspace only ever sees this as an aggregate signal.\",\n variant: \"success\",\n });\n } catch {\n addToast({\n title: \"Couldn't update what you share\",\n description: \"Nothing was changed. Please try again.\",\n variant: \"error\",\n });\n } finally {\n setSavingPolicyKey(null);\n }\n };\n\n const handleConsentToggle = async (consent: SourceConsent, next: boolean) => {\n const label = humanizeToken(consent.source);\n setSavingSource(consent.source);\n try {\n if (next) {\n const res = await grantConsent({\n variables: { source: consent.source },\n });\n if (res.error) throw new Error(res.error.message);\n } else {\n const res = await revokeConsent({\n variables: { source: consent.source },\n });\n if (res.error) throw new Error(res.error.message);\n }\n await refetchConsents();\n addToast({\n title: next ? `${label} connected` : `${label} disconnected`,\n variant: \"success\",\n });\n } catch {\n addToast({\n title: \"Couldn't update this source\",\n description: \"Nothing was changed. Please try again.\",\n variant: \"error\",\n });\n } finally {\n setSavingSource(null);\n }\n };\n\n return (\n <PageLayout {...pageProps}>\n {/* Three contexts: your private record · what you share · the workspace. */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* ── Personal (private to you) ─────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Lock className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n Personal{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (private to you)\n </span>\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Everything you capture about your life. Locked to your account —\n it never leaves this side.\n </p>\n </div>\n </div>\n\n {summaryError && !summary ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your private data summary right now.\n </div>\n ) : !summary || summary.totalPersonalEvents === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"Nothing private captured yet\"\n description=\"Log your first life entry and it will live here — always private to you.\"\n />\n ) : (\n <div className=\"flex flex-1 flex-col gap-4\">\n <StatCard\n title=\"Private life entries\"\n value={summary.totalPersonalEvents.toLocaleString()}\n icon={<Lock className=\"h-5 w-5\" />}\n description=\"captured in the last 30 days\"\n />\n\n {personalDomains.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n No life domains logged in this window yet.\n </p>\n ) : (\n <ul className=\"space-y-2.5\">\n {personalDomains.map((d) => {\n const pct =\n maxDomainSeconds > 0\n ? Math.round((d.totalSeconds / maxDomainSeconds) * 100)\n : 0;\n return (\n <li\n key={d.domain}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {lifeDomainLabel(d.domain)}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatSecondsHuman(d.totalSeconds)} ·{\" \"}\n {d.eventCount.toLocaleString()}{\" \"}\n {d.eventCount === 1 ? \"entry\" : \"entries\"}\n </p>\n </div>\n <Lock\n className=\"h-3.5 w-3.5 shrink-0 text-text-muted\"\n aria-label=\"Private to you\"\n />\n </div>\n {/* Data-driven share bar of this window's private time. */}\n <div className=\"mt-2 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg/60\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n )}\n </GlassCard>\n\n {/* ── What you share up to work ─────────────────────────────────── */}\n <div className=\"flex flex-col gap-4\">\n <GlassCard\n treatment=\"insight\"\n glow\n className=\"flex flex-col p-4 sm:p-6\"\n >\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-info-bg/40 text-status-info-text\">\n <Share2 className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n What you share up to work\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Aggregate signals only. Every switch is off until you turn it\n on.\n </p>\n </div>\n </div>\n\n {policyError && !policy ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your sharing settings right now.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {POLICY_TOGGLES.map((toggle) => {\n const checked = policy ? policy[toggle.key] : false;\n const saving = savingPolicyKey === toggle.key;\n return (\n <div\n key={toggle.key}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {toggle.label}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {toggle.description}\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <ToggleSwitch\n checked={checked}\n disabled={saving || !policy}\n ariaLabel={`Share \"${toggle.label}\" with your workspace`}\n onChange={(next) =>\n handlePolicyToggle(toggle.key, next)\n }\n />\n <span\n className={cn(\n \"text-[0.65rem] font-medium uppercase tracking-wide\",\n checked\n ? \"text-status-success-text\"\n : \"text-text-muted\",\n )}\n >\n {saving ? \"Saving…\" : checked ? \"On\" : \"Off\"}\n </span>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n {/* Strong, unmissable reassurance. */}\n <GlassCard\n treatment=\"metric\"\n className=\"border-status-success-border/40 p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-success-bg/40 text-status-success-text\">\n <ShieldCheck className=\"h-5 w-5\" />\n </div>\n <p className=\"text-sm font-semibold text-text-primary\">\n Your employer never sees your personal context.\n </p>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Corporate (your workspace) ────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n Corporate{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (your workspace)\n </span>\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n What the workspace can actually see about you.\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-1 flex-col gap-4\">\n <div className=\"flex items-start gap-2 rounded-lg border border-status-info-border/40 bg-status-info-bg/10 px-3 py-2.5 text-xs text-status-info-text\">\n <Info className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <span>\n Your workspace only ever receives aggregate work signals —\n coarse yes/no bands, never entries, locations, health or\n timestamps.\n </span>\n </div>\n\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Aggregate signals visible to work\n </p>\n {sharedAggregates.length === 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-4 text-center\">\n <p className=\"text-sm font-medium text-text-primary\">\n Nothing is shared right now\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Turn on a signal in the middle column and it will appear\n here.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {sharedAggregates.map((agg) => (\n <li\n key={agg}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-sunken/40 px-3 py-2.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-sm text-text-primary\">\n <ArrowUpRight className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n <span className=\"truncate\">{humanizeToken(agg)}</span>\n </span>\n <StatusBadge variant=\"success\" label=\"Shared\" dot />\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <p className=\"mt-auto text-xs text-text-muted\">\n This mirrors exactly what you've allowed — change it any time from\n the switches on the left.\n </p>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Connected sources & consent ─────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"mt-6 p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Plug className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Connected sources\n </h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Each capture source only feeds your private record once you\n consent — and you can withdraw at any moment.\n </p>\n </div>\n </div>\n\n {consentsError && consents.length === 0 ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your connected sources right now.\n </div>\n ) : consentsLoading && consents.length === 0 ? (\n <LoadingSkeleton variant=\"list\" count={3} />\n ) : consents.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No sources connected yet\"\n description=\"When you connect a capture source — a calendar, location or health signal — you'll grant and revoke exactly what it may feed here.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {consents.map((consent) => {\n const saving = savingSource === consent.source;\n return (\n <GlassCard key={consent.id} treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {humanizeToken(consent.source)}\n </p>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge\n variant=\"muted\"\n label={humanizeToken(consent.mode)}\n />\n {consent.onDeviceOnly && (\n <StatusBadge variant=\"info\" label=\"On-device only\" />\n )}\n {consent.retentionDays != null && (\n <StatusBadge\n variant=\"muted\"\n label={`Kept ${consent.retentionDays}d`}\n />\n )}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <ToggleSwitch\n checked={consent.granted}\n disabled={saving}\n ariaLabel={`Consent for ${humanizeToken(consent.source)}`}\n onChange={(next) => handleConsentToggle(consent, next)}\n />\n <span\n className={cn(\n \"text-[0.65rem] font-medium uppercase tracking-wide\",\n consent.granted\n ? \"text-status-success-text\"\n : \"text-text-muted\",\n )}\n >\n {saving\n ? \"Saving…\"\n : consent.granted\n ? \"Granted\"\n : \"Revoked\"}\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PersonalCorporatePage.js","names":[],"sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport {\n Lock,\n Building2,\n Share2,\n ShieldCheck,\n Plug,\n Info,\n ArrowUpRight,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport {\n PageLayout,\n StatCard,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n ToastContainer,\n useToast,\n} from \"../../components\";\nimport { cn } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useMyContextSharingPolicyQuery,\n useMySourceConsentsQuery,\n useMyPersonalDataSummaryQuery,\n useUpdateContextSharingPolicyMutation,\n useGrantSourceConsentMutation,\n useRevokeSourceConsentMutation,\n type MyPersonalDataSummaryQuery,\n type MySourceConsentsQuery,\n} from \"@/generated/wspace-operations\";\nimport {\n lifeDomainLabel,\n formatSecondsHuman,\n isoDaysAgo,\n isoNow,\n} from \"./domains\";\n\ntype PersonalDomainCount =\n MyPersonalDataSummaryQuery[\"myPersonalDataSummary\"][\"byDomain\"][number];\ntype SourceConsent = MySourceConsentsQuery[\"mySourceConsents\"][number];\n\n/** The three aggregate work signals the user can share up to their workspace. */\ntype PolicyKey = \"sustainableHours\" | \"recoveryBand\" | \"workLifeBoundary\";\n\nconst POLICY_TOGGLES: {\n key: PolicyKey;\n label: string;\n description: string;\n}[] = [\n {\n key: \"sustainableHours\",\n label: \"Sustainable hours\",\n description:\n \"A yes/no signal that your working hours sit within a healthy range — never the hours themselves.\",\n },\n {\n key: \"recoveryBand\",\n label: \"Recovery band\",\n description:\n \"Whether you're getting enough recovery between work blocks, as a single band — not what you did to recover.\",\n },\n {\n key: \"workLifeBoundary\",\n label: \"Work-life boundary\",\n description:\n \"Whether work is staying inside the hours you've set aside for it — a boundary flag, nothing more.\",\n },\n];\n\n/** Turn a raw enum/source token like \"SUSTAINABLE_HOURS\" or \"onDevice\" into prose. */\nfunction humanizeToken(raw: string): string {\n const spaced = raw\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim()\n .toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\n/**\n * A small, token-styled toggle switch. Kept local to this page (per brief) —\n * a headless button with `role=\"switch\"` so it stays accessible and themable.\n * No hover movement: the track only changes colour.\n */\nfunction ToggleSwitch({\n checked,\n onChange,\n disabled = false,\n ariaLabel,\n}: {\n checked: boolean;\n onChange: (next: boolean) => void;\n disabled?: boolean;\n ariaLabel: string;\n}) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={ariaLabel}\n disabled={disabled}\n onClick={() => onChange(!checked)}\n className={cn(\n \"relative inline-flex h-6 w-11 shrink-0 items-center rounded-full border transition-colors\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface\",\n checked\n ? \"border-transparent bg-action-primary-bg\"\n : \"border-border-subtle bg-bg-sunken\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <span\n className={cn(\n \"inline-block h-4 w-4 transform rounded-full shadow-sm transition-transform motion-reduce:transition-none\",\n checked\n ? \"translate-x-6 bg-action-primary-text\"\n : \"translate-x-1 bg-bg-surface\",\n )}\n />\n </button>\n );\n}\n\nexport function PersonalCorporatePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { toasts, addToast, dismissToast } = useToast();\n\n // Keep the range identity stable so `isoNow()` doesn't trigger refetch loops.\n const range = useMemo(() => ({ from: isoDaysAgo(30), to: isoNow() }), []);\n\n const {\n data: policyData,\n loading: policyLoading,\n error: policyError,\n } = useMyContextSharingPolicyQuery({ skip: !workspaceId });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n refetch: refetchSummary,\n } = useMyPersonalDataSummaryQuery({\n variables: { from: range.from, to: range.to },\n skip: !workspaceId,\n });\n\n const {\n data: consentsData,\n loading: consentsLoading,\n error: consentsError,\n refetch: refetchConsents,\n } = useMySourceConsentsQuery({ skip: !workspaceId });\n\n const [updatePolicy] = useUpdateContextSharingPolicyMutation();\n const [grantConsent] = useGrantSourceConsentMutation();\n const [revokeConsent] = useRevokeSourceConsentMutation();\n\n const [savingPolicyKey, setSavingPolicyKey] = useState<PolicyKey | null>(\n null,\n );\n const [savingSource, setSavingSource] = useState<string | null>(null);\n const [savingOnDeviceSource, setSavingOnDeviceSource] = useState<\n string | null\n >(null);\n\n const policy = policyData?.myContextSharingPolicy;\n const summary = summaryData?.myPersonalDataSummary;\n const consents: SourceConsent[] = consentsData?.mySourceConsents ?? [];\n\n // Biggest slices of private time first — most meaningful at a glance.\n const personalDomains = useMemo<PersonalDomainCount[]>(() => {\n const list = summary?.byDomain ?? [];\n return [...list].sort((a, b) => b.totalSeconds - a.totalSeconds);\n }, [summary]);\n\n const maxDomainSeconds = personalDomains[0]?.totalSeconds ?? 0;\n const sharedAggregates: string[] = summary?.sharedAggregates ?? [];\n\n const pageProps = {\n title: \"Personal & Corporate\",\n description: \"Two contexts, one boundary you control\",\n purpose:\n \"TimeCampus keeps two worlds apart: your private life record, which stays yours, and the workspace you work in. This is where you decide exactly which aggregate work signals — never your personal context — travel up to your employer.\",\n } as const;\n\n const initialLoading =\n !workspaceId ||\n (policyLoading && !policyData) ||\n (summaryLoading && !summaryData);\n\n if (initialLoading) {\n return (\n <PageLayout {...pageProps}>\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <LoadingSkeleton variant=\"card\" count={1} />\n <LoadingSkeleton variant=\"card\" count={1} />\n <LoadingSkeleton variant=\"card\" count={1} />\n </div>\n </PageLayout>\n );\n }\n\n // Both core surfaces failed and we have nothing to show — calm, honest error.\n if (policyError && summaryError && !policy && !summary) {\n return (\n <PageLayout {...pageProps}>\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 privacy settings right now. Please try again in\n a moment.\n </div>\n </PageLayout>\n );\n }\n\n const handlePolicyToggle = async (key: PolicyKey, next: boolean) => {\n if (!policy) return;\n const label = POLICY_TOGGLES.find((t) => t.key === key)?.label ?? \"signal\";\n setSavingPolicyKey(key);\n try {\n const res = await updatePolicy({\n variables: {\n input: {\n sustainableHours: policy.sustainableHours,\n recoveryBand: policy.recoveryBand,\n workLifeBoundary: policy.workLifeBoundary,\n [key]: next,\n },\n },\n });\n if (res.error) throw new Error(res.error.message);\n // Shared-aggregate list can change with the policy — keep Corporate honest.\n await refetchSummary();\n addToast({\n title: next ? `Now sharing \"${label}\"` : `Stopped sharing \"${label}\"`,\n description:\n \"Your workspace only ever sees this as an aggregate signal.\",\n variant: \"success\",\n });\n } catch {\n addToast({\n title: \"Couldn't update what you share\",\n description: \"Nothing was changed. Please try again.\",\n variant: \"error\",\n });\n } finally {\n setSavingPolicyKey(null);\n }\n };\n\n // BOFF-4202: let the user actually flip onDeviceOnly, not just see it.\n // Re-grants with the same mode + the flipped flag; grantSourceConsent's\n // upsert preserves `granted:true` on this path (only meaningful to toggle\n // on an already-connected source), so this never re-enables a revoked one.\n const handleOnDeviceOnlyToggle = async (\n consent: SourceConsent,\n next: boolean,\n ) => {\n const label = humanizeToken(consent.source);\n setSavingOnDeviceSource(consent.source);\n try {\n const res = await grantConsent({\n variables: {\n source: consent.source,\n mode: consent.mode,\n onDeviceOnly: next,\n },\n });\n if (res.error) throw new Error(res.error.message);\n await refetchConsents();\n addToast({\n title: next\n ? `${label} now on-device only`\n : `${label} on-device-only turned off`,\n description: next\n ? \"Only aggregated app/category/timing data reaches your workspace — no titles, URLs, or other details.\"\n : undefined,\n variant: \"success\",\n });\n } catch {\n addToast({\n title: \"Couldn't update on-device-only\",\n description: \"Nothing was changed. Please try again.\",\n variant: \"error\",\n });\n } finally {\n setSavingOnDeviceSource(null);\n }\n };\n\n const handleConsentToggle = async (consent: SourceConsent, next: boolean) => {\n const label = humanizeToken(consent.source);\n setSavingSource(consent.source);\n try {\n if (next) {\n const res = await grantConsent({\n variables: { source: consent.source },\n });\n if (res.error) throw new Error(res.error.message);\n } else {\n const res = await revokeConsent({\n variables: { source: consent.source },\n });\n if (res.error) throw new Error(res.error.message);\n }\n await refetchConsents();\n addToast({\n title: next ? `${label} connected` : `${label} disconnected`,\n variant: \"success\",\n });\n } catch {\n addToast({\n title: \"Couldn't update this source\",\n description: \"Nothing was changed. Please try again.\",\n variant: \"error\",\n });\n } finally {\n setSavingSource(null);\n }\n };\n\n return (\n <PageLayout {...pageProps}>\n {/* Three contexts: your private record · what you share · the workspace. */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* ── Personal (private to you) ─────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Lock className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n Personal{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (private to you)\n </span>\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Everything you capture about your life. Locked to your account —\n it never leaves this side.\n </p>\n </div>\n </div>\n\n {summaryError && !summary ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your private data summary right now.\n </div>\n ) : !summary || summary.totalPersonalEvents === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"Nothing private captured yet\"\n description=\"Log your first life entry and it will live here — always private to you.\"\n />\n ) : (\n <div className=\"flex flex-1 flex-col gap-4\">\n <StatCard\n title=\"Private life entries\"\n value={summary.totalPersonalEvents.toLocaleString()}\n icon={<Lock className=\"h-5 w-5\" />}\n description=\"captured in the last 30 days\"\n />\n\n {personalDomains.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n No life domains logged in this window yet.\n </p>\n ) : (\n <ul className=\"space-y-2.5\">\n {personalDomains.map((d) => {\n const pct =\n maxDomainSeconds > 0\n ? Math.round((d.totalSeconds / maxDomainSeconds) * 100)\n : 0;\n return (\n <li\n key={d.domain}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {lifeDomainLabel(d.domain)}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatSecondsHuman(d.totalSeconds)} ·{\" \"}\n {d.eventCount.toLocaleString()}{\" \"}\n {d.eventCount === 1 ? \"entry\" : \"entries\"}\n </p>\n </div>\n <Lock\n className=\"h-3.5 w-3.5 shrink-0 text-text-muted\"\n aria-label=\"Private to you\"\n />\n </div>\n {/* Data-driven share bar of this window's private time. */}\n <div className=\"mt-2 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg/60\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n )}\n </GlassCard>\n\n {/* ── What you share up to work ─────────────────────────────────── */}\n <div className=\"flex flex-col gap-4\">\n <GlassCard\n treatment=\"insight\"\n glow\n className=\"flex flex-col p-4 sm:p-6\"\n >\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-info-bg/40 text-status-info-text\">\n <Share2 className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n What you share up to work\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Aggregate signals only. Every switch is off until you turn it\n on.\n </p>\n </div>\n </div>\n\n {policyError && !policy ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your sharing settings right now.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {POLICY_TOGGLES.map((toggle) => {\n const checked = policy ? policy[toggle.key] : false;\n const saving = savingPolicyKey === toggle.key;\n return (\n <div\n key={toggle.key}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {toggle.label}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {toggle.description}\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <ToggleSwitch\n checked={checked}\n disabled={saving || !policy}\n ariaLabel={`Share \"${toggle.label}\" with your workspace`}\n onChange={(next) =>\n handlePolicyToggle(toggle.key, next)\n }\n />\n <span\n className={cn(\n \"text-[0.65rem] font-medium uppercase tracking-wide\",\n checked\n ? \"text-status-success-text\"\n : \"text-text-muted\",\n )}\n >\n {saving ? \"Saving…\" : checked ? \"On\" : \"Off\"}\n </span>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n {/* Strong, unmissable reassurance. */}\n <GlassCard\n treatment=\"metric\"\n className=\"border-status-success-border/40 p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-success-bg/40 text-status-success-text\">\n <ShieldCheck className=\"h-5 w-5\" />\n </div>\n <p className=\"text-sm font-semibold text-text-primary\">\n Your employer never sees your personal context.\n </p>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Corporate (your workspace) ────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n Corporate{\" \"}\n <span className=\"font-normal text-text-secondary\">\n (your workspace)\n </span>\n </h2>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n What the workspace can actually see about you.\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-1 flex-col gap-4\">\n <div className=\"flex items-start gap-2 rounded-lg border border-status-info-border/40 bg-status-info-bg/10 px-3 py-2.5 text-xs text-status-info-text\">\n <Info className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <span>\n Your workspace only ever receives aggregate work signals —\n coarse yes/no bands, never entries, locations, health or\n timestamps.\n </span>\n </div>\n\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Aggregate signals visible to work\n </p>\n {sharedAggregates.length === 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-4 text-center\">\n <p className=\"text-sm font-medium text-text-primary\">\n Nothing is shared right now\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Turn on a signal in the middle column and it will appear\n here.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {sharedAggregates.map((agg) => (\n <li\n key={agg}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-sunken/40 px-3 py-2.5\"\n >\n <span className=\"flex min-w-0 items-center gap-2 text-sm text-text-primary\">\n <ArrowUpRight className=\"h-4 w-4 shrink-0 text-status-success-text\" />\n <span className=\"truncate\">{humanizeToken(agg)}</span>\n </span>\n <StatusBadge variant=\"success\" label=\"Shared\" dot />\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <p className=\"mt-auto text-xs text-text-muted\">\n This mirrors exactly what you've allowed — change it any time from\n the switches on the left.\n </p>\n </div>\n </GlassCard>\n </div>\n\n {/* ── Connected sources & consent ─────────────────────────────────── */}\n <GlassCard treatment=\"glass\" className=\"mt-6 p-4 sm:p-6\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Plug className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Connected sources\n </h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Each capture source only feeds your private record once you\n consent — and you can withdraw at any moment.\n </p>\n </div>\n </div>\n\n {consentsError && consents.length === 0 ? (\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text\">\n Couldn't load your connected sources right now.\n </div>\n ) : consentsLoading && consents.length === 0 ? (\n <LoadingSkeleton variant=\"list\" count={3} />\n ) : consents.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No sources connected yet\"\n description=\"When you connect a capture source — a calendar, location or health signal — you'll grant and revoke exactly what it may feed here.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {consents.map((consent) => {\n const saving = savingSource === consent.source;\n return (\n <GlassCard key={consent.id} treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {humanizeToken(consent.source)}\n </p>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge\n variant=\"muted\"\n label={humanizeToken(consent.mode)}\n />\n {consent.granted && (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={consent.onDeviceOnly}\n disabled={savingOnDeviceSource === consent.source}\n onClick={() =>\n handleOnDeviceOnlyToggle(\n consent,\n !consent.onDeviceOnly,\n )\n }\n className={cn(\n \"rounded-full border px-2 py-0.5 text-[0.65rem] font-medium uppercase tracking-wide transition-colors\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50\",\n consent.onDeviceOnly\n ? \"border-status-info-border/60 bg-status-info-bg/40 text-status-info-text\"\n : \"border-border-subtle bg-bg-sunken/40 text-text-muted hover:text-text-secondary\",\n savingOnDeviceSource === consent.source &&\n \"cursor-not-allowed opacity-50\",\n )}\n >\n {savingOnDeviceSource === consent.source\n ? \"Saving…\"\n : consent.onDeviceOnly\n ? \"On-device only\"\n : \"Turn on on-device only\"}\n </button>\n )}\n {consent.retentionDays != null && (\n <StatusBadge\n variant=\"muted\"\n label={`Kept ${consent.retentionDays}d`}\n />\n )}\n </div>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <ToggleSwitch\n checked={consent.granted}\n disabled={saving}\n ariaLabel={`Consent for ${humanizeToken(consent.source)}`}\n onChange={(next) => handleConsentToggle(consent, next)}\n />\n <span\n className={cn(\n \"text-[0.65rem] font-medium uppercase tracking-wide\",\n consent.granted\n ? \"text-status-success-text\"\n : \"text-text-muted\",\n )}\n >\n {saving\n ? \"Saving…\"\n : consent.granted\n ? \"Granted\"\n : \"Revoked\"}\n </span>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-timecampus",
3
- "version": "2026.728.1",
3
+ "version": "2026.731.2",
4
4
  "description": "Burdenoff TimeCampus Microfrontend - AI-powered time management and productivity platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -26,15 +26,15 @@
26
26
  "types": "./dist/index.d.ts",
27
27
  "scripts": {
28
28
  "dev": "vite",
29
- "build": "tsc && vite build",
29
+ "build": "tsc && bash scripts/build-limits.sh --gate vite build",
30
30
  "lint": "eslint .",
31
31
  "lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
32
32
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
33
- "type:check": "tsc --noEmit",
34
- "test": "vitest run",
33
+ "type:check": "bash scripts/build-limits.sh tsc --noEmit",
34
+ "test": "bash scripts/build-limits.sh vitest run",
35
35
  "test:watch": "vitest",
36
- "test:coverage": "vitest run --coverage",
37
- "sanity": "bun run codegen && bun run lint:sanity && bun run format && bun run type:check && bun run test && bun run build && bun run build-storybook",
36
+ "test:coverage": "bash scripts/build-limits.sh vitest run --coverage",
37
+ "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run lint:sanity && bun run format && bun run type:check && bun run test && bun run build && bun run build-storybook'",
38
38
  "storybook": "storybook dev -p 6006",
39
39
  "build-storybook": "storybook build",
40
40
  "test:storybook": "test-storybook",