@burdenoff/microfe-timecampus 2026.830.1 → 2026.903.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/timecampus/components/index.js +4 -4
  2. package/dist/timecampus/pages/HomePage.d.ts.map +1 -1
  3. package/dist/timecampus/pages/HomePage.js +142 -131
  4. package/dist/timecampus/pages/HomePage.js.map +1 -1
  5. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +7 -7
  6. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  7. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +7 -7
  8. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  9. package/dist/timecampus/pages/dashboard/ReportsPage.js +13 -13
  10. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +7 -7
  11. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +7 -7
  12. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  13. package/dist/timecampus/pages/focus-score/FocusScorePage.js +13 -13
  14. package/dist/timecampus/pages/geofencing/GeofencingPage.js +135 -135
  15. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  16. package/dist/timecampus/pages/invoicing/InvoicingPage.js +7 -7
  17. package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -1
  18. package/dist/timecampus/pages/lifetime/DeadTimeRadarPage.js +8 -8
  19. package/dist/timecampus/pages/lifetime/FiniteLifePage.js +4 -4
  20. package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
  21. package/dist/timecampus/pages/lifetime/LifeBalancePage.js +104 -104
  22. package/dist/timecampus/pages/lifetime/LifeBalancePage.js.map +1 -1
  23. package/dist/timecampus/pages/lifetime/LifeTimelinePage.js +7 -7
  24. package/dist/timecampus/pages/lifetime/LifeTimelinePage.js.map +1 -1
  25. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js +33 -33
  26. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
  27. package/dist/timecampus/pages/lifetime/RecommendationsPage.js +80 -80
  28. package/dist/timecampus/pages/lifetime/RecommendationsPage.js.map +1 -1
  29. package/dist/timecampus/pages/lifetime/TimeWellSpentPage.js +16 -16
  30. package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +11 -11
  31. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +11 -11
  32. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  33. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +14 -14
  34. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  35. package/dist/timecampus/pages/meetings/MeetingsPage.js +12 -12
  36. package/dist/timecampus/pages/projects/ProjectsPage.js +5 -5
  37. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  38. package/dist/timecampus/pages/projects/TasksPage.js +8 -8
  39. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +8 -8
  40. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  41. package/dist/timecampus/pages/team/TeamMembersPage.js +8 -8
  42. package/dist/timecampus/pages/team-activity/TeamActivityPage.js +13 -13
  43. package/dist/timecampus/pages/time/TimeEntriesPage.js +5 -5
  44. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  45. package/dist/timecampus/pages/time/TimeTrackingPage.js +138 -138
  46. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  47. package/dist/timecampus/pages/time/TimerPage.js +14 -14
  48. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  49. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +26 -26
  50. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  51. package/dist/timecampus/pages/timeline/TimelinePage.js +8 -8
  52. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +79 -79
  53. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  54. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"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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"}
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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"}
@@ -2,16 +2,16 @@ 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 { useAcceptRecommendationMutation as i, useDismissRecommendationMutation as a, useGenerateMyRecommendationsMutation as o, useMyRecommendationPreferencesQuery as s, useMyRecommendationsQuery as c, useSnoozeRecommendationMutation as l, useUpdateMyRecommendationPreferencesMutation as u } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as d } from "../../components/PageLayout.js";
7
- import { StatusBadge as f } from "../../components/StatusBadge.js";
8
- import { EmptyState as p } from "../../components/EmptyState.js";
9
- import { Modal as m } from "../../components/Modal.js";
10
- import { ToastContainer as h, useToast as g } from "../../components/Toast.js";
5
+ import { EmptyState as i } from "../../components/EmptyState.js";
6
+ import { useAcceptRecommendationMutation as a, useDismissRecommendationMutation as o, useGenerateMyRecommendationsMutation as s, useMyRecommendationPreferencesQuery as c, useMyRecommendationsQuery as l, useSnoozeRecommendationMutation as u, useUpdateMyRecommendationPreferencesMutation as d } from "../../../generated/wspace-operations.js";
7
+ import { PageLayout as f } from "../../components/PageLayout.js";
8
+ import { StatusBadge as ee } from "../../components/StatusBadge.js";
9
+ import { Modal as p } from "../../components/Modal.js";
10
+ import { ToastContainer as m, useToast as h } from "../../components/Toast.js";
11
11
  import "../../components/index.js";
