@burdenoff/microfe-timecampus 2026.825.2 → 2026.828.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.
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.d.ts.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +75 -63
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.d.ts.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +130 -104
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/RecommendationsPage.d.ts.map +1 -1
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js +567 -222
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/recommendationEvidence.d.ts +55 -0
- package/dist/timecampus/pages/lifetime/recommendationEvidence.d.ts.map +1 -0
- package/dist/timecampus/pages/lifetime/recommendationEvidence.js +73 -0
- package/dist/timecampus/pages/lifetime/recommendationEvidence.js.map +1 -0
- package/dist/timecampus/pages/timeline/TimelinePage.d.ts.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +154 -126
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationUsagePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/application-usage/ApplicationUsagePage.tsx"],"names":[],"mappings":"
|
|
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
|
|
4
|
-
import { LoadingSkeleton as
|
|
5
|
-
import { useCategorizeApplicationMutation as ee, useDeleteApplicationUsageMutation as
|
|
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
|
|
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
|
|
13
|
-
import { DonutChart as
|
|
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
|
|
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
|
|
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
|
-
],
|
|
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
|
-
},
|
|
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
|
|
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:
|
|
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: !
|
|
53
|
+
skip: !n,
|
|
51
54
|
notifyOnNetworkStatusChange: !0
|
|
52
|
-
}), { data:
|
|
53
|
-
await Promise.all([
|
|
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
|
-
},
|
|
57
|
-
!j.trim() || !F || (await
|
|
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:
|
|
65
|
+
date: me()
|
|
63
66
|
} } }), A(!1), await J());
|
|
64
|
-
},
|
|
67
|
+
}, Me = (e) => {
|
|
65
68
|
B(e), H(e.category);
|
|
66
|
-
},
|
|
67
|
-
z && (await
|
|
69
|
+
}, Ne = async () => {
|
|
70
|
+
z && (await De({ variables: {
|
|
68
71
|
applicationName: z.applicationName,
|
|
69
72
|
category: V
|
|
70
73
|
} }), B(null), await J());
|
|
71
|
-
},
|
|
72
|
-
U && (await
|
|
73
|
-
},
|
|
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
|
|
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),
|
|
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
|
-
] : [],
|
|
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
|
|
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
|
|
117
|
+
return Ce ? /* @__PURE__ */ g(a, {
|
|
115
118
|
title: "App Usage",
|
|
116
119
|
description: "Track application usage and productivity",
|
|
117
|
-
children: /* @__PURE__ */ g(
|
|
120
|
+
children: /* @__PURE__ */ g(i, {
|
|
118
121
|
variant: "card",
|
|
119
122
|
count: 4
|
|
120
123
|
})
|
|
121
|
-
}) :
|
|
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(
|
|
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(
|
|
152
|
+
/* @__PURE__ */ g(r, {
|
|
150
153
|
title: "Productive Time",
|
|
151
|
-
value:
|
|
154
|
+
value: Fe,
|
|
152
155
|
description: "Share of tracked time"
|
|
153
156
|
}),
|
|
154
|
-
/* @__PURE__ */ g(
|
|
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(
|
|
162
|
+
/* @__PURE__ */ g(r, {
|
|
160
163
|
title: "Focus Sessions",
|
|
161
|
-
value: String(
|
|
162
|
-
icon: /* @__PURE__ */ g(
|
|
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__ */ _(
|
|
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__ */
|
|
187
|
+
}), /* @__PURE__ */ g("span", {
|
|
185
188
|
className: "text-xs text-text-secondary",
|
|
186
|
-
children:
|
|
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:
|
|
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(
|
|
244
|
+
/* @__PURE__ */ g(ae, {
|
|
233
245
|
label: e.category,
|
|
234
|
-
variant:
|
|
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: () =>
|
|
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(
|
|
266
|
-
data:
|
|
277
|
+
children: /* @__PURE__ */ g(se, {
|
|
278
|
+
data: Re,
|
|
267
279
|
centerLabel: "Total tracked",
|
|
268
|
-
centerValue:
|
|
269
|
-
valueFormat:
|
|
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(
|
|
277
|
-
data:
|
|
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:
|
|
285
|
-
xTickFormat:
|
|
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:
|
|
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:
|
|
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: [
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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: [
|
|
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 · {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 “\n {recatApp?.applicationName}”.\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 · {app.switches}{\" \"}\n switches\n {activeWindow !== null && (\n <>\n {\" \"}\n ·{\" \"}\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 “\n {recatApp?.applicationName}”.\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":"
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/dashboard/AnalyticsPage.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,aAAa,gCAsW5B"}
|