@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.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 (93) hide show
  1. package/dist/generated/wspace-operations.js +996 -816
  2. package/dist/generated/wspace-operations.js.map +1 -1
  3. package/dist/generated/wspace-types.js +28 -0
  4. package/dist/generated/wspace-types.js.map +1 -0
  5. package/dist/index.js +4 -4
  6. package/dist/timecampus/TimeCampusRoutes.js +16 -12
  7. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  8. package/dist/timecampus/components/Modal.js +65 -0
  9. package/dist/timecampus/components/Modal.js.map +1 -0
  10. package/dist/timecampus/components/StatusBadge.js +28 -0
  11. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  12. package/dist/timecampus/components/Toast.js +87 -0
  13. package/dist/timecampus/components/Toast.js.map +1 -0
  14. package/dist/timecampus/index.js +1 -1
  15. package/dist/timecampus/pages/HomePage.js +143 -135
  16. package/dist/timecampus/pages/HomePage.js.map +1 -1
  17. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  18. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  19. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  20. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  21. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  22. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  23. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  24. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  25. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
  26. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  27. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  28. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  29. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  30. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  31. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
  32. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  33. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  34. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  35. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  36. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  37. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  38. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  40. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  41. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  42. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  43. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  44. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
  46. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  48. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  49. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  50. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  51. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  52. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  54. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  56. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  57. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  58. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  59. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  60. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  61. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  62. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  64. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  65. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  66. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  67. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  68. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  69. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  70. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  71. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  72. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  74. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  75. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  76. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  77. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  78. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  79. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
  80. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  81. package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
  82. package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
  83. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  84. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  85. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  86. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  87. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  88. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  89. package/dist/timecampus/store/timecampusStore.js +13 -1
  90. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  91. package/dist/timecampus/utils/helpers.js +20 -10
  92. package/dist/timecampus/utils/helpers.js.map +1 -1
  93. package/package.json +1 -1
@@ -1,114 +1,246 @@
1
1
  import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { formatTimerDuration as t } from "../../utils/helpers.js";
