@burdenoff/microfe-timecampus 2026.625.1 → 2026.626.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 (101) hide show
  1. package/dist/timecampus/components/EmptyState.js +14 -0
  2. package/dist/timecampus/components/EmptyState.js.map +1 -0
  3. package/dist/timecampus/components/PageLayout.js +23 -15
  4. package/dist/timecampus/components/PageLayout.js.map +1 -1
  5. package/dist/timecampus/pages/applets/AppletStorePage.js +33 -30
  6. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  7. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +37 -31
  8. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  9. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +48 -44
  10. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  11. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +35 -31
  12. package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
  13. package/dist/timecampus/pages/channels/ChannelsPage.js +27 -24
  14. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  15. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +46 -38
  16. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  17. package/dist/timecampus/pages/dashboard/AnalyticsPage.js +66 -39
  18. package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
  19. package/dist/timecampus/pages/dashboard/ReportsPage.js +42 -36
  20. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  21. package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +2 -0
  22. package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
  23. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +35 -31
  24. package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
  25. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +31 -25
  26. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +43 -35
  28. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/focus-score/FocusScorePage.js +55 -51
  30. package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
  31. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +32 -28
  32. package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
  33. package/dist/timecampus/pages/geofencing/GeofencingPage.js +32 -25
  34. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  35. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +29 -25
  36. package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
  37. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +32 -29
  38. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  39. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +29 -25
  40. package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
  41. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +39 -35
  42. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  43. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +2 -0
  44. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +33 -29
  46. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  47. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +122 -104
  48. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  49. package/dist/timecampus/pages/meetings/MeetingsPage.js +42 -32
  50. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  51. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +2 -0
  52. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
  53. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +145 -114
  54. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  55. package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +49 -45
  56. package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
  57. package/dist/timecampus/pages/projects/ProjectCreatePage.js +28 -24
  58. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  59. package/dist/timecampus/pages/projects/ProjectDetailPage.js +109 -78
  60. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  61. package/dist/timecampus/pages/projects/ProjectsPage.js +11 -7
  62. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  63. package/dist/timecampus/pages/projects/TasksPage.js +40 -30
  64. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  65. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +50 -46
  66. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  67. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +18 -14
  68. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  69. package/dist/timecampus/pages/team/TeamMembersPage.js +41 -35
  70. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  71. package/dist/timecampus/pages/team/TeamPage.js +91 -61
  72. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  73. package/dist/timecampus/pages/team-activity/TeamActivityPage.js +40 -36
  74. package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
  75. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +33 -29
  76. package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
  77. package/dist/timecampus/pages/templates/TemplateApplicationPage.js +17 -13
  78. package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
  79. package/dist/timecampus/pages/templates/TemplateDetailPage.js +67 -49
  80. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  81. package/dist/timecampus/pages/templates/TemplatesPage.js +34 -31
  82. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  83. package/dist/timecampus/pages/time/TimeEntriesPage.js +48 -39
  84. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  85. package/dist/timecampus/pages/time/TimeTrackingPage.js +65 -64
  86. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  87. package/dist/timecampus/pages/time/TimerPage.js +103 -69
  88. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  89. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +53 -49
  90. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  91. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +65 -47
  92. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  93. package/dist/timecampus/pages/widgets/WidgetsPage.js +38 -31
  94. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  95. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +2 -0
  96. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  97. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +35 -31
  98. package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
  99. package/dist/timecampus/pages/workflows/WorkflowsPage.js +43 -33
  100. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  101. package/package.json +2 -2
@@ -3,42 +3,48 @@ import { formatRelativeTime as t, getPriorityColor as n, getStatusColor as r } f
3
3
  import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
4
4
  import { useListEmailTimeEntriesQuery as a } from "../../../generated/wspace-operations.js";
5
5
  import { PageLayout as o } from "../../components/PageLayout.js";
6
- import { Filter as s, Mail as c } from "lucide-react";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
6
+ import { EmptyState as s } from "../../components/EmptyState.js";
7
+ import { Filter as c, Mail as l } from "lucide-react";
8
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
+ import { GlassCard as f } from "@burdenoff/fe-libs/ui";
8
10
  //#region src/timecampus/pages/email-tracking/EmailTrackingPage.tsx
