@burdenoff/microfe-timecampus 2026.731.2 → 2026.820.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/generated/wspace-operations.d.ts +484 -861
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-operations.js +1377 -1377
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/index.js +5 -3
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/stories/EmptyState.stories.d.ts +0 -1
- package/dist/stories/LoadingSkeleton.stories.d.ts +0 -1
- package/dist/stories/StatCard.stories.d.ts +0 -1
- package/dist/stories/StatusBadge.stories.d.ts +0 -1
- package/dist/timecampus/AdminRoot.d.ts +8 -0
- package/dist/timecampus/AdminRoot.d.ts.map +1 -0
- package/dist/timecampus/AdminRoot.js +12 -0
- package/dist/timecampus/AdminRoot.js.map +1 -0
- package/dist/timecampus/AdminRoutes.d.ts +3 -0
- package/dist/timecampus/AdminRoutes.d.ts.map +1 -0
- package/dist/timecampus/AdminRoutes.js +8 -0
- package/dist/timecampus/AdminRoutes.js.map +1 -0
- package/dist/timecampus/TimeCampusRoot.d.ts +0 -1
- package/dist/timecampus/TimeCampusRoot.js +4 -2
- package/dist/timecampus/TimeCampusRoot.js.map +1 -1
- package/dist/timecampus/TimeCampusRoutes.js +1 -1
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/components/ChartCard.d.ts +0 -1
- package/dist/timecampus/components/ChartCard.js.map +1 -1
- package/dist/timecampus/components/DataTable.d.ts +0 -1
- package/dist/timecampus/components/EmptyState.d.ts +0 -1
- package/dist/timecampus/components/EmptyState.js.map +1 -1
- package/dist/timecampus/components/ErrorBoundary.d.ts +0 -1
- package/dist/timecampus/components/ErrorBoundary.js.map +1 -1
- package/dist/timecampus/components/FilterBar.d.ts +0 -1
- package/dist/timecampus/components/LoadingSkeleton.d.ts +0 -1
- package/dist/timecampus/components/LoadingSkeleton.js.map +1 -1
- package/dist/timecampus/components/Modal.d.ts +0 -1
- package/dist/timecampus/components/Modal.js +5 -3
- package/dist/timecampus/components/Modal.js.map +1 -1
- package/dist/timecampus/components/PageLayout.d.ts +0 -1
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/components/StatCard.d.ts +0 -1
- package/dist/timecampus/components/StatCard.js.map +1 -1
- package/dist/timecampus/components/StatusBadge.d.ts +0 -1
- package/dist/timecampus/components/StatusBadge.js.map +1 -1
- package/dist/timecampus/components/Toast.d.ts +0 -1
- package/dist/timecampus/components/Toast.js.map +1 -1
- package/dist/timecampus/components/charts/BalanceWheel.js.map +1 -1
- package/dist/timecampus/components/charts/BarsChart.d.ts +0 -1
- package/dist/timecampus/components/charts/BarsChart.js.map +1 -1
- package/dist/timecampus/components/charts/ChartParts.d.ts +0 -1
- package/dist/timecampus/components/charts/ChartParts.js.map +1 -1
- package/dist/timecampus/components/charts/DonutChart.d.ts +0 -1
- package/dist/timecampus/components/charts/DonutChart.js.map +1 -1
- package/dist/timecampus/components/charts/Heatmap.d.ts +0 -1
- package/dist/timecampus/components/charts/Heatmap.js +29 -29
- package/dist/timecampus/components/charts/Heatmap.js.map +1 -1
- package/dist/timecampus/components/charts/ProgressMeter.d.ts +0 -1
- package/dist/timecampus/components/charts/ProgressMeter.js +2 -2
- package/dist/timecampus/components/charts/ProgressMeter.js.map +1 -1
- package/dist/timecampus/components/charts/RadialGauge.d.ts +0 -1
- package/dist/timecampus/components/charts/RadialGauge.js +2 -2
- package/dist/timecampus/components/charts/RadialGauge.js.map +1 -1
- package/dist/timecampus/components/charts/TrendChart.d.ts +0 -1
- package/dist/timecampus/components/charts/TrendChart.js.map +1 -1
- package/dist/timecampus/components/charts/WeeksGrid.js +2 -2
- package/dist/timecampus/components/charts/WeeksGrid.js.map +1 -1
- package/dist/timecampus/components/charts/chartTheme.js.map +1 -1
- package/dist/timecampus/components/charts/index.js +10 -9
- package/dist/timecampus/components/index.js +18 -17
- package/dist/timecampus/index.d.ts +3 -1
- package/dist/timecampus/index.d.ts.map +1 -1
- package/dist/timecampus/index.js +8 -5
- package/dist/timecampus/pages/HomePage.d.ts +0 -1
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/admin/AdminPage.d.ts +3 -0
- package/dist/timecampus/pages/admin/AdminPage.d.ts.map +1 -0
- package/dist/timecampus/pages/admin/AdminPage.js +235 -0
- package/dist/timecampus/pages/admin/AdminPage.js.map +1 -0
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.d.ts.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.d.ts.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.d.ts.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.d.ts.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.d.ts.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/invoicing/InvoicingPage.d.ts.map +1 -1
- package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/DeadTimeRadarPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/LifeBalancePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/LifeTimelinePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js +1 -1
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/TimeWellSpentPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +9 -6
- package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/domains.d.ts +0 -1
- package/dist/timecampus/pages/lifetime/domains.js +1 -1
- package/dist/timecampus/pages/lifetime/domains.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.d.ts.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.d.ts +0 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.d.ts +0 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.d.ts +0 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.d.ts.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.d.ts.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +4 -4
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.d.ts.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/providers/TimeCampusProvider.d.ts +0 -1
- package/dist/timecampus/providers/TimeCampusProvider.d.ts.map +1 -1
- package/dist/timecampus/providers/TimeCampusProvider.js +39 -36
- package/dist/timecampus/providers/TimeCampusProvider.js.map +1 -1
- package/dist/timecampus/store/timecampusStore.js.map +1 -1
- package/dist/timecampus/types/index.d.ts +4 -1
- package/dist/timecampus/types/index.d.ts.map +1 -1
- package/dist/timecampus/utils/helpers.d.ts +0 -1
- package/dist/timecampus/utils/helpers.js.map +1 -1
- package/dist/types/assistant.d.ts +0 -1
- package/dist/utils/pageContext.js +1 -1
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +61 -59
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { StatCard as t } from "../../components/StatCard.js";
|
|
3
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
|
+
import { ErrorBoundary as r } from "../../components/ErrorBoundary.js";
|
|
5
|
+
import { PageLayout as i } from "../../components/PageLayout.js";
|
|
6
|
+
import { useState as a } from "react";
|
|
7
|
+
import { Activity as o, Flag as s, RefreshCw as c, RotateCcw as l, ShieldAlert as u } from "lucide-react";
|
|
8
|
+
import { gql as d } from "@apollo/client";
|
|
9
|
+
import { useMutation as f, useQuery as p } from "@apollo/client/react";
|
|
10
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as g } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/timecampus/pages/admin/AdminPage.tsx
|
|
13
|
+
var _ = d`
|
|
14
|
+
query TimeCampusAdminWorkspaceState($workspaceId: ID!) {
|
|
15
|
+
timeCampusAdminWorkspaceState(workspaceId: $workspaceId) {
|
|
16
|
+
workspaceId
|
|
17
|
+
timeEntryCount
|
|
18
|
+
projectCount
|
|
19
|
+
taskCount
|
|
20
|
+
pomodoroSessionCount
|
|
21
|
+
meetingCount
|
|
22
|
+
expenseCount
|
|
23
|
+
invoiceCount
|
|
24
|
+
pendingApprovalCount
|
|
25
|
+
locationEntryCount
|
|
26
|
+
focusScoreCount
|
|
27
|
+
applicationUsageCount
|
|
28
|
+
generatedAt
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`, v = d`
|
|
32
|
+
query TimeCampusFeatureFlags($workspaceId: ID!) {
|
|
33
|
+
timeCampusFeatureFlags(workspaceId: $workspaceId) {
|
|
34
|
+
workspaceId
|
|
35
|
+
items {
|
|
36
|
+
key
|
|
37
|
+
defaultValue
|
|
38
|
+
value
|
|
39
|
+
category
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`, y = d`
|
|
44
|
+
mutation ReprocessTimeCampusTimeline($workspaceId: ID!) {
|
|
45
|
+
reprocessTimeCampusTimeline(workspaceId: $workspaceId) {
|
|
46
|
+
success
|
|
47
|
+
message
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`, b = d`
|
|
51
|
+
mutation ResetTimeCampusDemoData($workspaceId: ID!) {
|
|
52
|
+
resetTimeCampusDemoData(workspaceId: $workspaceId) {
|
|
53
|
+
success
|
|
54
|
+
message
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`, x = () => {
|
|
58
|
+
let { workspaceId: r = "" } = e(), [d, x] = a(null), { data: S, loading: C, error: w, refetch: T } = p(_, {
|
|
59
|
+
variables: { workspaceId: r },
|
|
60
|
+
skip: !r
|
|
61
|
+
}), { data: E, loading: D, error: O } = p(v, {
|
|
62
|
+
variables: { workspaceId: r },
|
|
63
|
+
skip: !r
|
|
64
|
+
}), [k, { loading: A }] = f(y), [j, { loading: M }] = f(b);
|
|
65
|
+
if (!r) return /* @__PURE__ */ h("div", {
|
|
66
|
+
className: "flex min-h-[40vh] items-center justify-center text-text-secondary",
|
|
67
|
+
children: [/* @__PURE__ */ m(u, { className: "mr-2 h-5 w-5" }), "Workspace context is required."]
|
|
68
|
+
});
|
|
69
|
+
let N = S?.timeCampusAdminWorkspaceState, P = E?.timeCampusFeatureFlags?.items ?? [];
|
|
70
|
+
return /* @__PURE__ */ h(i, {
|
|
71
|
+
title: "TimeCampus Admin",
|
|
72
|
+
description: "Workspace state, feature flags, and super-admin maintenance actions.",
|
|
73
|
+
children: [
|
|
74
|
+
d && /* @__PURE__ */ h("div", {
|
|
75
|
+
className: "mb-4 rounded-md bg-status-info-bg px-4 py-3 text-sm text-status-info-text",
|
|
76
|
+
children: [d, /* @__PURE__ */ m("button", {
|
|
77
|
+
type: "button",
|
|
78
|
+
onClick: () => x(null),
|
|
79
|
+
className: "ml-4 underline",
|
|
80
|
+
children: "Dismiss"
|
|
81
|
+
})]
|
|
82
|
+
}),
|
|
83
|
+
(w || O) && /* @__PURE__ */ h("div", {
|
|
84
|
+
className: "mb-4 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text",
|
|
85
|
+
children: [
|
|
86
|
+
"Failed to load admin data.",
|
|
87
|
+
" ",
|
|
88
|
+
w?.message ?? O?.message
|
|
89
|
+
]
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ h("section", {
|
|
92
|
+
className: "mb-8",
|
|
93
|
+
children: [/* @__PURE__ */ h("h2", {
|
|
94
|
+
className: "mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
95
|
+
children: [/* @__PURE__ */ m(o, { className: "h-5 w-5" }), "Workspace State"]
|
|
96
|
+
}), C ? /* @__PURE__ */ m(n, {}) : /* @__PURE__ */ h("div", {
|
|
97
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ m(t, {
|
|
100
|
+
title: "Time Entries",
|
|
101
|
+
value: N?.timeEntryCount ?? 0
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ m(t, {
|
|
104
|
+
title: "Projects",
|
|
105
|
+
value: N?.projectCount ?? 0
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ m(t, {
|
|
108
|
+
title: "Tasks",
|
|
109
|
+
value: N?.taskCount ?? 0
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ m(t, {
|
|
112
|
+
title: "Pomodoro Sessions",
|
|
113
|
+
value: N?.pomodoroSessionCount ?? 0
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ m(t, {
|
|
116
|
+
title: "Meetings",
|
|
117
|
+
value: N?.meetingCount ?? 0
|
|
118
|
+
}),
|
|
119
|
+
/* @__PURE__ */ m(t, {
|
|
120
|
+
title: "Expenses",
|
|
121
|
+
value: N?.expenseCount ?? 0
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ m(t, {
|
|
124
|
+
title: "Invoices",
|
|
125
|
+
value: N?.invoiceCount ?? 0
|
|
126
|
+
}),
|
|
127
|
+
/* @__PURE__ */ m(t, {
|
|
128
|
+
title: "Pending Approvals",
|
|
129
|
+
value: N?.pendingApprovalCount ?? 0
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ m(t, {
|
|
132
|
+
title: "Location Entries",
|
|
133
|
+
value: N?.locationEntryCount ?? 0
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ m(t, {
|
|
136
|
+
title: "Focus Scores",
|
|
137
|
+
value: N?.focusScoreCount ?? 0
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ m(t, {
|
|
140
|
+
title: "App Usage Records",
|
|
141
|
+
value: N?.applicationUsageCount ?? 0
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
})]
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ h("section", {
|
|
147
|
+
className: "mb-8",
|
|
148
|
+
children: [/* @__PURE__ */ h("h2", {
|
|
149
|
+
className: "mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
150
|
+
children: [/* @__PURE__ */ m(s, { className: "h-5 w-5" }), "Feature Flags"]
|
|
151
|
+
}), D ? /* @__PURE__ */ m(n, {}) : /* @__PURE__ */ m(g, {
|
|
152
|
+
className: "overflow-hidden",
|
|
153
|
+
children: /* @__PURE__ */ h("table", {
|
|
154
|
+
className: "w-full text-sm",
|
|
155
|
+
children: [/* @__PURE__ */ m("thead", {
|
|
156
|
+
className: "bg-bg-subtle",
|
|
157
|
+
children: /* @__PURE__ */ h("tr", { children: [
|
|
158
|
+
/* @__PURE__ */ m("th", {
|
|
159
|
+
className: "px-4 py-3 text-left font-medium text-text-secondary",
|
|
160
|
+
children: "Key"
|
|
161
|
+
}),
|
|
162
|
+
/* @__PURE__ */ m("th", {
|
|
163
|
+
className: "px-4 py-3 text-left font-medium text-text-secondary",
|
|
164
|
+
children: "Category"
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ m("th", {
|
|
167
|
+
className: "px-4 py-3 text-left font-medium text-text-secondary",
|
|
168
|
+
children: "Default"
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ m("th", {
|
|
171
|
+
className: "px-4 py-3 text-left font-medium text-text-secondary",
|
|
172
|
+
children: "Current"
|
|
173
|
+
})
|
|
174
|
+
] })
|
|
175
|
+
}), /* @__PURE__ */ m("tbody", { children: P.map((e) => /* @__PURE__ */ h("tr", {
|
|
176
|
+
className: "border-t border-border-subtle",
|
|
177
|
+
children: [
|
|
178
|
+
/* @__PURE__ */ m("td", {
|
|
179
|
+
className: "px-4 py-3 font-mono text-text-primary",
|
|
180
|
+
children: e.key
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ m("td", {
|
|
183
|
+
className: "px-4 py-3 text-text-secondary",
|
|
184
|
+
children: e.category
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ m("td", {
|
|
187
|
+
className: "px-4 py-3 text-text-secondary",
|
|
188
|
+
children: String(e.defaultValue)
|
|
189
|
+
}),
|
|
190
|
+
/* @__PURE__ */ m("td", {
|
|
191
|
+
className: "px-4 py-3",
|
|
192
|
+
children: /* @__PURE__ */ m("span", {
|
|
193
|
+
className: `inline-flex rounded-full px-2 py-1 text-xs font-medium ${e.value ? "bg-status-success-bg text-status-success-text" : "bg-status-error-bg text-status-error-text"}`,
|
|
194
|
+
children: e.value ? "Enabled" : "Disabled"
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
]
|
|
198
|
+
}, e.key)) })]
|
|
199
|
+
})
|
|
200
|
+
})]
|
|
201
|
+
}),
|
|
202
|
+
/* @__PURE__ */ h("section", { children: [/* @__PURE__ */ h("h2", {
|
|
203
|
+
className: "mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
204
|
+
children: [/* @__PURE__ */ m(c, { className: "h-5 w-5" }), "Maintenance"]
|
|
205
|
+
}), /* @__PURE__ */ h(g, {
|
|
206
|
+
className: "flex flex-wrap items-center gap-4 p-4",
|
|
207
|
+
children: [/* @__PURE__ */ h("button", {
|
|
208
|
+
type: "button",
|
|
209
|
+
onClick: async () => {
|
|
210
|
+
if (!window.confirm("Reprocess timeline categorization for this workspace?")) return;
|
|
211
|
+
let e = await k({ variables: { workspaceId: r } });
|
|
212
|
+
x(e.data?.reprocessTimeCampusTimeline?.message ?? "Done");
|
|
213
|
+
},
|
|
214
|
+
disabled: A,
|
|
215
|
+
className: "inline-flex items-center gap-2 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",
|
|
216
|
+
children: [/* @__PURE__ */ m(c, { className: "h-4 w-4" }), "Reprocess Timeline"]
|
|
217
|
+
}), /* @__PURE__ */ h("button", {
|
|
218
|
+
type: "button",
|
|
219
|
+
onClick: async () => {
|
|
220
|
+
if (!window.confirm("Reset demo data for this workspace? This cannot be undone.")) return;
|
|
221
|
+
let e = await j({ variables: { workspaceId: r } });
|
|
222
|
+
x(e.data?.resetTimeCampusDemoData?.message ?? "Done"), await T();
|
|
223
|
+
},
|
|
224
|
+
disabled: M,
|
|
225
|
+
className: "inline-flex items-center gap-2 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",
|
|
226
|
+
children: [/* @__PURE__ */ m(l, { className: "h-4 w-4" }), "Reset Demo Data"]
|
|
227
|
+
})]
|
|
228
|
+
})] })
|
|
229
|
+
]
|
|
230
|
+
});
|
|
231
|
+
}, S = () => /* @__PURE__ */ m(r, { children: /* @__PURE__ */ m(x, {}) });
|
|
232
|
+
//#endregion
|
|
233
|
+
export { S as AdminPage };
|
|
234
|
+
|
|
235
|
+
//# sourceMappingURL=AdminPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminPage.js","names":[],"sources":["../../../../src/timecampus/pages/admin/AdminPage.tsx"],"sourcesContent":["/**\n * TimeCampus Admin Page\n *\n * Product-specific governance surface mounted inside `admin-app` at\n * `/admin/timecampus`. Displays workspace state and feature flags, and\n * exposes super-admin maintenance mutations.\n */\n\nimport { useState, type FC } from \"react\";\nimport { gql } from \"@apollo/client\";\nimport { useQuery, useMutation } from \"@apollo/client/react\";\nimport {\n Activity,\n Flag,\n RefreshCw,\n RotateCcw,\n ShieldAlert,\n} from \"lucide-react\";\n\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { ErrorBoundary } from \"../../components/ErrorBoundary\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\n\nconst GET_WORKSPACE_STATE = gql`\n query TimeCampusAdminWorkspaceState($workspaceId: ID!) {\n timeCampusAdminWorkspaceState(workspaceId: $workspaceId) {\n workspaceId\n timeEntryCount\n projectCount\n taskCount\n pomodoroSessionCount\n meetingCount\n expenseCount\n invoiceCount\n pendingApprovalCount\n locationEntryCount\n focusScoreCount\n applicationUsageCount\n generatedAt\n }\n }\n`;\n\nconst GET_FEATURE_FLAGS = gql`\n query TimeCampusFeatureFlags($workspaceId: ID!) {\n timeCampusFeatureFlags(workspaceId: $workspaceId) {\n workspaceId\n items {\n key\n defaultValue\n value\n category\n }\n }\n }\n`;\n\nconst REPROCESS_TIMELINE = gql`\n mutation ReprocessTimeCampusTimeline($workspaceId: ID!) {\n reprocessTimeCampusTimeline(workspaceId: $workspaceId) {\n success\n message\n }\n }\n`;\n\nconst RESET_DEMO_DATA = gql`\n mutation ResetTimeCampusDemoData($workspaceId: ID!) {\n resetTimeCampusDemoData(workspaceId: $workspaceId) {\n success\n message\n }\n }\n`;\n\ninterface FeatureFlagItem {\n key: string;\n defaultValue: boolean;\n value: boolean;\n category: string;\n}\n\ninterface WorkspaceStateData {\n timeCampusAdminWorkspaceState: {\n workspaceId: string;\n timeEntryCount: number;\n projectCount: number;\n taskCount: number;\n pomodoroSessionCount: number;\n meetingCount: number;\n expenseCount: number;\n invoiceCount: number;\n pendingApprovalCount: number;\n locationEntryCount: number;\n focusScoreCount: number;\n applicationUsageCount: number;\n generatedAt: string;\n };\n}\n\ninterface FeatureFlagsData {\n timeCampusFeatureFlags: {\n workspaceId: string;\n items: FeatureFlagItem[];\n };\n}\n\ninterface MutationResult {\n reprocessTimeCampusTimeline: { success: boolean; message: string };\n resetTimeCampusDemoData: { success: boolean; message: string };\n}\n\ninterface WorkspaceVariables {\n workspaceId: string;\n}\n\nconst AdminContent: FC = () => {\n const { workspaceId = \"\" } = useTimeCampus();\n const [actionMessage, setActionMessage] = useState<string | null>(null);\n\n const {\n data: stateData,\n loading: stateLoading,\n error: stateError,\n refetch: refetchState,\n } = useQuery(GET_WORKSPACE_STATE, {\n variables: { workspaceId } as WorkspaceVariables,\n skip: !workspaceId,\n });\n\n const {\n data: flagsData,\n loading: flagsLoading,\n error: flagsError,\n } = useQuery(GET_FEATURE_FLAGS, {\n variables: { workspaceId } as WorkspaceVariables,\n skip: !workspaceId,\n });\n\n const [reprocessTimeline, { loading: reprocessLoading }] =\n useMutation(REPROCESS_TIMELINE);\n const [resetDemoData, { loading: resetLoading }] =\n useMutation(RESET_DEMO_DATA);\n\n if (!workspaceId) {\n return (\n <div className=\"flex min-h-[40vh] items-center justify-center text-text-secondary\">\n <ShieldAlert className=\"mr-2 h-5 w-5\" />\n Workspace context is required.\n </div>\n );\n }\n\n const state = (stateData as WorkspaceStateData | undefined)\n ?.timeCampusAdminWorkspaceState;\n const flags = ((flagsData as FeatureFlagsData | undefined)\n ?.timeCampusFeatureFlags?.items ?? []) as FeatureFlagItem[];\n\n const handleReprocess = async (): Promise<void> => {\n if (\n !window.confirm(\"Reprocess timeline categorization for this workspace?\")\n )\n return;\n const result = await reprocessTimeline({\n variables: { workspaceId } as WorkspaceVariables,\n });\n setActionMessage(\n (result.data as MutationResult | undefined | null)\n ?.reprocessTimeCampusTimeline?.message ?? \"Done\",\n );\n };\n\n const handleResetDemo = async (): Promise<void> => {\n if (\n !window.confirm(\n \"Reset demo data for this workspace? This cannot be undone.\",\n )\n )\n return;\n const result = await resetDemoData({\n variables: { workspaceId } as WorkspaceVariables,\n });\n setActionMessage(\n (result.data as MutationResult | undefined | null)\n ?.resetTimeCampusDemoData?.message ?? \"Done\",\n );\n await refetchState();\n };\n\n return (\n <PageLayout\n title=\"TimeCampus Admin\"\n description=\"Workspace state, feature flags, and super-admin maintenance actions.\"\n >\n {actionMessage && (\n <div className=\"mb-4 rounded-md bg-status-info-bg px-4 py-3 text-sm text-status-info-text\">\n {actionMessage}\n <button\n type=\"button\"\n onClick={() => setActionMessage(null)}\n className=\"ml-4 underline\"\n >\n Dismiss\n </button>\n </div>\n )}\n\n {(stateError || flagsError) && (\n <div className=\"mb-4 rounded-md bg-status-error-bg px-4 py-3 text-sm text-status-error-text\">\n Failed to load admin data.{\" \"}\n {stateError?.message ?? flagsError?.message}\n </div>\n )}\n\n <section className=\"mb-8\">\n <h2 className=\"mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Activity className=\"h-5 w-5\" />\n Workspace State\n </h2>\n {stateLoading ? (\n <LoadingSkeleton />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard title=\"Time Entries\" value={state?.timeEntryCount ?? 0} />\n <StatCard title=\"Projects\" value={state?.projectCount ?? 0} />\n <StatCard title=\"Tasks\" value={state?.taskCount ?? 0} />\n <StatCard\n title=\"Pomodoro Sessions\"\n value={state?.pomodoroSessionCount ?? 0}\n />\n <StatCard title=\"Meetings\" value={state?.meetingCount ?? 0} />\n <StatCard title=\"Expenses\" value={state?.expenseCount ?? 0} />\n <StatCard title=\"Invoices\" value={state?.invoiceCount ?? 0} />\n <StatCard\n title=\"Pending Approvals\"\n value={state?.pendingApprovalCount ?? 0}\n />\n <StatCard\n title=\"Location Entries\"\n value={state?.locationEntryCount ?? 0}\n />\n <StatCard\n title=\"Focus Scores\"\n value={state?.focusScoreCount ?? 0}\n />\n <StatCard\n title=\"App Usage Records\"\n value={state?.applicationUsageCount ?? 0}\n />\n </div>\n )}\n </section>\n\n <section className=\"mb-8\">\n <h2 className=\"mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Flag className=\"h-5 w-5\" />\n Feature Flags\n </h2>\n {flagsLoading ? (\n <LoadingSkeleton />\n ) : (\n <GlassCard className=\"overflow-hidden\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-subtle\">\n <tr>\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary\">\n Key\n </th>\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary\">\n Category\n </th>\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary\">\n Default\n </th>\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary\">\n Current\n </th>\n </tr>\n </thead>\n <tbody>\n {flags.map((flag) => (\n <tr key={flag.key} className=\"border-t border-border-subtle\">\n <td className=\"px-4 py-3 font-mono text-text-primary\">\n {flag.key}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {flag.category}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {String(flag.defaultValue)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex rounded-full px-2 py-1 text-xs font-medium ${\n flag.value\n ? \"bg-status-success-bg text-status-success-text\"\n : \"bg-status-error-bg text-status-error-text\"\n }`}\n >\n {flag.value ? \"Enabled\" : \"Disabled\"}\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </GlassCard>\n )}\n </section>\n\n <section>\n <h2 className=\"mb-4 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <RefreshCw className=\"h-5 w-5\" />\n Maintenance\n </h2>\n <GlassCard className=\"flex flex-wrap items-center gap-4 p-4\">\n <button\n type=\"button\"\n onClick={handleReprocess}\n disabled={reprocessLoading}\n className=\"inline-flex items-center gap-2 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 <RefreshCw className=\"h-4 w-4\" />\n Reprocess Timeline\n </button>\n <button\n type=\"button\"\n onClick={handleResetDemo}\n disabled={resetLoading}\n className=\"inline-flex items-center gap-2 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 <RotateCcw className=\"h-4 w-4\" />\n Reset Demo Data\n </button>\n </GlassCard>\n </section>\n </PageLayout>\n );\n};\n\nexport const AdminPage: FC = () => {\n return (\n <ErrorBoundary>\n <AdminContent />\n </ErrorBoundary>\n );\n};\n"],"mappings":"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"}
|
|
@@ -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":"AA2EA,wBAAgB,oBAAoB,gCAyjBnC"}
|
|
@@ -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} 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: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n applicationUsages: [\n ...prev.applicationUsages,\n ...fetchMoreResult.applicationUsages,\n ],\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const totalScreenTimeSeconds = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.timeSpent,\n 0,\n );\n const totalSwitches = apps.reduce(\n (sum: number, app: ApplicationUsageFieldsFragment) => sum + app.switches,\n 0,\n );\n const productiveRatio =\n summary?.productivityRatio != null\n ? `${Math.round(summary.productivityRatio * 100)}%`\n : \"--\";\n\n // Count focus sessions (apps with > 90 min continuous usage)\n const focusSessions = apps.filter(\n (app: ApplicationUsageFieldsFragment) => app.timeSpent >= 5400,\n ).length;\n\n // 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 } 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientPortalPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ClientPortalPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/client-portal/ClientPortalPage.tsx"],"names":[],"mappings":"AAiGA,wBAAgB,gBAAgB,gCA8rB/B"}
|