12
- import { TimeCampusLifeDomain as _ } from "../../../generated/wspace-types.js";
13
- import { lifeDomainLabel as v } from "./domains.js";
14
- import { formatEvidenceRef as y, formatHourOfDay as b, humaniseToken as x, parseEvidenceRefs as ee, parseSuggestedAction as S } from "./recommendationEvidence.js";
12
+ import { TimeCampusLifeDomain as g } from "../../../generated/wspace-types.js";
13
+ import { lifeDomainLabel as _ } from "./domains.js";
14
+ import { formatEvidenceRef as v, formatHourOfDay as y, humaniseToken as b, parseEvidenceRefs as x, parseSuggestedAction as S } from "./recommendationEvidence.js";
15
15
  import { useMemo as C, useState as w } from "react";
16
16
  import { BellOff as T, Briefcase as E, Bus as D, CalendarClock as O, Check as k, CheckCircle2 as te, ChevronDown as ne, Circle as re, Clock as A, Coffee as j, GraduationCap as ie, Handshake as ae, Heart as oe, HeartPulse as se, Home as M, Hourglass as ce, Lightbulb as N, Microscope as le, Moon as ue, ShieldCheck as P, Sparkles as F, Users as I, Wallet as L, X as R } from "lucide-react";
17
17
  import { Fragment as de, jsx as z, jsxs as B } from "react/jsx-runtime";
@@ -56,20 +56,20 @@ function K(e) {
56
56
  });
57
57
  }
58
58
  var q = Array.from({ length: 24 }, (e, t) => t), me = {
59
- [_.Work]: E,
60
- [_.Sleep]: ue,
61
- [_.Health]: se,
62
- [_.Family]: I,
63
- [_.Learning]: ie,
64
- [_.Leisure]: j,
65
- [_.Commute]: D,
66
- [_.Waiting]: ce,
67
- [_.Chores]: M,
68
- [_.SelfCare]: oe,
69
- [_.Social]: ae,
70
- [_.FinanceAdmin]: L,
71
- [_.Reflection]: F,
72
- [_.Other]: re
59
+ [g.Work]: E,
60
+ [g.Sleep]: ue,
61
+ [g.Health]: se,
62
+ [g.Family]: I,
63
+ [g.Learning]: ie,
64
+ [g.Leisure]: j,
65
+ [g.Commute]: D,
66
+ [g.Waiting]: ce,
67
+ [g.Chores]: M,
68
+ [g.SelfCare]: oe,
69
+ [g.Social]: ae,
70
+ [g.FinanceAdmin]: L,
71
+ [g.Reflection]: F,
72
+ [g.Other]: re
73
73
  };
74
74
  function he(e) {
75
75
  return e ? me[e] ?? N : N;
@@ -112,7 +112,7 @@ function _e({ checked: e, onChange: n, label: r, disabled: i }) {
112
112
  });
113
113
  }
114
114
  function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o, onMuteKind: s }) {
115
- let c = he(e.domain), l = U(e.state), u = ge(e.state), [d, p] = w(!1), m = ee(e.evidenceRefs), h = S(e.suggestedAction), g = `rec-evidence-${e.id}`;
115
+ let c = he(e.domain), l = U(e.state), u = ge(e.state), [d, f] = w(!1), p = x(e.evidenceRefs), m = S(e.suggestedAction), h = `rec-evidence-${e.id}`;
116
116
  return /* @__PURE__ */ B(V, {
117
117
  treatment: "glass",
118
118
  className: "flex flex-col gap-3 p-4 sm:p-5",
@@ -128,13 +128,13 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
128
128
  className: "min-w-0",
129
129
  children: [/* @__PURE__ */ z("p", {
130
130
  className: "truncate text-xs font-medium uppercase tracking-wide text-text-secondary",
131
- children: x(e.kind)
131
+ children: b(e.kind)
132
132
  }), /* @__PURE__ */ z("h3", {
133
133
  className: "mt-0.5 text-base font-semibold text-text-primary",
134
134
  children: e.title
135
135
  })]
136
136
  })]