9
- function d() {
10
- let { workspaceId: d = "" } = e(), { data: f, loading: p, error: m } = a({
11
+ function p() {
12
+ let { workspaceId: p = "" } = e(), { data: m, loading: h, error: g } = a({
11
13
  variables: { limit: 50 },
12
- skip: !d
13
- }), h = f?.emailTimeEntries ?? [];
14
- return /* @__PURE__ */ l(o, {
14
+ skip: !p
15
+ }), _ = m?.emailTimeEntries ?? [];
16
+ return /* @__PURE__ */ u(o, {
15
17
  title: "Email Tracking",
16
18
  description: "Track time spent on emails",
17
- actions: /* @__PURE__ */ u("button", {
19
+ purpose: "Surface the hidden hours email steals from your day — every message linked to the time it took and the project it belonged to — so inbox work finally shows up in your timesheets and reports.",
20
+ actions: /* @__PURE__ */ d("button", {
18
21
  className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
19
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Filter"]
22
+ children: [/* @__PURE__ */ u(c, { className: "h-4 w-4" }), "Filter"]
20
23
  }),
21
- children: p ? /* @__PURE__ */ l(i, {
24
+ children: h ? /* @__PURE__ */ u(i, {
22
25
  variant: "list",
23
26
  count: 4
24
- }) : m ? /* @__PURE__ */ l("div", {
27
+ }) : g ? /* @__PURE__ */ u("div", {
25
28
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
26
29
  children: "Failed to load email entries. Please try again later."
27
- }) : /* @__PURE__ */ l("div", {
28
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
29
- children: /* @__PURE__ */ l("div", {
30
+ }) : _.length === 0 ? /* @__PURE__ */ u(s, {
31
+ illustration: "empty-notifications",
32
+ title: "No email entries found",
33
+ description: "Once email tracking is connected, the time you spend reading and replying shows up here."
34
+ }) : /* @__PURE__ */ u(f, {
35
+ treatment: "glass",
36
+ glow: !0,
37
+ className: "overflow-hidden",
38
+ children: /* @__PURE__ */ u("div", {
30
39
  className: "divide-y divide-border",
31
- children: h.length === 0 ? /* @__PURE__ */ l("div", {
32
- className: "p-8 text-center text-sm text-text-secondary",
33
- children: "No email entries found."
34
- }) : h.map((e) => /* @__PURE__ */ u("div", {
40
+ children: _.map((e) => /* @__PURE__ */ d("div", {
35
41
  className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer",
36
- children: [/* @__PURE__ */ u("div", {
42
+ children: [/* @__PURE__ */ d("div", {
37
43
  className: "flex items-center gap-3",
38
- children: [/* @__PURE__ */ l(c, { className: `h-5 w-5 ${e.isImportant ? "text-primary" : "text-text-secondary"}` }), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
44
+ children: [/* @__PURE__ */ u(l, { className: `h-5 w-5 ${e.isImportant ? "text-primary" : "text-text-secondary"}` }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
39
45
  className: `text-sm ${e.isImportant ? "font-semibold text-text-primary" : "font-medium text-text-primary"}`,
40
46
  children: e.subject ?? "(No subject)"
41
- }), /* @__PURE__ */ u("p", {
47
+ }), /* @__PURE__ */ d("p", {
42
48
  className: "text-xs text-text-secondary",
43
49
  children: [
44
50
  e.sender ?? "Unknown",
@@ -50,12 +56,12 @@ function d() {
50
56
  "min"
51
57
  ]
52
58
  })] })]
53
- }), /* @__PURE__ */ u("div", {
59
+ }), /* @__PURE__ */ d("div", {
54
60
  className: "flex items-center gap-2",
55
- children: [/* @__PURE__ */ l("span", {
61
+ children: [/* @__PURE__ */ u("span", {
56
62
  className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${n(e.isImportant ? "high" : "low")}`,
57
63
  children: e.isImportant ? "high" : "normal"
58
- }), /* @__PURE__ */ l("span", {
64
+ }), /* @__PURE__ */ u("span", {
59
65
  className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${r(e.category ?? "active")}`,
60
66
  children: e.category ?? "uncategorized"
61
67
  })]
@@ -66,6 +72,6 @@ function d() {
66
72
  });
67
73
  }
68
74
  //#endregion
69
- export { d as EmailTrackingPage };
75
+ export { p as EmailTrackingPage };
70
76
 
71
77
  //# sourceMappingURL=EmailTrackingPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EmailTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/email-tracking/EmailTrackingPage.tsx"],"sourcesContent":["import { Mail, Filter } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n getPriorityColor,\n formatRelativeTime,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListEmailTimeEntriesQuery,\n type EmailTimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function EmailTrackingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { data, loading, error } = useListEmailTimeEntriesQuery({\n variables: { limit: 50 },\n skip: !workspaceId,\n });\n\n const emails = data?.emailTimeEntries ?? [];\n\n return (\n <PageLayout\n title=\"Email Tracking\"\n description=\"Track time spent on emails\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <Filter className=\"h-4 w-4\" />\n Filter\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : error ? (\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 Failed to load email entries. Please try again later.\n </div>\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {emails.length === 0 ? (\n <div className=\"p-8 text-center text-sm text-text-secondary\">\n No email entries found.\n </div>\n ) : (\n emails.map((email: EmailTimeEntryFieldsFragment) => (\n <div\n key={email.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <Mail\n className={`h-5 w-5 ${email.isImportant ? \"text-primary\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm ${email.isImportant ? \"font-semibold text-text-primary\" : \"font-medium text-text-primary\"}`}\n >\n {email.subject ?? \"(No subject)\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {email.sender ?? \"Unknown\"} &middot;{\" \"}\n {formatRelativeTime(email.date)} &middot; ~\n {email.duration ?? 0}min\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(email.isImportant ? \"high\" : \"low\")}`}\n >\n {email.isImportant ? \"high\" : \"normal\"}\n </span>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(email.category ?? \"active\")}`}\n >\n {email.category ?? \"uncategorized\"}\n </span>\n </div>\n </div>\n ))\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,IAAoB;CAClC,IAAM,EAAE,iBAAc,OAAO,GAAe,EACtC,EAAE,SAAM,YAAS,aAAU,EAA6B;EAC5D,WAAW,EAAE,OAAO,IAAI;EACxB,MAAM,CAAC;EACR,CAAC,EAEI,IAAS,GAAM,oBAAoB,EAAE;AAE3C,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,SAEvB;;YAGV,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA,GAC1C,IACF,kBAAC,OAAD;GAAK,WAAU;aAA2G;GAEpH,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,WAAW,IACjB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAEvD,CAAA,GAEN,EAAO,KAAK,MACV,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,WAAW,EAAM,cAAc,iBAAiB,yBAC3D,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OACE,WAAW,WAAW,EAAM,cAAc,oCAAoC;iBAE7E,EAAM,WAAW;OAChB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAM,UAAU;QAAU;QAAU;QACpC,EAAmB,EAAM,KAAK;QAAC;QAC/B,EAAM,YAAY;QAAE;QACnB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAiB,EAAM,cAAc,SAAS,MAAM;iBAE1H,EAAM,cAAc,SAAS;OACzB,CAAA,EACP,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAM,YAAY,SAAS;iBAEhH,EAAM,YAAY;OACd,CAAA,CACH;QACF;OAhCC,EAAM,GAgCP,CACN;IAEA,CAAA;GACF,CAAA;EAEG,CAAA"}
1
+ {"version":3,"file":"EmailTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/email-tracking/EmailTrackingPage.tsx"],"sourcesContent":["import { Mail, Filter } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n getPriorityColor,\n formatRelativeTime,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListEmailTimeEntriesQuery,\n type EmailTimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function EmailTrackingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { data, loading, error } = useListEmailTimeEntriesQuery({\n variables: { limit: 50 },\n skip: !workspaceId,\n });\n\n const emails = data?.emailTimeEntries ?? [];\n\n return (\n <PageLayout\n title=\"Email Tracking\"\n description=\"Track time spent on emails\"\n purpose=\"Surface the hidden hours email steals from your day — every message linked to the time it took and the project it belonged to — so inbox work finally shows up in your timesheets and reports.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <Filter className=\"h-4 w-4\" />\n Filter\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : error ? (\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 Failed to load email entries. Please try again later.\n </div>\n ) : emails.length === 0 ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No email entries found\"\n description=\"Once email tracking is connected, the time you spend reading and replying shows up here.\"\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {emails.map((email: EmailTimeEntryFieldsFragment) => (\n <div\n key={email.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors cursor-pointer\"\n >\n <div className=\"flex items-center gap-3\">\n <Mail\n className={`h-5 w-5 ${email.isImportant ? \"text-primary\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm ${email.isImportant ? \"font-semibold text-text-primary\" : \"font-medium text-text-primary\"}`}\n >\n {email.subject ?? \"(No subject)\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {email.sender ?? \"Unknown\"} &middot;{\" \"}\n {formatRelativeTime(email.date)} &middot; ~\n {email.duration ?? 0}min\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(email.isImportant ? \"high\" : \"low\")}`}\n >\n {email.isImportant ? \"high\" : \"normal\"}\n </span>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(email.category ?? \"active\")}`}\n >\n {email.category ?? \"uncategorized\"}\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgB,IAAoB;CAClC,IAAM,EAAE,iBAAc,OAAO,GAAe,EACtC,EAAE,SAAM,YAAS,aAAU,EAA6B;EAC5D,WAAW,EAAE,OAAO,IAAI;EACxB,MAAM,CAAC;EACR,CAAC,EAEI,IAAS,GAAM,oBAAoB,EAAE;AAE3C,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,SAEvB;;YAGV,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA,GAC1C,IACF,kBAAC,OAAD;GAAK,WAAU;aAA2G;GAEpH,CAAA,GACJ,EAAO,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MACX,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,WAAW,EAAM,cAAc,iBAAiB,yBAC3D,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OACE,WAAW,WAAW,EAAM,cAAc,oCAAoC;iBAE7E,EAAM,WAAW;OAChB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAM,UAAU;QAAU;QAAU;QACpC,EAAmB,EAAM,KAAK;QAAC;QAC/B,EAAM,YAAY;QAAE;QACnB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAiB,EAAM,cAAc,SAAS,MAAM;iBAE1H,EAAM,cAAc,SAAS;OACzB,CAAA,EACP,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAM,YAAY,SAAS;iBAEhH,EAAM,YAAY;OACd,CAAA,CACH;QACF;OAhCC,EAAM,GAgCP,CACN;IACE,CAAA;GACI,CAAA;EAEH,CAAA"}
@@ -4,72 +4,80 @@ import { StatCard as i } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as a } from "../../components/LoadingSkeleton.js";
5
5
  import { useGetExpenseStatsQuery as o, useListExpensesQuery as s } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as c } from "../../components/PageLayout.js";
7
- import { DollarSign as l, Plus as u, Receipt as d } from "lucide-react";
8
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
7
+ import { EmptyState as l } from "../../components/EmptyState.js";
8
+ import { DollarSign as u, Plus as d, Receipt as f } from "lucide-react";
9
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
9
10
  //#region src/timecampus/pages/expense-tracking/ExpenseTrackingPage.tsx
10
- function m() {
11
- let { workspaceId: m = "" } = e(), { data: h, loading: g, error: _ } = s({ variables: { workspaceId: m } }), { data: v, loading: y, error: b } = o({ variables: { workspaceId: m } }), x = h?.expenses ?? [], S = v?.expenseStats;
12
- return g && y ? /* @__PURE__ */ f(c, {
11
+ function h() {
12
+ let { workspaceId: h = "" } = e(), { data: g, loading: _, error: v } = s({ variables: { workspaceId: h } }), { data: y, loading: b, error: x } = o({ variables: { workspaceId: h } }), S = g?.expenses ?? [], C = y?.expenseStats;
13
+ return _ && b ? /* @__PURE__ */ p(c, {
13
14
  title: "Expense Tracking",
14
15
  description: "Track and manage work expenses",
15
- children: /* @__PURE__ */ f(a, {
16
+ children: /* @__PURE__ */ p(a, {
16
17
  variant: "list",
17
18
  count: 4
18
19
  })
19
- }) : _ && b ? /* @__PURE__ */ f(c, {
20
+ }) : v && x ? /* @__PURE__ */ p(c, {
20
21
  title: "Expense Tracking",
21
22
  description: "Track and manage work expenses",
22
- children: /* @__PURE__ */ f("div", {
23
+ children: /* @__PURE__ */ p("div", {
23
24
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
24
25
  children: "Failed to load expense data. Please try again later."
25
26
  })
26
- }) : /* @__PURE__ */ p(c, {
27
+ }) : /* @__PURE__ */ m(c, {
27
28
  title: "Expense Tracking",
28
29
  description: "Track and manage work expenses",
29
- actions: /* @__PURE__ */ p("button", {
30
+ purpose: "Capture project-related costs alongside the hours you log — receipts, categories and approvals in one place — so the bill you send a client reflects the full cost of the work, not just time.",
31
+ actions: /* @__PURE__ */ m("button", {
30
32
  className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
31
- children: [/* @__PURE__ */ f(u, { className: "h-4 w-4" }), "Add Expense"]
33
+ children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), "Add Expense"]
32
34
  }),
33
- children: [/* @__PURE__ */ p("div", {
35
+ children: [/* @__PURE__ */ m("div", {
34
36
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
35
37
  children: [
36
- /* @__PURE__ */ f(i, {
38
+ /* @__PURE__ */ p(i, {
39
+ featured: !0,
37
40
  title: "Total This Month",
38
- value: t(S?.totalAmount ?? 0),
39
- icon: /* @__PURE__ */ f(l, { className: "h-5 w-5" })
41
+ value: t(C?.totalAmount ?? 0),
42
+ icon: /* @__PURE__ */ p(u, { className: "h-5 w-5" })
40
43
  }),
41
- /* @__PURE__ */ f(i, {
44
+ /* @__PURE__ */ p(i, {
42
45
  title: "Pending",
43
- value: String(S?.pendingCount ?? 0),
46
+ value: String(C?.pendingCount ?? 0),
44
47
  description: "expenses awaiting approval"
45
48
  }),
46
- /* @__PURE__ */ f(i, {
49
+ /* @__PURE__ */ p(i, {
47
50
  title: "Approved",
48
- value: String(S?.approvedCount ?? 0),
51
+ value: String(C?.approvedCount ?? 0),
49
52
  description: "expenses approved"
50
53
  }),
51
- /* @__PURE__ */ f(i, {
54
+ /* @__PURE__ */ p(i, {
52
55
  title: "Average",
53
- value: t(S?.averageAmount ?? 0),
54
- icon: /* @__PURE__ */ f(d, { className: "h-5 w-5" }),
56
+ value: t(C?.averageAmount ?? 0),
57
+ icon: /* @__PURE__ */ p(f, { className: "h-5 w-5" }),
55
58
  description: "per expense"
56
59
  })
57
60
  ]
58
- }), /* @__PURE__ */ f("div", {
61
+ }), S.length === 0 && !_ ? /* @__PURE__ */ p(l, {
62
+ illustration: "empty-data",
63
+ title: "No expenses recorded yet",
64
+ description: "Add your first expense to track project costs and route them for approval.",
65
+ action: /* @__PURE__ */ m("button", {
66
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
67
+ children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), "Add Expense"]
68
+ })
69
+ }) : /* @__PURE__ */ p("div", {
59
70
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
60
71
  children: /* @__PURE__ */ p("div", {
61
72
  className: "divide-y divide-border",
62
- children: [x.length === 0 && !g && /* @__PURE__ */ f("div", {
63
- className: "p-4 text-center text-sm text-text-secondary",
64
- children: "No expenses recorded yet."
65
- }), x.map((e) => /* @__PURE__ */ p("div", {
73
+ children: S.map((e) => /* @__PURE__ */ m("div", {
66
74
  className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
67
- children: [/* @__PURE__ */ p("div", {
75
+ children: [/* @__PURE__ */ m("div", {
68
76
  className: "flex items-center gap-3",
69
- children: [/* @__PURE__ */ f(d, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
77
+ children: [/* @__PURE__ */ p(f, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
70
78
  className: "text-sm font-medium text-text-primary",
71
79
  children: e.description ?? "Untitled expense"
72
- }), /* @__PURE__ */ p("p", {
80
+ }), /* @__PURE__ */ m("p", {
73
81
  className: "text-xs text-text-secondary",
74
82
  children: [
75
83
  e.category,
@@ -81,22 +89,22 @@ function m() {
81
89
  n(e.date)
82
90
  ]
83
91
  })] })]
84
- }), /* @__PURE__ */ p("div", {
92
+ }), /* @__PURE__ */ m("div", {
85
93
  className: "flex items-center gap-3",
86
- children: [/* @__PURE__ */ f("p", {
94
+ children: [/* @__PURE__ */ p("p", {
87
95
  className: "text-sm font-medium text-text-primary",
88
96
  children: t(e.amount)
89
- }), /* @__PURE__ */ f("span", {
97
+ }), /* @__PURE__ */ p("span", {
90
98
  className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${r(e.status)}`,
91
99
  children: e.status
92
100
  })]
93
101
  })]
94
- }, e.id))]
102
+ }, e.id))
95
103
  })
96
104
  })]
97
105
  });
98
106
  }
99
107
  //#endregion
100
- export { m as ExpenseTrackingPage };
108
+ export { h as ExpenseTrackingPage };
101
109
 
102
110
  //# sourceMappingURL=ExpenseTrackingPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpenseTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/expense-tracking/ExpenseTrackingPage.tsx"],"sourcesContent":["import { Receipt, Plus, DollarSign } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n formatCurrency,\n formatDate,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListExpensesQuery,\n useGetExpenseStatsQuery,\n type ExpenseFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ExpenseTrackingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: expensesData,\n loading: expensesLoading,\n error: expensesError,\n } = useListExpensesQuery({\n variables: { workspaceId },\n });\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetExpenseStatsQuery({\n variables: { workspaceId },\n });\n\n const expenses = expensesData?.expenses ?? [];\n const stats = statsData?.expenseStats;\n\n if (expensesLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (expensesError && statsError) {\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load expense data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Expense\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Total This Month\"\n value={formatCurrency(stats?.totalAmount ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Pending\"\n value={String(stats?.pendingCount ?? 0)}\n description=\"expenses awaiting approval\"\n />\n <StatCard\n title=\"Approved\"\n value={String(stats?.approvedCount ?? 0)}\n description=\"expenses approved\"\n />\n <StatCard\n title=\"Average\"\n value={formatCurrency(stats?.averageAmount ?? 0)}\n icon={<Receipt className=\"h-5 w-5\" />}\n description=\"per expense\"\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {expenses.length === 0 && !expensesLoading && (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n No expenses recorded yet.\n </div>\n )}\n {expenses.map((expense: ExpenseFieldsFragment) => (\n <div\n key={expense.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <Receipt className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {expense.description ?? \"Untitled expense\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {expense.category} &middot;{\" \"}\n {expense.projectId ?? \"No project\"} &middot;{\" \"}\n {formatDate(expense.date)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatCurrency(expense.amount)}\n </p>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(expense.status)}`}\n >\n {expense.status}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAgB,IAAsB;CACpC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAAqB,EACvB,WAAW,EAAE,gBAAa,EAC3B,CAAC,EACI,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAAwB,EAC1B,WAAW,EAAE,gBAAa,EAC3B,CAAC,EAEI,IAAW,GAAc,YAAY,EAAE,EACvC,IAAQ,GAAW;AA0BzB,QAxBI,KAAmB,IAEnB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,KAAiB,IAEjB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;YAPb,CAUE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,EAAe,GAAO,eAAe,EAAE;KAC9C,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;KACvC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,EAAe,GAAO,iBAAiB,EAAE;KAChD,MAAM,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;KACrC,aAAY;KACZ,CAAA;IACE;MAEN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAS,WAAW,KAAK,CAAC,KACzB,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAEvD,CAAA,EAEP,EAAS,KAAK,MACb,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA,EACnD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ,eAAe;OACtB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAQ;QAAS;QAAU;QAC3B,EAAQ,aAAa;QAAa;QAAU;QAC5C,EAAW,EAAQ,KAAK;QACvB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAQ,OAAO;OAC7B,CAAA,EACJ,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAQ,OAAO;iBAEpG,EAAQ;OACJ,CAAA,CACH;QACF;OA1BC,EAAQ,GA0BT,CACN,CACE;;GACF,CAAA,CACK"}
1
+ {"version":3,"file":"ExpenseTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/expense-tracking/ExpenseTrackingPage.tsx"],"sourcesContent":["import { Receipt, Plus, DollarSign } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n getStatusColor,\n formatCurrency,\n formatDate,\n} from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListExpensesQuery,\n useGetExpenseStatsQuery,\n type ExpenseFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ExpenseTrackingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: expensesData,\n loading: expensesLoading,\n error: expensesError,\n } = useListExpensesQuery({\n variables: { workspaceId },\n });\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetExpenseStatsQuery({\n variables: { workspaceId },\n });\n\n const expenses = expensesData?.expenses ?? [];\n const stats = statsData?.expenseStats;\n\n if (expensesLoading && statsLoading) {\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (expensesError && statsError) {\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load expense data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Expense Tracking\"\n description=\"Track and manage work expenses\"\n purpose=\"Capture project-related costs alongside the hours you log — receipts, categories and approvals in one place — so the bill you send a client reflects the full cost of the work, not just time.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Expense\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total This Month\"\n value={formatCurrency(stats?.totalAmount ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Pending\"\n value={String(stats?.pendingCount ?? 0)}\n description=\"expenses awaiting approval\"\n />\n <StatCard\n title=\"Approved\"\n value={String(stats?.approvedCount ?? 0)}\n description=\"expenses approved\"\n />\n <StatCard\n title=\"Average\"\n value={formatCurrency(stats?.averageAmount ?? 0)}\n icon={<Receipt className=\"h-5 w-5\" />}\n description=\"per expense\"\n />\n </div>\n\n {expenses.length === 0 && !expensesLoading ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No expenses recorded yet\"\n description=\"Add your first expense to track project costs and route them for approval.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Expense\n </button>\n }\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {expenses.map((expense: ExpenseFieldsFragment) => (\n <div\n key={expense.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <Receipt className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {expense.description ?? \"Untitled expense\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {expense.category} &middot;{\" \"}\n {expense.projectId ?? \"No project\"} &middot;{\" \"}\n {formatDate(expense.date)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatCurrency(expense.amount)}\n </p>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(expense.status)}`}\n >\n {expense.status}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,IAAsB;CACpC,IAAM,EAAE,iBAAc,OAAO,GAAe,EAEtC,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAAqB,EACvB,WAAW,EAAE,gBAAa,EAC3B,CAAC,EACI,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAAwB,EAC1B,WAAW,EAAE,gBAAa,EAC3B,CAAC,EAEI,IAAW,GAAc,YAAY,EAAE,EACvC,IAAQ,GAAW;AA0BzB,QAxBI,KAAmB,IAEnB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,KAAiB,IAEjB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;YARb,CAWE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,UAAA;KACA,OAAM;KACN,OAAO,EAAe,GAAO,eAAe,EAAE;KAC9C,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;KACxC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,gBAAgB,EAAE;KACvC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,OAAO,GAAO,iBAAiB,EAAE;KACxC,aAAY;KACZ,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,EAAe,GAAO,iBAAiB,EAAE;KAChD,MAAM,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;KACrC,aAAY;KACZ,CAAA;IACE;MAEL,EAAS,WAAW,KAAK,CAAC,IACzB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA,EACnD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ,eAAe;OACtB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAQ;QAAS;QAAU;QAC3B,EAAQ,aAAa;QAAa;QAAU;QAC5C,EAAW,EAAQ,KAAK;QACvB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAQ,OAAO;OAC7B,CAAA,EACJ,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAQ,OAAO;iBAEpG,EAAQ;OACJ,CAAA,CACH;QACF;OA1BC,EAAQ,GA0BT,CACN;IACE,CAAA;GACF,CAAA,CAEG"}
@@ -3,119 +3,123 @@ import { StatCard as t } from "../../components/StatCard.js";
3
3
  import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
4
4
  import { useGetFocusScoreSummaryQuery as r, useGetTodayFocusScoreQuery as i, useListFocusScoresQuery as a } from "../../../generated/wspace-operations.js";
5
5
  import { PageLayout as o } from "../../components/PageLayout.js";
6
- import { Brain as s, Shield as c, Target as l, Zap as u } from "lucide-react";
7
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
6
+ import { EmptyState as s } from "../../components/EmptyState.js";
7
+ import { Brain as c, Shield as l, Target as u, Zap as d } from "lucide-react";
8
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
8
9
  //#region src/timecampus/pages/focus-score/FocusScorePage.tsx
9
- var p = {
10
+ var m = {
10
11
  deepWork: "bg-accent-blue",
11
12
  consistency: "bg-status-success-text",
12
13
  taskCompletion: "bg-accent-purple",
13
14
  distractionResistance: "bg-status-warning-text",
14
15
  energyManagement: "bg-accent-teal"
15
16
  };
16
- function m() {
17
- let { workspaceId: m = "" } = e(), { data: h, loading: g, error: _ } = a({
17
+ function h() {
18
+ let { workspaceId: h = "" } = e(), { data: g, loading: _, error: v } = a({
18
19
  variables: { limit: 30 },
19
- skip: !m
20
- }), { data: v, loading: y, error: b } = r({ skip: !m }), { data: x, loading: S, error: C } = i({ skip: !m }), w = g || y || S, T = _ || b || C, E = x?.todayFocusScore, D = v?.focusScoreSummary, O = h?.focusScores ?? [], k = E ? [
20
+ skip: !h
21
+ }), { data: y, loading: b, error: x } = r({ skip: !h }), { data: S, loading: C, error: w } = i({ skip: !h }), T = _ || b || C, E = v || x || w, D = S?.todayFocusScore, O = y?.focusScoreSummary, k = g?.focusScores ?? [], A = D ? [
21
22
  {
22
23
  label: "Deep Work",
23
- score: E.deepWorkScore ?? 0,
24
- color: p.deepWork
24
+ score: D.deepWorkScore ?? 0,
25
+ color: m.deepWork
25
26
  },
26
27
  {
27
28
  label: "Consistency",
28
- score: E.consistencyScore ?? 0,
29
- color: p.consistency
29
+ score: D.consistencyScore ?? 0,
30
+ color: m.consistency
30
31
  },
31
32
  {
32
33
  label: "Task Completion",
33
- score: E.taskCompletionScore ?? 0,
34
- color: p.taskCompletion
34
+ score: D.taskCompletionScore ?? 0,
35
+ color: m.taskCompletion
35
36
  },
36
37
  {
37
38
  label: "Distraction Resistance",
38
- score: E.distractionResistanceScore ?? 0,
39
- color: p.distractionResistance
39
+ score: D.distractionResistanceScore ?? 0,
40
+ color: m.distractionResistance
40
41
  },
41
42
  {
42
43
  label: "Energy Management",
43
- score: E.energyManagementScore ?? 0,
44
- color: p.energyManagement
44
+ score: D.energyManagementScore ?? 0,
45
+ color: m.energyManagement
45
46
  }
46
- ] : [], A = E?.overallScore == null ? "--" : `${E.overallScore}/100`, j = O.filter((e) => e.overallScore >= 70).length, M = O.reduce((e, t) => e + (t.overallScore ?? 0), 0), N = Math.floor(M / 60), P = M % 60, F = N > 0 ? `${N}h ${P}m` : `${P}m`, I = D?.trend === "up" ? 7 : D?.trend === "down" ? -5 : 0, L = D?.highestScore == null ? "--" : `${D.highestScore}`;
47
- return w ? /* @__PURE__ */ d(o, {
47
+ ] : [], j = D?.overallScore == null ? "--" : `${D.overallScore}/100`, M = k.filter((e) => e.overallScore >= 70).length, N = k.reduce((e, t) => e + (t.overallScore ?? 0), 0), P = Math.floor(N / 60), F = N % 60, I = P > 0 ? `${P}h ${F}m` : `${F}m`, L = O?.trend === "up" ? 7 : O?.trend === "down" ? -5 : 0, R = O?.highestScore == null ? "--" : `${O.highestScore}`;
48
+ return T ? /* @__PURE__ */ f(o, {
48
49
  title: "Focus Score",
49
50
  description: "Measure and improve your focus",
50
- children: /* @__PURE__ */ d(n, {
51
+ children: /* @__PURE__ */ f(n, {
51
52
  variant: "card",
52
53
  count: 4
53
54
  })
54
- }) : T ? /* @__PURE__ */ d(o, {
55
+ }) : E ? /* @__PURE__ */ f(o, {
55
56
  title: "Focus Score",
56
57
  description: "Measure and improve your focus",
57
- children: /* @__PURE__ */ d("div", {
58
+ children: /* @__PURE__ */ f("div", {
58
59
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
59
60
  children: "Failed to load focus score data. Please try again later."
60
61
  })
61
- }) : /* @__PURE__ */ f(o, {
62
+ }) : /* @__PURE__ */ p(o, {
62
63
  title: "Focus Score",
63
64
  description: "Measure and improve your focus",
64
- children: [/* @__PURE__ */ f("div", {
65
+ purpose: "Get a single, honest number for how focused you were today — built from deep work, consistency, follow-through and distraction resistance — so you can see what's improving and what to work on next.",
66
+ children: [/* @__PURE__ */ p("div", {
65
67
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
66
68
  children: [
67
- /* @__PURE__ */ d(t, {
69
+ /* @__PURE__ */ f(t, {
70
+ featured: !0,
68
71
  title: "Overall Score",
69
- value: A,
70
- icon: /* @__PURE__ */ d(s, { className: "h-5 w-5" }),
71
- trend: I === 0 ? void 0 : {
72
- value: I,
72
+ value: j,
73
+ icon: /* @__PURE__ */ f(c, { className: "h-5 w-5" }),
74
+ trend: L === 0 ? void 0 : {
75
+ value: L,
73
76
  label: "vs last week"
74
77
  },
75
78
  iconBgColor: "bg-status-info-bg text-status-info-text"
76
79
  }),
77
- /* @__PURE__ */ d(t, {
80
+ /* @__PURE__ */ f(t, {
78
81
  title: "Focus Sessions",
79
- value: String(j),
80
- icon: /* @__PURE__ */ d(l, { className: "h-5 w-5" }),
82
+ value: String(M),
83
+ icon: /* @__PURE__ */ f(u, { className: "h-5 w-5" }),
81
84
  description: "Completed today"
82
85
  }),
83
- /* @__PURE__ */ d(t, {
86
+ /* @__PURE__ */ f(t, {
84
87
  title: "Total Focus Time",
85
- value: F,
86
- icon: /* @__PURE__ */ d(u, { className: "h-5 w-5" })
88
+ value: I,
89
+ icon: /* @__PURE__ */ f(d, { className: "h-5 w-5" })
87
90
  }),
88
- /* @__PURE__ */ d(t, {
91
+ /* @__PURE__ */ f(t, {
89
92
  title: "Highest Score",
90
- value: L,
91
- icon: /* @__PURE__ */ d(c, { className: "h-5 w-5" }),
93
+ value: R,
94
+ icon: /* @__PURE__ */ f(l, { className: "h-5 w-5" }),
92
95
  description: "Personal best"
93
96
  })
94
97
  ]
95
- }), /* @__PURE__ */ f("div", {
98
+ }), /* @__PURE__ */ p("div", {
96
99
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
97
- children: [/* @__PURE__ */ d("h3", {
100
+ children: [/* @__PURE__ */ f("h3", {
98
101
  className: "mb-4 text-sm font-semibold text-text-primary",
99
102
  children: "Focus Components"
100
- }), k.length === 0 ? /* @__PURE__ */ d("div", {
101
- className: "flex h-32 items-center justify-center text-sm text-text-secondary",
102
- children: "No focus score recorded today."
103
- }) : /* @__PURE__ */ d("div", {
103
+ }), A.length === 0 ? /* @__PURE__ */ f(s, {
104
+ illustration: "empty-data",
105
+ title: "No focus score recorded today",
106
+ description: "Track some focused work today and your score breakdown will appear here."
107
+ }) : /* @__PURE__ */ f("div", {
104
108
  className: "space-y-4",
105
- children: k.map((e) => /* @__PURE__ */ f("div", {
109
+ children: A.map((e) => /* @__PURE__ */ p("div", {
106
110
  className: "space-y-1",
107
- children: [/* @__PURE__ */ f("div", {
111
+ children: [/* @__PURE__ */ p("div", {
108
112
  className: "flex items-center justify-between text-sm",
109
- children: [/* @__PURE__ */ d("span", {
113
+ children: [/* @__PURE__ */ f("span", {
110
114
  className: "font-medium text-text-primary",
111
115
  children: e.label
112
- }), /* @__PURE__ */ f("span", {
116
+ }), /* @__PURE__ */ p("span", {
113
117
  className: "text-text-secondary",
114
118
  children: [e.score, "%"]
115
119
  })]
116
- }), /* @__PURE__ */ d("div", {
120
+ }), /* @__PURE__ */ f("div", {
117
121
  className: "h-2.5 w-full rounded-full bg-bg-sunken",
118
- children: /* @__PURE__ */ d("div", {
122
+ children: /* @__PURE__ */ f("div", {
119
123
  className: `h-2.5 rounded-full ${e.color}`,
120
124
  style: { width: `${e.score}%` }
121
125
  })
@@ -126,6 +130,6 @@ function m() {
126
130
  });
127
131
  }
128
132
  //#endregion
129
- export { m as FocusScorePage };
133
+ export { h as FocusScorePage };
130
134
 
131
135
  //# sourceMappingURL=FocusScorePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FocusScorePage.js","names":[],"sources":["../../../../src/timecampus/pages/focus-score/FocusScorePage.tsx"],"sourcesContent":["import { Brain, Target, Zap, Shield } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListFocusScoresQuery,\n useGetFocusScoreSummaryQuery,\n useGetTodayFocusScoreQuery,\n type FocusScoreFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst COMPONENT_COLORS: Record<string, string> = {\n deepWork: \"bg-accent-blue\",\n consistency: \"bg-status-success-text\",\n taskCompletion: \"bg-accent-purple\",\n distractionResistance: \"bg-status-warning-text\",\n energyManagement: \"bg-accent-teal\",\n};\n\nexport function FocusScorePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: scoresData,\n loading: scoresLoading,\n error: scoresError,\n } = useListFocusScoresQuery({\n variables: { limit: 30 },\n skip: !workspaceId,\n });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n } = useGetFocusScoreSummaryQuery({\n skip: !workspaceId,\n });\n\n const {\n data: todayData,\n loading: todayLoading,\n error: todayError,\n } = useGetTodayFocusScoreQuery({\n skip: !workspaceId,\n });\n\n const loading = scoresLoading || summaryLoading || todayLoading;\n const error = scoresError || summaryError || todayError;\n const today = todayData?.todayFocusScore;\n const summary = summaryData?.focusScoreSummary;\n const scores = scoresData?.focusScores ?? [];\n\n // Build focus components from today's score\n const focusComponents = today\n ? [\n {\n label: \"Deep Work\",\n score: today.deepWorkScore ?? 0,\n color: COMPONENT_COLORS.deepWork,\n },\n {\n label: \"Consistency\",\n score: today.consistencyScore ?? 0,\n color: COMPONENT_COLORS.consistency,\n },\n {\n label: \"Task Completion\",\n score: today.taskCompletionScore ?? 0,\n color: COMPONENT_COLORS.taskCompletion,\n },\n {\n label: \"Distraction Resistance\",\n score: today.distractionResistanceScore ?? 0,\n color: COMPONENT_COLORS.distractionResistance,\n },\n {\n label: \"Energy Management\",\n score: today.energyManagementScore ?? 0,\n color: COMPONENT_COLORS.energyManagement,\n },\n ]\n : [];\n\n // Calculate stats\n const overallScore =\n today?.overallScore != null ? `${today.overallScore}/100` : \"--\";\n const completedSessions = scores.filter(\n (s: FocusScoreFieldsFragment) => s.overallScore >= 70,\n ).length;\n const totalFocusMinutes = scores.reduce(\n (sum: number, s: FocusScoreFieldsFragment) => sum + (s.overallScore ?? 0),\n 0,\n );\n const totalFocusHours = Math.floor(totalFocusMinutes / 60);\n const totalFocusMins = totalFocusMinutes % 60;\n const focusTimeDisplay =\n totalFocusHours > 0\n ? `${totalFocusHours}h ${totalFocusMins}m`\n : `${totalFocusMins}m`;\n const trendValue =\n summary?.trend === \"up\" ? 7 : summary?.trend === \"down\" ? -5 : 0;\n\n // Longest streak from highest score\n const highestScore =\n summary?.highestScore != null ? `${summary.highestScore}` : \"--\";\n\n if (loading) {\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n >\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 Failed to load focus score data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Overall Score\"\n value={overallScore}\n icon={<Brain className=\"h-5 w-5\" />}\n trend={\n trendValue !== 0\n ? { value: trendValue, label: \"vs last week\" }\n : undefined\n }\n iconBgColor=\"bg-status-info-bg text-status-info-text\"\n />\n <StatCard\n title=\"Focus Sessions\"\n value={String(completedSessions)}\n icon={<Target className=\"h-5 w-5\" />}\n description=\"Completed today\"\n />\n <StatCard\n title=\"Total Focus Time\"\n value={focusTimeDisplay}\n icon={<Zap className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Highest Score\"\n value={highestScore}\n icon={<Shield className=\"h-5 w-5\" />}\n description=\"Personal best\"\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Focus Components\n </h3>\n {focusComponents.length === 0 ? (\n <div className=\"flex h-32 items-center justify-center text-sm text-text-secondary\">\n No focus score recorded today.\n </div>\n ) : (\n <div className=\"space-y-4\">\n {focusComponents.map((component) => (\n <div key={component.label} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium text-text-primary\">\n {component.label}\n </span>\n <span className=\"text-text-secondary\">\n {component.score}%\n </span>\n </div>\n <div className=\"h-2.5 w-full rounded-full bg-bg-sunken\">\n <div\n className={`h-2.5 rounded-full ${component.color}`}\n style={{ width: `${component.score}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FocusScorePage.js","names":[],"sources":["../../../../src/timecampus/pages/focus-score/FocusScorePage.tsx"],"sourcesContent":["import { Brain, Target, Zap, Shield } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListFocusScoresQuery,\n useGetFocusScoreSummaryQuery,\n useGetTodayFocusScoreQuery,\n type FocusScoreFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst COMPONENT_COLORS: Record<string, string> = {\n deepWork: \"bg-accent-blue\",\n consistency: \"bg-status-success-text\",\n taskCompletion: \"bg-accent-purple\",\n distractionResistance: \"bg-status-warning-text\",\n energyManagement: \"bg-accent-teal\",\n};\n\nexport function FocusScorePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: scoresData,\n loading: scoresLoading,\n error: scoresError,\n } = useListFocusScoresQuery({\n variables: { limit: 30 },\n skip: !workspaceId,\n });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n } = useGetFocusScoreSummaryQuery({\n skip: !workspaceId,\n });\n\n const {\n data: todayData,\n loading: todayLoading,\n error: todayError,\n } = useGetTodayFocusScoreQuery({\n skip: !workspaceId,\n });\n\n const loading = scoresLoading || summaryLoading || todayLoading;\n const error = scoresError || summaryError || todayError;\n const today = todayData?.todayFocusScore;\n const summary = summaryData?.focusScoreSummary;\n const scores = scoresData?.focusScores ?? [];\n\n // Build focus components from today's score\n const focusComponents = today\n ? [\n {\n label: \"Deep Work\",\n score: today.deepWorkScore ?? 0,\n color: COMPONENT_COLORS.deepWork,\n },\n {\n label: \"Consistency\",\n score: today.consistencyScore ?? 0,\n color: COMPONENT_COLORS.consistency,\n },\n {\n label: \"Task Completion\",\n score: today.taskCompletionScore ?? 0,\n color: COMPONENT_COLORS.taskCompletion,\n },\n {\n label: \"Distraction Resistance\",\n score: today.distractionResistanceScore ?? 0,\n color: COMPONENT_COLORS.distractionResistance,\n },\n {\n label: \"Energy Management\",\n score: today.energyManagementScore ?? 0,\n color: COMPONENT_COLORS.energyManagement,\n },\n ]\n : [];\n\n // Calculate stats\n const overallScore =\n today?.overallScore != null ? `${today.overallScore}/100` : \"--\";\n const completedSessions = scores.filter(\n (s: FocusScoreFieldsFragment) => s.overallScore >= 70,\n ).length;\n const totalFocusMinutes = scores.reduce(\n (sum: number, s: FocusScoreFieldsFragment) => sum + (s.overallScore ?? 0),\n 0,\n );\n const totalFocusHours = Math.floor(totalFocusMinutes / 60);\n const totalFocusMins = totalFocusMinutes % 60;\n const focusTimeDisplay =\n totalFocusHours > 0\n ? `${totalFocusHours}h ${totalFocusMins}m`\n : `${totalFocusMins}m`;\n const trendValue =\n summary?.trend === \"up\" ? 7 : summary?.trend === \"down\" ? -5 : 0;\n\n // Longest streak from highest score\n const highestScore =\n summary?.highestScore != null ? `${summary.highestScore}` : \"--\";\n\n if (loading) {\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n >\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 Failed to load focus score data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Focus Score\"\n description=\"Measure and improve your focus\"\n purpose=\"Get a single, honest number for how focused you were today — built from deep work, consistency, follow-through and distraction resistance — so you can see what's improving and what to work on next.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Overall Score\"\n value={overallScore}\n icon={<Brain className=\"h-5 w-5\" />}\n trend={\n trendValue !== 0\n ? { value: trendValue, label: \"vs last week\" }\n : undefined\n }\n iconBgColor=\"bg-status-info-bg text-status-info-text\"\n />\n <StatCard\n title=\"Focus Sessions\"\n value={String(completedSessions)}\n icon={<Target className=\"h-5 w-5\" />}\n description=\"Completed today\"\n />\n <StatCard\n title=\"Total Focus Time\"\n value={focusTimeDisplay}\n icon={<Zap className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Highest Score\"\n value={highestScore}\n icon={<Shield className=\"h-5 w-5\" />}\n description=\"Personal best\"\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Focus Components\n </h3>\n {focusComponents.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No focus score recorded today\"\n description=\"Track some focused work today and your score breakdown will appear here.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {focusComponents.map((component) => (\n <div key={component.label} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium text-text-primary\">\n {component.label}\n </span>\n <span className=\"text-text-secondary\">\n {component.score}%\n </span>\n </div>\n <div className=\"h-2.5 w-full rounded-full bg-bg-sunken\">\n <div\n className={`h-2.5 rounded-full ${component.color}`}\n style={{ width: `${component.score}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}