@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
@@ -2,121 +2,349 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { formatHours as t } from "../../utils/helpers.js";
3
3
  import { StatCard as n } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
5
- import { useGetAppUsageSummaryQuery as i, useListApplicationUsagesQuery as a } from "../../../generated/wspace-operations.js";
5
+ import { useCategorizeApplicationMutation as ee, useDeleteApplicationUsageMutation as te, useGetAppUsageSummaryQuery as i, useListApplicationUsagesQuery as a, useRecordApplicationUsageMutation as ne } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as o } from "../../components/PageLayout.js";
7
- import { EmptyState as s } from "../../components/EmptyState.js";
8
- import { Clock as c, Monitor as l } from "lucide-react";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
+ import { EmptyState as re } from "../../components/EmptyState.js";
8
+ import { Modal as s } from "../../components/Modal.js";
9
+ import { useState as c } from "react";
10
+ import { Clock as ie, Monitor as l, Plus as u, Tag as d, Trash2 as f } from "lucide-react";
11
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
10
12
  //#region src/timecampus/pages/application-usage/ApplicationUsagePage.tsx
11
- var f = {
13
+ var g = 50, _ = [
14
+ "productive",
15
+ "neutral",
16
+ "communication",
17
+ "distracting"
18
+ ], ae = {
12
19
  productive: "bg-status-success-bg text-status-success-text",
13
20
  neutral: "bg-bg-sunken text-text-secondary",
14
21
  communication: "bg-status-info-bg text-status-info-text",
15
22
  distracting: "bg-status-error-bg text-status-error-text"
16
- };
17
- function p() {
18
- let { workspaceId: p = "" } = e(), { data: m, loading: h, error: g } = a({
19
- variables: { limit: 50 },
20
- skip: !p
21
- }), { data: _, loading: v, error: y } = i({ skip: !p }), b = h || v, x = g || y, S = m?.applicationUsages ?? [], C = _?.appUsageSummary, w = S.reduce((e, t) => e + t.timeSpent, 0), T = S.reduce((e, t) => e + t.switches, 0), E = C?.productivityRatio == null ? "--" : `${Math.round(C.productivityRatio * 100)}%`, D = S.filter((e) => e.timeSpent >= 5400).length;
22
- return b ? /* @__PURE__ */ u(o, {
23
+ }, v = "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", y = "block text-sm font-medium text-text-primary";
24
+ function oe() {
25
+ return (/* @__PURE__ */ new Date()).toISOString();
26
+ }
27
+ function b() {
28
+ let { workspaceId: b = "" } = e(), { data: x, loading: S, error: C, fetchMore: w, refetch: T } = a({
29
+ variables: {
30
+ limit: g,
31
+ offset: 0
32
+ },
33
+ skip: !b,
34
+ notifyOnNetworkStatusChange: !0
35
+ }), { data: E, loading: se, error: ce, refetch: le } = i({ skip: !b }), ue = S || se, de = C || ce, D = x?.applicationUsages ?? [], O = E?.appUsageSummary, fe = D.length > 0 && D.length % g === 0, [k, A] = c(!1), [j, M] = c(!1), [N, P] = c(""), [F, I] = c("productive"), [L, R] = c(""), [z, B] = c(""), [V, H] = c(null), [U, W] = c("productive"), [G, K] = c(null), [pe, { loading: q, error: me }] = ne(), [he, { loading: J, error: ge }] = ee(), [_e, { loading: Y, error: ve }] = te(), X = async () => {
36
+ await Promise.all([T(), le()]);
37
+ }, Z = () => {
38
+ P(""), I("productive"), R(""), B(""), M(!0);
39
+ }, ye = async () => {
40
+ !N.trim() || !L || (await pe({ variables: { input: {
41
+ applicationName: N.trim(),
42
+ category: F,
43
+ timeSpent: Math.round(Number(L) * 60),
44
+ sessions: z ? Number(z) : void 0,
45
+ date: oe()
46
+ } } }), M(!1), await X());
47
+ }, be = (e) => {
48
+ H(e), W(e.category);
49
+ }, xe = async () => {
50
+ V && (await he({ variables: {
51
+ applicationName: V.applicationName,
52
+ category: U
53
+ } }), H(null), await X());
54
+ }, Se = async () => {
55
+ G && (await _e({ variables: { id: G.id } }), K(null), await X());
56
+ }, Ce = async () => {
57
+ A(!0);
58
+ try {
59
+ await w({
60
+ variables: { offset: D.length },
61
+ updateQuery: (e, { fetchMoreResult: t }) => t ? {
62
+ ...e,
63
+ applicationUsages: [...e.applicationUsages, ...t.applicationUsages]
64
+ } : e
65
+ });
66
+ } finally {
67
+ A(!1);
68
+ }
69
+ }, Q = D.reduce((e, t) => e + t.timeSpent, 0), $ = D.reduce((e, t) => e + t.switches, 0), we = O?.productivityRatio == null ? "--" : `${Math.round(O.productivityRatio * 100)}%`, Te = D.filter((e) => e.timeSpent >= 5400).length;
70
+ return ue ? /* @__PURE__ */ m(o, {
23
71
  title: "App Usage",
24
72
  description: "Track application usage and productivity",
25
- children: /* @__PURE__ */ u(r, {
73
+ children: /* @__PURE__ */ m(r, {
26
74
  variant: "card",
27
75
  count: 4
28
76
  })
29
- }) : x ? /* @__PURE__ */ u(o, {
77
+ }) : de ? /* @__PURE__ */ m(o, {
30
78
  title: "App Usage",
31
79
  description: "Track application usage and productivity",
32
- children: /* @__PURE__ */ u("div", {
80
+ children: /* @__PURE__ */ m("div", {
33
81
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
34
82
  children: "Failed to load application usage. Please try again later."
35
83
  })
36
- }) : /* @__PURE__ */ d(o, {
84
+ }) : /* @__PURE__ */ h(o, {
37
85
  title: "App Usage",
38
86
  description: "Track application usage and productivity",
39
87
  purpose: "See where your screen time actually goes — which apps are productive, how often you context-switch, and how many real focus sessions you get — so you can cut the digital distractions eating your day.",
40
- children: [/* @__PURE__ */ d("div", {
41
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
42
- children: [
43
- /* @__PURE__ */ u(n, {
44
- featured: !0,
45
- title: "Total Screen Time",
46
- value: t(w / 3600),
47
- icon: /* @__PURE__ */ u(l, { className: "h-5 w-5" })
48
- }),
49
- /* @__PURE__ */ u(n, {
50
- title: "Productive Time",
51
- value: E,
52
- trend: C?.productivityRatio == null ? void 0 : {
53
- value: 5,
54
- label: "vs last week"
55
- }
56
- }),
57
- /* @__PURE__ */ u(n, {
58
- title: "App Switches",
59
- value: String(T),
60
- description: `Avg. ${S.length > 0 ? Math.round(T / S.length) : 0}/app`
61
- }),
62
- /* @__PURE__ */ u(n, {
63
- title: "Focus Sessions",
64
- value: String(D),
65
- icon: /* @__PURE__ */ u(c, { className: "h-5 w-5" }),
66
- description: "90+ min each"
88
+ actions: /* @__PURE__ */ h("button", {
89
+ onClick: Z,
90
+ 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",
91
+ children: [/* @__PURE__ */ m(u, { className: "h-4 w-4" }), "Add Usage"]
92
+ }),
93
+ children: [
94
+ /* @__PURE__ */ h("div", {
95
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
96
+ children: [
97
+ /* @__PURE__ */ m(n, {
98
+ featured: !0,
99
+ title: "Total Screen Time",
100
+ value: t(Q / 3600),
101
+ icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" })
102
+ }),
103
+ /* @__PURE__ */ m(n, {
104
+ title: "Productive Time",
105
+ value: we,
106
+ trend: O?.productivityRatio == null ? void 0 : {
107
+ value: 5,
108
+ label: "vs last week"
109
+ }
110
+ }),
111
+ /* @__PURE__ */ m(n, {
112
+ title: "App Switches",
113
+ value: String($),
114
+ description: `Avg. ${D.length > 0 ? Math.round($ / D.length) : 0}/app`
115
+ }),
116
+ /* @__PURE__ */ m(n, {
117
+ title: "Focus Sessions",
118
+ value: String(Te),
119
+ icon: /* @__PURE__ */ m(ie, { className: "h-5 w-5" }),
120
+ description: "90+ min each"
121
+ })
122
+ ]
123
+ }),
124
+ D.length === 0 ? /* @__PURE__ */ m(re, {
125
+ illustration: "empty-data",
126
+ title: "No application usage data found",
127
+ description: "Record app usage manually or connect the desktop tracker, and your apps will appear here ranked by time spent.",
128
+ action: /* @__PURE__ */ h("button", {
129
+ onClick: Z,
130
+ 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",
131
+ children: [/* @__PURE__ */ m(u, { className: "h-4 w-4" }), "Add Usage"]
67
132
  })
68
- ]
69
- }), S.length === 0 ? /* @__PURE__ */ u(s, {
70
- illustration: "empty-data",
71
- title: "No application usage data found",
72
- description: "Once the desktop tracker is active, the apps you use will appear here ranked by time spent."
73
- }) : /* @__PURE__ */ u("div", {
74
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
75
- children: /* @__PURE__ */ u("div", {
76
- className: "divide-y divide-border",
77
- children: S.map((e) => /* @__PURE__ */ d("div", {
78
- className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
79
- children: [/* @__PURE__ */ d("div", {
80
- className: "flex items-center gap-3",
81
- children: [/* @__PURE__ */ u(l, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
82
- className: "text-sm font-medium text-text-primary",
83
- children: e.applicationName
84
- }), /* @__PURE__ */ d("p", {
85
- className: "text-xs text-text-secondary",
86
- children: [e.sessions, " sessions"]
87
- })] })]
88
- }), /* @__PURE__ */ d("div", {
89
- className: "flex items-center gap-4",
90
- children: [
91
- /* @__PURE__ */ u("div", {
92
- className: "text-right",
93
- children: /* @__PURE__ */ u("p", {
133
+ }) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("div", {
134
+ className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
135
+ children: /* @__PURE__ */ m("div", {
136
+ className: "divide-y divide-border",
137
+ children: D.map((e) => /* @__PURE__ */ h("div", {
138
+ className: "flex items-center justify-between gap-3 p-4 transition-colors hover:bg-bg-sunken/50",
139
+ children: [/* @__PURE__ */ h("div", {
140
+ className: "flex min-w-0 items-center gap-3",
141
+ children: [/* @__PURE__ */ m(l, { className: "h-5 w-5 shrink-0 text-text-secondary" }), /* @__PURE__ */ h("div", {
142
+ className: "min-w-0",
143
+ children: [/* @__PURE__ */ m("p", {
144
+ className: "truncate text-sm font-medium text-text-primary",
145
+ children: e.applicationName
146
+ }), /* @__PURE__ */ h("p", {
147
+ className: "text-xs text-text-secondary",
148
+ children: [e.sessions, " sessions"]
149
+ })]
150
+ })]
151
+ }), /* @__PURE__ */ h("div", {
152
+ className: "flex shrink-0 items-center gap-3 sm:gap-4",
153
+ children: [
154
+ /* @__PURE__ */ m("p", {
94
155
  className: "text-sm font-mono font-medium text-text-primary",
95
156
  children: t(e.timeSpent / 3600)
96
- })
97
- }),
98
- /* @__PURE__ */ u("span", {
99
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${f[e.category] || ""}`,
100
- children: e.category
101
- }),
102
- /* @__PURE__ */ u("div", {
103
- className: "w-16",
104
- children: /* @__PURE__ */ u("div", {
105
- className: "h-2 rounded-full bg-bg-sunken",
106
- children: /* @__PURE__ */ u("div", {
107
- className: "h-2 rounded-full bg-action-primary-bg",
108
- style: { width: `${Math.min(100, Math.round(e.timeSpent / (w || 1) * 100))}%` }
157
+ }),
158
+ /* @__PURE__ */ m("span", {
159
+ className: `hidden rounded-full px-2 py-0.5 text-xs font-medium sm:inline-flex ${ae[e.category] || "bg-bg-sunken text-text-secondary"}`,
160
+ children: e.category
161
+ }),
162
+ /* @__PURE__ */ m("div", {
163
+ className: "hidden w-16 lg:block",
164
+ children: /* @__PURE__ */ m("div", {
165
+ className: "h-2 rounded-full bg-bg-sunken",
166
+ children: /* @__PURE__ */ m("div", {
167
+ className: "h-2 rounded-full bg-action-primary-bg",
168
+ style: { width: `${Math.min(100, Math.round(e.timeSpent / (Q || 1) * 100))}%` }
169
+ })
109
170
  })
171
+ }),
172
+ /* @__PURE__ */ m("button", {
173
+ onClick: () => be(e),
174
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
175
+ "aria-label": "Recategorize application",
176
+ children: /* @__PURE__ */ m(d, { className: "h-4 w-4" })
177
+ }),
178
+ /* @__PURE__ */ m("button", {
179
+ onClick: () => K(e),
180
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text",
181
+ "aria-label": "Delete usage entry",
182
+ children: /* @__PURE__ */ m(f, { className: "h-4 w-4" })
110
183
  })
111
- })
184
+ ]
185
+ })]
186
+ }, e.id))
187
+ })
188
+ }), fe && /* @__PURE__ */ m("div", {
189
+ className: "mt-4 flex justify-center",
190
+ children: /* @__PURE__ */ m("button", {
191
+ onClick: Ce,
192
+ disabled: k,
193
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
194
+ children: k ? "Loading..." : "Load more"
195
+ })
196
+ })] }),
197
+ /* @__PURE__ */ h(s, {
198
+ open: j,
199
+ onClose: () => M(!1),
200
+ title: "Record App Usage",
201
+ description: "Manually log time spent in an application",
202
+ footer: /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("button", {
203
+ onClick: () => M(!1),
204
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
205
+ children: "Cancel"
206
+ }), /* @__PURE__ */ m("button", {
207
+ onClick: ye,
208
+ disabled: q || !N.trim() || !L,
209
+ 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",
210
+ children: q ? "Saving..." : "Record"
211
+ })] }),
212
+ children: [me && /* @__PURE__ */ m("div", {
213
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
214
+ children: "Failed to record usage. Please try again."
215
+ }), /* @__PURE__ */ h("div", {
216
+ className: "space-y-4",
217
+ children: [
218
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
219
+ className: y,
220
+ children: "Application Name"
221
+ }), /* @__PURE__ */ m("input", {
222
+ type: "text",
223
+ placeholder: "e.g. Visual Studio Code",
224
+ value: N,
225
+ onChange: (e) => P(e.target.value),
226
+ className: v
227
+ })] }),
228
+ /* @__PURE__ */ h("div", {
229
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
230
+ children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
231
+ className: y,
232
+ children: "Category"
233
+ }), /* @__PURE__ */ m("select", {
234
+ value: F,
235
+ onChange: (e) => I(e.target.value),
236
+ className: v,
237
+ children: _.map((e) => /* @__PURE__ */ m("option", {
238
+ value: e,
239
+ className: "capitalize",
240
+ children: e
241
+ }, e))
242
+ })] }), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
243
+ className: y,
244
+ children: "Time Spent (minutes)"
245
+ }), /* @__PURE__ */ m("input", {
246
+ type: "number",
247
+ min: "0",
248
+ placeholder: "0",
249
+ value: L,
250
+ onChange: (e) => R(e.target.value),
251
+ className: v
252
+ })] })]
253
+ }),
254
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
255
+ className: y,
256
+ children: "Sessions"
257
+ }), /* @__PURE__ */ m("input", {
258
+ type: "number",
259
+ min: "0",
260
+ placeholder: "0",
261
+ value: z,
262
+ onChange: (e) => B(e.target.value),
263
+ className: v
264
+ })] })
265
+ ]
266
+ })]
267
+ }),
268
+ /* @__PURE__ */ h(s, {
269
+ open: V !== null,
270
+ onClose: () => H(null),
271
+ title: "Recategorize Application",
272
+ description: V?.applicationName,
273
+ size: "sm",
274
+ footer: /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("button", {
275
+ onClick: () => H(null),
276
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
277
+ children: "Cancel"
278
+ }), /* @__PURE__ */ m("button", {
279
+ onClick: xe,
280
+ disabled: J,
281
+ 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",
282
+ children: J ? "Saving..." : "Save"
283
+ })] }),
284
+ children: [
285
+ ge && /* @__PURE__ */ m("div", {
286
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
287
+ children: "Failed to recategorize. Please try again."
288
+ }),
289
+ /* @__PURE__ */ m("label", {
290
+ className: y,
291
+ children: "Category"
292
+ }),
293
+ /* @__PURE__ */ m("select", {
294
+ value: U,
295
+ onChange: (e) => W(e.target.value),
296
+ className: v,
297
+ children: _.map((e) => /* @__PURE__ */ m("option", {
298
+ value: e,
299
+ className: "capitalize",
300
+ children: e
301
+ }, e))
302
+ }),
303
+ /* @__PURE__ */ h("p", {
304
+ className: "mt-3 text-xs text-text-secondary",
305
+ children: [
306
+ "This updates the category for all usage of “",
307
+ V?.applicationName,
308
+ "”."
112
309
  ]
113
- })]
114
- }, e.id))
310
+ })
311
+ ]
312
+ }),
313
+ /* @__PURE__ */ h(s, {
314
+ open: G !== null,
315
+ onClose: () => K(null),
316
+ title: "Delete Usage Entry",
317
+ size: "sm",
318
+ footer: /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("button", {
319
+ onClick: () => K(null),
320
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
321
+ children: "Cancel"
322
+ }), /* @__PURE__ */ m("button", {
323
+ onClick: Se,
324
+ disabled: Y,
325
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50",
326
+ children: Y ? "Deleting..." : "Delete"
327
+ })] }),
328
+ children: [ve && /* @__PURE__ */ m("div", {
329
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
330
+ children: "Failed to delete entry. Please try again."
331
+ }), /* @__PURE__ */ h("p", {
332
+ className: "text-sm text-text-secondary",
333
+ children: [
334
+ "Are you sure you want to delete the usage entry for",
335
+ " ",
336
+ /* @__PURE__ */ m("span", {
337
+ className: "font-medium text-text-primary",
338
+ children: G?.applicationName
339
+ }),
340
+ "? This action cannot be undone."
341
+ ]
342
+ })]
115
343
  })
116
- })]
344
+ ]
117
345
  });
118
346
  }
119
347
  //#endregion
120
- export { p as ApplicationUsagePage };
348
+ export { b as ApplicationUsagePage };
121
349
 
122
350
  //# sourceMappingURL=ApplicationUsagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationUsagePage.js","names":[],"sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsagePage.tsx"],"sourcesContent":["import { Monitor, Clock } 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, getStatusColor } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListApplicationUsagesQuery,\n useGetAppUsageSummaryQuery,\n type ApplicationUsageFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst categoryColors: Record<string, string> = {\n productive: \"bg-status-success-bg text-status-success-text\",\n neutral: \"bg-bg-sunken text-text-secondary\",\n communication: \"bg-status-info-bg text-status-info-text\",\n distracting: \"bg-status-error-bg text-status-error-text\",\n};\n\nexport function ApplicationUsagePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: usagesData,\n loading: usagesLoading,\n error: usagesError,\n } = useListApplicationUsagesQuery({\n variables: { limit: 50 },\n skip: !workspaceId,\n });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n } = useGetAppUsageSummaryQuery({\n skip: !workspaceId,\n });\n\n const loading = usagesLoading || summaryLoading;\n const error = usagesError || summaryError;\n const apps = usagesData?.applicationUsages ?? [];\n const summary = summaryData?.appUsageSummary;\n\n const totalScreenTimeSeconds = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.timeSpent,\n 0,\n );\n const totalSwitches = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.switches,\n 0,\n );\n const productiveRatio =\n summary?.productivityRatio != null\n ? `${Math.round(summary.productivityRatio * 100)}%`\n : \"--\";\n\n // Count focus sessions (apps with > 90 min continuous usage)\n const focusSessions = apps.filter(\n (app: ApplicationUsageFieldsFragment) => app.timeSpent >= 5400,\n ).length;\n\n if (loading) {\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\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 application usage. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\n purpose=\"See where your screen time actually goes — which apps are productive, how often you context-switch, and how many real focus sessions you get — so you can cut the digital distractions eating your day.\"\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 Screen Time\"\n value={formatHours(totalScreenTimeSeconds / 3600)}\n icon={<Monitor className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Productive Time\"\n value={productiveRatio}\n trend={\n summary?.productivityRatio != null\n ? { value: 5, label: \"vs last week\" }\n : undefined\n }\n />\n <StatCard\n title=\"App Switches\"\n value={String(totalSwitches)}\n description={`Avg. ${apps.length > 0 ? Math.round(totalSwitches / apps.length) : 0}/app`}\n />\n <StatCard\n title=\"Focus Sessions\"\n value={String(focusSessions)}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"90+ min each\"\n />\n </div>\n\n {apps.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No application usage data found\"\n description=\"Once the desktop tracker is active, the apps you use will appear here ranked by time spent.\"\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {apps.map((app: ApplicationUsageFieldsFragment) => (\n <div\n key={app.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 <Monitor className=\"h-5 w-5 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {app.applicationName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {app.sessions} sessions\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours(app.timeSpent / 3600)}\n </p>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${categoryColors[app.category] || \"\"}`}\n >\n {app.category}\n </span>\n <div className=\"w-16\">\n <div className=\"h-2 rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${Math.min(100, Math.round((app.timeSpent / (totalScreenTimeSeconds || 1)) * 100))}%`,\n }}\n />\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ApplicationUsagePage.js","names":[],"sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsagePage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { Monitor, Clock, Plus, Tag, Trash2 } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { formatHours } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListApplicationUsagesQuery,\n useGetAppUsageSummaryQuery,\n useRecordApplicationUsageMutation,\n useCategorizeApplicationMutation,\n useDeleteApplicationUsageMutation,\n type ApplicationUsageFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 50;\n\nconst CATEGORY_OPTIONS = [\n \"productive\",\n \"neutral\",\n \"communication\",\n \"distracting\",\n] as const;\n\nconst categoryColors: Record<string, string> = {\n productive: \"bg-status-success-bg text-status-success-text\",\n neutral: \"bg-bg-sunken text-text-secondary\",\n communication: \"bg-status-info-bg text-status-info-text\",\n distracting: \"bg-status-error-bg text-status-error-text\",\n};\n\nconst inputClasses =\n \"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\";\nconst labelClasses = \"block text-sm font-medium text-text-primary\";\n\nfunction todayIso(): string {\n return new Date().toISOString();\n}\n\nexport function ApplicationUsagePage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const {\n data: usagesData,\n loading: usagesLoading,\n error: usagesError,\n fetchMore,\n refetch: refetchUsages,\n } = useListApplicationUsagesQuery({\n variables: { limit: PAGE_SIZE, offset: 0 },\n skip: !workspaceId,\n notifyOnNetworkStatusChange: true,\n });\n\n const {\n data: summaryData,\n loading: summaryLoading,\n error: summaryError,\n refetch: refetchSummary,\n } = useGetAppUsageSummaryQuery({\n skip: !workspaceId,\n });\n\n const loading = usagesLoading || summaryLoading;\n const error = usagesError || summaryError;\n const apps = usagesData?.applicationUsages ?? [];\n const summary = summaryData?.appUsageSummary;\n const hasMore = apps.length > 0 && apps.length % PAGE_SIZE === 0;\n\n const [loadingMore, setLoadingMore] = useState(false);\n\n // Add-usage modal\n const [addOpen, setAddOpen] = useState(false);\n const [appName, setAppName] = useState(\"\");\n const [appCategory, setAppCategory] = useState<string>(\"productive\");\n const [appMinutes, setAppMinutes] = useState(\"\");\n const [appSessions, setAppSessions] = useState(\"\");\n\n // Recategorize + delete\n const [recatApp, setRecatApp] =\n useState<ApplicationUsageFieldsFragment | null>(null);\n const [recatCategory, setRecatCategory] = useState<string>(\"productive\");\n const [deleteApp, setDeleteApp] =\n useState<ApplicationUsageFieldsFragment | null>(null);\n\n const [recordUsage, { loading: recording, error: recordError }] =\n useRecordApplicationUsageMutation();\n const [categorize, { loading: categorizing, error: categorizeError }] =\n useCategorizeApplicationMutation();\n const [removeUsage, { loading: deleting, error: deleteError }] =\n useDeleteApplicationUsageMutation();\n\n const refreshAll = async () => {\n await Promise.all([refetchUsages(), refetchSummary()]);\n };\n\n const openAdd = () => {\n setAppName(\"\");\n setAppCategory(\"productive\");\n setAppMinutes(\"\");\n setAppSessions(\"\");\n setAddOpen(true);\n };\n\n const handleRecord = async () => {\n if (!appName.trim() || !appMinutes) return;\n await recordUsage({\n variables: {\n input: {\n applicationName: appName.trim(),\n category: appCategory,\n timeSpent: Math.round(Number(appMinutes) * 60),\n sessions: appSessions ? Number(appSessions) : undefined,\n date: todayIso(),\n },\n },\n });\n setAddOpen(false);\n await refreshAll();\n };\n\n const openRecat = (app: ApplicationUsageFieldsFragment) => {\n setRecatApp(app);\n setRecatCategory(app.category);\n };\n\n const handleRecategorize = async () => {\n if (!recatApp) return;\n await categorize({\n variables: {\n applicationName: recatApp.applicationName,\n category: recatCategory,\n },\n });\n setRecatApp(null);\n await refreshAll();\n };\n\n const handleDelete = async () => {\n if (!deleteApp) return;\n await removeUsage({ variables: { id: deleteApp.id } });\n setDeleteApp(null);\n await refreshAll();\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { offset: apps.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n applicationUsages: [\n ...prev.applicationUsages,\n ...fetchMoreResult.applicationUsages,\n ],\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const totalScreenTimeSeconds = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.timeSpent,\n 0,\n );\n const totalSwitches = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.switches,\n 0,\n );\n const productiveRatio =\n summary?.productivityRatio != null\n ? `${Math.round(summary.productivityRatio * 100)}%`\n : \"--\";\n\n // Count focus sessions (apps with > 90 min continuous usage)\n const focusSessions = apps.filter(\n (app: ApplicationUsageFieldsFragment) => app.timeSpent >= 5400,\n ).length;\n\n if (loading) {\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\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 application usage. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"App Usage\"\n description=\"Track application usage and productivity\"\n purpose=\"See where your screen time actually goes — which apps are productive, how often you context-switch, and how many real focus sessions you get — so you can cut the digital distractions eating your day.\"\n actions={\n <button\n onClick={openAdd}\n 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 >\n <Plus className=\"h-4 w-4\" />\n Add Usage\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 Screen Time\"\n value={formatHours(totalScreenTimeSeconds / 3600)}\n icon={<Monitor className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Productive Time\"\n value={productiveRatio}\n trend={\n summary?.productivityRatio != null\n ? { value: 5, label: \"vs last week\" }\n : undefined\n }\n />\n <StatCard\n title=\"App Switches\"\n value={String(totalSwitches)}\n description={`Avg. ${apps.length > 0 ? Math.round(totalSwitches / apps.length) : 0}/app`}\n />\n <StatCard\n title=\"Focus Sessions\"\n value={String(focusSessions)}\n icon={<Clock className=\"h-5 w-5\" />}\n description=\"90+ min each\"\n />\n </div>\n\n {apps.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No application usage data found\"\n description=\"Record app usage manually or connect the desktop tracker, and your apps will appear here ranked by time spent.\"\n action={\n <button\n onClick={openAdd}\n 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 >\n <Plus className=\"h-4 w-4\" />\n Add Usage\n </button>\n }\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 {apps.map((app: ApplicationUsageFieldsFragment) => (\n <div\n key={app.id}\n className=\"flex items-center justify-between gap-3 p-4 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <Monitor className=\"h-5 w-5 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {app.applicationName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {app.sessions} sessions\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-3 sm:gap-4\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours(app.timeSpent / 3600)}\n </p>\n <span\n className={`hidden rounded-full px-2 py-0.5 text-xs font-medium sm:inline-flex ${categoryColors[app.category] || \"bg-bg-sunken text-text-secondary\"}`}\n >\n {app.category}\n </span>\n <div className=\"hidden w-16 lg:block\">\n <div className=\"h-2 rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${Math.min(100, Math.round((app.timeSpent / (totalScreenTimeSeconds || 1)) * 100))}%`,\n }}\n />\n </div>\n </div>\n <button\n onClick={() => openRecat(app)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Recategorize application\"\n >\n <Tag className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteApp(app)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text\"\n aria-label=\"Delete usage entry\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </div>\n {hasMore && (\n <div className=\"mt-4 flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Add usage modal */}\n <Modal\n open={addOpen}\n onClose={() => setAddOpen(false)}\n title=\"Record App Usage\"\n description=\"Manually log time spent in an application\"\n footer={\n <>\n <button\n onClick={() => setAddOpen(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={handleRecord}\n disabled={recording || !appName.trim() || !appMinutes}\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 {recording ? \"Saving...\" : \"Record\"}\n </button>\n </>\n }\n >\n {recordError && (\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 record usage. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className={labelClasses}>Application Name</label>\n <input\n type=\"text\"\n placeholder=\"e.g. Visual Studio Code\"\n value={appName}\n onChange={(e) => setAppName(e.target.value)}\n className={inputClasses}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClasses}>Category</label>\n <select\n value={appCategory}\n onChange={(e) => setAppCategory(e.target.value)}\n className={inputClasses}\n >\n {CATEGORY_OPTIONS.map((c) => (\n <option key={c} value={c} className=\"capitalize\">\n {c}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClasses}>Time Spent (minutes)</label>\n <input\n type=\"number\"\n min=\"0\"\n placeholder=\"0\"\n value={appMinutes}\n onChange={(e) => setAppMinutes(e.target.value)}\n className={inputClasses}\n />\n </div>\n </div>\n <div>\n <label className={labelClasses}>Sessions</label>\n <input\n type=\"number\"\n min=\"0\"\n placeholder=\"0\"\n value={appSessions}\n onChange={(e) => setAppSessions(e.target.value)}\n className={inputClasses}\n />\n </div>\n </div>\n </Modal>\n\n {/* Recategorize modal */}\n <Modal\n open={recatApp !== null}\n onClose={() => setRecatApp(null)}\n title=\"Recategorize Application\"\n description={recatApp?.applicationName}\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setRecatApp(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={handleRecategorize}\n disabled={categorizing}\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 {categorizing ? \"Saving...\" : \"Save\"}\n </button>\n </>\n }\n >\n {categorizeError && (\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 recategorize. Please try again.\n </div>\n )}\n <label className={labelClasses}>Category</label>\n <select\n value={recatCategory}\n onChange={(e) => setRecatCategory(e.target.value)}\n className={inputClasses}\n >\n {CATEGORY_OPTIONS.map((c) => (\n <option key={c} value={c} className=\"capitalize\">\n {c}\n </option>\n ))}\n </select>\n <p className=\"mt-3 text-xs text-text-secondary\">\n This updates the category for all usage of &ldquo;\n {recatApp?.applicationName}&rdquo;.\n </p>\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteApp !== null}\n onClose={() => setDeleteApp(null)}\n title=\"Delete Usage Entry\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteApp(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={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteError && (\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 delete entry. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete the usage entry for{\" \"}\n <span className=\"font-medium text-text-primary\">\n {deleteApp?.applicationName}\n </span>\n ? This action cannot be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}