137
- }), e.confidence != null && /* @__PURE__ */ z(f, {
137
+ }), e.confidence != null && /* @__PURE__ */ z(ee, {
138
138
  variant: "info",
139
139
  label: `${G(e.confidence)} confident`,
140
140
  className: "shrink-0"
@@ -149,7 +149,7 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
149
149
  children: [
150
150
  e.domain && /* @__PURE__ */ z("span", {
151
151
  className: X,
152
- children: v(e.domain)
152
+ children: _(e.domain)
153
153
  }),
154
154
  e.valueAlignment != null && /* @__PURE__ */ B("span", {
155
155
  className: X,
@@ -177,7 +177,7 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
177
177
  })
178
178
  ]
179
179
  }),
180
- h && /* @__PURE__ */ B("div", {
180
+ m && /* @__PURE__ */ B("div", {
181
181
  className: "rounded-lg border border-border-seam bg-bg-sunken/40 p-3",
182
182
  children: [
183
183
  /* @__PURE__ */ z("p", {
@@ -186,11 +186,11 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
186
186
  }),
187
187
  /* @__PURE__ */ z("p", {
188
188
  className: "mt-1 text-sm font-medium text-text-primary",
189
- children: x(h.type)
189
+ children: b(m.type)
190
190
  }),
191
- h.details.length > 0 && /* @__PURE__ */ z("dl", {
191
+ m.details.length > 0 && /* @__PURE__ */ z("dl", {
192
192
  className: "mt-1.5 flex flex-wrap gap-x-4 gap-y-1",
193
- children: h.details.map((e) => /* @__PURE__ */ B("div", {
193
+ children: m.details.map((e) => /* @__PURE__ */ B("div", {
194
194
  className: "flex items-center gap-1.5",
195
195
  children: [/* @__PURE__ */ z("dt", {
196
196
  className: "text-xs text-text-secondary",
@@ -203,13 +203,13 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
203
203
  })
204
204
  ]
205
205
  }),
206
- m.length > 0 && /* @__PURE__ */ B("div", {
206
+ p.length > 0 && /* @__PURE__ */ B("div", {
207
207
  className: "rounded-lg border border-border-seam bg-bg-sunken/40",
208
208
  children: [/* @__PURE__ */ B("button", {
209
209
  type: "button",
210
- onClick: () => p((e) => !e),
210
+ onClick: () => f((e) => !e),
211
211
  "aria-expanded": d,
212
- "aria-controls": g,
212
+ "aria-controls": h,
213
213
  className: "flex w-full items-center justify-between gap-2 px-3 py-2 text-left",
214
214
  children: [/* @__PURE__ */ B("span", {
215
215
  className: "flex min-w-0 items-center gap-2",
@@ -217,22 +217,22 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
217
217
  className: "truncate text-xs text-text-secondary",
218
218
  children: [
219
219
  "Evidence: ",
220
- y(m[0]),
221
- m.length > 1 ? ` · +${m.length - 1} more` : ""
220
+ v(p[0]),
221
+ p.length > 1 ? ` · +${p.length - 1} more` : ""
222
222
  ]
223
223
  })]
224
224
  }), /* @__PURE__ */ z(ne, { className: t("h-4 w-4 shrink-0 text-text-secondary transition-transform", d && "rotate-180") })]
225
225
  }), d && /* @__PURE__ */ B("dl", {
226
- id: g,
226
+ id: h,
227
227
  className: "space-y-1.5 border-t border-border-seam px-3 py-2.5",
228
- children: [m.map((e, t) => /* @__PURE__ */ B("div", {
228
+ children: [p.map((e, t) => /* @__PURE__ */ B("div", {
229
229
  className: "flex items-baseline justify-between gap-3",
230
230
  children: [/* @__PURE__ */ z("dt", {
231
231
  className: "text-xs text-text-secondary",
232
- children: y(e).split(": ")[0]
232
+ children: v(e).split(": ")[0]
233
233
  }), /* @__PURE__ */ z("dd", {
234
234
  className: "text-xs font-medium tabular-nums text-text-primary",
235
- children: y(e).split(": ").slice(1).join(": ")
235
+ children: v(e).split(": ").slice(1).join(": ")
236
236
  })]
237
237
  }, `${e.metric}-${e.period ?? e.domain ?? t}`)), /* @__PURE__ */ z("p", {
238
238
  className: "pt-1 text-xs text-text-tertiary",
@@ -269,7 +269,7 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
269
269
  onClick: () => s(e.kind),
270
270
  disabled: n || r,
271
271
  className: Y,
272
- title: r ? `${x(e.kind)} suggestions are already muted` : `Stop generating ${x(e.kind)} suggestions`,
272
+ title: r ? `${b(e.kind)} suggestions are already muted` : `Stop generating ${b(e.kind)} suggestions`,
273
273
  children: [/* @__PURE__ */ z(T, { className: "h-4 w-4" }), r ? "Muted" : "Mute this kind"]
274
274
  })
275
275
  ]
@@ -277,7 +277,7 @@ function ve({ rec: e, busy: n, muted: r, onAccept: i, onSnooze: a, onDismiss: o,
277
277
  className: "flex flex-col gap-2 pt-1",
278
278
  children: [/* @__PURE__ */ B("div", {
279
279
  className: "flex items-center justify-between gap-2",
280
- children: [/* @__PURE__ */ z(f, {
280
+ children: [/* @__PURE__ */ z(ee, {
281
281
  variant: u.variant,
282
282
  label: u.label
283
283
  }), /* @__PURE__ */ z("span", {
@@ -298,7 +298,7 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
298
298
  count: 1
299
299
  });
300
300
  if (!e) return null;
301
- let c = b(e.quietHoursStart), l = b(e.quietHoursEnd), u = c !== null && l !== null, d = "rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40 disabled:opacity-50";
301
+ let c = y(e.quietHoursStart), l = y(e.quietHoursEnd), u = c !== null && l !== null, d = "rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg/40 disabled:opacity-50";
302
302
  return /* @__PURE__ */ B(V, {
303
303
  treatment: "glass",
304
304
  className: "p-4 sm:p-5",
@@ -367,7 +367,7 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
367
367
  children: "—"
368
368
  }), q.map((e) => /* @__PURE__ */ z("option", {
369
369
  value: e,
370
- children: b(e)
370
+ children: y(e)
371
371
  }, e))]
372
372
  })]
373
373
  }),
@@ -383,7 +383,7 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
383
383
  children: "—"
384
384
  }), q.map((e) => /* @__PURE__ */ z("option", {
385
385
  value: e,
386
- children: b(e)
386
+ children: y(e)
387
387
  }, e))]
388
388
  })]
389
389
  }),
@@ -419,8 +419,8 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
419
419
  disabled: i,
420
420
  onClick: () => s(e),
421
421
  className: t(X, "hover:text-text-primary disabled:opacity-50"),
422
- title: `Unmute ${x(e)} suggestions`,
423
- children: [x(e), /* @__PURE__ */ z(R, { className: "h-3 w-3" })]
422
+ title: `Unmute ${b(e)} suggestions`,
423
+ children: [b(e), /* @__PURE__ */ z(R, { className: "h-3 w-3" })]
424
424
  }, e))
425
425
  })]
426
426
  }),
@@ -433,7 +433,7 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
433
433
  children: "Cadence"
434
434
  }), /* @__PURE__ */ z("span", {
435
435
  className: "text-xs text-text-secondary",
436
- children: x(e.cadence)
436
+ children: b(e.cadence)
437
437
  })]
438
438
  }), e.optedInDomains.length > 0 && /* @__PURE__ */ B(de, { children: [/* @__PURE__ */ z("p", {
439
439
  className: "mt-3 text-sm font-medium text-text-primary",
@@ -442,7 +442,7 @@ function ye({ prefs: e, loading: n, saving: i, onToggleOutputGuard: a, onQuietHo
442
442
  className: "mt-1.5 flex flex-wrap gap-1.5",
443
443
  children: e.optedInDomains.map((e) => /* @__PURE__ */ z("span", {
444
444
  className: X,
445
- children: v(e)
445
+ children: _(e)
446
446
  }, e))
447
447
  })] })]
448
448
  })
@@ -472,23 +472,23 @@ function be(e) {
472
472
  }
473
473
  }
474
474
  function xe() {
475
- let { workspaceId: t } = e(), { toasts: f, addToast: _, dismissToast: v } = g(), [y, b] = w(H.New), [ee, S] = w(null), [T, E] = w(null), [D, O] = w(""), { data: k, loading: ne, error: re, refetch: j } = c({
475
+ let { workspaceId: t } = e(), { toasts: ee, addToast: g, dismissToast: _ } = h(), [v, y] = w(H.New), [x, S] = w(null), [T, E] = w(null), [D, O] = w(""), { data: k, loading: ne, error: re, refetch: j } = l({
476
476
  variables: { state: null },
477
477
  skip: !t
478
- }), { data: ie, loading: ae, refetch: oe } = s({ skip: !t }), [se, { loading: M }] = o(), [ce] = i(), [N] = a(), [le] = l(), [ue, { loading: I }] = u(), L = ie?.myRecommendationPreferences, U = C(() => new Set(L?.mutedKinds ?? []), [L?.mutedKinds]), W = C(() => k?.myRecommendations ?? [], [k?.myRecommendations]), G = C(() => {
478
+ }), { data: ie, loading: ae, refetch: oe } = c({ skip: !t }), [se, { loading: M }] = s(), [ce] = a(), [N] = o(), [le] = u(), [ue, { loading: I }] = d(), L = ie?.myRecommendationPreferences, U = C(() => new Set(L?.mutedKinds ?? []), [L?.mutedKinds]), W = C(() => k?.myRecommendations ?? [], [k?.myRecommendations]), G = C(() => {
479
479
  let e = {};
480
480
  for (let t of W) e[t.state] = (e[t.state] ?? 0) + 1;
481
481
  return e;
482
- }, [W]), K = C(() => W.filter((e) => e.state === y), [W, y]), q = G[H.New] ?? 0, me = G[H.Accepted] ?? 0, he = G[H.Snoozed] ?? 0, ge = async () => {
482
+ }, [W]), K = C(() => W.filter((e) => e.state === v), [W, v]), q = G[H.New] ?? 0, me = G[H.Accepted] ?? 0, he = G[H.Snoozed] ?? 0, ge = async () => {
483
483
  try {
484
484
  let e = (await se()).data?.generateMyRecommendations ?? [];
485
- _({
485
+ g({
486
486
  title: e.length > 0 ? `${e.length} suggestion${e.length === 1 ? "" : "s"} ready` : "No new suggestions",
487
487
  description: e.length > 0 ? "Fresh, values-aware ideas are in your New feed." : "You're all caught up — nothing new to suggest right now.",
488
488
  variant: e.length > 0 ? "success" : "info"
489
- }), b(H.New), await j();
489
+ }), y(H.New), await j();
490
490
  } catch {
491
- _({
491
+ g({
492
492
  title: "Couldn't generate recommendations",
493
493
  description: "Please try again in a moment.",
494
494
  variant: "error"
@@ -497,12 +497,12 @@ function xe() {
497
497
  }, X = async (e) => {
498
498
  S(e);
499
499
  try {
500
- await ce({ variables: { id: e } }), _({
500
+ await ce({ variables: { id: e } }), g({
501
501
  title: "Accepted — nice",
502
502
  variant: "success"
503
503
  }), await j();
504
504
  } catch {
505
- _({
505
+ g({
506
506
  title: "Couldn't accept that one",
507
507
  variant: "error"
508
508
  });
@@ -515,13 +515,13 @@ function xe() {
515
515
  await le({ variables: {
516
516
  id: e,
517
517
  until: pe()
518
- } }), _({
518
+ } }), g({
519
519
  title: "Snoozed for a week",
520
520
  description: "It'll resurface later — no nagging in between.",
521
521
  variant: "info"
522
522
  }), await j();
523
523
  } catch {
524
- _({
524
+ g({
525
525
  title: "Couldn't snooze that one",
526
526
  variant: "error"
527
527
  });
@@ -539,12 +539,12 @@ function xe() {
539
539
  await N({ variables: {
540
540
  id: e.id,
541
541
  reason: t.length > 0 ? t : null
542
- } }), _({
542
+ } }), g({
543
543
  title: "Dismissed",
544
544
  variant: "success"
545
545
  }), E(null), O(""), await j();
546
546
  } catch {
547
- _({
547
+ g({
548
548
  title: "Couldn't dismiss that one",
549
549
  variant: "error"
550
550
  });
@@ -553,12 +553,12 @@ function xe() {
553
553
  }
554
554
  }, Z = async (e, t) => {
555
555
  try {
556
- await ue({ variables: { input: e } }), _({
556
+ await ue({ variables: { input: e } }), g({
557
557
  title: t,
558
558
  variant: "success"
559
559
  }), await oe();
560
560
  } catch {
561
- _({
561
+ g({
562
562
  title: "Couldn't save that guardrail",
563
563
  description: "Please try again in a moment.",
564
564
  variant: "error"
@@ -568,14 +568,14 @@ function xe() {
568
568
  quietHoursStart: e,
569
569
  quietHoursEnd: t
570
570
  }, e === null && t === null ? "Quiet hours cleared" : "Quiet hours saved"), Te = (e) => {
571
- U.has(e) || Z({ mutedKinds: [...U, e] }, `${x(e)} suggestions muted`);
571
+ U.has(e) || Z({ mutedKinds: [...U, e] }, `${b(e)} suggestions muted`);
572
572
  }, Ee = /* @__PURE__ */ z(ye, {
573
573
  prefs: L,
574
574
  loading: ae,
575
575
  saving: I,
576
576
  onToggleOutputGuard: Ce,
577
577
  onQuietHoursChange: we,
578
- onUnmuteKind: (e) => void Z({ mutedKinds: [...U].filter((t) => t !== e) }, `${x(e)} suggestions unmuted`)
578
+ onUnmuteKind: (e) => void Z({ mutedKinds: [...U].filter((t) => t !== e) }, `${b(e)} suggestions unmuted`)
579
579
  }), Q = /* @__PURE__ */ B("button", {
580
580
  type: "button",
581
581
  onClick: ge,
@@ -602,7 +602,7 @@ function xe() {
602
602
  })]
603
603
  })
604
604
  });
605
- if (!t || ne) return /* @__PURE__ */ z(d, {
605
+ if (!t || ne) return /* @__PURE__ */ z(f, {
606
606
  title: "Recommendations",
607
607
  description: "Values-aware suggestions from your real timeline",
608
608
  purpose: $,
@@ -612,7 +612,7 @@ function xe() {
612
612
  count: 4
613
613
  })
614
614
  });
615
- if (re) return /* @__PURE__ */ z(d, {
615
+ if (re) return /* @__PURE__ */ z(f, {
616
616
  title: "Recommendations",
617
617
  description: "Values-aware suggestions from your real timeline",
618
618
  purpose: $,
@@ -622,8 +622,8 @@ function xe() {
622
622
  children: "Couldn't load your recommendations right now. Please try again in a moment."
623
623
  })
624
624
  });
625
- let Oe = W.length > 0, ke = be(y);
626
- return /* @__PURE__ */ B(d, {
625
+ let Oe = W.length > 0, ke = be(v);
626
+ return /* @__PURE__ */ B(f, {
627
627
  title: "Recommendations",
628
628
  description: "Values-aware suggestions from your real timeline",
629
629
  purpose: $,
@@ -665,12 +665,12 @@ function xe() {
665
665
  role: "tablist",
666
666
  "aria-label": "Filter recommendations by state",
667
667
  children: fe.map((e) => {
668
- let t = e.key === y, n = G[e.key] ?? 0;
668
+ let t = e.key === v, n = G[e.key] ?? 0;
669
669
  return /* @__PURE__ */ B("button", {
670
670
  type: "button",
671
671
  role: "tab",
672
672
  "aria-selected": t,
673
- onClick: () => b(e.key),
673
+ onClick: () => y(e.key),
674
674
  className: t ? "flex items-center gap-1.5 rounded-md bg-bg-surface px-3 py-1.5 text-sm font-medium text-text-primary shadow-sm" : "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary",
675
675
  children: [e.label, /* @__PURE__ */ z("span", {
676
676
  className: t ? "rounded-full bg-action-primary-bg/15 px-1.5 text-xs font-semibold text-primary" : "rounded-full bg-bg-sunken px-1.5 text-xs font-semibold text-text-secondary",
@@ -680,16 +680,16 @@ function xe() {
680
680
  })
681
681
  })
682
682
  }),
683
- K.length === 0 ? /* @__PURE__ */ z(p, {
683
+ K.length === 0 ? /* @__PURE__ */ z(i, {
684
684
  illustration: "empty-data",
685
685
  title: ke.title,
686
686
  description: ke.description,
687
- action: y === H.New ? Q : void 0
687
+ action: v === H.New ? Q : void 0
688
688
  }) : /* @__PURE__ */ z("div", {
689
689
  className: "space-y-4",
690
690
  children: K.map((e) => /* @__PURE__ */ z(ve, {
691
691
  rec: e,
692
- busy: ee === e.id || I,
692
+ busy: x === e.id || I,
693
693
  muted: U.has(e.kind),
694
694
  onAccept: X,
695
695
  onSnooze: _e,
@@ -702,14 +702,14 @@ function xe() {
702
702
  }), /* @__PURE__ */ z("div", { children: Ee })]
703
703
  }) : /* @__PURE__ */ B("div", {
704
704
  className: "grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]",
705
- children: [/* @__PURE__ */ B("div", { children: [/* @__PURE__ */ z(p, {
705
+ children: [/* @__PURE__ */ B("div", { children: [/* @__PURE__ */ z(i, {
706
706
  illustration: "empty-data",
707
707
  title: "No recommendations yet",
708
708
  description: "TimeCampus turns your real timeline into gentle, values-aware suggestions — like protecting a focus block or reclaiming a commute. Generate your first set to see what it notices.",
709
709
  action: Q
710
710
  }), De] }), /* @__PURE__ */ z("div", { children: Ee })]
711
711
  }),
712
- /* @__PURE__ */ B(m, {
712
+ /* @__PURE__ */ B(p, {
713
713
  open: T !== null,
714
714
  onClose: () => E(null),
715
715
  title: "Dismiss this suggestion",
@@ -723,7 +723,7 @@ function xe() {
723
723
  }), /* @__PURE__ */ B("button", {
724
724
  type: "button",
725
725
  onClick: () => void Se(),
726
- disabled: ee !== null,
726
+ disabled: x !== null,
727
727
  className: J,
728
728
  children: [/* @__PURE__ */ z(R, { className: "h-4 w-4" }), "Dismiss"]
729
729
  })] }),
@@ -754,9 +754,9 @@ function xe() {
754
754
  })
755
755
  ]
756
756
  }),
757
- /* @__PURE__ */ z(h, {
758
- toasts: f,
759
- onDismiss: v
757
+ /* @__PURE__ */ z(m, {
758
+ toasts: ee,
759
+ onDismiss: _
760
760
  })
761
761
  ]
762
762
  });