@burdenoff/microfe-timecampus 2026.825.2 → 2026.827.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationUsagePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsagePage.tsx"],"names":[],"mappings":"AA2EA,wBAAgB,oBAAoB,gCAyjBnC"}
1
+ {"version":3,"file":"ApplicationUsagePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsagePage.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,oBAAoB,gCAwkBnC"}
@@ -1,28 +1,28 @@
1
1
  import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
- import { formatHours as t } from "../../utils/helpers.js";
3
- import { StatCard as n } from "../../components/StatCard.js";
4
- import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
5
- import { useCategorizeApplicationMutation as ee, useDeleteApplicationUsageMutation as i, useGetAppUsageSummaryQuery as te, useListApplicationUsagesQuery as ne, useRecordApplicationUsageMutation as re } from "../../../generated/wspace-operations.js";
2
+ import { formatHours as t, formatTime as n } from "../../utils/helpers.js";
3
+ import { StatCard as r } from "../../components/StatCard.js";
4
+ import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
5
+ import { useCategorizeApplicationMutation as ee, useDeleteApplicationUsageMutation as te, useGetAppUsageSummaryQuery as ne, useListApplicationUsagesQuery as re, useRecordApplicationUsageMutation as ie } from "../../../generated/wspace-operations.js";
6
6
  import { PageLayout as a } from "../../components/PageLayout.js";
7
- import { StatusBadge as ie } from "../../components/StatusBadge.js";
7
+ import { StatusBadge as ae } from "../../components/StatusBadge.js";
8
8
  import { EmptyState as o } from "../../components/EmptyState.js";
9
9
  import { Modal as s } from "../../components/Modal.js";
10
10
  import { ChartCard as c } from "../../components/ChartCard.js";
11
11
  import { chartTokens as l } from "../../components/charts/chartTheme.js";
12
- import { TrendChart as ae } from "../../components/charts/TrendChart.js";
13
- import { DonutChart as oe } from "../../components/charts/DonutChart.js";
12
+ import { TrendChart as oe } from "../../components/charts/TrendChart.js";
13
+ import { DonutChart as se } from "../../components/charts/DonutChart.js";
14
14
  import "../../components/index.js";
15
15
  import { useState as u } from "react";
16
- import { Clock as se, Monitor as d, Plus as f, Tag as p, Trash2 as m } from "lucide-react";
16
+ import { Clock as ce, Monitor as d, Plus as f, Tag as p, Trash2 as m } from "lucide-react";
17
17
  import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
18
- import { GlassCard as ce } from "@burdenoff/fe-libs/ui";
18
+ import { GlassCard as le } from "@burdenoff/fe-libs/ui";
19
19
  //#region src/timecampus/pages/application-usage/ApplicationUsagePage.tsx