3
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";
4
+ import { GetTimeEntryStatsDocument as r, ListRunningTimersDocument as i, ListTimeEntriesDocument as ee, useDeleteTimeEntryMutation as a, useListProjectsQuery as o, useListRunningTimersQuery as s, useStartTimerMutation as c, useStopTimerMutation as l } from "../../../generated/wspace-operations.js";
5
+ import { useTimeCampusStore as u } from "../../store/timecampusStore.js";
6
+ import { PageLayout as d } from "../../components/PageLayout.js";
7
+ import { EmptyState as f } from "../../components/EmptyState.js";
8
+ import { Modal as p } from "../../components/Modal.js";
9
+ import { useEffect as m, useState as h } from "react";
10
+ import { Play as g, RotateCcw as te, Square as _ } from "lucide-react";
11
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { GlassCard as x, NextSteps as S } from "@burdenoff/fe-libs/ui";
11
13
  //#region src/timecampus/pages/time/TimerPage.tsx
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, {
14
+ function C() {
15
+ let { activeTimer: C, tickTimer: w, syncTimerFromServer: T } = u(), E = e(), [D, O] = h(!1), [k, A] = h(""), [j, M] = h(""), [N, P] = h(!1), [F, I] = h(null), { data: L, loading: R, refetch: z } = s({ fetchPolicy: "cache-and-network" }), { data: B } = o({ variables: { limit: 50 } }), V = B?.projects?.items ?? [], H = L?.runningTimers ?? [], U = H[0] ?? null, W = [
16
+ { query: i },
17
+ {
18
+ query: ee,
19
+ variables: { limit: 50 }
20
+ },
21
+ { query: r }
22
+ ], [G, { loading: K, error: q }] = c({ refetchQueries: W }), [J, { loading: Y }] = l({ refetchQueries: W }), [X, { loading: Z }] = a({ refetchQueries: W });
23
+ m(() => {
24
+ U ? T({
25
+ timeEntryId: U.timeEntry.id,
26
+ elapsedSeconds: U.elapsedSeconds,
27
+ projectId: U.timeEntry.projectId ?? void 0,
28
+ taskId: U.timeEntry.taskId ?? void 0,
29
+ description: U.timeEntry.description ?? void 0
30
+ }) : R || T(null);
31
+ }, [
32
+ U?.timeEntry.id,
33
+ U?.elapsedSeconds,
34
+ R
35
+ ]), m(() => {
36
+ if (!C?.isRunning) return;
37
+ let e = setInterval(() => w(), 1e3);
38
+ return () => clearInterval(e);
39
+ }, [C?.isRunning, w]);
40
+ let ne = async () => {
41
+ await G({ variables: { input: {
42
+ description: k.trim() || void 0,
43
+ projectId: j || void 0,
44
+ billable: N,
45
+ source: "timer"
46
+ } } }), O(!1), A(""), M(""), P(!1), await z();
47
+ }, Q = async () => {
48
+ U && (await J({ variables: { id: U.timeEntry.id } }), await z());
49
+ }, re = async () => {
50
+ F && (await J({ variables: { id: F.timeEntry.id } }), await X({ variables: { id: F.timeEntry.id } }), I(null), await z());
51
+ }, $ = !!U;
52
+ return /* @__PURE__ */ b(d, {
15
53
  title: "Timer",
16
54
  description: "Focus timer for deep work sessions",
17
55
  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", {
19
- className: "mx-auto max-w-lg",
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
- }
56
+ children: [
57
+ /* @__PURE__ */ b("div", {
58
+ className: "mx-auto max-w-lg",
59
+ children: [
60
+ /* @__PURE__ */ y(S, {
61
+ storageKey: "timecampus-timer",
62
+ className: "mb-6",
63
+ steps: [
64
+ {
65
+ id: "start",
66
+ label: $ ? "Stop the running timer" : "Start the timer",
67
+ description: $ ? "End this session and save it as a time entry." : "Begin tracking the task you're working on right now.",
68
+ onClick: () => $ ? Q() : O(!0)
69
+ },
70
+ {
71
+ id: "assign-project",
72
+ label: "Assign work to a project",
73
+ description: "Pick a project so this time bills correctly.",
74
+ onClick: () => E("/projects")
75
+ },
76
+ {
77
+ id: "review-entries",
78
+ label: "Review your time entries",
79
+ description: "Check and tidy everything you've logged.",
80
+ onClick: () => E("/time/entries")
81
+ }
82
+ ]
83
+ }),
84
+ /* @__PURE__ */ b(x, {
85
+ treatment: "glass",
86
+ glow: !0,
87
+ className: "p-8 text-center",
88
+ children: [
89
+ /* @__PURE__ */ y("p", {
90
+ className: "text-6xl font-mono font-bold text-text-primary",
91
+ children: C ? t(C.seconds) : "00:00:00"
92
+ }),
93
+ C?.description && /* @__PURE__ */ y("p", {
94
+ className: "mt-2 text-sm text-text-secondary",
95
+ children: C.description
96
+ }),
97
+ /* @__PURE__ */ b("div", {
98
+ className: "mt-6 flex items-center justify-center gap-3",
99
+ children: [$ ? /* @__PURE__ */ b("button", {
100
+ onClick: Q,
101
+ disabled: Y,
102
+ 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 disabled:opacity-50",
103
+ children: [/* @__PURE__ */ y(_, { className: "h-4 w-4" }), Y ? "Stopping..." : "Stop"]
104
+ }) : /* @__PURE__ */ b("button", {
105
+ onClick: () => O(!0),
106
+ disabled: K,
107
+ 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 disabled:opacity-50",
108
+ children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }), "Start"]
109
+ }), /* @__PURE__ */ b("button", {
110
+ onClick: () => U && I(U),
111
+ disabled: !$ || Z,
112
+ 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 disabled:opacity-50",
113
+ title: "Discard the running timer without saving",
114
+ children: [/* @__PURE__ */ y(te, { className: "h-4 w-4" }), "Reset"]
115
+ })]
116
+ })
117
+ ]
118
+ }),
119
+ /* @__PURE__ */ b("div", {
120
+ className: "mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
121
+ children: [/* @__PURE__ */ y("h3", {
122
+ className: "mb-4 text-sm font-semibold text-text-primary",
123
+ children: "Running Timers"
124
+ }), R && H.length === 0 ? /* @__PURE__ */ y(n, {
125
+ variant: "list",
126
+ count: 2
127
+ }) : H.length === 0 ? /* @__PURE__ */ y(f, {
128
+ illustration: "empty-sessions",
129
+ title: "No active timers on the server",
130
+ description: "Timers you start sync here so you can pick them up on any device."
131
+ }) : /* @__PURE__ */ y("div", {
132
+ className: "flex flex-col gap-2",
133
+ children: H.map((e) => /* @__PURE__ */ b("div", {
134
+ className: "flex items-center justify-between rounded-lg border border-border-subtle p-3",
135
+ children: [/* @__PURE__ */ b("div", {
136
+ className: "min-w-0 flex-1",
137
+ children: [/* @__PURE__ */ y("p", {
138
+ className: "text-sm font-medium text-text-primary",
139
+ children: e.timeEntry.project?.name ?? "No Project"
140
+ }), /* @__PURE__ */ y("p", {
141
+ className: "text-xs text-text-secondary",
142
+ children: e.timeEntry.description ?? "No description"
143
+ })]
144
+ }), /* @__PURE__ */ y("p", {
145
+ className: "ml-4 text-sm font-mono font-medium text-text-primary",
146
+ children: t(e.elapsedSeconds)
147
+ })]
148
+ }, e.timeEntry.id))
149
+ })]
150
+ })
151
+ ]
152
+ }),
153
+ /* @__PURE__ */ b(p, {
154
+ open: D,
155
+ onClose: () => O(!1),
156
+ title: "Start a timer",
157
+ description: "Track the task you're working on right now.",
158
+ footer: /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("button", {
159
+ onClick: () => O(!1),
160
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
161
+ children: "Cancel"
162
+ }), /* @__PURE__ */ y("button", {
163
+ onClick: ne,
164
+ disabled: K,
165
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
166
+ children: K ? "Starting..." : "Start Timer"
167
+ })] }),
168
+ children: [q && /* @__PURE__ */ y("div", {
169
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
170
+ children: "Failed to start the timer. Please try again."
171
+ }), /* @__PURE__ */ b("div", {
172
+ className: "space-y-4",
173
+ children: [
174
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
175
+ className: "block text-sm font-medium text-text-primary",
176
+ children: "What are you working on?"
177
+ }), /* @__PURE__ */ y("input", {
178
+ type: "text",
179
+ placeholder: "Description",
180
+ value: k,
181
+ onChange: (e) => A(e.target.value),
182
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary"
183
+ })] }),
184
+ /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
185
+ className: "block text-sm font-medium text-text-primary",
186
+ children: "Project"
187
+ }), /* @__PURE__ */ b("select", {
188
+ value: j,
189
+ onChange: (e) => M(e.target.value),
190
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
191
+ children: [/* @__PURE__ */ y("option", {
192
+ value: "",
193
+ children: "No Project"
194
+ }), V.map((e) => /* @__PURE__ */ y("option", {
195
+ value: e.id,
196
+ children: e.name
197
+ }, e.id))]
198
+ })] }),
199
+ /* @__PURE__ */ b("label", {
200
+ className: "flex items-center gap-2 text-sm text-text-primary",
201
+ children: [/* @__PURE__ */ y("input", {
202
+ type: "checkbox",
203
+ checked: N,
204
+ onChange: (e) => P(e.target.checked),
205
+ className: "h-4 w-4 rounded border-border-subtle"
206
+ }), "Billable"]
207
+ })
43
208
  ]
