@burdenoff/microfe-timecampus 2026.625.1 → 2026.626.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/timecampus/components/EmptyState.js +14 -0
- package/dist/timecampus/components/EmptyState.js.map +1 -0
- package/dist/timecampus/components/PageLayout.js +23 -15
- package/dist/timecampus/components/PageLayout.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +33 -30
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +37 -31
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +48 -44
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js +35 -31
- package/dist/timecampus/pages/application-usage/ApplicationUsageSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +27 -24
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +46 -38
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js +66 -39
- package/dist/timecampus/pages/dashboard/AnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +42 -36
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/email-tracking/EmailAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js +35 -31
- package/dist/timecampus/pages/email-tracking/EmailSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +31 -25
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +43 -35
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +55 -51
- package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js +32 -28
- package/dist/timecampus/pages/focus-score/FocusScoreSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +32 -25
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js +29 -25
- package/dist/timecampus/pages/geofencing/GeofencingSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +32 -29
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js +29 -25
- package/dist/timecampus/pages/location-tracking/LocationPrivacySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +39 -35
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +33 -29
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +122 -104
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +42 -32
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +2 -0
- package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +145 -114
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js +49 -45
- package/dist/timecampus/pages/pomodoro/PomodoroSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +28 -24
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +109 -78
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +11 -7
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +40 -30
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +50 -46
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +18 -14
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +41 -35
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +91 -61
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +40 -36
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js +33 -29
- package/dist/timecampus/pages/team-activity/TeamActivitySettingsPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js +17 -13
- package/dist/timecampus/pages/templates/TemplateApplicationPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +67 -49
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +34 -31
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +48 -39
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +65 -64
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +103 -69
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +53 -49
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +65 -47
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +38 -31
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +2 -0
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js +35 -31
- package/dist/timecampus/pages/work-patterns/WorkPatternsSettingsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +43 -33
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -4,101 +4,102 @@ import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
|
4
4
|
import { useTimeCampusStore as i } from "../../store/timecampusStore.js";
|
|
5
5
|
import { useGetTimeEntryStatsQuery as a, useListTimeEntriesQuery as o } from "../../../generated/wspace-operations.js";
|
|
6
6
|
import { PageLayout as s } from "../../components/PageLayout.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { EmptyState as c } from "../../components/EmptyState.js";
|
|
8
|
+
import { Clock as l, Pause as u, Play as d, Square as f } from "lucide-react";
|
|
9
|
+
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
10
|
+
import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
11
|
//#region src/timecampus/pages/time/TimeTrackingPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let { t:
|
|
13
|
-
let n =
|
|
12
|
+
function _() {
|
|
13
|
+
let { t: _ } = g(), v = (e, t) => {
|
|
14
|
+
let n = _(e);
|
|
14
15
|
return n === e ? t : n;
|
|
15
|
-
}, { activeTimer:
|
|
16
|
-
return
|
|
17
|
-
title:
|
|
18
|
-
description:
|
|
19
|
-
children: /* @__PURE__ */
|
|
16
|
+
}, { activeTimer: y, startTimer: b, stopTimer: x } = i(), { data: S, loading: C } = a(), { data: w, loading: T } = o({ variables: { limit: 10 } }), E = S?.timeEntryStats, D = w?.timeEntries?.items ?? [];
|
|
17
|
+
return C ? /* @__PURE__ */ m(s, {
|
|
18
|
+
title: v("timecampus.timeTracking.title", "Time Tracking"),
|
|
19
|
+
description: v("timecampus.timeTracking.description", "Track and manage your work hours"),
|
|
20
|
+
children: /* @__PURE__ */ m(r, {
|
|
20
21
|
variant: "card",
|
|
21
22
|
count: 4
|
|
22
23
|
})
|
|
23
|
-
}) : /* @__PURE__ */
|
|
24
|
-
title:
|
|
25
|
-
description:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
}) : /* @__PURE__ */ h(s, {
|
|
25
|
+
title: v("timecampus.timeTracking.title", "Time Tracking"),
|
|
26
|
+
description: v("timecampus.timeTracking.description", "Track and manage your work hours"),
|
|
27
|
+
purpose: v("timecampus.timeTracking.purpose", "Start a timer the moment you begin work and your hours are captured automatically — review today's totals, see what's billable, and stop guessing where your day went."),
|
|
28
|
+
actions: /* @__PURE__ */ m("button", {
|
|
29
|
+
onClick: () => y ? x() : b(),
|
|
30
|
+
className: `inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${y ? "bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"}`,
|
|
31
|
+
children: y ? /* @__PURE__ */ h(p, { children: [
|
|
32
|
+
/* @__PURE__ */ m(f, { className: "h-4 w-4" }),
|
|
33
|
+
v("timecampus.timeTracking.actions.stop", "Stop"),
|
|
32
34
|
" (",
|
|
33
|
-
t(
|
|
35
|
+
t(y.seconds),
|
|
34
36
|
")"
|
|
35
|
-
] }) : /* @__PURE__ */
|
|
37
|
+
] }) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m(d, { className: "h-4 w-4" }), v("timecampus.timeTracking.actions.startTimer", "Start Timer")] })
|
|
36
38
|
}),
|
|
37
|
-
children: [/* @__PURE__ */
|
|
39
|
+
children: [/* @__PURE__ */ h("div", {
|
|
38
40
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
39
41
|
children: [
|
|
40
|
-
/* @__PURE__ */
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
/* @__PURE__ */ m(n, {
|
|
43
|
+
featured: !0,
|
|
44
|
+
title: v("timecampus.timeTracking.stats.today", "Today"),
|
|
45
|
+
value: e(E?.totalHours ?? 0),
|
|
46
|
+
icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
|
|
44
47
|
trend: {
|
|
45
48
|
value: 15,
|
|
46
|
-
label:
|
|
49
|
+
label: v("timecampus.timeTracking.stats.vsYesterday", "vs yesterday")
|
|
47
50
|
}
|
|
48
51
|
}),
|
|
49
|
-
/* @__PURE__ */
|
|
50
|
-
title:
|
|
51
|
-
value: e(
|
|
52
|
-
icon: /* @__PURE__ */
|
|
53
|
-
description: `${
|
|
52
|
+
/* @__PURE__ */ m(n, {
|
|
53
|
+
title: v("timecampus.timeTracking.stats.billable", "Billable"),
|
|
54
|
+
value: e(E?.billableHours ?? 0),
|
|
55
|
+
icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
|
|
56
|
+
description: `${E?.totalEntries ?? 0} ${v("timecampus.timeTracking.stats.entries", "entries")}`
|
|
54
57
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
-
title:
|
|
57
|
-
value:
|
|
58
|
-
icon: /* @__PURE__ */
|
|
59
|
-
description:
|
|
58
|
+
/* @__PURE__ */ m(n, {
|
|
59
|
+
title: v("timecampus.timeTracking.stats.activeTimers", "Active Timers"),
|
|
60
|
+
value: E?.activeTimers ?? 0,
|
|
61
|
+
icon: /* @__PURE__ */ m(l, { className: "h-5 w-5" }),
|
|
62
|
+
description: v("timecampus.timeTracking.stats.currentlyRunning", "Currently running")
|
|
60
63
|
}),
|
|
61
|
-
/* @__PURE__ */
|
|
62
|
-
title:
|
|
63
|
-
value:
|
|
64
|
-
icon:
|
|
64
|
+
/* @__PURE__ */ m(n, {
|
|
65
|
+
title: v("timecampus.timeTracking.stats.activeTimer", "Active Timer"),
|
|
66
|
+
value: y ? t(y.seconds) : v("timecampus.timeTracking.stats.stopped", "Stopped"),
|
|
67
|
+
icon: m(y ? u : d, { className: "h-5 w-5" })
|
|
65
68
|
})
|
|
66
69
|
]
|
|
67
|
-
}), /* @__PURE__ */
|
|
70
|
+
}), /* @__PURE__ */ h("div", {
|
|
68
71
|
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
72
|
+
children: [/* @__PURE__ */ m("h3", {
|
|
70
73
|
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
71
|
-
children:
|
|
72
|
-
}),
|
|
74
|
+
children: v("timecampus.timeTracking.entries.title", "Today's Entries")
|
|
75
|
+
}), T ? /* @__PURE__ */ m(r, {
|
|
73
76
|
variant: "list",
|
|
74
77
|
count: 4
|
|
75
|
-
}) :
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})
|
|
81
|
-
}) : /* @__PURE__ */ p("div", {
|
|
78
|
+
}) : D.length === 0 ? /* @__PURE__ */ m(c, {
|
|
79
|
+
illustration: "empty-data",
|
|
80
|
+
title: v("timecampus.timeTracking.entries.empty.title", "No time entries yet"),
|
|
81
|
+
description: v("timecampus.timeTracking.entries.empty", "No time entries yet. Start a timer to begin tracking.")
|
|
82
|
+
}) : /* @__PURE__ */ m("div", {
|
|
82
83
|
className: "flex flex-col gap-2",
|
|
83
|
-
children:
|
|
84
|
+
children: D.map((t) => /* @__PURE__ */ h("div", {
|
|
84
85
|
className: "flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ h("div", {
|
|
86
87
|
className: "min-w-0 flex-1",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
88
|
+
children: [/* @__PURE__ */ m("p", {
|
|
88
89
|
className: "text-sm font-medium text-text-primary",
|
|
89
|
-
children: t.project?.name ??
|
|
90
|
-
}), /* @__PURE__ */
|
|
90
|
+
children: t.project?.name ?? v("timecampus.timeTracking.entries.noProject", "No Project")
|
|
91
|
+
}), /* @__PURE__ */ m("p", {
|
|
91
92
|
className: "text-xs text-text-secondary",
|
|
92
|
-
children: t.description ??
|
|
93
|
+
children: t.description ?? v("timecampus.timeTracking.entries.noDescription", "No description")
|
|
93
94
|
})]
|
|
94
|
-
}), /* @__PURE__ */
|
|
95
|
+
}), /* @__PURE__ */ h("div", {
|
|
95
96
|
className: "ml-4 text-right",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
97
|
+
children: [/* @__PURE__ */ m("p", {
|
|
97
98
|
className: "text-sm font-mono font-medium text-text-primary",
|
|
98
99
|
children: e((t.duration ?? 0) / 3600)
|
|
99
|
-
}), /* @__PURE__ */
|
|
100
|
+
}), /* @__PURE__ */ m("span", {
|
|
100
101
|
className: "inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
101
|
-
children: t.isRunning ?
|
|
102
|
+
children: t.isRunning ? v("timecampus.timeTracking.entries.status.running", "running") : v("timecampus.timeTracking.entries.status.completed", "completed")
|
|
102
103
|
})]
|
|
103
104
|
})]
|
|
104
105
|
}, t.id))
|
|
@@ -107,6 +108,6 @@ function g() {
|
|
|
107
108
|
});
|
|
108
109
|
}
|
|
109
110
|
//#endregion
|
|
110
|
-
export {
|
|
111
|
+
export { _ as TimeTrackingPage };
|
|
111
112
|
|
|
112
113
|
//# sourceMappingURL=TimeTrackingPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90\" : \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"}`}\n >\n {activeTimer ? (\n <>\n <Square className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.stop\", \"Stop\")} (\n {formatTimerDuration(activeTimer.seconds)})\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.startTimer\", \"Start Timer\")}\n </>\n )}\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title={t(\"timecampus.timeTracking.stats.today\", \"Today\")}\n value={formatHours(stats?.totalHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{\n value: 15,\n label: t(\n \"timecampus.timeTracking.stats.vsYesterday\",\n \"vs yesterday\",\n ),\n }}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.billable\", \"Billable\")}\n value={formatHours(stats?.billableHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n description={`${stats?.totalEntries ?? 0} ${t(\"timecampus.timeTracking.stats.entries\", \"entries\")}`}\n />\n <StatCard\n title={t(\n \"timecampus.timeTracking.stats.activeTimers\",\n \"Active Timers\",\n )}\n value={stats?.activeTimers ?? 0}\n icon={<Clock className=\"h-5 w-5\" />}\n description={t(\n \"timecampus.timeTracking.stats.currentlyRunning\",\n \"Currently running\",\n )}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.activeTimer\", \"Active Timer\")}\n value={\n activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : t(\"timecampus.timeTracking.stats.stopped\", \"Stopped\")\n }\n icon={\n activeTimer ? (\n <Pause className=\"h-5 w-5\" />\n ) : (\n <Play className=\"h-5 w-5\" />\n )\n }\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {t(\"timecampus.timeTracking.entries.title\", \"Today's Entries\")}\n </h3>\n {entriesLoading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : todayEntries.length === 0 ? (\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <p className=\"text-sm text-text-secondary\">\n {t(\n \"timecampus.timeTracking.entries.empty\",\n \"No time entries yet. Start a timer to begin tracking.\",\n )}\n </p>\n </div>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {todayEntries.map((entry: TimeEntryFieldsFragment) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ??\n t(\n \"timecampus.timeTracking.entries.noProject\",\n \"No Project\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {entry.description ??\n t(\n \"timecampus.timeTracking.entries.noDescription\",\n \"No description\",\n )}\n </p>\n </div>\n <div className=\"ml-4 text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {entry.isRunning\n ? t(\n \"timecampus.timeTracking.entries.status.running\",\n \"running\",\n )\n : t(\n \"timecampus.timeTracking.entries.status.completed\",\n \"completed\",\n )}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeTrackingPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeTrackingPage.tsx"],"sourcesContent":["import { Clock, Play, Pause, Square } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours, formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useGetTimeEntryStatsQuery,\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport function TimeTrackingPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: statsData, loading: statsLoading } =\n useGetTimeEntryStatsQuery();\n const { data: entriesData, loading: entriesLoading } =\n useListTimeEntriesQuery({\n variables: { limit: 10 },\n });\n\n const stats = statsData?.timeEntryStats;\n const todayEntries = entriesData?.timeEntries?.items ?? [];\n\n if (statsLoading) {\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.timeTracking.title\", \"Time Tracking\")}\n description={t(\n \"timecampus.timeTracking.description\",\n \"Track and manage your work hours\",\n )}\n purpose={t(\n \"timecampus.timeTracking.purpose\",\n \"Start a timer the moment you begin work and your hours are captured automatically — review today's totals, see what's billable, and stop guessing where your day went.\",\n )}\n actions={\n <button\n onClick={() => (activeTimer ? stopTimer() : startTimer())}\n className={`inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium ${activeTimer ? \"bg-action-danger-bg text-action-primary-text hover:bg-action-danger-bg/90\" : \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\"}`}\n >\n {activeTimer ? (\n <>\n <Square className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.stop\", \"Stop\")} (\n {formatTimerDuration(activeTimer.seconds)})\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4\" />\n {t(\"timecampus.timeTracking.actions.startTimer\", \"Start Timer\")}\n </>\n )}\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title={t(\"timecampus.timeTracking.stats.today\", \"Today\")}\n value={formatHours(stats?.totalHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n trend={{\n value: 15,\n label: t(\n \"timecampus.timeTracking.stats.vsYesterday\",\n \"vs yesterday\",\n ),\n }}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.billable\", \"Billable\")}\n value={formatHours(stats?.billableHours ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n description={`${stats?.totalEntries ?? 0} ${t(\"timecampus.timeTracking.stats.entries\", \"entries\")}`}\n />\n <StatCard\n title={t(\n \"timecampus.timeTracking.stats.activeTimers\",\n \"Active Timers\",\n )}\n value={stats?.activeTimers ?? 0}\n icon={<Clock className=\"h-5 w-5\" />}\n description={t(\n \"timecampus.timeTracking.stats.currentlyRunning\",\n \"Currently running\",\n )}\n />\n <StatCard\n title={t(\"timecampus.timeTracking.stats.activeTimer\", \"Active Timer\")}\n value={\n activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : t(\"timecampus.timeTracking.stats.stopped\", \"Stopped\")\n }\n icon={\n activeTimer ? (\n <Pause className=\"h-5 w-5\" />\n ) : (\n <Play className=\"h-5 w-5\" />\n )\n }\n />\n </div>\n\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n {t(\"timecampus.timeTracking.entries.title\", \"Today's Entries\")}\n </h3>\n {entriesLoading ? (\n <LoadingSkeleton variant=\"list\" count={4} />\n ) : todayEntries.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={t(\n \"timecampus.timeTracking.entries.empty.title\",\n \"No time entries yet\",\n )}\n description={t(\n \"timecampus.timeTracking.entries.empty\",\n \"No time entries yet. Start a timer to begin tracking.\",\n )}\n />\n ) : (\n <div className=\"flex flex-col gap-2\">\n {todayEntries.map((entry: TimeEntryFieldsFragment) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3 transition-colors hover:bg-bg-sunken/50\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ??\n t(\n \"timecampus.timeTracking.entries.noProject\",\n \"No Project\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {entry.description ??\n t(\n \"timecampus.timeTracking.entries.noDescription\",\n \"No description\",\n )}\n </p>\n </div>\n <div className=\"ml-4 text-right\">\n <p className=\"text-sm font-mono font-medium text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {entry.isRunning\n ? t(\n \"timecampus.timeTracking.entries.status.running\",\n \"running\",\n )\n : t(\n \"timecampus.timeTracking.entries.status.completed\",\n \"completed\",\n )}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,80 +1,114 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { useTimeCampusNavigate as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { formatTimerDuration as t } from "../../utils/helpers.js";
|
|
3
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
4
|
+
import { useTimeCampusStore as r } from "../../store/timecampusStore.js";
|
|
5
|
+
import { useListRunningTimersQuery as i } from "../../../generated/wspace-operations.js";
|
|
6
|
+
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
7
|
+
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
8
|
+
import { Play as s, RotateCcw as c, Square as l } from "lucide-react";
|
|
9
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
10
|
+
import { GlassCard as f, NextSteps as p } from "@burdenoff/fe-libs/ui";
|
|
8
11
|
//#region src/timecampus/pages/time/TimerPage.tsx
|
|
9
|
-
function
|
|
10
|
-
let { activeTimer:
|
|
11
|
-
return /* @__PURE__ */
|
|
12
|
+
function m() {
|
|
13
|
+
let { activeTimer: m, startTimer: h, stopTimer: g } = r(), _ = e(), { data: v, loading: y } = i(), b = v?.runningTimers ?? [];
|
|
14
|
+
return /* @__PURE__ */ u(a, {
|
|
12
15
|
title: "Timer",
|
|
13
16
|
description: "Focus timer for deep work sessions",
|
|
14
|
-
|
|
17
|
+
purpose: "A single, distraction-free stopwatch for the task in front of you — start it when you begin focused work and it logs the session as a time entry you can attribute to a project later.",
|
|
18
|
+
children: /* @__PURE__ */ d("div", {
|
|
15
19
|
className: "mx-auto max-w-lg",
|
|
16
|
-
children: [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ u(p, {
|
|
22
|
+
storageKey: "timecampus-timer",
|
|
23
|
+
className: "mb-6",
|
|
24
|
+
steps: [
|
|
25
|
+
{
|
|
26
|
+
id: "start",
|
|
27
|
+
label: m ? "Stop the running timer" : "Start the timer",
|
|
28
|
+
description: m ? "End this session and save it as a time entry." : "Begin tracking the task you're working on right now.",
|
|
29
|
+
onClick: () => m ? g() : h()
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "assign-project",
|
|
33
|
+
label: "Assign work to a project",
|
|
34
|
+
description: "Pick a project so this time bills correctly.",
|
|
35
|
+
onClick: () => _("/projects")
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "review-entries",
|
|
39
|
+
label: "Review your time entries",
|
|
40
|
+
description: "Check and tidy everything you've logged.",
|
|
41
|
+
onClick: () => _("/time/entries")
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ d(f, {
|
|
46
|
+
treatment: "glass",
|
|
47
|
+
glow: !0,
|
|
48
|
+
className: "p-8 text-center",
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ u("p", {
|
|
51
|
+
className: "text-6xl font-mono font-bold text-text-primary",
|
|
52
|
+
children: m ? t(m.seconds) : "00:00:00"
|
|
53
|
+
}),
|
|
54
|
+
m?.description && /* @__PURE__ */ u("p", {
|
|
55
|
+
className: "mt-2 text-sm text-text-secondary",
|
|
56
|
+
children: m.description
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ d("div", {
|
|
59
|
+
className: "mt-6 flex items-center justify-center gap-3",
|
|
60
|
+
children: [m ? /* @__PURE__ */ d("button", {
|
|
61
|
+
onClick: g,
|
|
62
|
+
className: "inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90",
|
|
63
|
+
children: [/* @__PURE__ */ u(l, { className: "h-4 w-4" }), "Stop"]
|
|
64
|
+
}) : /* @__PURE__ */ d("button", {
|
|
65
|
+
onClick: () => h(),
|
|
66
|
+
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
67
|
+
children: [/* @__PURE__ */ u(s, { className: "h-4 w-4" }), "Start"]
|
|
68
|
+
}), /* @__PURE__ */ d("button", {
|
|
69
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
70
|
+
children: [/* @__PURE__ */ u(c, { className: "h-4 w-4" }), "Reset"]
|
|
66
71
|
})]
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ d("div", {
|
|
76
|
+
className: "mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6",
|
|
77
|
+
children: [/* @__PURE__ */ u("h3", {
|
|
78
|
+
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
79
|
+
children: "Running Timers"
|
|
80
|
+
}), y ? /* @__PURE__ */ u(n, {
|
|
81
|
+
variant: "list",
|
|
82
|
+
count: 2
|
|
83
|
+
}) : b.length === 0 ? /* @__PURE__ */ u(o, {
|
|
84
|
+
illustration: "empty-sessions",
|
|
85
|
+
title: "No active timers on the server",
|
|
86
|
+
description: "Timers you start sync here so you can pick them up on any device."
|
|
87
|
+
}) : /* @__PURE__ */ u("div", {
|
|
88
|
+
className: "flex flex-col gap-2",
|
|
89
|
+
children: b.map((e) => /* @__PURE__ */ d("div", {
|
|
90
|
+
className: "flex items-center justify-between rounded-lg border border-border-subtle p-3",
|
|
91
|
+
children: [/* @__PURE__ */ d("div", {
|
|
92
|
+
className: "min-w-0 flex-1",
|
|
93
|
+
children: [/* @__PURE__ */ u("p", {
|
|
94
|
+
className: "text-sm font-medium text-text-primary",
|
|
95
|
+
children: e.timeEntry.project?.name ?? "No Project"
|
|
96
|
+
}), /* @__PURE__ */ u("p", {
|
|
97
|
+
className: "text-xs text-text-secondary",
|
|
98
|
+
children: e.timeEntry.description ?? "No description"
|
|
99
|
+
})]
|
|
100
|
+
}), /* @__PURE__ */ u("p", {
|
|
101
|
+
className: "ml-4 text-sm font-mono font-medium text-text-primary",
|
|
102
|
+
children: t(e.elapsedSeconds)
|
|
103
|
+
})]
|
|
104
|
+
}, e.timeEntry.id))
|
|
105
|
+
})]
|
|
106
|
+
})
|
|
107
|
+
]
|
|
74
108
|
})
|
|
75
109
|
});
|
|
76
110
|
}
|
|
77
111
|
//#endregion
|
|
78
|
-
export {
|
|
112
|
+
export { m as TimerPage };
|
|
79
113
|
|
|
80
114
|
//# sourceMappingURL=TimerPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout
|
|
1
|
+
{"version":3,"file":"TimerPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimerPage.tsx"],"sourcesContent":["import { Play, Square, RotateCcw } from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport { formatTimerDuration } from \"../../utils/helpers\";\nimport { useTimeCampusStore } from \"../../store/timecampusStore\";\nimport {\n useListRunningTimersQuery,\n type ListRunningTimersQuery,\n} from \"@/generated/wspace-operations\";\n\ntype RunningTimerItem = ListRunningTimersQuery[\"runningTimers\"][number];\n\nexport function TimerPage() {\n const { activeTimer, startTimer, stopTimer } = useTimeCampusStore();\n const navigate = useTimeCampusNavigate();\n\n const { data: runningData, loading: runningLoading } =\n useListRunningTimersQuery();\n\n const runningTimers = runningData?.runningTimers ?? [];\n\n return (\n <PageLayout\n title=\"Timer\"\n description=\"Focus timer for deep work sessions\"\n purpose=\"A single, distraction-free stopwatch for the task in front of you — start it when you begin focused work and it logs the session as a time entry you can attribute to a project later.\"\n >\n <div className=\"mx-auto max-w-lg\">\n <NextSteps\n storageKey=\"timecampus-timer\"\n className=\"mb-6\"\n steps={[\n {\n id: \"start\",\n label: activeTimer ? \"Stop the running timer\" : \"Start the timer\",\n description: activeTimer\n ? \"End this session and save it as a time entry.\"\n : \"Begin tracking the task you're working on right now.\",\n onClick: () => (activeTimer ? stopTimer() : startTimer()),\n },\n {\n id: \"assign-project\",\n label: \"Assign work to a project\",\n description: \"Pick a project so this time bills correctly.\",\n onClick: () => navigate(\"/projects\"),\n },\n {\n id: \"review-entries\",\n label: \"Review your time entries\",\n description: \"Check and tidy everything you've logged.\",\n onClick: () => navigate(\"/time/entries\"),\n },\n ]}\n />\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <p className=\"text-6xl font-mono font-bold text-text-primary\">\n {activeTimer\n ? formatTimerDuration(activeTimer.seconds)\n : \"00:00:00\"}\n </p>\n {activeTimer?.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {activeTimer.description}\n </p>\n )}\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {activeTimer ? (\n <button\n onClick={stopTimer}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-danger-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90\"\n >\n <Square className=\"h-4 w-4\" />\n Stop\n </button>\n ) : (\n <button\n onClick={() => startTimer()}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n )}\n <button className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\">\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </GlassCard>\n\n {/* Server-side running timers */}\n <div className=\"mt-6 rounded-xl border border-border-seam bg-gloss-card shadow-pop p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Running Timers\n </h3>\n {runningLoading ? (\n <LoadingSkeleton variant=\"list\" count={2} />\n ) : runningTimers.length === 0 ? (\n <EmptyState\n illustration=\"empty-sessions\"\n title=\"No active timers on the server\"\n description=\"Timers you start sync here so you can pick them up on any device.\"\n />\n ) : (\n <div className=\"flex flex-col gap-2\">\n {runningTimers.map((timer: RunningTimerItem) => (\n <div\n key={timer.timeEntry.id}\n className=\"flex items-center justify-between rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {timer.timeEntry.project?.name ?? \"No Project\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {timer.timeEntry.description ?? \"No description\"}\n </p>\n </div>\n <p className=\"ml-4 text-sm font-mono font-medium text-text-primary\">\n {formatTimerDuration(timer.elapsedSeconds)}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAeA,SAAgB,IAAY;CAC1B,IAAM,EAAE,gBAAa,eAAY,iBAAc,GAAoB,EAC7D,IAAW,GAAuB,EAElC,EAAE,MAAM,GAAa,SAAS,MAClC,GAA2B,EAEvB,IAAgB,GAAa,iBAAiB,EAAE;AAEtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;YAER,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,YAAW;KACX,WAAU;KACV,OAAO;MACL;OACE,IAAI;OACJ,OAAO,IAAc,2BAA2B;OAChD,aAAa,IACT,kDACA;OACJ,eAAgB,IAAc,GAAW,GAAG,GAAY;OACzD;MACD;OACE,IAAI;OACJ,OAAO;OACP,aAAa;OACb,eAAe,EAAS,YAAY;OACrC;MACD;OACE,IAAI;OACJ,OAAO;OACP,aAAa;OACb,eAAe,EAAS,gBAAgB;OACzC;MACF;KACD,CAAA;IACF,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,EAAoB,EAAY,QAAQ,GACxC;OACF,CAAA;MACH,GAAa,eACZ,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAY;OACX,CAAA;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,IACC,kBAAC,UAAD;QACE,SAAS;QACT,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,OAEvB;YAET,kBAAC,UAAD;QACE,eAAe,GAAY;QAC3B,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,QAErB;WAEX,kBAAC,UAAD;QAAQ,WAAU;kBAAlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAE1B;UACL;;MACI;;IAGZ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAExD,CAAA,EACJ,IACC,kBAAC,GAAD;MAAiB,SAAQ;MAAO,OAAO;MAAK,CAAA,GAC1C,EAAc,WAAW,IAC3B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAM,UAAU,SAAS,QAAQ;SAChC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAM,UAAU,eAAe;SAC9B,CAAA,CACA;WACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAoB,EAAM,eAAe;QACxC,CAAA,CACA;SAdC,EAAM,UAAU,GAcjB,CACN;MACE,CAAA,CAEJ;;IACF;;EACK,CAAA"}
|