@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.
- 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 +153 -125
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,78 +2,84 @@ import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/
|
|
|
2
2
|
import { formatHours as n } from "../../utils/helpers.js";
|
|
3
3
|
import { StatCard as r } from "../../components/StatCard.js";
|
|
4
4
|
import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
|
|
5
|
-
import { useGetFocusScoreSummaryQuery as a, useGetTimeEntryStatsQuery as o, useGetWorkPatternSummaryQuery as s } from "../../../generated/wspace-operations.js";
|
|
6
|
-
import { PageLayout as
|
|
7
|
-
import { StatusBadge as
|
|
8
|
-
import { ChartCard as
|
|
9
|
-
import { BarsChart as
|
|
10
|
-
import { DonutChart as
|
|
11
|
-
import { RadialGauge as
|
|
5
|
+
import { useGetFocusScoreSummaryQuery as a, useGetTimeEntryStatsQuery as o, useGetWorkPatternSummaryQuery as s, useListProjectsQuery as c } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as l } from "../../components/PageLayout.js";
|
|
7
|
+
import { StatusBadge as u } from "../../components/StatusBadge.js";
|
|
8
|
+
import { ChartCard as d } from "../../components/ChartCard.js";
|
|
9
|
+
import { BarsChart as f } from "../../components/charts/BarsChart.js";
|
|
10
|
+
import { DonutChart as p } from "../../components/charts/DonutChart.js";
|
|
11
|
+
import { RadialGauge as m } from "../../components/charts/RadialGauge.js";
|
|
12
12
|
import "../../components/index.js";
|
|
13
|
-
import { WorkPatternInsightType as
|
|
14
|
-
import { Brain as
|
|
15
|
-
import { jsx as
|
|
16
|
-
import { NextSteps as
|
|
13
|
+
import { WorkPatternInsightType as h } from "../../../generated/wspace-types.js";
|
|
14
|
+
import { Brain as g, CalendarRange as _, Clock as v, DollarSign as y, FolderOpen as b, Gauge as x, Lightbulb as S, PieChart as C, TrendingUp as w } from "lucide-react";
|
|
15
|
+
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
16
|
+
import { NextSteps as D } from "@burdenoff/fe-libs/ui";
|
|
17
17
|
//#region src/timecampus/pages/dashboard/AnalyticsPage.tsx
|
|
18
|
-
var
|
|
19
|
-
[
|
|
18
|
+
var O = {
|
|
19
|
+
[h.Positive]: {
|
|
20
20
|
variant: "success",
|
|
21
21
|
label: "Positive"
|
|
22
22
|
},
|
|
23
|
-
[
|
|
23
|
+
[h.Negative]: {
|
|
24
24
|
variant: "error",
|
|
25
25
|
label: "Needs work"
|
|
26
26
|
},
|
|
27
|
-
[
|
|
27
|
+
[h.Warning]: {
|
|
28
28
|
variant: "warning",
|
|
29
29
|
label: "Warning"
|
|
30
30
|
},
|
|
31
|
-
[
|
|
31
|
+
[h.Recommendation]: {
|
|
32
32
|
variant: "info",
|
|
33
33
|
label: "Tip"
|
|
34
34
|
},
|
|
35
|
-
[
|
|
35
|
+
[h.Neutral]: {
|
|
36
36
|
variant: "muted",
|
|
37
37
|
label: "Insight"
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
-
function
|
|
41
|
-
let { workspaceId:
|
|
42
|
-
|
|
40
|
+
function k() {
|
|
41
|
+
let { workspaceId: h = "" } = e(), k = t(), { data: A, loading: j, error: M } = o({ skip: !h }), { data: N, loading: P } = a({ skip: !h }), { data: F, loading: I } = s({ skip: !h }), { data: L, loading: R } = c({
|
|
42
|
+
variables: { pagination: { limit: 200 } },
|
|
43
|
+
skip: !h
|
|
44
|
+
}), z = A?.timeEntryStats, B = N?.focusScoreSummary, V = F?.workPatternSummary;
|
|
45
|
+
if (j || P || I || R) return /* @__PURE__ */ T(l, {
|
|
43
46
|
title: "Analytics",
|
|
44
47
|
description: "Insights into your time tracking and productivity",
|
|
45
|
-
children: /* @__PURE__ */
|
|
48
|
+
children: /* @__PURE__ */ T(i, {
|
|
46
49
|
variant: "card",
|
|
47
50
|
count: 4
|
|
48
51
|
})
|
|
49
52
|
});
|
|
50
|
-
if (
|
|
53
|
+
if (M) return /* @__PURE__ */ T(l, {
|
|
51
54
|
title: "Analytics",
|
|
52
55
|
description: "Insights into your time tracking and productivity",
|
|
53
|
-
children: /* @__PURE__ */
|
|
56
|
+
children: /* @__PURE__ */ T("div", {
|
|
54
57
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
55
58
|
children: "Failed to load analytics. Please try again later."
|
|
56
59
|
})
|
|
57
60
|
});
|
|
58
|
-
let
|
|
61
|
+
let H = z ? Math.max(0, Math.round(z.billableHours)) : 0, U = z ? Math.max(0, Math.round(z.totalHours - z.billableHours)) : 0, W = z && z.totalHours > 0 ? Math.round(z.billableHours / z.totalHours * 100) : 0, G = [{
|
|
59
62
|
name: "Billable",
|
|
60
|
-
value:
|
|
63
|
+
value: H
|
|
61
64
|
}, {
|
|
62
65
|
name: "Non-billable",
|
|
63
|
-
value:
|
|
64
|
-
}],
|
|
66
|
+
value: U
|
|
67
|
+
}], K = V ? [{
|
|
65
68
|
label: "Focus",
|
|
66
|
-
hours: Math.max(0,
|
|
69
|
+
hours: Math.max(0, V.averageFocusHours)
|
|
67
70
|
}, {
|
|
68
71
|
label: "Meetings",
|
|
69
|
-
hours: Math.max(0,
|
|
70
|
-
}] : [],
|
|
71
|
-
|
|
72
|
+
hours: Math.max(0, V.averageMeetingHours)
|
|
73
|
+
}] : [], q = B?.trend ? B.trend.charAt(0).toUpperCase() + B.trend.slice(1) : void 0, J = (L?.projects?.items ?? []).filter((e) => (e.trackedHours ?? 0) > 0), Y = [...J].sort((e, t) => (t.trackedHours ?? 0) - (e.trackedHours ?? 0)).slice(0, 10).map((e) => ({
|
|
74
|
+
project: e.name,
|
|
75
|
+
hours: Math.round((e.trackedHours ?? 0) * 100) / 100
|
|
76
|
+
})), X = J.length;
|
|
77
|
+
return /* @__PURE__ */ E(l, {
|
|
72
78
|
title: "Analytics",
|
|
73
79
|
description: "Insights into your time tracking and productivity",
|
|
74
80
|
purpose: "Turn the raw hours you track into answers — spot whether your focus is improving, where billable time is going, and which habits to change to get more done with less burnout.",
|
|
75
81
|
children: [
|
|
76
|
-
/* @__PURE__ */
|
|
82
|
+
/* @__PURE__ */ T(D, {
|
|
77
83
|
storageKey: "timecampus-analytics",
|
|
78
84
|
className: "mb-6",
|
|
79
85
|
steps: [
|
|
@@ -81,149 +87,169 @@ function D() {
|
|
|
81
87
|
id: "reports",
|
|
82
88
|
label: "Build a detailed report",
|
|
83
89
|
description: "Slice time by project, client or person.",
|
|
84
|
-
onClick: () =>
|
|
90
|
+
onClick: () => k("/reports")
|
|
85
91
|
},
|
|
86
92
|
{
|
|
87
93
|
id: "focus",
|
|
88
94
|
label: "Dig into your focus score",
|
|
89
95
|
description: "Understand what's helping or hurting deep work.",
|
|
90
|
-
onClick: () =>
|
|
96
|
+
onClick: () => k("/focus-score")
|
|
91
97
|
},
|
|
92
98
|
{
|
|
93
99
|
id: "patterns",
|
|
94
100
|
label: "Review your work patterns",
|
|
95
101
|
description: "See how your week actually breaks down.",
|
|
96
|
-
onClick: () =>
|
|
102
|
+
onClick: () => k("/work-patterns")
|
|
97
103
|
}
|
|
98
104
|
]
|
|
99
105
|
}),
|
|
100
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ E("div", {
|
|
101
107
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
102
108
|
children: [
|
|
103
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ T(r, {
|
|
104
110
|
title: "Total Hours",
|
|
105
|
-
value:
|
|
106
|
-
icon: /* @__PURE__ */
|
|
107
|
-
description:
|
|
111
|
+
value: z ? n(z.totalHours) : "--",
|
|
112
|
+
icon: /* @__PURE__ */ T(v, { className: "h-5 w-5" }),
|
|
113
|
+
description: z ? `${z.totalEntries} entries` : void 0
|
|
108
114
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ T(r, {
|
|
110
116
|
title: "Billable Hours",
|
|
111
|
-
value:
|
|
112
|
-
icon: /* @__PURE__ */
|
|
113
|
-
description:
|
|
117
|
+
value: z ? n(z.billableHours) : "--",
|
|
118
|
+
icon: /* @__PURE__ */ T(y, { className: "h-5 w-5" }),
|
|
119
|
+
description: z ? `${W}% of tracked time` : void 0,
|
|
114
120
|
iconBgColor: "bg-status-success-bg text-status-success-text"
|
|
115
121
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ T(r, {
|
|
117
123
|
featured: !0,
|
|
118
124
|
title: "Avg Focus Score",
|
|
119
|
-
value:
|
|
120
|
-
icon: /* @__PURE__ */
|
|
121
|
-
description:
|
|
125
|
+
value: B ? `${Math.round(B.averageScore)}` : "--",
|
|
126
|
+
icon: /* @__PURE__ */ T(g, { className: "h-5 w-5" }),
|
|
127
|
+
description: q ? `${q} trend` : void 0,
|
|
122
128
|
iconBgColor: "bg-status-info-bg text-status-info-text"
|
|
123
129
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ T(r, {
|
|
125
131
|
title: "Avg Productivity",
|
|
126
|
-
value:
|
|
127
|
-
icon: /* @__PURE__ */
|
|
128
|
-
description:
|
|
132
|
+
value: V ? `${Math.round(V.averageProductivity)}%` : "--",
|
|
133
|
+
icon: /* @__PURE__ */ T(w, { className: "h-5 w-5" }),
|
|
134
|
+
description: z && z.activeTimers > 0 ? `${z.activeTimers} timer(s) running` : void 0
|
|
129
135
|
})
|
|
130
136
|
]
|
|
131
137
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ E("div", {
|
|
133
139
|
className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
134
|
-
children: [/* @__PURE__ */
|
|
140
|
+
children: [/* @__PURE__ */ T(d, {
|
|
135
141
|
title: "Focus Score",
|
|
136
142
|
description: "Your average deep-work score across the tracked window",
|
|
137
|
-
actions: /* @__PURE__ */
|
|
138
|
-
children:
|
|
143
|
+
actions: /* @__PURE__ */ T(x, { className: "h-4 w-4 text-text-secondary" }),
|
|
144
|
+
children: B ? /* @__PURE__ */ E("div", {
|
|
139
145
|
className: "space-y-4",
|
|
140
146
|
children: [
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
value:
|
|
147
|
+
/* @__PURE__ */ T(m, {
|
|
148
|
+
value: B.averageScore,
|
|
143
149
|
min: 0,
|
|
144
150
|
max: 100,
|
|
145
151
|
label: "Avg score",
|
|
146
|
-
sublabel:
|
|
152
|
+
sublabel: q ? `${q} trend` : void 0,
|
|
147
153
|
thresholds: {
|
|
148
154
|
warning: 75,
|
|
149
155
|
danger: 50
|
|
150
156
|
},
|
|
151
157
|
height: 196
|
|
152
158
|
}),
|
|
153
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ E("div", {
|
|
154
160
|
className: "grid grid-cols-2 gap-3",
|
|
155
|
-
children: [/* @__PURE__ */
|
|
161
|
+
children: [/* @__PURE__ */ E("div", {
|
|
156
162
|
className: "rounded-lg border border-border-seam bg-bg-sunken/40 p-3",
|
|
157
|
-
children: [/* @__PURE__ */
|
|
163
|
+
children: [/* @__PURE__ */ T("p", {
|
|
158
164
|
className: "text-xs text-text-secondary",
|
|
159
165
|
children: "Lowest"
|
|
160
|
-
}), /* @__PURE__ */
|
|
166
|
+
}), /* @__PURE__ */ T("p", {
|
|
161
167
|
className: "mt-0.5 text-lg font-semibold tabular-nums text-text-primary",
|
|
162
|
-
children: Math.round(
|
|
168
|
+
children: Math.round(B.lowestScore)
|
|
163
169
|
})]
|
|
164
|
-
}), /* @__PURE__ */
|
|
170
|
+
}), /* @__PURE__ */ E("div", {
|
|
165
171
|
className: "rounded-lg border border-border-seam bg-bg-sunken/40 p-3",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ T("p", {
|
|
167
173
|
className: "text-xs text-text-secondary",
|
|
168
174
|
children: "Highest"
|
|
169
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ T("p", {
|
|
170
176
|
className: "mt-0.5 text-lg font-semibold tabular-nums text-text-primary",
|
|
171
|
-
children: Math.round(
|
|
177
|
+
children: Math.round(B.highestScore)
|
|
172
178
|
})]
|
|
173
179
|
})]
|
|
174
180
|
}),
|
|
175
|
-
(
|
|
181
|
+
(B.topFactor || B.improvementArea) && /* @__PURE__ */ E("div", {
|
|
176
182
|
className: "flex flex-wrap items-center gap-2",
|
|
177
|
-
children: [
|
|
183
|
+
children: [B.topFactor && /* @__PURE__ */ T(u, {
|
|
178
184
|
variant: "success",
|
|
179
|
-
label: `Strength: ${
|
|
180
|
-
}),
|
|
185
|
+
label: `Strength: ${B.topFactor}`
|
|
186
|
+
}), B.improvementArea && /* @__PURE__ */ T(u, {
|
|
181
187
|
variant: "warning",
|
|
182
|
-
label: `Improve: ${
|
|
188
|
+
label: `Improve: ${B.improvementArea}`
|
|
183
189
|
})]
|
|
184
190
|
})
|
|
185
191
|
]
|
|
186
|
-
}) : /* @__PURE__ */
|
|
192
|
+
}) : /* @__PURE__ */ T("p", {
|
|
187
193
|
className: "py-8 text-center text-sm text-text-secondary",
|
|
188
194
|
children: "No focus score data yet."
|
|
189
195
|
})
|
|
190
|
-
}), /* @__PURE__ */
|
|
196
|
+
}), /* @__PURE__ */ T(d, {
|
|
191
197
|
title: "Time Distribution",
|
|
192
198
|
description: "Billable vs non-billable across everything you tracked",
|
|
193
|
-
actions: /* @__PURE__ */ C
|
|
194
|
-
children:
|
|
195
|
-
data:
|
|
199
|
+
actions: /* @__PURE__ */ T(C, { className: "h-4 w-4 text-text-secondary" }),
|
|
200
|
+
children: z && z.totalHours > 0 ? /* @__PURE__ */ T(p, {
|
|
201
|
+
data: G,
|
|
196
202
|
centerLabel: "Tracked",
|
|
197
|
-
centerValue: `${Math.round(
|
|
203
|
+
centerValue: `${Math.round(z.totalHours)}h`,
|
|
198
204
|
valueFormat: n,
|
|
199
205
|
height: 240,
|
|
200
206
|
ariaLabel: "Billable versus non-billable hours"
|
|
201
|
-
}) : /* @__PURE__ */
|
|
207
|
+
}) : /* @__PURE__ */ T("p", {
|
|
202
208
|
className: "py-8 text-center text-sm text-text-secondary",
|
|
203
209
|
children: "No time entries tracked yet."
|
|
204
210
|
})
|
|
205
211
|
})]
|
|
206
212
|
}),
|
|
207
|
-
|
|
213
|
+
/* @__PURE__ */ T(d, {
|
|
214
|
+
title: "Time by project",
|
|
215
|
+
description: X > 0 ? `Tracked hours across ${X} project${X === 1 ? "" : "s"}${X > Y.length ? ` — top ${Y.length} shown` : ""}` : "Tracked hours per project",
|
|
216
|
+
actions: /* @__PURE__ */ T(b, { className: "h-4 w-4 text-text-secondary" }),
|
|
217
|
+
className: "mt-6",
|
|
218
|
+
children: /* @__PURE__ */ T(f, {
|
|
219
|
+
data: Y,
|
|
220
|
+
xKey: "project",
|
|
221
|
+
series: [{
|
|
222
|
+
key: "hours",
|
|
223
|
+
label: "Tracked hours"
|
|
224
|
+
}],
|
|
225
|
+
orientation: "horizontal",
|
|
226
|
+
valueFormat: n,
|
|
227
|
+
categoryWidth: 140,
|
|
228
|
+
height: Math.max(160, Y.length * 34),
|
|
229
|
+
ariaLabel: "Tracked hours by project",
|
|
230
|
+
emptyLabel: "No project has tracked hours yet — log time against a project and it will appear here."
|
|
231
|
+
})
|
|
232
|
+
}),
|
|
233
|
+
V && /* @__PURE__ */ T(d, {
|
|
208
234
|
title: "Work Patterns",
|
|
209
235
|
description: "How your average day splits and what to change",
|
|
210
|
-
actions: /* @__PURE__ */
|
|
236
|
+
actions: /* @__PURE__ */ T(_, { className: "h-4 w-4 text-text-secondary" }),
|
|
211
237
|
className: "mt-6",
|
|
212
|
-
children: /* @__PURE__ */
|
|
238
|
+
children: /* @__PURE__ */ E("div", {
|
|
213
239
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
214
|
-
children: [/* @__PURE__ */
|
|
240
|
+
children: [/* @__PURE__ */ E("div", {
|
|
215
241
|
className: "space-y-4",
|
|
216
|
-
children: [/* @__PURE__ */
|
|
242
|
+
children: [/* @__PURE__ */ E("div", {
|
|
217
243
|
className: "flex flex-wrap gap-2",
|
|
218
|
-
children: [
|
|
244
|
+
children: [V.mostProductiveDay && /* @__PURE__ */ T(u, {
|
|
219
245
|
variant: "success",
|
|
220
|
-
label: `Peak: ${
|
|
221
|
-
}),
|
|
246
|
+
label: `Peak: ${V.mostProductiveDay}`
|
|
247
|
+
}), V.leastProductiveDay && /* @__PURE__ */ T(u, {
|
|
222
248
|
variant: "muted",
|
|
223
|
-
label: `Quietest: ${
|
|
249
|
+
label: `Quietest: ${V.leastProductiveDay}`
|
|
224
250
|
})]
|
|
225
|
-
}),
|
|
226
|
-
data:
|
|
251
|
+
}), K.some((e) => e.hours > 0) ? /* @__PURE__ */ T(f, {
|
|
252
|
+
data: K,
|
|
227
253
|
xKey: "label",
|
|
228
254
|
series: [{
|
|
229
255
|
key: "hours",
|
|
@@ -234,37 +260,37 @@ function D() {
|
|
|
234
260
|
categoryWidth: 80,
|
|
235
261
|
height: 140,
|
|
236
262
|
ariaLabel: "Average focus versus meeting hours per day"
|
|
237
|
-
}) : /* @__PURE__ */
|
|
263
|
+
}) : /* @__PURE__ */ T("p", {
|
|
238
264
|
className: "text-sm text-text-secondary",
|
|
239
265
|
children: "Not enough tracked days to break down your average day yet."
|
|
240
266
|
})]
|
|
241
|
-
}), /* @__PURE__ */
|
|
267
|
+
}), /* @__PURE__ */ T("div", {
|
|
242
268
|
className: "space-y-3",
|
|
243
|
-
children:
|
|
269
|
+
children: V.insights.length === 0 ? /* @__PURE__ */ T("p", {
|
|
244
270
|
className: "text-sm text-text-secondary",
|
|
245
271
|
children: "No pattern insights surfaced yet — keep tracking and they'll appear here."
|
|
246
|
-
}) :
|
|
247
|
-
let n =
|
|
248
|
-
return /* @__PURE__ */
|
|
272
|
+
}) : V.insights.map((e, t) => {
|
|
273
|
+
let n = O[e.type];
|
|
274
|
+
return /* @__PURE__ */ E("div", {
|
|
249
275
|
className: "rounded-lg border border-border-subtle bg-bg-sunken/40 p-3",
|
|
250
276
|
children: [
|
|
251
|
-
/* @__PURE__ */
|
|
277
|
+
/* @__PURE__ */ E("div", {
|
|
252
278
|
className: "flex items-start justify-between gap-3",
|
|
253
|
-
children: [/* @__PURE__ */
|
|
279
|
+
children: [/* @__PURE__ */ T("p", {
|
|
254
280
|
className: "text-sm font-medium text-text-primary",
|
|
255
281
|
children: e.title
|
|
256
|
-
}), /* @__PURE__ */
|
|
282
|
+
}), /* @__PURE__ */ T(u, {
|
|
257
283
|
variant: n.variant,
|
|
258
284
|
label: n.label
|
|
259
285
|
})]
|
|
260
286
|
}),
|
|
261
|
-
/* @__PURE__ */
|
|
287
|
+
/* @__PURE__ */ T("p", {
|
|
262
288
|
className: "mt-1 text-xs text-text-secondary",
|
|
263
289
|
children: e.description
|
|
264
290
|
}),
|
|
265
|
-
e.recommendation && /* @__PURE__ */
|
|
291
|
+
e.recommendation && /* @__PURE__ */ E("p", {
|
|
266
292
|
className: "mt-1.5 flex items-start gap-1.5 text-xs text-text-secondary",
|
|
267
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ T(S, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-status-info-text" }), /* @__PURE__ */ T("span", { children: e.recommendation })]
|
|
268
294
|
})
|
|
269
295
|
]
|
|
270
296
|
}, `${e.title}-${t}`);
|
|
@@ -276,6 +302,6 @@ function D() {
|
|
|
276
302
|
});
|
|
277
303
|
}
|
|
278
304
|
//#endregion
|
|
279
|
-
export {
|
|
305
|
+
export { k as AnalyticsPage };
|
|
280
306
|
|
|
281
307
|
//# sourceMappingURL=AnalyticsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/dashboard/AnalyticsPage.tsx"],"sourcesContent":["import {\n Clock,\n DollarSign,\n Brain,\n TrendingUp,\n Gauge,\n PieChart as PieIcon,\n CalendarRange,\n Lightbulb,\n} from \"lucide-react\";\nimport { NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n StatCard,\n StatusBadge,\n ChartCard,\n RadialGauge,\n DonutChart,\n BarsChart,\n type DonutSlice,\n} from \"../../components\";\nimport { formatHours } from \"../../utils/helpers\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n WorkPatternInsightType,\n type WorkPatternInsight,\n} from \"@/generated/wspace-types\";\nimport {\n useGetTimeEntryStatsQuery,\n useGetFocusScoreSummaryQuery,\n useGetWorkPatternSummaryQuery,\n} from \"@/generated/wspace-operations\";\n\ntype BadgeVariant = \"success\" | \"warning\" | \"error\" | \"info\" | \"muted\";\n\nconst INSIGHT_META: Record<\n WorkPatternInsightType,\n { variant: BadgeVariant; label: string }\n> = {\n [WorkPatternInsightType.Positive]: { variant: \"success\", label: \"Positive\" },\n [WorkPatternInsightType.Negative]: { variant: \"error\", label: \"Needs work\" },\n [WorkPatternInsightType.Warning]: { variant: \"warning\", label: \"Warning\" },\n [WorkPatternInsightType.Recommendation]: { variant: \"info\", label: \"Tip\" },\n [WorkPatternInsightType.Neutral]: { variant: \"muted\", label: \"Insight\" },\n};\n\nexport function AnalyticsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const { data: focusData, loading: focusLoading } =\n useGetFocusScoreSummaryQuery({\n skip: !workspaceId,\n });\n\n const { data: patternData, loading: patternLoading } =\n useGetWorkPatternSummaryQuery({\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const focus = focusData?.focusScoreSummary;\n const pattern = patternData?.workPatternSummary;\n\n if (statsLoading || focusLoading || patternLoading) {\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (statsError) {\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load analytics. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n // Billable vs non-billable split from the real time-entry aggregate.\n const billableHours = stats\n ? Math.max(0, Math.round(stats.billableHours))\n : 0;\n const nonBillableHours = stats\n ? Math.max(0, Math.round(stats.totalHours - stats.billableHours))\n : 0;\n const billablePct =\n stats && stats.totalHours > 0\n ? Math.round((stats.billableHours / stats.totalHours) * 100)\n : 0;\n const distributionSlices: DonutSlice[] = [\n { name: \"Billable\", value: billableHours },\n { name: \"Non-billable\", value: nonBillableHours },\n ];\n\n // Average-day split from the real work-pattern aggregate.\n const dayBreakdown = pattern\n ? [\n { label: \"Focus\", hours: Math.max(0, pattern.averageFocusHours) },\n { label: \"Meetings\", hours: Math.max(0, pattern.averageMeetingHours) },\n ]\n : [];\n\n const trendText = focus?.trend\n ? focus.trend.charAt(0).toUpperCase() + focus.trend.slice(1)\n : undefined;\n\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n purpose=\"Turn the raw hours you track into answers — spot whether your focus is improving, where billable time is going, and which habits to change to get more done with less burnout.\"\n >\n <NextSteps\n storageKey=\"timecampus-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: \"reports\",\n label: \"Build a detailed report\",\n description: \"Slice time by project, client or person.\",\n onClick: () => navigate(\"/reports\"),\n },\n {\n id: \"focus\",\n label: \"Dig into your focus score\",\n description: \"Understand what's helping or hurting deep work.\",\n onClick: () => navigate(\"/focus-score\"),\n },\n {\n id: \"patterns\",\n label: \"Review your work patterns\",\n description: \"See how your week actually breaks down.\",\n onClick: () => navigate(\"/work-patterns\"),\n },\n ]}\n />\n\n {/* KPI row */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Total Hours\"\n value={stats ? formatHours(stats.totalHours) : \"--\"}\n icon={<Clock className=\"h-5 w-5\" />}\n description={stats ? `${stats.totalEntries} entries` : undefined}\n />\n <StatCard\n title=\"Billable Hours\"\n value={stats ? formatHours(stats.billableHours) : \"--\"}\n icon={<DollarSign className=\"h-5 w-5\" />}\n description={stats ? `${billablePct}% of tracked time` : undefined}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n featured\n title=\"Avg Focus Score\"\n value={focus ? `${Math.round(focus.averageScore)}` : \"--\"}\n icon={<Brain className=\"h-5 w-5\" />}\n description={trendText ? `${trendText} trend` : undefined}\n iconBgColor=\"bg-status-info-bg text-status-info-text\"\n />\n <StatCard\n title=\"Avg Productivity\"\n value={pattern ? `${Math.round(pattern.averageProductivity)}%` : \"--\"}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n description={\n stats && stats.activeTimers > 0\n ? `${stats.activeTimers} timer(s) running`\n : undefined\n }\n />\n </div>\n\n {/* Focus gauge + time distribution */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Focus Score\"\n description=\"Your average deep-work score across the tracked window\"\n actions={<Gauge className=\"h-4 w-4 text-text-secondary\" />}\n >\n {focus ? (\n <div className=\"space-y-4\">\n <RadialGauge\n value={focus.averageScore}\n min={0}\n max={100}\n label=\"Avg score\"\n sublabel={trendText ? `${trendText} trend` : undefined}\n thresholds={{ warning: 75, danger: 50 }}\n height={196}\n />\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken/40 p-3\">\n <p className=\"text-xs text-text-secondary\">Lowest</p>\n <p className=\"mt-0.5 text-lg font-semibold tabular-nums text-text-primary\">\n {Math.round(focus.lowestScore)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken/40 p-3\">\n <p className=\"text-xs text-text-secondary\">Highest</p>\n <p className=\"mt-0.5 text-lg font-semibold tabular-nums text-text-primary\">\n {Math.round(focus.highestScore)}\n </p>\n </div>\n </div>\n {(focus.topFactor || focus.improvementArea) && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {focus.topFactor && (\n <StatusBadge\n variant=\"success\"\n label={`Strength: ${focus.topFactor}`}\n />\n )}\n {focus.improvementArea && (\n <StatusBadge\n variant=\"warning\"\n label={`Improve: ${focus.improvementArea}`}\n />\n )}\n </div>\n )}\n </div>\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No focus score data yet.\n </p>\n )}\n </ChartCard>\n\n <ChartCard\n title=\"Time Distribution\"\n description=\"Billable vs non-billable across everything you tracked\"\n actions={<PieIcon className=\"h-4 w-4 text-text-secondary\" />}\n >\n {stats && stats.totalHours > 0 ? (\n <DonutChart\n data={distributionSlices}\n centerLabel=\"Tracked\"\n centerValue={`${Math.round(stats.totalHours)}h`}\n valueFormat={formatHours}\n height={240}\n ariaLabel=\"Billable versus non-billable hours\"\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No time entries tracked yet.\n </p>\n )}\n </ChartCard>\n </div>\n\n {/* Work pattern insights */}\n {pattern && (\n <ChartCard\n title=\"Work Patterns\"\n description=\"How your average day splits and what to change\"\n actions={<CalendarRange className=\"h-4 w-4 text-text-secondary\" />}\n className=\"mt-6\"\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap gap-2\">\n {pattern.mostProductiveDay && (\n <StatusBadge\n variant=\"success\"\n label={`Peak: ${pattern.mostProductiveDay}`}\n />\n )}\n {pattern.leastProductiveDay && (\n <StatusBadge\n variant=\"muted\"\n label={`Quietest: ${pattern.leastProductiveDay}`}\n />\n )}\n </div>\n {dayBreakdown.some((d) => d.hours > 0) ? (\n <BarsChart\n data={dayBreakdown}\n xKey=\"label\"\n series={[{ key: \"hours\", label: \"Avg hours / day\" }]}\n orientation=\"horizontal\"\n valueFormat={formatHours}\n categoryWidth={80}\n height={140}\n ariaLabel=\"Average focus versus meeting hours per day\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Not enough tracked days to break down your average day yet.\n </p>\n )}\n </div>\n\n <div className=\"space-y-3\">\n {pattern.insights.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No pattern insights surfaced yet — keep tracking and they'll\n appear here.\n </p>\n ) : (\n pattern.insights.map(\n (insight: WorkPatternInsight, index: number) => {\n const meta = INSIGHT_META[insight.type];\n return (\n <div\n key={`${insight.title}-${index}`}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {insight.title}\n </p>\n <StatusBadge\n variant={meta.variant}\n label={meta.label}\n />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {insight.description}\n </p>\n {insight.recommendation && (\n <p className=\"mt-1.5 flex items-start gap-1.5 text-xs text-text-secondary\">\n <Lightbulb className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-status-info-text\" />\n <span>{insight.recommendation}</span>\n </p>\n )}\n </div>\n );\n },\n )\n )}\n </div>\n </div>\n </ChartCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/dashboard/AnalyticsPage.tsx"],"sourcesContent":["import {\n Clock,\n DollarSign,\n Brain,\n TrendingUp,\n Gauge,\n FolderOpen,\n PieChart as PieIcon,\n CalendarRange,\n Lightbulb,\n} from \"lucide-react\";\nimport { NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n StatCard,\n StatusBadge,\n ChartCard,\n RadialGauge,\n DonutChart,\n BarsChart,\n type ChartDatum,\n type DonutSlice,\n} from \"../../components\";\nimport { formatHours } from \"../../utils/helpers\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n WorkPatternInsightType,\n type WorkPatternInsight,\n} from \"@/generated/wspace-types\";\nimport {\n useGetTimeEntryStatsQuery,\n useGetFocusScoreSummaryQuery,\n useGetWorkPatternSummaryQuery,\n useListProjectsQuery,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\n\ntype BadgeVariant = \"success\" | \"warning\" | \"error\" | \"info\" | \"muted\";\n\ntype ProjectItem = NonNullable<ListProjectsQuery[\"projects\"]>[\"items\"][number];\n\nconst INSIGHT_META: Record<\n WorkPatternInsightType,\n { variant: BadgeVariant; label: string }\n> = {\n [WorkPatternInsightType.Positive]: { variant: \"success\", label: \"Positive\" },\n [WorkPatternInsightType.Negative]: { variant: \"error\", label: \"Needs work\" },\n [WorkPatternInsightType.Warning]: { variant: \"warning\", label: \"Warning\" },\n [WorkPatternInsightType.Recommendation]: { variant: \"info\", label: \"Tip\" },\n [WorkPatternInsightType.Neutral]: { variant: \"muted\", label: \"Insight\" },\n};\n\nexport function AnalyticsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const { data: focusData, loading: focusLoading } =\n useGetFocusScoreSummaryQuery({\n skip: !workspaceId,\n });\n\n const { data: patternData, loading: patternLoading } =\n useGetWorkPatternSummaryQuery({\n skip: !workspaceId,\n });\n\n // Where the tracked hours landed, per project. `trackedHours` is the running\n // total the time-entry service keeps on each project row — no client maths.\n const { data: projectsData, loading: projectsLoading } = useListProjectsQuery(\n {\n variables: { pagination: { limit: 200 } },\n skip: !workspaceId,\n },\n );\n\n const stats = statsData?.timeEntryStats;\n const focus = focusData?.focusScoreSummary;\n const pattern = patternData?.workPatternSummary;\n\n if (statsLoading || focusLoading || patternLoading || projectsLoading) {\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (statsError) {\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load analytics. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n // Billable vs non-billable split from the real time-entry aggregate.\n const billableHours = stats\n ? Math.max(0, Math.round(stats.billableHours))\n : 0;\n const nonBillableHours = stats\n ? Math.max(0, Math.round(stats.totalHours - stats.billableHours))\n : 0;\n const billablePct =\n stats && stats.totalHours > 0\n ? Math.round((stats.billableHours / stats.totalHours) * 100)\n : 0;\n const distributionSlices: DonutSlice[] = [\n { name: \"Billable\", value: billableHours },\n { name: \"Non-billable\", value: nonBillableHours },\n ];\n\n // Average-day split from the real work-pattern aggregate.\n const dayBreakdown = pattern\n ? [\n { label: \"Focus\", hours: Math.max(0, pattern.averageFocusHours) },\n { label: \"Meetings\", hours: Math.max(0, pattern.averageMeetingHours) },\n ]\n : [];\n\n const trendText = focus?.trend\n ? focus.trend.charAt(0).toUpperCase() + focus.trend.slice(1)\n : undefined;\n\n // Time by project — the ten projects carrying the most tracked hours.\n const allProjects: ProjectItem[] = projectsData?.projects?.items ?? [];\n const trackedProjects = allProjects.filter(\n (project: ProjectItem) => (project.trackedHours ?? 0) > 0,\n );\n const projectHours: ChartDatum[] = [...trackedProjects]\n .sort(\n (a: ProjectItem, b: ProjectItem) =>\n (b.trackedHours ?? 0) - (a.trackedHours ?? 0),\n )\n .slice(0, 10)\n .map((project: ProjectItem) => ({\n project: project.name,\n hours: Math.round((project.trackedHours ?? 0) * 100) / 100,\n }));\n const projectsWithTime = trackedProjects.length;\n\n return (\n <PageLayout\n title=\"Analytics\"\n description=\"Insights into your time tracking and productivity\"\n purpose=\"Turn the raw hours you track into answers — spot whether your focus is improving, where billable time is going, and which habits to change to get more done with less burnout.\"\n >\n <NextSteps\n storageKey=\"timecampus-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: \"reports\",\n label: \"Build a detailed report\",\n description: \"Slice time by project, client or person.\",\n onClick: () => navigate(\"/reports\"),\n },\n {\n id: \"focus\",\n label: \"Dig into your focus score\",\n description: \"Understand what's helping or hurting deep work.\",\n onClick: () => navigate(\"/focus-score\"),\n },\n {\n id: \"patterns\",\n label: \"Review your work patterns\",\n description: \"See how your week actually breaks down.\",\n onClick: () => navigate(\"/work-patterns\"),\n },\n ]}\n />\n\n {/* KPI row */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Total Hours\"\n value={stats ? formatHours(stats.totalHours) : \"--\"}\n icon={<Clock className=\"h-5 w-5\" />}\n description={stats ? `${stats.totalEntries} entries` : undefined}\n />\n <StatCard\n title=\"Billable Hours\"\n value={stats ? formatHours(stats.billableHours) : \"--\"}\n icon={<DollarSign className=\"h-5 w-5\" />}\n description={stats ? `${billablePct}% of tracked time` : undefined}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n featured\n title=\"Avg Focus Score\"\n value={focus ? `${Math.round(focus.averageScore)}` : \"--\"}\n icon={<Brain className=\"h-5 w-5\" />}\n description={trendText ? `${trendText} trend` : undefined}\n iconBgColor=\"bg-status-info-bg text-status-info-text\"\n />\n <StatCard\n title=\"Avg Productivity\"\n value={pattern ? `${Math.round(pattern.averageProductivity)}%` : \"--\"}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n description={\n stats && stats.activeTimers > 0\n ? `${stats.activeTimers} timer(s) running`\n : undefined\n }\n />\n </div>\n\n {/* Focus gauge + time distribution */}\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <ChartCard\n title=\"Focus Score\"\n description=\"Your average deep-work score across the tracked window\"\n actions={<Gauge className=\"h-4 w-4 text-text-secondary\" />}\n >\n {focus ? (\n <div className=\"space-y-4\">\n <RadialGauge\n value={focus.averageScore}\n min={0}\n max={100}\n label=\"Avg score\"\n sublabel={trendText ? `${trendText} trend` : undefined}\n thresholds={{ warning: 75, danger: 50 }}\n height={196}\n />\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken/40 p-3\">\n <p className=\"text-xs text-text-secondary\">Lowest</p>\n <p className=\"mt-0.5 text-lg font-semibold tabular-nums text-text-primary\">\n {Math.round(focus.lowestScore)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam bg-bg-sunken/40 p-3\">\n <p className=\"text-xs text-text-secondary\">Highest</p>\n <p className=\"mt-0.5 text-lg font-semibold tabular-nums text-text-primary\">\n {Math.round(focus.highestScore)}\n </p>\n </div>\n </div>\n {(focus.topFactor || focus.improvementArea) && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {focus.topFactor && (\n <StatusBadge\n variant=\"success\"\n label={`Strength: ${focus.topFactor}`}\n />\n )}\n {focus.improvementArea && (\n <StatusBadge\n variant=\"warning\"\n label={`Improve: ${focus.improvementArea}`}\n />\n )}\n </div>\n )}\n </div>\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No focus score data yet.\n </p>\n )}\n </ChartCard>\n\n <ChartCard\n title=\"Time Distribution\"\n description=\"Billable vs non-billable across everything you tracked\"\n actions={<PieIcon className=\"h-4 w-4 text-text-secondary\" />}\n >\n {stats && stats.totalHours > 0 ? (\n <DonutChart\n data={distributionSlices}\n centerLabel=\"Tracked\"\n centerValue={`${Math.round(stats.totalHours)}h`}\n valueFormat={formatHours}\n height={240}\n ariaLabel=\"Billable versus non-billable hours\"\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No time entries tracked yet.\n </p>\n )}\n </ChartCard>\n </div>\n\n {/* Where the tracked hours actually landed */}\n <ChartCard\n title=\"Time by project\"\n description={\n projectsWithTime > 0\n ? `Tracked hours across ${projectsWithTime} project${projectsWithTime === 1 ? \"\" : \"s\"}${projectsWithTime > projectHours.length ? ` — top ${projectHours.length} shown` : \"\"}`\n : \"Tracked hours per project\"\n }\n actions={<FolderOpen className=\"h-4 w-4 text-text-secondary\" />}\n className=\"mt-6\"\n >\n <BarsChart\n data={projectHours}\n xKey=\"project\"\n series={[{ key: \"hours\", label: \"Tracked hours\" }]}\n orientation=\"horizontal\"\n valueFormat={formatHours}\n categoryWidth={140}\n height={Math.max(160, projectHours.length * 34)}\n ariaLabel=\"Tracked hours by project\"\n emptyLabel=\"No project has tracked hours yet — log time against a project and it will appear here.\"\n />\n </ChartCard>\n\n {/* Work pattern insights */}\n {pattern && (\n <ChartCard\n title=\"Work Patterns\"\n description=\"How your average day splits and what to change\"\n actions={<CalendarRange className=\"h-4 w-4 text-text-secondary\" />}\n className=\"mt-6\"\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap gap-2\">\n {pattern.mostProductiveDay && (\n <StatusBadge\n variant=\"success\"\n label={`Peak: ${pattern.mostProductiveDay}`}\n />\n )}\n {pattern.leastProductiveDay && (\n <StatusBadge\n variant=\"muted\"\n label={`Quietest: ${pattern.leastProductiveDay}`}\n />\n )}\n </div>\n {dayBreakdown.some((d) => d.hours > 0) ? (\n <BarsChart\n data={dayBreakdown}\n xKey=\"label\"\n series={[{ key: \"hours\", label: \"Avg hours / day\" }]}\n orientation=\"horizontal\"\n valueFormat={formatHours}\n categoryWidth={80}\n height={140}\n ariaLabel=\"Average focus versus meeting hours per day\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Not enough tracked days to break down your average day yet.\n </p>\n )}\n </div>\n\n <div className=\"space-y-3\">\n {pattern.insights.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No pattern insights surfaced yet — keep tracking and they'll\n appear here.\n </p>\n ) : (\n pattern.insights.map(\n (insight: WorkPatternInsight, index: number) => {\n const meta = INSIGHT_META[insight.type];\n return (\n <div\n key={`${insight.title}-${index}`}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/40 p-3\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {insight.title}\n </p>\n <StatusBadge\n variant={meta.variant}\n label={meta.label}\n />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {insight.description}\n </p>\n {insight.recommendation && (\n <p className=\"mt-1.5 flex items-start gap-1.5 text-xs text-text-secondary\">\n <Lightbulb className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-status-info-text\" />\n <span>{insight.recommendation}</span>\n </p>\n )}\n </div>\n );\n },\n )\n )}\n </div>\n </div>\n </ChartCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecommendationsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/RecommendationsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RecommendationsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/RecommendationsPage.tsx"],"names":[],"mappings":"AA6oBA,wBAAgB,mBAAmB,gCA4blC"}
|