44
- }),
45
- /* @__PURE__ */ d(f, {
46
- treatment: "glass",
47
- glow: !0,
48
- className: "p-8 text-center",
209
+ })]
210
+ }),
211
+ /* @__PURE__ */ y(p, {
212
+ open: !!F,
213
+ onClose: () => I(null),
214
+ title: "Discard running timer?",
215
+ description: "This stops the timer and deletes the in-progress entry. It cannot be undone.",
216
+ footer: /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("button", {
217
+ onClick: () => I(null),
218
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
219
+ children: "Cancel"
220
+ }), /* @__PURE__ */ y("button", {
221
+ onClick: re,
222
+ disabled: Y || Z,
223
+ className: "rounded-md bg-action-danger-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90 disabled:opacity-50",
224
+ children: Y || Z ? "Discarding..." : "Discard"
225
+ })] }),
226
+ children: /* @__PURE__ */ b("p", {
227
+ className: "text-sm text-text-secondary",
49
228
  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"
229
+ "The timer for",
230
+ " ",
231
+ /* @__PURE__ */ y("span", {
232
+ className: "font-medium text-text-primary",
233
+ children: F?.timeEntry.project?.name ?? F?.timeEntry.description ?? "this session"
53
234
  }),
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"]
71
- })]
72
- })
235
+ " ",
236
+ "will be discarded."
73
237
  ]
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
238
  })
