@burdenoff/microfe-timecampus 2026.625.1 → 2026.626.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -4,101 +4,102 @@ import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
4
4
  import { useTimeCampusStore as i } from "../../store/timecampusStore.js";
5
5
  import { useGetTimeEntryStatsQuery as a, useListTimeEntriesQuery as o } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as s } from "../../components/PageLayout.js";
7
- import { Clock as c, Pause as l, Play as u, Square as d } from "lucide-react";
8
- import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
9
- import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
+ import { EmptyState as c } from "../../components/EmptyState.js";
8
+ import { Clock as l, Pause as u, Play as d, Square as f } from "lucide-react";
9
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
11
  //#region src/timecampus/pages/time/TimeTrackingPage.tsx
11
- function g() {
12
- let { t: g } = h(), _ = (e, t) => {
13
- let n = g(e);
12
+ function _() {
13
+ let { t: _ } = g(), v = (e, t) => {
14
+ let n = _(e);
14
15
  return n === e ? t : n;
15
- }, { activeTimer: v, startTimer: y, stopTimer: b } = i(), { data: x, loading: S } = a(), { data: C, loading: w } = o({ variables: { limit: 10 } }), T = x?.timeEntryStats, E = C?.timeEntries?.items ?? [];
16
- return S ? /* @__PURE__ */ p(s, {
17
- title: _("timecampus.timeTracking.title", "Time Tracking"),
18
- description: _("timecampus.timeTracking.description", "Track and manage your work hours"),
19
- children: /* @__PURE__ */ p(r, {
16
+ }, { activeTimer: y, startTimer: b, stopTimer: x } = i(), { data: S, loading: C } = a(), { data: w, loading: T } = o({ variables: { limit: 10 } }), E = S?.timeEntryStats, D = w?.timeEntries?.items ?? [];
17
+ return C ? /* @__PURE__ */ m(s, {
18
+ title: v("timecampus.timeTracking.title", "Time Tracking"),
19
+ description: v("timecampus.timeTracking.description", "Track and manage your work hours"),
20
+ children: /* @__PURE__ */ m(r, {
20
21
  variant: "card",
21
22
  count: 4
22
23
  })
23
- }) : /* @__PURE__ */ m(s, {
24
- title: _("timecampus.timeTracking.title", "Time Tracking"),
25
- description: _("timecampus.timeTracking.description", "Track and manage your work hours"),
26
- actions: /* @__PURE__ */ p("button", {
27
- onClick: () => v ? b() : y(),
28
- className: `inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${v ? "bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"}`,
29
- children: v ? /* @__PURE__ */ m(f, { children: [
30
- /* @__PURE__ */ p(d, { className: "h-4 w-4" }),
31
- _("timecampus.timeTracking.actions.stop", "Stop"),
24
+ }) : /* @__PURE__ */ h(s, {
25
+ title: v("timecampus.timeTracking.title", "Time Tracking"),
26
+ description: v("timecampus.timeTracking.description", "Track and manage your work hours"),
27
+ purpose: v("timecampus.timeTracking.purpose", "Start a timer the moment you begin work and your hours are captured automatically — review today's totals, see what's billable, and stop guessing where your day went."),
28
+ actions: /* @__PURE__ */ m("button", {
29
+ onClick: () => y ? x() : b(),
30
+ className: `inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${y ? "bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"}`,
31
+ children: y ? /* @__PURE__ */ h(p, { children: [
32
+ /* @__PURE__ */ m(f, { className: "h-4 w-4" }),
33
+ v("timecampus.timeTracking.actions.stop", "Stop"),
32
34
  " (",
33
- t(v.seconds),
35
+ t(y.seconds),
34
36
  ")"
35
- ] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(u, { className: "h-4 w-4" }), _("timecampus.timeTracking.actions.startTimer", "Start Timer")] })
37
+ ] }) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m(d, { className: "h-4 w-4" }), v("timecampus.timeTracking.actions.startTimer", "Start Timer")] })
36
38
  }),
37
- children: [/* @__PURE__ */ m("div", {
39
+ children: [/* @__PURE__ */ h("div", {
38
40
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
39
41
  children: [
40
- /* @__PURE__ */ p(n, {
41
- title: _("timecampus.timeTracking.stats.today", "Today"),
42
- value: e(T?.totalHours ?? 0),
43
- icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
42
+ /* @__PURE__ */ m(n, {
43
+ featured: !0,
44
+ title: v("timecampus.timeTracking.stats.today", "Today"),
45
+ value: e(E?.totalHours ?? 0),
46
+ icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
44
47
  trend: {
45
48
  value: 15,
46
- label: _("timecampus.timeTracking.stats.vsYesterday", "vs yesterday")
49
+ label: v("timecampus.timeTracking.stats.vsYesterday", "vs yesterday")
47
50
  }
48
51
  }),
49
- /* @__PURE__ */ p(n, {
50
- title: _("timecampus.timeTracking.stats.billable", "Billable"),
51
- value: e(T?.billableHours ?? 0),
52
- icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
53
- description: `${T?.totalEntries ?? 0} ${_("timecampus.timeTracking.stats.entries", "entries")}`
52
+ /* @__PURE__ */ m(n, {
53
+ title: v("timecampus.timeTracking.stats.billable", "Billable"),
54
+ value: e(E?.billableHours ?? 0),
55
+ icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
56
+ description: `${E?.totalEntries ?? 0} ${v("timecampus.timeTracking.stats.entries", "entries")}`
54
57
  }),
55
- /* @__PURE__ */ p(n, {
56
- title: _("timecampus.timeTracking.stats.activeTimers", "Active Timers"),
57
- value: T?.activeTimers ?? 0,
58
- icon: /* @__PURE__ */ p(c, { className: "h-5 w-5" }),
59
- description: _("timecampus.timeTracking.stats.currentlyRunning", "Currently running")
58
+ /* @__PURE__ */ m(n, {
59
+ title: v("timecampus.timeTracking.stats.activeTimers", "Active Timers"),
60
+ value: E?.activeTimers ?? 0,
61
+ icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
62
+ description: v("timecampus.timeTracking.stats.currentlyRunning", "Currently running")
60
63
  }),
61
- /* @__PURE__ */ p(n, {
62
- title: _("timecampus.timeTracking.stats.activeTimer", "Active Timer"),
63
- value: v ? t(v.seconds) : _("timecampus.timeTracking.stats.stopped", "Stopped"),
64
- icon: p(v ? l : u, { className: "h-5 w-5" })
64
+ /* @__PURE__ */ m(n, {
65
+ title: v("timecampus.timeTracking.stats.activeTimer", "Active Timer"),
66
+ value: y ? t(y.seconds) : v("timecampus.timeTracking.stats.stopped", "Stopped"),
67
+ icon: m(y ? u : d, { className: "h-5 w-5" })
65
68
  })
66
69
  ]
67
- }), /* @__PURE__ */ m("div", {
70
+ }), /* @__PURE__ */ h("div", {
68
71
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
69
- children: [/* @__PURE__ */ p("h3", {
72
+ children: [/* @__PURE__ */ m("h3", {
70
73
  className: "mb-4 text-sm font-semibold text-text-primary",
71
- children: _("timecampus.timeTracking.entries.title", "Today's Entries")
72
- }), w ? /* @__PURE__ */ p(r, {
74
+ children: v("timecampus.timeTracking.entries.title", "Today's Entries")
75
+ }), T ? /* @__PURE__ */ m(r, {
73
76
  variant: "list",
74
77
  count: 4
75
- }) : E.length === 0 ? /* @__PURE__ */ p("div", {
76
- className: "flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle",
77
- children: /* @__PURE__ */ p("p", {
78
- className: "text-sm text-text-secondary",
79
- children: _("timecampus.timeTracking.entries.empty", "No time entries yet. Start a timer to begin tracking.")
80
- })
81
- }) : /* @__PURE__ */ p("div", {
78
+ }) : D.length === 0 ? /* @__PURE__ */ m(c, {
79
+ illustration: "empty-data",
80
+ title: v("timecampus.timeTracking.entries.empty.title", "No time entries yet"),
81
+ description: v("timecampus.timeTracking.entries.empty", "No time entries yet. Start a timer to begin tracking.")
82
+ }) : /* @__PURE__ */ m("div", {
82
83
  className: "flex flex-col gap-2",
83
- children: E.map((t) => /* @__PURE__ */ m("div", {
84
+ children: D.map((t) => /* @__PURE__ */ h("div", {
84
85
  className: "flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50",
85
- children: [/* @__PURE__ */ m("div", {
86
+ children: [/* @__PURE__ */ h("div", {
86
87
  className: "min-w-0 flex-1",
87
- children: [/* @__PURE__ */ p("p", {
88
+ children: [/* @__PURE__ */ m("p", {
88
89
  className: "text-sm font-medium text-text-primary",
89
- children: t.project?.name ?? _("timecampus.timeTracking.entries.noProject", "No Project")
90
- }), /* @__PURE__ */ p("p", {
90
+ children: t.project?.name ?? v("timecampus.timeTracking.entries.noProject", "No Project")
91
+ }), /* @__PURE__ */ m("p", {
91
92
  className: "text-xs text-text-secondary",
92
- children: t.description ?? _("timecampus.timeTracking.entries.noDescription", "No description")
93
+ children: t.description ?? v("timecampus.timeTracking.entries.noDescription", "No description")
93
94
  })]
94
- }), /* @__PURE__ */ m("div", {
95
+ }), /* @__PURE__ */ h("div", {
95
96
  className: "ml-4 text-right",
96
- children: [/* @__PURE__ */ p("p", {
97
+ children: [/* @__PURE__ */ m("p", {
97
98
  className: "text-sm font-mono font-medium text-text-primary",
98
99
  children: e((t.duration ?? 0) / 3600)
99
- }), /* @__PURE__ */ p("span", {
100
+ }), /* @__PURE__ */ m("span", {
100
101
  className: "inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
101
- children: t.isRunning ? _("timecampus.timeTracking.entries.status.running", "running") : _("timecampus.timeTracking.entries.status.completed", "completed")
102
+ children: t.isRunning ? v("timecampus.timeTracking.entries.status.running", "running") : v("timecampus.timeTracking.entries.status.completed", "completed")
102
103
  })]
103
104
  })]
104
105
  }, t.id))
@@ -107,6 +108,6 @@ function g() {
107
108
  });
108
109
  }
109
110
  //#endregion
110
- export { g as TimeTrackingPage };
111
+ export { _ as TimeTrackingPage };
111
112
 
112
113
  //# sourceMappingURL=TimeTrackingPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90\" : \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"}`}\n >\n {activeTimer ? (\n <>\n <Square className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.stop\", \"Stop\")} (\n {formatTimerDuration(activeTimer.seconds)})\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.startTimer\", \"Start Timer\")}\n </>\n )}\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={t(\"timecampus.timeTracking.stats.today\", \"Today\")}\n value={formatHours(stats?.totalHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{\n value: 15,\n label: t(\n \"timecampus.timeTracking.stats.vsYesterday\",\n \"vs yesterday\",\n ),\n }}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.billable\", \"Billable\")}\n value={formatHours(stats?.billableHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n description={`${stats?.totalEntries ?? 0} ${t(\"timecampus.timeTracking.stats.entries\", \"entries\")}`}\n />\n <StatCard\n title={t(\n \"timecampus.timeTracking.stats.activeTimers\",\n \"Active Timers\",\n )}\n value={stats?.activeTimers ?? 0}\n icon={<Clock className=\"h-5 w-5\" />}\n description={t(\n \"timecampus.timeTracking.stats.currentlyRunning\",\n \"Currently running\",\n )}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.activeTimer\", \"Active Timer\")}\n value={\n activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : t(\"timecampus.timeTracking.stats.stopped\", \"Stopped\")\n }\n icon={\n activeTimer ? (\n <Pause className=\"h-5 w-5\" />\n ) : (\n <Play className=\"h-5 w-5\" />\n )\n }\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 {t(\"timecampus.timeTracking.entries.title\", \"Today's Entries\")}\n </h3>\n {entriesLoading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : todayEntries.length === 0 ? (\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <p className=\"text-sm text-text-secondary\">\n {t(\n \"timecampus.timeTracking.entries.empty\",\n \"No time entries yet. Start a timer to begin tracking.\",\n )}\n </p>\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {todayEntries.map((entry: TimeEntryFieldsFragment) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ??\n t(\n \"timecampus.timeTracking.entries.noProject\",\n \"No Project\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {entry.description ??\n t(\n \"timecampus.timeTracking.entries.noDescription\",\n \"No description\",\n )}\n </p>\n </div>\n <div className=\"ml-4 text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {entry.isRunning\n ? t(\n \"timecampus.timeTracking.entries.status.running\",\n \"running\",\n )\n : t(\n \"timecampus.timeTracking.entries.status.completed\",\n \"completed\",\n )}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAgB,IAAmB;CACjC,IAAM,EAAE,GAAG,MAAc,GAAS,EAC5B,KAAK,GAAa,MAA6B;EACnD,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAE9B,EAAE,gBAAa,eAAY,iBAAc,GAAoB,EAE7D,EAAE,MAAM,GAAW,SAAS,MAChC,GAA2B,EACvB,EAAE,MAAM,GAAa,SAAS,MAClC,EAAwB,EACtB,WAAW,EAAE,OAAO,IAAI,EACzB,CAAC,EAEE,IAAQ,GAAW,gBACnB,IAAe,GAAa,aAAa,SAAS,EAAE;AAgB1D,QAdI,IAEA,kBAAC,GAAD;EACE,OAAO,EAAE,iCAAiC,gBAAgB;EAC1D,aAAa,EACX,uCACA,mCACD;YAED,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAKf,kBAAC,GAAD;EACE,OAAO,EAAE,iCAAiC,gBAAgB;EAC1D,aAAa,EACX,uCACA,mCACD;EACD,SACE,kBAAC,UAAD;GACE,eAAgB,IAAc,GAAW,GAAG,GAAY;GACxD,WAAW,2EAA2E,IAAc,8EAA8E;aAEjL,IACC,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;IAC7B,EAAE,wCAAwC,OAAO;IAAC;IAClD,EAAoB,EAAY,QAAQ;IAAC;IACzC,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAE,8CAA8C,cAAc,CAC9D,EAAA,CAAA;GAEE,CAAA;YAvBb,CA0BE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAO,EAAE,uCAAuC,QAAQ;KACxD,OAAO,EAAY,GAAO,cAAc,EAAE;KAC1C,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,OAAO;MACL,OAAO;MACP,OAAO,EACL,6CACA,eACD;MACF;KACD,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAE,0CAA0C,WAAW;KAC9D,OAAO,EAAY,GAAO,iBAAiB,EAAE;KAC7C,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,aAAa,GAAG,GAAO,gBAAgB,EAAE,GAAG,EAAE,yCAAyC,UAAU;KACjG,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EACL,8CACA,gBACD;KACD,OAAO,GAAO,gBAAgB;KAC9B,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,aAAa,EACX,kDACA,oBACD;KACD,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAE,6CAA6C,eAAe;KACrE,OACE,IACI,EAAoB,EAAY,QAAQ,GACxC,EAAE,yCAAyC,UAAU;KAE3D,MAEI,EADF,IACG,IAEA,GAFD,EAAO,WAAU,WAAY,CAED;KAGhC,CAAA;IACE;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAE,yCAAyC,kBAAkB;IAC3D,CAAA,EACJ,IACC,kBAAC,GAAD;IAAiB,SAAQ;IAAO,OAAO;IAAK,CAAA,GAC1C,EAAa,WAAW,IAC1B,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,yCACA,wDACD;KACC,CAAA;IACA,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAa,KAAK,MACjB,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAM,SAAS,QACd,EACE,6CACA,aACD;OACD,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAM,eACL,EACE,iDACA,iBACD;OACD,CAAA,CACA;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,GAAa,EAAM,YAAY,KAAK,KAAK;OACxC,CAAA,EACJ,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAM,YACH,EACE,kDACA,UACD,GACD,EACE,oDACA,YACD;OACA,CAAA,CACH;QACF;OAnCC,EAAM,GAmCP,CACN;IACE,CAAA,CAEJ;KACK"}
1
+ {"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n purpose={t(\n \"timecampus.timeTracking.purpose\",\n \"Start a timer the moment you begin work and your hours are captured automatically — review today's totals, see what's billable, and stop guessing where your day went.\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90\" : \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"}`}\n >\n {activeTimer ? (\n <>\n <Square className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.stop\", \"Stop\")} (\n {formatTimerDuration(activeTimer.seconds)})\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.startTimer\", \"Start Timer\")}\n </>\n )}\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={t(\"timecampus.timeTracking.stats.today\", \"Today\")}\n value={formatHours(stats?.totalHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{\n value: 15,\n label: t(\n \"timecampus.timeTracking.stats.vsYesterday\",\n \"vs yesterday\",\n ),\n }}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.billable\", \"Billable\")}\n value={formatHours(stats?.billableHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n description={`${stats?.totalEntries ?? 0} ${t(\"timecampus.timeTracking.stats.entries\", \"entries\")}`}\n />\n <StatCard\n title={t(\n \"timecampus.timeTracking.stats.activeTimers\",\n \"Active Timers\",\n )}\n value={stats?.activeTimers ?? 0}\n icon={<Clock className=\"h-5 w-5\" />}\n description={t(\n \"timecampus.timeTracking.stats.currentlyRunning\",\n \"Currently running\",\n )}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.activeTimer\", \"Active Timer\")}\n value={\n activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : t(\"timecampus.timeTracking.stats.stopped\", \"Stopped\")\n }\n icon={\n activeTimer ? (\n <Pause className=\"h-5 w-5\" />\n ) : (\n <Play className=\"h-5 w-5\" />\n )\n }\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 {t(\"timecampus.timeTracking.entries.title\", \"Today's Entries\")}\n </h3>\n {entriesLoading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : todayEntries.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={t(\n \"timecampus.timeTracking.entries.empty.title\",\n \"No time entries yet\",\n )}\n description={t(\n \"timecampus.timeTracking.entries.empty\",\n \"No time entries yet. Start a timer to begin tracking.\",\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-2\">\n {todayEntries.map((entry: TimeEntryFieldsFragment) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ??\n t(\n \"timecampus.timeTracking.entries.noProject\",\n \"No Project\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {entry.description ??\n t(\n \"timecampus.timeTracking.entries.noDescription\",\n \"No description\",\n )}\n </p>\n </div>\n <div className=\"ml-4 text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {entry.isRunning\n ? t(\n \"timecampus.timeTracking.entries.status.running\",\n \"running\",\n )\n : t(\n \"timecampus.timeTracking.entries.status.completed\",\n \"completed\",\n )}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,80 +1,114 @@
1
- import { formatTimerDuration as e } from "../../utils/helpers.js";
2
- import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
- import { useTimeCampusStore as n } from "../../store/timecampusStore.js";
4
- import { useListRunningTimersQuery as r } from "../../../generated/wspace-operations.js";
5
- import { PageLayout as i } from "../../components/PageLayout.js";
6
- import { Play as a, RotateCcw as o, Square as s } from "lucide-react";
7
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
1
+ import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
2
+ import { formatTimerDuration as t } from "../../utils/helpers.js";
3
+ import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
4
+ import { useTimeCampusStore as r } from "../../store/timecampusStore.js";
5
+ import { useListRunningTimersQuery as i } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as a } from "../../components/PageLayout.js";
7
+ import { EmptyState as o } from "../../components/EmptyState.js";
8
+ import { Play as s, RotateCcw as c, Square as l } from "lucide-react";
9
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
10
+ import { GlassCard as f, NextSteps as p } from "@burdenoff/fe-libs/ui";
8
11
  //#region src/timecampus/pages/time/TimerPage.tsx
9
- function u() {
10
- let { activeTimer: u, startTimer: d, stopTimer: f } = n(), { data: p, loading: m } = r(), h = p?.runningTimers ?? [];
11
- return /* @__PURE__ */ c(i, {
12
+ function m() {
13
+ let { activeTimer: m, startTimer: h, stopTimer: g } = r(), _ = e(), { data: v, loading: y } = i(), b = v?.runningTimers ?? [];
14
+ return /* @__PURE__ */ u(a, {
12
15
  title: "Timer",
13
16
  description: "Focus timer for deep work sessions",
14
- children: /* @__PURE__ */ l("div", {
17
+ purpose: "A single, distraction-free stopwatch for the task in front of you — start it when you begin focused work and it logs the session as a time entry you can attribute to a project later.",
18
+ children: /* @__PURE__ */ d("div", {
15
19
  className: "mx-auto max-w-lg",
16
- children: [/* @__PURE__ */ l("div", {
17
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-8 text-center",
18
- children: [
19
- /* @__PURE__ */ c("p", {
20
- className: "text-6xl font-mono font-bold text-text-primary",
21
- children: u ? e(u.seconds) : "00:00:00"
22
- }),
23
- u?.description && /* @__PURE__ */ c("p", {
24
- className: "mt-2 text-sm text-text-secondary",
25
- children: u.description
26
- }),
27
- /* @__PURE__ */ l("div", {
28
- className: "mt-6 flex items-center justify-center gap-3",
29
- children: [u ? /* @__PURE__ */ l("button", {
30
- onClick: f,
31
- className: "inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90",
32
- children: [/* @__PURE__ */ c(s, { className: "h-4 w-4" }), "Stop"]
33
- }) : /* @__PURE__ */ l("button", {
34
- onClick: () => d(),
35
- className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
36
- children: [/* @__PURE__ */ c(a, { className: "h-4 w-4" }), "Start"]
37
- }), /* @__PURE__ */ l("button", {
38
- className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
39
- children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Reset"]
40
- })]
41
- })
42
- ]
43
- }), /* @__PURE__ */ l("div", {
44
- className: "mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
45
- children: [/* @__PURE__ */ c("h3", {
46
- className: "mb-4 text-sm font-semibold text-text-primary",
47
- children: "Running Timers"
48
- }), m ? /* @__PURE__ */ c(t, {
49
- variant: "list",
50
- count: 2
51
- }) : h.length === 0 ? /* @__PURE__ */ c("p", {
52
- className: "text-center text-sm text-text-secondary py-4",
53
- children: "No active timers on the server"
54
- }) : /* @__PURE__ */ c("div", {
55
- className: "flex flex-col gap-2",
56
- children: h.map((t) => /* @__PURE__ */ l("div", {
57
- className: "flex items-center justify-between rounded-lg border border-border-subtle p-3",
58
- children: [/* @__PURE__ */ l("div", {
59
- className: "min-w-0 flex-1",
60
- children: [/* @__PURE__ */ c("p", {
61
- className: "text-sm font-medium text-text-primary",
62
- children: t.timeEntry.project?.name ?? "No Project"
63
- }), /* @__PURE__ */ c("p", {
64
- className: "text-xs text-text-secondary",
65
- children: t.timeEntry.description ?? "No description"
20
+ children: [
21
+ /* @__PURE__ */ u(p, {
22
+ storageKey: "timecampus-timer",
23
+ className: "mb-6",
24
+ steps: [
25
+ {
26
+ id: "start",
27
+ label: m ? "Stop the running timer" : "Start the timer",
28
+ description: m ? "End this session and save it as a time entry." : "Begin tracking the task you're working on right now.",
29
+ onClick: () => m ? g() : h()
30
+ },
31
+ {
32
+ id: "assign-project",
33
+ label: "Assign work to a project",
34
+ description: "Pick a project so this time bills correctly.",
35
+ onClick: () => _("/projects")
36
+ },
37
+ {
38
+ id: "review-entries",
39
+ label: "Review your time entries",
40
+ description: "Check and tidy everything you've logged.",
41
+ onClick: () => _("/time/entries")
42
+ }
43
+ ]
44
+ }),
45
+ /* @__PURE__ */ d(f, {
46
+ treatment: "glass",
47
+ glow: !0,
48
+ className: "p-8 text-center",
49
+ children: [
50
+ /* @__PURE__ */ u("p", {
51
+ className: "text-6xl font-mono font-bold text-text-primary",
52
+ children: m ? t(m.seconds) : "00:00:00"
53
+ }),
54
+ m?.description && /* @__PURE__ */ u("p", {
55
+ className: "mt-2 text-sm text-text-secondary",
56
+ children: m.description
57
+ }),
58
+ /* @__PURE__ */ d("div", {
59
+ className: "mt-6 flex items-center justify-center gap-3",
60
+ children: [m ? /* @__PURE__ */ d("button", {
61
+ onClick: g,
62
+ className: "inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90",
63
+ children: [/* @__PURE__ */ u(l, { className: "h-4 w-4" }), "Stop"]
64
+ }) : /* @__PURE__ */ d("button", {
65
+ onClick: () => h(),
66
+ className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
67
+ children: [/* @__PURE__ */ u(s, { className: "h-4 w-4" }), "Start"]
68
+ }), /* @__PURE__ */ d("button", {
69
+ className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
70
+ children: [/* @__PURE__ */ u(c, { className: "h-4 w-4" }), "Reset"]
66
71
  })]
67
- }), /* @__PURE__ */ c("p", {
68
- className: "ml-4 text-sm font-mono font-medium text-text-primary",
69
- children: e(t.elapsedSeconds)
70
- })]
71
- }, t.timeEntry.id))
72
- })]
73
- })]
72
+ })
73
+ ]
74
+ }),
75
+ /* @__PURE__ */ d("div", {
76
+ className: "mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
77
+ children: [/* @__PURE__ */ u("h3", {
78
+ className: "mb-4 text-sm font-semibold text-text-primary",
79
+ children: "Running Timers"
80
+ }), y ? /* @__PURE__ */ u(n, {
81
+ variant: "list",
82
+ count: 2
83
+ }) : b.length === 0 ? /* @__PURE__ */ u(o, {
84
+ illustration: "empty-sessions",
85
+ title: "No active timers on the server",
86
+ description: "Timers you start sync here so you can pick them up on any device."
87
+ }) : /* @__PURE__ */ u("div", {
88
+ className: "flex flex-col gap-2",
89
+ children: b.map((e) => /* @__PURE__ */ d("div", {
90
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-3",
91
+ children: [/* @__PURE__ */ d("div", {
92
+ className: "min-w-0 flex-1",
93
+ children: [/* @__PURE__ */ u("p", {
94
+ className: "text-sm font-medium text-text-primary",
95
+ children: e.timeEntry.project?.name ?? "No Project"
96
+ }), /* @__PURE__ */ u("p", {
97
+ className: "text-xs text-text-secondary",
98
+ children: e.timeEntry.description ?? "No description"
99
+ })]
100
+ }), /* @__PURE__ */ u("p", {
101
+ className: "ml-4 text-sm font-mono font-medium text-text-primary",
102
+ children: t(e.elapsedSeconds)
103
+ })]
104
+ }, e.timeEntry.id))
105
+ })]
106
+ })
107
+ ]
74
108
  })
75
109
  });
76
110
  }
77
111
  //#endregion
78
- export { u as TimerPage };
112
+ export { m as TimerPage };
79
113
 
80
114
  //# sourceMappingURL=TimerPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout title=\"Timer\" description=\"Focus timer for deep work sessions\">\n <div className=\"mx-auto max-w-lg\">\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-8 text-center\">\n <p className=\"text-6xl font-mono font-bold text-text-primary\">\n {activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : \"00:00:00\"}\n </p>\n {activeTimer?.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {activeTimer.description}\n </p>\n )}\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {activeTimer ? (\n <button\n onClick={stopTimer}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90\"\n >\n <Square className=\"h-4 w-4\" />\n Stop\n </button>\n ) : (\n <button\n onClick={() => startTimer()}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n )}\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </div>\n\n {/* Server-side running timers */}\n <div className=\"mt-6 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 Running Timers\n </h3>\n {runningLoading ? (\n <LoadingSkeleton variant=\"list\" count={2} />\n ) : runningTimers.length === 0 ? (\n <p className=\"text-center text-sm text-text-secondary py-4\">\n No active timers on the server\n </p>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {runningTimers.map((timer: RunningTimerItem) => (\n <div\n key={timer.timeEntry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {timer.timeEntry.project?.name ?? \"No Project\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {timer.timeEntry.description ?? \"No description\"}\n </p>\n </div>\n <p className=\"ml-4 text-sm font-mono font-medium text-text-primary\">\n {formatTimerDuration(timer.elapsedSeconds)}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,IAAY;CAC1B,IAAM,EAAE,gBAAa,eAAY,iBAAc,GAAoB,EAE7D,EAAE,MAAM,GAAa,SAAS,MAClC,GAA2B,EAEvB,IAAgB,GAAa,iBAAiB,EAAE;AAEtD,QACE,kBAAC,GAAD;EAAY,OAAM;EAAQ,aAAY;YACpC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD;MAAG,WAAU;gBACV,IACG,EAAoB,EAAY,QAAQ,GACxC;MACF,CAAA;KACH,GAAa,eACZ,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAY;MACX,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,IACC,kBAAC,UAAD;OACE,SAAS;OACT,WAAU;iBAFZ,CAIE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,OAEvB;WAET,kBAAC,UAAD;OACE,eAAe,GAAY;OAC3B,WAAU;iBAFZ,CAIE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,QAErB;UAEX,kBAAC,UAAD;OAAQ,WAAU;iBAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAE1B;SACL;;KACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA+C;KAExD,CAAA,EACJ,IACC,kBAAC,GAAD;KAAiB,SAAQ;KAAO,OAAO;KAAK,CAAA,GAC1C,EAAc,WAAW,IAC3B,kBAAC,KAAD;KAAG,WAAU;eAA+C;KAExD,CAAA,GAEJ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,UAAU,SAAS,QAAQ;QAChC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,UAAU,eAAe;QAC9B,CAAA,CACA;UACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAoB,EAAM,eAAe;OACxC,CAAA,CACA;QAdC,EAAM,UAAU,GAcjB,CACN;KACE,CAAA,CAEJ;MACF;;EACK,CAAA"}
1
+ {"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n const navigate = useTimeCampusNavigate();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout\n title=\"Timer\"\n description=\"Focus timer for deep work sessions\"\n purpose=\"A single, distraction-free stopwatch for the task in front of you — start it when you begin focused work and it logs the session as a time entry you can attribute to a project later.\"\n >\n <div className=\"mx-auto max-w-lg\">\n <NextSteps\n storageKey=\"timecampus-timer\"\n className=\"mb-6\"\n steps={[\n {\n id: \"start\",\n label: activeTimer ? \"Stop the running timer\" : \"Start the timer\",\n description: activeTimer\n ? \"End this session and save it as a time entry.\"\n : \"Begin tracking the task you're working on right now.\",\n onClick: () => (activeTimer ? stopTimer() : startTimer()),\n },\n {\n id: \"assign-project\",\n label: \"Assign work to a project\",\n description: \"Pick a project so this time bills correctly.\",\n onClick: () => navigate(\"/projects\"),\n },\n {\n id: \"review-entries\",\n label: \"Review your time entries\",\n description: \"Check and tidy everything you've logged.\",\n onClick: () => navigate(\"/time/entries\"),\n },\n ]}\n />\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <p className=\"text-6xl font-mono font-bold text-text-primary\">\n {activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : \"00:00:00\"}\n </p>\n {activeTimer?.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {activeTimer.description}\n </p>\n )}\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {activeTimer ? (\n <button\n onClick={stopTimer}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90\"\n >\n <Square className=\"h-4 w-4\" />\n Stop\n </button>\n ) : (\n <button\n onClick={() => startTimer()}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n )}\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </GlassCard>\n\n {/* Server-side running timers */}\n <div className=\"mt-6 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 Running Timers\n </h3>\n {runningLoading ? (\n <LoadingSkeleton variant=\"list\" count={2} />\n ) : runningTimers.length === 0 ? (\n <EmptyState\n illustration=\"empty-sessions\"\n title=\"No active timers on the server\"\n description=\"Timers you start sync here so you can pick them up on any device.\"\n />\n ) : (\n <div className=\"flex flex-col gap-2\">\n {runningTimers.map((timer: RunningTimerItem) => (\n <div\n key={timer.timeEntry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {timer.timeEntry.project?.name ?? \"No Project\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {timer.timeEntry.description ?? \"No description\"}\n </p>\n </div>\n <p className=\"ml-4 text-sm font-mono font-medium text-text-primary\">\n {formatTimerDuration(timer.elapsedSeconds)}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAeA,SAAgB,IAAY;CAC1B,IAAM,EAAE,gBAAa,eAAY,iBAAc,GAAoB,EAC7D,IAAW,GAAuB,EAElC,EAAE,MAAM,GAAa,SAAS,MAClC,GAA2B,EAEvB,IAAgB,GAAa,iBAAiB,EAAE;AAEtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,YAAW;KACX,WAAU;KACV,OAAO;MACL;OACE,IAAI;OACJ,OAAO,IAAc,2BAA2B;OAChD,aAAa,IACT,kDACA;OACJ,eAAgB,IAAc,GAAW,GAAG,GAAY;OACzD;MACD;OACE,IAAI;OACJ,OAAO;OACP,aAAa;OACb,eAAe,EAAS,YAAY;OACrC;MACD;OACE,IAAI;OACJ,OAAO;OACP,aAAa;OACb,eAAe,EAAS,gBAAgB;OACzC;MACF;KACD,CAAA;IACF,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,EAAoB,EAAY,QAAQ,GACxC;OACF,CAAA;MACH,GAAa,eACZ,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAY;OACX,CAAA;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,IACC,kBAAC,UAAD;QACE,SAAS;QACT,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,OAEvB;YAET,kBAAC,UAAD;QACE,eAAe,GAAY;QAC3B,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,QAErB;WAEX,kBAAC,UAAD;QAAQ,WAAU;kBAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAE1B;UACL;;MACI;;IAGZ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAExD,CAAA,EACJ,IACC,kBAAC,GAAD;MAAiB,SAAQ;MAAO,OAAO;MAAK,CAAA,GAC1C,EAAc,WAAW,IAC3B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAM,UAAU,SAAS,QAAQ;SAChC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAM,UAAU,eAAe;SAC9B,CAAA,CACA;WACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAoB,EAAM,eAAe;QACxC,CAAA,CACA;SAdC,EAAM,UAAU,GAcjB,CACN;MACE,CAAA,CAEJ;;IACF;;EACK,CAAA"}