20
20
  var v = 50, y = [
21
21
  "productive",
22
22
  "neutral",
23
23
  "communication",
24
24
  "distracting"
25
- ], le = {
25
+ ], ue = {
26
26
  productive: "success",
27
27
  neutral: "muted",
28
28
  communication: "info",
@@ -31,49 +31,52 @@ var v = 50, y = [
31
31
  productive: l.positive,
32
32
  neutral: l.series[3],
33
33
  distracting: l.negative
34
- }, ue = (e) => `${e.toFixed(e >= 10 ? 0 : 1)}h`, de = (e) => t(e / 3600), fe = (e) => {
34
+ }, de = (e) => `${e.toFixed(e >= 10 ? 0 : 1)}h`, fe = (e) => t(e / 3600), pe = (e) => {
35
35
  let t = new Date(String(e));
36
36
  return Number.isNaN(t.getTime()) ? String(e) : t.toLocaleDateString("en-US", {
37
37
  month: "short",
38
38
  day: "numeric"
39
39
  });
40
40
  }, x = "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", S = "block text-sm font-medium text-text-primary";
41
- function pe() {
41
+ function me() {
42
42
  return (/* @__PURE__ */ new Date()).toISOString();
43
43
  }
44
+ function he(e, t) {
45
+ return !e || !t ? null : `${n(e)} – ${n(t)}`;
46
+ }
44
47
  function C() {
45
- let { workspaceId: l = "" } = e(), { data: C, loading: me, error: he, fetchMore: ge, refetch: _e } = ne({
48
+ let { workspaceId: n = "" } = e(), { data: l, loading: C, error: ge, fetchMore: _e, refetch: ve } = re({
46
49
  variables: { pagination: {
47
50
  limit: v,
48
51
  offset: 0
49
52
  } },
50
- skip: !l,
53
+ skip: !n,
51
54
  notifyOnNetworkStatusChange: !0
52
- }), { data: ve, loading: ye, error: be, refetch: xe } = te({ skip: !l }), Se = me || ye, Ce = he || be, w = C?.applicationUsages ?? [], T = ve?.appUsageSummary, E = w.length > 0 && w.length % v === 0, [D, O] = u(!1), [k, A] = u(!1), [j, M] = u(""), [N, P] = u("productive"), [F, I] = u(""), [L, R] = u(""), [z, B] = u(null), [V, H] = u("productive"), [U, W] = u(null), [we, { loading: G, error: Te }] = re(), [Ee, { loading: K, error: De }] = ee(), [Oe, { loading: q, error: ke }] = i(), J = async () => {
53
- await Promise.all([_e(), xe()]);
55
+ }), { data: ye, loading: be, error: xe, refetch: Se } = ne({ skip: !n }), Ce = C || be, we = ge || xe, w = l?.applicationUsages ?? [], T = ye?.appUsageSummary, E = w.length > 0 && w.length % v === 0, [D, O] = u(!1), [k, A] = u(!1), [j, M] = u(""), [N, P] = u("productive"), [F, I] = u(""), [L, R] = u(""), [z, B] = u(null), [V, H] = u("productive"), [U, W] = u(null), [Te, { loading: G, error: Ee }] = ie(), [De, { loading: K, error: Oe }] = ee(), [ke, { loading: q, error: Ae }] = te(), J = async () => {
56
+ await Promise.all([ve(), Se()]);
54
57
  }, Y = () => {
55
58
  M(""), P("productive"), I(""), R(""), A(!0);
56
- }, Ae = async () => {
57
- !j.trim() || !F || (await we({ variables: { input: {
59
+ }, je = async () => {
60
+ !j.trim() || !F || (await Te({ variables: { input: {
58
61
  applicationName: j.trim(),
59
62
  category: N,
60
63
  timeSpent: Math.round(Number(F) * 60),
61
64
  sessions: L ? Number(L) : void 0,
62
- date: pe()
65
+ date: me()
63
66
  } } }), A(!1), await J());
64
- }, X = (e) => {
67
+ }, Me = (e) => {
65
68
  B(e), H(e.category);
66
- }, je = async () => {
67
- z && (await Ee({ variables: {
69
+ }, Ne = async () => {
70
+ z && (await De({ variables: {
68
71
  applicationName: z.applicationName,
69
72
  category: V
70
73
  } }), B(null), await J());
71
- }, Me = async () => {
72
- U && (await Oe({ variables: { id: U.id } }), W(null), await J());
73
- }, Ne = async () => {
74
+ }, X = async () => {
75
+ U && (await ke({ variables: { id: U.id } }), W(null), await J());
76
+ }, Pe = async () => {
74
77
  O(!0);
75
78
  try {
76
- await ge({
79
+ await _e({
77
80
  variables: { pagination: {
78
81
  limit: v,
79
82
  offset: w.length
@@ -86,7 +89,7 @@ function C() {
86
89
  } finally {
87
90
  O(!1);
88
91
  }
89
- }, Z = w.reduce((e, t) => e + t.timeSpent, 0), Q = w.reduce((e, t) => e + t.switches, 0), Pe = T?.productivityRatio == null ? "--" : `${Math.round(T.productivityRatio * 100)}%`, Fe = w.filter((e) => e.timeSpent >= 5400).length, Ie = [...w].sort((e, t) => t.timeSpent - e.timeSpent), Le = T ? [
92
+ }, Z = w.reduce((e, t) => e + t.timeSpent, 0), Q = w.reduce((e, t) => e + t.switches, 0), Fe = T?.productivityRatio == null ? "--" : `${Math.round(T.productivityRatio * 100)}%`, Ie = w.filter((e) => e.timeSpent >= 5400).length, Le = [...w].sort((e, t) => t.timeSpent - e.timeSpent), Re = T ? [
90
93
  {
91
94
  name: "Productive",
92
95
  value: T.productiveTime,
@@ -102,23 +105,23 @@ function C() {
102
105
  value: T.distractingTime,
103
106
  color: b.distracting
104
107
  }
105
- ] : [], Re = (T?.productiveTime ?? 0) + (T?.neutralTime ?? 0) + (T?.distractingTime ?? 0), $ = /* @__PURE__ */ new Map();
108
+ ] : [], ze = (T?.productiveTime ?? 0) + (T?.neutralTime ?? 0) + (T?.distractingTime ?? 0), $ = /* @__PURE__ */ new Map();
106
109
  for (let e of w) {
107
110
  let t = (e.date ?? "").slice(0, 10);
108
111
  t && $.set(t, ($.get(t) ?? 0) + e.timeSpent);
109
112
  }
110
- let ze = Array.from($.entries()).sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => ({
113
+ let Be = Array.from($.entries()).sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => ({
111
114
  day: e,
112
115
  hours: Math.round(t / 3600 * 100) / 100
113
116
  }));
114
- return Se ? /* @__PURE__ */ g(a, {
117
+ return Ce ? /* @__PURE__ */ g(a, {
115
118
  title: "App Usage",
116
119
  description: "Track application usage and productivity",
117
- children: /* @__PURE__ */ g(r, {
120
+ children: /* @__PURE__ */ g(i, {
118
121
  variant: "card",
119
122
  count: 4
120
123
  })
121
- }) : Ce ? /* @__PURE__ */ g(a, {
124
+ }) : we ? /* @__PURE__ */ g(a, {
122
125
  title: "App Usage",
123
126
  description: "Track application usage and productivity",
124
127
  children: /* @__PURE__ */ g("div", {
@@ -140,26 +143,26 @@ function C() {
140
143
  children: [/* @__PURE__ */ _("div", {
141
144
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
142
145
  children: [
143
- /* @__PURE__ */ g(n, {
146
+ /* @__PURE__ */ g(r, {
144
147
  featured: !0,
145
148
  title: "Total Screen Time",
146
149
  value: t(Z / 3600),
147
150
  icon: /* @__PURE__ */ g(d, { className: "h-5 w-5" })
148
151
  }),
149
- /* @__PURE__ */ g(n, {
152
+ /* @__PURE__ */ g(r, {
150
153
  title: "Productive Time",
151
- value: Pe,
154
+ value: Fe,
152
155
  description: "Share of tracked time"
153
156
  }),
154
- /* @__PURE__ */ g(n, {
157
+ /* @__PURE__ */ g(r, {
155
158
  title: "App Switches",
156
159
  value: String(Q),
157
160
  description: `Avg. ${w.length > 0 ? Math.round(Q / w.length) : 0}/app`
158
161
  }),
159
- /* @__PURE__ */ g(n, {
162
+ /* @__PURE__ */ g(r, {
160
163
  title: "Focus Sessions",
161
- value: String(Fe),
162
- icon: /* @__PURE__ */ g(se, { className: "h-5 w-5" }),
164
+ value: String(Ie),
165
+ icon: /* @__PURE__ */ g(ce, { className: "h-5 w-5" }),
163
166
  description: "90+ min each"
164
167
  })
165
168
  ]
@@ -173,7 +176,7 @@ function C() {
173
176
  children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), "Add Usage"]
174
177
  })
175
178
  }) : /* @__PURE__ */ _(h, { children: [
176
- /* @__PURE__ */ _(ce, {
179
+ /* @__PURE__ */ _(le, {
177
180
  treatment: "glass",
178
181
  className: "overflow-hidden",
179
182
  children: [/* @__PURE__ */ _("div", {
@@ -181,14 +184,14 @@ function C() {
181
184
  children: [/* @__PURE__ */ g("h3", {
182
185
  className: "text-sm font-semibold text-text-primary",
183
186
  children: "Ranked Applications"
184
- }), /* @__PURE__ */ _("span", {
187
+ }), /* @__PURE__ */ g("span", {
185
188
  className: "text-xs text-text-secondary",
186
- children: [w.length, " tracked"]
189
+ children: T?.totalApps == null ? `${w.length} tracked` : `${w.length} of ${T.totalApps} app${T.totalApps === 1 ? "" : "s"} tracked`
187
190
  })]
188
191
  }), /* @__PURE__ */ g("div", {
189
192
  className: "divide-y divide-border",
190
- children: Ie.map((e, n) => {
191
- let r = Math.min(100, Math.round(e.timeSpent / (Z || 1) * 100));
193
+ children: Le.map((e, n) => {
194
+ let r = Math.min(100, Math.round(e.timeSpent / (Z || 1) * 100)), i = he(e.firstUsed, e.lastUsed);
192
195
  return /* @__PURE__ */ _("div", {
193
196
  className: "flex items-center gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:gap-4",
194
197
  children: [
@@ -212,7 +215,16 @@ function C() {
212
215
  " sessions · ",
213
216
  e.switches,
214
217
  " ",
215
- "switches"
218
+ "switches",
219
+ i !== null && /* @__PURE__ */ _(h, { children: [
220
+ " ",
221
+ "·",
222
+ " ",
223
+ /* @__PURE__ */ g("span", {
224
+ className: "tabular-nums",
225
+ children: i
226
+ })
227
+ ] })
216
228
  ]
217
229
  })]
218
230
  }),
@@ -229,9 +241,9 @@ function C() {
229
241
  children: [r, "%"]
230
242
  })]
231
243
  }),
232
- /* @__PURE__ */ g(ie, {
244
+ /* @__PURE__ */ g(ae, {
233
245
  label: e.category,
234
- variant: le[e.category] ?? "muted",
246
+ variant: ue[e.category] ?? "muted",
235
247
  className: "hidden capitalize sm:inline-flex"
236
248
  }),
237
249
  /* @__PURE__ */ g("p", {
@@ -241,7 +253,7 @@ function C() {
241
253
  /* @__PURE__ */ _("div", {
242
254
  className: "flex shrink-0 items-center gap-1",
243
255
  children: [/* @__PURE__ */ g("button", {
244
- onClick: () => X(e),
256
+ onClick: () => Me(e),
245
257
  className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
246
258
  "aria-label": "Recategorize application",
247
259
  children: /* @__PURE__ */ g(p, { className: "h-4 w-4" })
@@ -262,27 +274,27 @@ function C() {
262
274
  children: [/* @__PURE__ */ g(c, {
263
275
  title: "Productivity Split",
264
276
  description: "Where your tracked screen time goes",
265
- children: /* @__PURE__ */ g(oe, {
266
- data: Le,
277
+ children: /* @__PURE__ */ g(se, {
278
+ data: Re,
267
279
  centerLabel: "Total tracked",
268
- centerValue: Re,
269
- valueFormat: de,
280
+ centerValue: ze,
281
+ valueFormat: fe,
270
282
  height: 260,
271
283
  emptyLabel: "No categorized usage yet"
272
284
  })
273
285
  }), /* @__PURE__ */ g(c, {
274
286
  title: "Daily Usage Trend",
275
287
  description: "Tracked time per day",
276
- children: /* @__PURE__ */ g(ae, {
277
- data: ze,
288
+ children: /* @__PURE__ */ g(oe, {
289
+ data: Be,
278
290
  xKey: "day",
279
291
  series: [{
280
292
  key: "hours",
281
293
  label: "Screen time"
282
294
  }],
283
295
  type: "area",
284
- valueFormat: ue,
285
- xTickFormat: fe,
296
+ valueFormat: de,
297
+ xTickFormat: pe,
286
298
  height: 260,
287
299
  emptyLabel: "No usage recorded yet"
288
300
  })
@@ -291,7 +303,7 @@ function C() {
291
303
  E && /* @__PURE__ */ g("div", {
292
304
  className: "mt-4 flex justify-center",
293
305
  children: /* @__PURE__ */ g("button", {
294
- onClick: Ne,
306
+ onClick: Pe,
295
307
  disabled: D,
296
308
  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",
297
309
  children: D ? "Loading..." : "Load more"
@@ -309,12 +321,12 @@ function C() {
309
321
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
310
322
  children: "Cancel"
311
323
  }), /* @__PURE__ */ g("button", {
312
- onClick: Ae,
324
+ onClick: je,
313
325
  disabled: G || !j.trim() || !F,
314
326
  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",
315
327
  children: G ? "Saving..." : "Record"
316
328
  })] }),
317
- children: [Te && /* @__PURE__ */ g("div", {
329
+ children: [Ee && /* @__PURE__ */ g("div", {
318
330
  className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
319
331
  children: "Failed to record usage. Please try again."
320
332
  }), /* @__PURE__ */ _("div", {
@@ -381,13 +393,13 @@ function C() {
381
393
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
382
394
  children: "Cancel"
383
395
  }), /* @__PURE__ */ g("button", {
384
- onClick: je,
396
+ onClick: Ne,
385
397
  disabled: K,
386
398
  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",
387
399
  children: K ? "Saving..." : "Save"
388
400
  })] }),
389
401
  children: [
390
- De && /* @__PURE__ */ g("div", {
402
+ Oe && /* @__PURE__ */ g("div", {
391
403
  className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
392
404
  children: "Failed to recategorize. Please try again."
393
405
  }),
@@ -425,12 +437,12 @@ function C() {
425
437
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
426
438
  children: "Cancel"
427
439
  }), /* @__PURE__ */ g("button", {
428
- onClick: Me,
440
+ onClick: X,
429
441
  disabled: q,
430
442
  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",
431
443
  children: q ? "Deleting..." : "Delete"
432
444
  })] }),
433
- children: [ke && /* @__PURE__ */ g("div", {
445
+ children: [Ae && /* @__PURE__ */ g("div", {
434
446
  className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
435
447
  children: "Failed to delete entry. Please try again."
436
448
  }), /* @__PURE__ */ _("p", {
@@ -1 +1 @@
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 { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n ChartCard,\n DonutChart,\n TrendChart,\n chartTokens,\n type ChartDatum,\n type DonutSlice,\n type ValueFormatter,\n} from \"../../components\";\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 type ListApplicationUsagesQuery,\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 CATEGORY_VARIANT: Record<string, \"success\" | \"muted\" | \"info\" | \"error\"> =\n {\n productive: \"success\",\n neutral: \"muted\",\n communication: \"info\",\n distracting: \"error\",\n };\n\n/** Category ring hues — token-only, echoing productive→positive / distracting→negative. */\nconst CATEGORY_SLICE_COLOR: Record<string, string> = {\n productive: chartTokens.positive,\n neutral: chartTokens.series[3],\n distracting: chartTokens.negative,\n};\n\nconst formatChartHours: ValueFormatter = (value) =>\n `${value.toFixed(value >= 10 ? 0 : 1)}h`;\n\nconst formatSecondsAsHours: ValueFormatter = (value) =>\n formatHours(value / 3600);\n\nconst formatDayTick = (value: string | number): string => {\n const parsed = new Date(String(value));\n return Number.isNaN(parsed.getTime())\n ? String(value)\n : parsed.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\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: { pagination: { 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: { pagination: { limit: PAGE_SIZE, offset: apps.length } },\n updateQuery: (\n prev: ListApplicationUsagesQuery,\n { fetchMoreResult }: { fetchMoreResult?: ListApplicationUsagesQuery },\n ) => {\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 // Rank the loaded apps by time spent (drives the numbered list).\n const rankedApps = [...apps].sort(\n (a: ApplicationUsageFieldsFragment, b: ApplicationUsageFieldsFragment) =>\n b.timeSpent - a.timeSpent,\n );\n\n // Productive / neutral / distracting split from the real usage summary.\n const categorySlices: DonutSlice[] = summary\n ? [\n {\n name: \"Productive\",\n value: summary.productiveTime,\n color: CATEGORY_SLICE_COLOR.productive,\n },\n {\n name: \"Neutral\",\n value: summary.neutralTime,\n color: CATEGORY_SLICE_COLOR.neutral,\n },\n {\n name: \"Distracting\",\n value: summary.distractingTime,\n color: CATEGORY_SLICE_COLOR.distracting,\n },\n ]\n : [];\n const categoryTotalSeconds =\n (summary?.productiveTime ?? 0) +\n (summary?.neutralTime ?? 0) +\n (summary?.distractingTime ?? 0);\n\n // Daily usage trend — sum the loaded apps' tracked time per calendar day.\n const usageByDay = new Map<string, number>();\n for (const app of apps as ApplicationUsageFieldsFragment[]) {\n const day = (app.date ?? \"\").slice(0, 10);\n if (!day) continue;\n usageByDay.set(day, (usageByDay.get(day) ?? 0) + app.timeSpent);\n }\n const usageTrend: ChartDatum[] = Array.from(usageByDay.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([day, seconds]) => ({\n day,\n hours: Math.round((seconds / 3600) * 100) / 100,\n }));\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=\"space-y-6\">\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 description=\"Share of tracked time\"\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 <GlassCard treatment=\"glass\" className=\"overflow-hidden\">\n <div className=\"flex items-center justify-between gap-3 border-b border-border-seam px-4 py-3 sm:px-6\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Ranked Applications\n </h3>\n <span className=\"text-xs text-text-secondary\">\n {apps.length} tracked\n </span>\n </div>\n <div className=\"divide-y divide-border\">\n {rankedApps.map(\n (app: ApplicationUsageFieldsFragment, index: number) => {\n const share = Math.min(\n 100,\n Math.round(\n (app.timeSpent / (totalScreenTimeSeconds || 1)) * 100,\n ),\n );\n return (\n <div\n key={app.id}\n className=\"flex items-center gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:gap-4\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold tabular-nums text-text-secondary\">\n {index + 1}\n </span>\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Monitor className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\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 &middot; {app.switches}{\" \"}\n switches\n </p>\n </div>\n <div className=\"hidden w-28 shrink-0 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={{ width: `${share}%` }}\n />\n </div>\n <p className=\"mt-1 text-right text-xs tabular-nums text-text-secondary\">\n {share}%\n </p>\n </div>\n <StatusBadge\n label={app.category}\n variant={CATEGORY_VARIANT[app.category] ?? \"muted\"}\n className=\"hidden capitalize sm:inline-flex\"\n />\n <p className=\"shrink-0 font-mono text-sm font-medium tabular-nums text-text-primary\">\n {formatHours(app.timeSpent / 3600)}\n </p>\n <div className=\"flex shrink-0 items-center gap-1\">\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 },\n )}\n </div>\n </GlassCard>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <ChartCard\n title=\"Productivity Split\"\n description=\"Where your tracked screen time goes\"\n >\n <DonutChart\n data={categorySlices}\n centerLabel=\"Total tracked\"\n centerValue={categoryTotalSeconds}\n valueFormat={formatSecondsAsHours}\n height={260}\n emptyLabel=\"No categorized usage yet\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Daily Usage Trend\"\n description=\"Tracked time per day\"\n >\n <TrendChart\n data={usageTrend}\n xKey=\"day\"\n series={[{ key: \"hours\", label: \"Screen time\" }]}\n type=\"area\"\n valueFormat={formatChartHours}\n xTickFormat={formatDayTick}\n height={260}\n emptyLabel=\"No usage recorded yet\"\n />\n </ChartCard>\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 </div>\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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"}
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 { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n ChartCard,\n DonutChart,\n TrendChart,\n chartTokens,\n type ChartDatum,\n type DonutSlice,\n type ValueFormatter,\n} from \"../../components\";\nimport { formatHours, formatTime } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListApplicationUsagesQuery,\n useGetAppUsageSummaryQuery,\n useRecordApplicationUsageMutation,\n useCategorizeApplicationMutation,\n useDeleteApplicationUsageMutation,\n type ApplicationUsageFieldsFragment,\n type ListApplicationUsagesQuery,\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 CATEGORY_VARIANT: Record<string, \"success\" | \"muted\" | \"info\" | \"error\"> =\n {\n productive: \"success\",\n neutral: \"muted\",\n communication: \"info\",\n distracting: \"error\",\n };\n\n/** Category ring hues — token-only, echoing productive→positive / distracting→negative. */\nconst CATEGORY_SLICE_COLOR: Record<string, string> = {\n productive: chartTokens.positive,\n neutral: chartTokens.series[3],\n distracting: chartTokens.negative,\n};\n\nconst formatChartHours: ValueFormatter = (value) =>\n `${value.toFixed(value >= 10 ? 0 : 1)}h`;\n\nconst formatSecondsAsHours: ValueFormatter = (value) =>\n formatHours(value / 3600);\n\nconst formatDayTick = (value: string | number): string => {\n const parsed = new Date(String(value));\n return Number.isNaN(parsed.getTime())\n ? String(value)\n : parsed.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\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\n/**\n * The window an app was actually in use for, when the capturing client\n * recorded one. Both timestamps are optional on `ApplicationUsage`, so a\n * half-known window is shown as nothing rather than as a guessed bound.\n */\nfunction usageWindow(\n firstUsed: string | null | undefined,\n lastUsed: string | null | undefined,\n): string | null {\n if (!firstUsed || !lastUsed) return null;\n return `${formatTime(firstUsed)} – ${formatTime(lastUsed)}`;\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: { pagination: { 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: { pagination: { limit: PAGE_SIZE, offset: apps.length } },\n updateQuery: (\n prev: ListApplicationUsagesQuery,\n { fetchMoreResult }: { fetchMoreResult?: ListApplicationUsagesQuery },\n ) => {\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 // Rank the loaded apps by time spent (drives the numbered list).\n const rankedApps = [...apps].sort(\n (a: ApplicationUsageFieldsFragment, b: ApplicationUsageFieldsFragment) =>\n b.timeSpent - a.timeSpent,\n );\n\n // Productive / neutral / distracting split from the real usage summary.\n const categorySlices: DonutSlice[] = summary\n ? [\n {\n name: \"Productive\",\n value: summary.productiveTime,\n color: CATEGORY_SLICE_COLOR.productive,\n },\n {\n name: \"Neutral\",\n value: summary.neutralTime,\n color: CATEGORY_SLICE_COLOR.neutral,\n },\n {\n name: \"Distracting\",\n value: summary.distractingTime,\n color: CATEGORY_SLICE_COLOR.distracting,\n },\n ]\n : [];\n const categoryTotalSeconds =\n (summary?.productiveTime ?? 0) +\n (summary?.neutralTime ?? 0) +\n (summary?.distractingTime ?? 0);\n\n // Daily usage trend — sum the loaded apps' tracked time per calendar day.\n const usageByDay = new Map<string, number>();\n for (const app of apps as ApplicationUsageFieldsFragment[]) {\n const day = (app.date ?? \"\").slice(0, 10);\n if (!day) continue;\n usageByDay.set(day, (usageByDay.get(day) ?? 0) + app.timeSpent);\n }\n const usageTrend: ChartDatum[] = Array.from(usageByDay.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([day, seconds]) => ({\n day,\n hours: Math.round((seconds / 3600) * 100) / 100,\n }));\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=\"space-y-6\">\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 description=\"Share of tracked time\"\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 <GlassCard treatment=\"glass\" className=\"overflow-hidden\">\n <div className=\"flex items-center justify-between gap-3 border-b border-border-seam px-4 py-3 sm:px-6\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Ranked Applications\n </h3>\n <span className=\"text-xs text-text-secondary\">\n {summary?.totalApps != null\n ? `${apps.length} of ${summary.totalApps} app${summary.totalApps === 1 ? \"\" : \"s\"} tracked`\n : `${apps.length} tracked`}\n </span>\n </div>\n <div className=\"divide-y divide-border\">\n {rankedApps.map(\n (app: ApplicationUsageFieldsFragment, index: number) => {\n const share = Math.min(\n 100,\n Math.round(\n (app.timeSpent / (totalScreenTimeSeconds || 1)) * 100,\n ),\n );\n const activeWindow = usageWindow(\n app.firstUsed,\n app.lastUsed,\n );\n return (\n <div\n key={app.id}\n className=\"flex items-center gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:gap-4\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold tabular-nums text-text-secondary\">\n {index + 1}\n </span>\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Monitor className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\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 &middot; {app.switches}{\" \"}\n switches\n {activeWindow !== null && (\n <>\n {\" \"}\n &middot;{\" \"}\n <span className=\"tabular-nums\">\n {activeWindow}\n </span>\n </>\n )}\n </p>\n </div>\n <div className=\"hidden w-28 shrink-0 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={{ width: `${share}%` }}\n />\n </div>\n <p className=\"mt-1 text-right text-xs tabular-nums text-text-secondary\">\n {share}%\n </p>\n </div>\n <StatusBadge\n label={app.category}\n variant={CATEGORY_VARIANT[app.category] ?? \"muted\"}\n className=\"hidden capitalize sm:inline-flex\"\n />\n <p className=\"shrink-0 font-mono text-sm font-medium tabular-nums text-text-primary\">\n {formatHours(app.timeSpent / 3600)}\n </p>\n <div className=\"flex shrink-0 items-center gap-1\">\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 },\n )}\n </div>\n </GlassCard>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <ChartCard\n title=\"Productivity Split\"\n description=\"Where your tracked screen time goes\"\n >\n <DonutChart\n data={categorySlices}\n centerLabel=\"Total tracked\"\n centerValue={categoryTotalSeconds}\n valueFormat={formatSecondsAsHours}\n height={260}\n emptyLabel=\"No categorized usage yet\"\n />\n </ChartCard>\n\n <ChartCard\n title=\"Daily Usage Trend\"\n description=\"Tracked time per day\"\n >\n <TrendChart\n data={usageTrend}\n xKey=\"day\"\n series={[{ key: \"hours\", label: \"Screen time\" }]}\n type=\"area\"\n valueFormat={formatChartHours}\n xTickFormat={formatDayTick}\n height={260}\n emptyLabel=\"No usage recorded yet\"\n />\n </ChartCard>\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 </div>\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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/dashboard/AnalyticsPage.tsx"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,gCAoT5B"}
1
+ {"version":3,"file":"AnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/dashboard/AnalyticsPage.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,aAAa,gCAsW5B"}