107
- ]
108
- })
239
+ })
240
+ ]
109
241
  });
110
242
  }
111
243
  //#endregion
112
- export { m as TimerPage };
244
+ export { C as TimerPage };
113
245
 
114
246
  //# 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 { 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"}
1
+ {"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { Play, Square, RotateCcw } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { Modal } from \"../../components/Modal\";\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 useStartTimerMutation,\n useStopTimerMutation,\n useDeleteTimeEntryMutation,\n useListProjectsQuery,\n ListRunningTimersDocument,\n ListTimeEntriesDocument,\n GetTimeEntryStatsDocument,\n type ListRunningTimersQuery,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\ntype ProjectOption = ListProjectsQuery[\"projects\"][\"items\"][number];\n\nexport function TimerPage() {\n const { activeTimer, tickTimer, syncTimerFromServer } = useTimeCampusStore();\n const navigate = useTimeCampusNavigate();\n\n const [startOpen, setStartOpen] = useState(false);\n const [description, setDescription] = useState(\"\");\n const [projectId, setProjectId] = useState(\"\");\n const [billable, setBillable] = useState(false);\n const [resetTarget, setResetTarget] = useState<RunningTimerItem | null>(null);\n\n const {\n data: runningData,\n loading: runningLoading,\n refetch: refetchRunning,\n } = useListRunningTimersQuery({ fetchPolicy: \"cache-and-network\" });\n\n const { data: projectsData } = useListProjectsQuery({\n variables: { limit: 50 },\n });\n const projects: ProjectOption[] = projectsData?.projects?.items ?? [];\n\n const runningTimers = runningData?.runningTimers ?? [];\n const serverTimer = runningTimers[0] ?? null;\n\n const refetchAll = [\n { query: ListRunningTimersDocument },\n { query: ListTimeEntriesDocument, variables: { limit: 50 } },\n { query: GetTimeEntryStatsDocument },\n ];\n\n const [startTimerMutation, { loading: starting, error: startError }] =\n useStartTimerMutation({ refetchQueries: refetchAll });\n const [stopTimerMutation, { loading: stopping }] = useStopTimerMutation({\n refetchQueries: refetchAll,\n });\n const [deleteTimeEntry, { loading: deleting }] = useDeleteTimeEntryMutation({\n refetchQueries: refetchAll,\n });\n\n // Backend is the source of truth for whether a timer is running — mirror the\n // server running timer into the local display store whenever it changes.\n useEffect(() => {\n if (serverTimer) {\n syncTimerFromServer({\n timeEntryId: serverTimer.timeEntry.id,\n elapsedSeconds: serverTimer.elapsedSeconds,\n projectId: serverTimer.timeEntry.projectId ?? undefined,\n taskId: serverTimer.timeEntry.taskId ?? undefined,\n description: serverTimer.timeEntry.description ?? undefined,\n });\n } else if (!runningLoading) {\n syncTimerFromServer(null);\n }\n // Only re-sync when the server's running timer identity/elapsed changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [serverTimer?.timeEntry.id, serverTimer?.elapsedSeconds, runningLoading]);\n\n // Local ticking display for the live seconds counter.\n useEffect(() => {\n if (!activeTimer?.isRunning) return;\n const interval = setInterval(() => tickTimer(), 1000);\n return () => clearInterval(interval);\n }, [activeTimer?.isRunning, tickTimer]);\n\n const handleStart = async () => {\n await startTimerMutation({\n variables: {\n input: {\n description: description.trim() || undefined,\n projectId: projectId || undefined,\n billable,\n source: \"timer\",\n },\n },\n });\n setStartOpen(false);\n setDescription(\"\");\n setProjectId(\"\");\n setBillable(false);\n await refetchRunning();\n };\n\n const handleStop = async () => {\n if (!serverTimer) return;\n await stopTimerMutation({ variables: { id: serverTimer.timeEntry.id } });\n await refetchRunning();\n };\n\n const handleReset = async () => {\n if (!resetTarget) return;\n // Discard the in-progress session: stop it, then delete the entry.\n await stopTimerMutation({ variables: { id: resetTarget.timeEntry.id } });\n await deleteTimeEntry({ variables: { id: resetTarget.timeEntry.id } });\n setResetTarget(null);\n await refetchRunning();\n };\n\n const isRunning = Boolean(serverTimer);\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: isRunning ? \"Stop the running timer\" : \"Start the timer\",\n description: isRunning\n ? \"End this session and save it as a time entry.\"\n : \"Begin tracking the task you're working on right now.\",\n onClick: () => (isRunning ? handleStop() : setStartOpen(true)),\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 {isRunning ? (\n <button\n onClick={handleStop}\n disabled={stopping}\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 disabled:opacity-50\"\n >\n <Square className=\"h-4 w-4\" />\n {stopping ? \"Stopping...\" : \"Stop\"}\n </button>\n ) : (\n <button\n onClick={() => setStartOpen(true)}\n disabled={starting}\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 disabled:opacity-50\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n )}\n <button\n onClick={() => serverTimer && setResetTarget(serverTimer)}\n disabled={!isRunning || deleting}\n 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 disabled:opacity-50\"\n title=\"Discard the running timer without saving\"\n >\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 && runningTimers.length === 0 ? (\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\n {/* Start timer modal */}\n <Modal\n open={startOpen}\n onClose={() => setStartOpen(false)}\n title=\"Start a timer\"\n description=\"Track the task you're working on right now.\"\n footer={\n <>\n <button\n onClick={() => setStartOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleStart}\n disabled={starting}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {starting ? \"Starting...\" : \"Start Timer\"}\n </button>\n </>\n }\n >\n {startError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to start the timer. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n What are you working on?\n </label>\n <input\n type=\"text\"\n placeholder=\"Description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project\n </label>\n <select\n value={projectId}\n onChange={(e) => setProjectId(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">No Project</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}\n </option>\n ))}\n </select>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={billable}\n onChange={(e) => setBillable(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n Billable\n </label>\n </div>\n </Modal>\n\n {/* Reset / discard confirm modal */}\n <Modal\n open={Boolean(resetTarget)}\n onClose={() => setResetTarget(null)}\n title=\"Discard running timer?\"\n description=\"This stops the timer and deletes the in-progress entry. It cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setResetTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleReset}\n disabled={stopping || deleting}\n className=\"rounded-md bg-action-danger-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90 disabled:opacity-50\"\n >\n {stopping || deleting ? \"Discarding...\" : \"Discard\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n The timer for{\" \"}\n <span className=\"font-medium text-text-primary\">\n {resetTarget?.timeEntry.project?.name ??\n resetTarget?.timeEntry.description ??\n \"this session\"}\n </span>{\" \"}\n will be discarded.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}