@burdenoff/microfe-timecampus 2026.830.2 → 2026.904.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/timecampus/components/index.js +4 -4
- package/dist/timecampus/pages/HomePage.d.ts.map +1 -1
- package/dist/timecampus/pages/HomePage.js +149 -133
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +7 -7
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +7 -7
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +13 -13
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +7 -7
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +7 -7
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/focus-score/FocusScorePage.js +13 -13
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +135 -135
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/invoicing/InvoicingPage.js +7 -7
- package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/DeadTimeRadarPage.js +8 -8
- package/dist/timecampus/pages/lifetime/FiniteLifePage.js +4 -4
- package/dist/timecampus/pages/lifetime/FiniteLifePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/LifeBalancePage.js +104 -104
- package/dist/timecampus/pages/lifetime/LifeBalancePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/LifeTimelinePage.js +7 -7
- package/dist/timecampus/pages/lifetime/LifeTimelinePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js +33 -33
- package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js +80 -80
- package/dist/timecampus/pages/lifetime/RecommendationsPage.js.map +1 -1
- package/dist/timecampus/pages/lifetime/TimeWellSpentPage.js +16 -16
- package/dist/timecampus/pages/lifetime/ValuesGoalsPage.js +11 -11
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +11 -11
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +14 -14
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingsPage.js +12 -12
- package/dist/timecampus/pages/projects/ProjectsPage.js +5 -5
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +8 -8
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +8 -8
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +8 -8
- package/dist/timecampus/pages/team-activity/TeamActivityPage.js +13 -13
- package/dist/timecampus/pages/time/TimeEntriesPage.js +5 -5
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +138 -138
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +14 -14
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +26 -26
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +8 -8
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +79 -79
- package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@ import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/
|
|
|
2
2
|
import { formatHours as n, getInitials as r } from "../../utils/helpers.js";
|
|
3
3
|
import { StatCard as i } from "../../components/StatCard.js";
|
|
4
4
|
import { LoadingSkeleton as a } from "../../components/LoadingSkeleton.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
6
|
+
import { useListTimeEntriesQuery as s } from "../../../generated/wspace-operations.js";
|
|
7
|
+
import { PageLayout as c } from "../../components/PageLayout.js";
|
|
8
8
|
import { Modal as l } from "../../components/Modal.js";
|
|
9
9
|
import { ChartCard as u } from "../../components/ChartCard.js";
|
|
10
10
|
import { BarsChart as d } from "../../components/charts/BarsChart.js";
|
|
@@ -20,7 +20,7 @@ function S() {
|
|
|
20
20
|
onClick: () => T(!0),
|
|
21
21
|
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",
|
|
22
22
|
children: [/* @__PURE__ */ v(g, { className: "h-4 w-4" }), "Invite Member"]
|
|
23
|
-
}), { data: D, loading: O, error: k } =
|
|
23
|
+
}), { data: D, loading: O, error: k } = s({
|
|
24
24
|
variables: { pagination: { limit: 100 } },
|
|
25
25
|
skip: !S
|
|
26
26
|
}), A = D?.timeEntries?.items ?? [], j = A.reduce((e, t) => {
|
|
@@ -37,7 +37,7 @@ function S() {
|
|
|
37
37
|
name: e.name,
|
|
38
38
|
hours: Math.round(e.totalHours * 10) / 10
|
|
39
39
|
}));
|
|
40
|
-
return O ? /* @__PURE__ */ v(
|
|
40
|
+
return O ? /* @__PURE__ */ v(c, {
|
|
41
41
|
title: "Team Members",
|
|
42
42
|
description: "Manage team members and roles",
|
|
43
43
|
actions: E,
|
|
@@ -45,14 +45,14 @@ function S() {
|
|
|
45
45
|
variant: "list",
|
|
46
46
|
count: 5
|
|
47
47
|
})
|
|
48
|
-
}) : k ? /* @__PURE__ */ v(
|
|
48
|
+
}) : k ? /* @__PURE__ */ v(c, {
|
|
49
49
|
title: "Team Members",
|
|
50
50
|
description: "Manage team members and roles",
|
|
51
51
|
children: /* @__PURE__ */ v("div", {
|
|
52
52
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
53
53
|
children: "Failed to load team data. Please try again later."
|
|
54
54
|
})
|
|
55
|
-
}) : /* @__PURE__ */ y(
|
|
55
|
+
}) : /* @__PURE__ */ y(c, {
|
|
56
56
|
title: "Team Members",
|
|
57
57
|
description: "Manage team members and roles",
|
|
58
58
|
purpose: "Understand how effort is distributed across your team and projects — use it to spot who's overloaded, where time is concentrated, and when to invite or reassign people.",
|
|
@@ -62,7 +62,7 @@ function S() {
|
|
|
62
62
|
children: [/* @__PURE__ */ v("div", {
|
|
63
63
|
className: "rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text",
|
|
64
64
|
children: "Team member data is managed by the groups module. Showing time entry activity by project."
|
|
65
|
-
}), M.length === 0 ? /* @__PURE__ */ v(
|
|
65
|
+
}), M.length === 0 ? /* @__PURE__ */ v(o, {
|
|
66
66
|
illustration: "empty-people",
|
|
67
67
|
title: "No time entry data available",
|
|
68
68
|
description: "Once your team starts tracking time, their activity by project shows up here."
|
|
@@ -2,10 +2,10 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
|
2
2
|
import { formatRelativeTime as t } from "../../utils/helpers.js";
|
|
3
3
|
import { StatCard as n } from "../../components/StatCard.js";
|
|
4
4
|
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { EmptyState as i } from "../../components/EmptyState.js";
|
|
6
|
+
import { useGetTimeEntryStatsQuery as a, useListTeamWorkPatternsQuery as o } from "../../../generated/wspace-operations.js";
|
|
7
|
+
import { PageLayout as s } from "../../components/PageLayout.js";
|
|
8
|
+
import { StatusBadge as c } from "../../components/StatusBadge.js";
|
|
9
9
|
import { ChartCard as l } from "../../components/ChartCard.js";
|
|
10
10
|
import { BarsChart as u } from "../../components/charts/BarsChart.js";
|
|
11
11
|
import { ProgressMeter as d } from "../../components/charts/ProgressMeter.js";
|
|
@@ -18,11 +18,11 @@ var y = (e) => `${Number.isInteger(e) ? e : e.toFixed(1)}h`, b = (e) => e.slice(
|
|
|
18
18
|
day: "numeric"
|
|
19
19
|
});
|
|
20
20
|
function S() {
|
|
21
|
-
let { workspaceId: S = "" } = e(), { data: C, loading: w, error: T } =
|
|
21
|
+
let { workspaceId: S = "" } = e(), { data: C, loading: w, error: T } = a({ skip: !S }), { data: E, loading: D, error: O } = o({
|
|
22
22
|
variables: { pagination: { limit: 10 } },
|
|
23
23
|
skip: !S
|
|
24
24
|
}), k = C?.timeEntryStats, A = E?.teamWorkPatterns ?? [];
|
|
25
|
-
if (w && D) return /* @__PURE__ */ _(
|
|
25
|
+
if (w && D) return /* @__PURE__ */ _(s, {
|
|
26
26
|
title: "Team Activity",
|
|
27
27
|
description: "Real-time team activity feed",
|
|
28
28
|
children: /* @__PURE__ */ _(r, {
|
|
@@ -30,7 +30,7 @@ function S() {
|
|
|
30
30
|
count: 5
|
|
31
31
|
})
|
|
32
32
|
});
|
|
33
|
-
if (T && O) return /* @__PURE__ */ _(
|
|
33
|
+
if (T && O) return /* @__PURE__ */ _(s, {
|
|
34
34
|
title: "Team Activity",
|
|
35
35
|
description: "Real-time team activity feed",
|
|
36
36
|
children: /* @__PURE__ */ _("div", {
|
|
@@ -44,7 +44,7 @@ function S() {
|
|
|
44
44
|
meetings: Math.round(e.meetingHours * 10) / 10,
|
|
45
45
|
breaks: Math.round(e.breakHours * 10) / 10
|
|
46
46
|
}));
|
|
47
|
-
return /* @__PURE__ */ _(
|
|
47
|
+
return /* @__PURE__ */ _(s, {
|
|
48
48
|
title: "Team Activity",
|
|
49
49
|
description: "Real-time team activity feed",
|
|
50
50
|
purpose: "A live pulse of what your team is doing right now — who's tracking, recent deep-work sessions, and today's hours — so you stay connected to the team's progress without interrupting anyone.",
|
|
@@ -111,7 +111,7 @@ function S() {
|
|
|
111
111
|
children: [/* @__PURE__ */ _("h3", {
|
|
112
112
|
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
113
113
|
children: "Activity Feed"
|
|
114
|
-
}), A.length === 0 && !D ? /* @__PURE__ */ _(
|
|
114
|
+
}), A.length === 0 && !D ? /* @__PURE__ */ _(i, {
|
|
115
115
|
illustration: "empty-notifications",
|
|
116
116
|
title: "No recent activity yet",
|
|
117
117
|
description: "As your team tracks time and completes focus sessions, their activity will stream in here."
|
|
@@ -144,7 +144,7 @@ function S() {
|
|
|
144
144
|
className: "text-xs text-text-secondary",
|
|
145
145
|
children: t(r.createdAt)
|
|
146
146
|
}),
|
|
147
|
-
a && /* @__PURE__ */ _(
|
|
147
|
+
a && /* @__PURE__ */ _(c, {
|
|
148
148
|
variant: "success",
|
|
149
149
|
label: "Deep work"
|
|
150
150
|
})
|
|
@@ -190,15 +190,15 @@ function S() {
|
|
|
190
190
|
o && /* @__PURE__ */ v("div", {
|
|
191
191
|
className: "mt-3 flex flex-wrap gap-1.5",
|
|
192
192
|
children: [
|
|
193
|
-
r.energyLevel && /* @__PURE__ */ _(
|
|
193
|
+
r.energyLevel && /* @__PURE__ */ _(c, {
|
|
194
194
|
variant: "info",
|
|
195
195
|
label: `Energy: ${r.energyLevel}`
|
|
196
196
|
}),
|
|
197
|
-
r.mood && /* @__PURE__ */ _(
|
|
197
|
+
r.mood && /* @__PURE__ */ _(c, {
|
|
198
198
|
variant: "muted",
|
|
199
199
|
label: `Mood: ${r.mood}`
|
|
200
200
|
}),
|
|
201
|
-
r.stressLevel && /* @__PURE__ */ _(
|
|
201
|
+
r.stressLevel && /* @__PURE__ */ _(c, {
|
|
202
202
|
variant: "warning",
|
|
203
203
|
label: `Stress: ${r.stressLevel}`
|
|
204
204
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { formatDate as e, formatHours as t, getInitials as n } from "../../utils/helpers.js";
|
|
2
2
|
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
3
|
-
import {
|
|
3
|
+
import { EmptyState as i } from "../../components/EmptyState.js";
|
|
4
|
+
import { useCreateTimeEntryMutation as a, useDeleteTimeEntryMutation as o, useDuplicateTimeEntryMutation as s, useListProjectsQuery as ee, useListTimeEntriesQuery as te, useUpdateTimeEntryMutation as ne } from "../../../generated/wspace-operations.js";
|
|
4
5
|
import { PageLayout as c } from "../../components/PageLayout.js";
|
|
5
6
|
import { StatusBadge as l } from "../../components/StatusBadge.js";
|
|
6
|
-
import { EmptyState as ne } from "../../components/EmptyState.js";
|
|
7
7
|
import { Modal as u } from "../../components/Modal.js";
|
|
8
8
|
import { useMemo as re, useState as d } from "react";
|
|
9
9
|
import { Copy as ie, Download as ae, Filter as oe, Pencil as f, Plus as p, Trash2 as se } from "lucide-react";
|
|
@@ -93,7 +93,7 @@ function x({ form: e, onChange: t, projects: n }) {
|
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
function S() {
|
|
96
|
-
let [S, C] = d({}), [le, w] = d(!1), [T, E] = d("all"), [D, O] = d(""), [k, A] = d(""), [j, M] = d(!1), [N, P] = d(b), [F, I] = d(null), [L, R] = d(b), [z, B] = d(null), V = Object.keys(S).length > 0, { data: H, loading: U, error: ue, fetchMore: de, refetch: W } =
|
|
96
|
+
let [S, C] = d({}), [le, w] = d(!1), [T, E] = d("all"), [D, O] = d(""), [k, A] = d(""), [j, M] = d(!1), [N, P] = d(b), [F, I] = d(null), [L, R] = d(b), [z, B] = d(null), V = Object.keys(S).length > 0, { data: H, loading: U, error: ue, fetchMore: de, refetch: W } = te({
|
|
97
97
|
variables: {
|
|
98
98
|
pagination: {
|
|
99
99
|
limit: _,
|
|
@@ -102,7 +102,7 @@ function S() {
|
|
|
102
102
|
filter: V ? S : void 0
|
|
103
103
|
},
|
|
104
104
|
notifyOnNetworkStatusChange: !0
|
|
105
|
-
}), { data: fe } =
|
|
105
|
+
}), { data: fe } = ee({ variables: { pagination: { limit: 100 } } }), G = fe?.projects?.items ?? [], K = re(() => H?.timeEntries?.items ?? [], [H?.timeEntries?.items]), pe = H?.timeEntries?.hasMore ?? !1, me = H?.timeEntries?.total ?? 0, [he, { loading: q, error: ge }] = a(), [_e, { loading: J, error: ve }] = ne(), [ye, { loading: Y }] = o(), [be, { loading: X }] = s(), [Z, Q] = d(!1), xe = async () => {
|
|
106
106
|
Q(!0);
|
|
107
107
|
try {
|
|
108
108
|
await de({
|
|
@@ -216,7 +216,7 @@ function S() {
|
|
|
216
216
|
U && K.length === 0 ? /* @__PURE__ */ h(r, {
|
|
217
217
|
variant: "table",
|
|
218
218
|
count: 5
|
|
219
|
-
}) : K.length === 0 ? /* @__PURE__ */ h(
|
|
219
|
+
}) : K.length === 0 ? /* @__PURE__ */ h(i, {
|
|
220
220
|
illustration: "empty-data",
|
|
221
221
|
title: "No time entries found",
|
|
222
222
|
description: "Track time with a timer or add a manual entry — your entries will appear here.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeEntriesPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeEntriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Plus, Filter, Download, Pencil, Copy, Trash2 } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { Modal } from \"../../components/Modal\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { formatHours, formatDate, getInitials } from \"../../utils/helpers\";\nimport {\n useListTimeEntriesQuery,\n useCreateTimeEntryMutation,\n useUpdateTimeEntryMutation,\n useDeleteTimeEntryMutation,\n useDuplicateTimeEntryMutation,\n useListProjectsQuery,\n type TimeEntryFieldsFragment,\n type ListProjectsQuery,\n type ListTimeEntriesQuery,\n} from \"@/generated/wspace-operations\";\nimport type { TimeEntryFilterInput } from \"@/generated/wspace-types\";\n\ntype ProjectOption = ListProjectsQuery[\"projects\"][\"items\"][number];\n\nconst PAGE_SIZE = 50;\n\n/** Convert an ISO datetime to a value usable by <input type=\"datetime-local\">. */\nfunction toLocalInput(iso?: string | null): string {\n if (!iso) return \"\";\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 16);\n}\n\n/** Convert a datetime-local value to an ISO string, or undefined if empty. */\nfunction toISO(local: string): string | undefined {\n if (!local) return undefined;\n const d = new Date(local);\n if (Number.isNaN(d.getTime())) return undefined;\n return d.toISOString();\n}\n\ninterface EntryFormState {\n description: string;\n projectId: string;\n startTime: string;\n endTime: string;\n billable: boolean;\n}\n\nfunction emptyForm(): EntryFormState {\n const now = new Date();\n const local = new Date(now.getTime() - now.getTimezoneOffset() * 60000);\n return {\n description: \"\",\n projectId: \"\",\n startTime: local.toISOString().slice(0, 16),\n endTime: \"\",\n billable: false,\n };\n}\n\ninterface EntryFormFieldsProps {\n form: EntryFormState;\n onChange: (patch: Partial<EntryFormState>) => void;\n projects: ProjectOption[];\n}\n\nfunction EntryFormFields({ form, onChange, projects }: EntryFormFieldsProps) {\n return (\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Description\n </label>\n <input\n type=\"text\"\n placeholder=\"What did you work on?\"\n value={form.description}\n onChange={(e) => onChange({ description: e.target.value })}\n className=\"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\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project\n </label>\n <select\n value={form.projectId}\n onChange={(e) => onChange({ projectId: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">No Project</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}\n </option>\n ))}\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Start\n </label>\n <input\n type=\"datetime-local\"\n value={form.startTime}\n onChange={(e) => onChange({ startTime: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n End\n </label>\n <input\n type=\"datetime-local\"\n value={form.endTime}\n onChange={(e) => onChange({ endTime: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={form.billable}\n onChange={(e) => onChange({ billable: e.target.checked })}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n Billable\n </label>\n </div>\n );\n}\n\nexport function TimeEntriesPage() {\n // Filters\n const [filter, setFilter] = useState<TimeEntryFilterInput>({});\n const [filterOpen, setFilterOpen] = useState(false);\n const [draftBillable, setDraftBillable] = useState<\"all\" | \"yes\" | \"no\">(\n \"all\",\n );\n const [draftProjectId, setDraftProjectId] = useState(\"\");\n const [draftSearch, setDraftSearch] = useState(\"\");\n\n // Create / edit / delete modal state\n const [createOpen, setCreateOpen] = useState(false);\n const [createForm, setCreateForm] = useState<EntryFormState>(emptyForm);\n const [editEntry, setEditEntry] = useState<TimeEntryFieldsFragment | null>(\n null,\n );\n const [editForm, setEditForm] = useState<EntryFormState>(emptyForm);\n const [deleteEntry, setDeleteEntry] =\n useState<TimeEntryFieldsFragment | null>(null);\n\n const hasFilter = Object.keys(filter).length > 0;\n\n const { data, loading, error, fetchMore, refetch } = useListTimeEntriesQuery({\n variables: {\n pagination: { limit: PAGE_SIZE, offset: 0 },\n filter: hasFilter ? filter : undefined,\n },\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: projectsData } = useListProjectsQuery({\n variables: { pagination: { limit: 100 } },\n });\n const projects: ProjectOption[] = projectsData?.projects?.items ?? [];\n\n const entries: TimeEntryFieldsFragment[] = useMemo(\n () => data?.timeEntries?.items ?? [],\n [data?.timeEntries?.items],\n );\n const hasMore = data?.timeEntries?.hasMore ?? false;\n const total = data?.timeEntries?.total ?? 0;\n\n const [createEntry, { loading: creating, error: createError }] =\n useCreateTimeEntryMutation();\n const [updateEntry, { loading: updating, error: updateError }] =\n useUpdateTimeEntryMutation();\n const [removeEntry, { loading: deleting }] = useDeleteTimeEntryMutation();\n const [duplicateEntry, { loading: duplicating }] =\n useDuplicateTimeEntryMutation();\n\n const [loadingMore, setLoadingMore] = useState(false);\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { limit: PAGE_SIZE, offset: entries.length } },\n updateQuery: (\n prev: ListTimeEntriesQuery,\n { fetchMoreResult }: { fetchMoreResult?: ListTimeEntriesQuery },\n ) => {\n if (!fetchMoreResult) return prev;\n return {\n ...fetchMoreResult,\n timeEntries: {\n ...fetchMoreResult.timeEntries,\n items: [\n ...prev.timeEntries.items,\n ...fetchMoreResult.timeEntries.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const openCreate = () => {\n setCreateForm(emptyForm());\n setCreateOpen(true);\n };\n\n const handleCreate = async () => {\n const startTime = toISO(createForm.startTime);\n if (!startTime) return;\n await createEntry({\n variables: {\n input: {\n description: createForm.description.trim() || undefined,\n projectId: createForm.projectId || undefined,\n startTime,\n endTime: toISO(createForm.endTime),\n billable: createForm.billable,\n source: \"manual\",\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const openEdit = (entry: TimeEntryFieldsFragment) => {\n setEditEntry(entry);\n setEditForm({\n description: entry.description ?? \"\",\n projectId: entry.projectId ?? \"\",\n startTime: toLocalInput(entry.startTime),\n endTime: toLocalInput(entry.endTime),\n billable: entry.billable,\n });\n };\n\n const handleUpdate = async () => {\n if (!editEntry) return;\n const startTime = toISO(editForm.startTime);\n if (!startTime) return;\n await updateEntry({\n variables: {\n id: editEntry.id,\n input: {\n description: editForm.description.trim() || undefined,\n projectId: editForm.projectId || undefined,\n startTime,\n endTime: toISO(editForm.endTime),\n billable: editForm.billable,\n },\n },\n });\n setEditEntry(null);\n await refetch();\n };\n\n const handleDuplicate = async (entry: TimeEntryFieldsFragment) => {\n await duplicateEntry({ variables: { id: entry.id } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteEntry) return;\n await removeEntry({ variables: { id: deleteEntry.id } });\n setDeleteEntry(null);\n await refetch();\n };\n\n const applyFilter = () => {\n const next: TimeEntryFilterInput = {};\n if (draftBillable !== \"all\") next.billable = draftBillable === \"yes\";\n if (draftProjectId) next.projectId = draftProjectId;\n if (draftSearch.trim()) next.search = draftSearch.trim();\n setFilter(next);\n setFilterOpen(false);\n };\n\n const clearFilter = () => {\n setDraftBillable(\"all\");\n setDraftProjectId(\"\");\n setDraftSearch(\"\");\n setFilter({});\n setFilterOpen(false);\n };\n\n // Client-side CSV export of the currently loaded rows. There is no backend\n // export operation, so this exports what has been fetched into the table.\n const handleExport = () => {\n const header = [\n \"Date\",\n \"Project\",\n \"Task/Description\",\n \"Duration (hours)\",\n \"Billable\",\n \"Status\",\n ];\n const rows = entries.map((entry) => [\n formatDate(entry.startTime),\n entry.project?.name ?? \"No Project\",\n entry.task?.title ?? entry.description ?? \"\",\n ((entry.duration ?? 0) / 3600).toFixed(2),\n entry.billable ? \"Yes\" : \"No\",\n entry.isRunning ? \"running\" : \"completed\",\n ]);\n const escape = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\n const csv = [header, ...rows]\n .map((row) => row.map((cell: string) => escape(String(cell))).join(\",\"))\n .join(\"\\n\");\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8;\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `time-entries-${new Date().toISOString().slice(0, 10)}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n };\n\n if (error) {\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load time entries. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n purpose=\"Every tracked block of work in one auditable list — filter, correct and confirm entries here before they roll into timesheets, approvals and client invoices.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => {\n setDraftBillable(\n filter.billable === undefined\n ? \"all\"\n : filter.billable\n ? \"yes\"\n : \"no\",\n );\n setDraftProjectId(filter.projectId ?? \"\");\n setDraftSearch(filter.search ?? \"\");\n setFilterOpen(true);\n }}\n className={`inline-flex items-center gap-1.5 rounded-md border px-3 py-2 text-sm font-medium hover:bg-bg-sunken/50 ${hasFilter ? \"border-action-primary-border text-primary\" : \"border-border-subtle text-text-primary\"}`}\n >\n <Filter className=\"h-4 w-4\" />\n Filter{hasFilter ? ` (${Object.keys(filter).length})` : \"\"}\n </button>\n <button\n onClick={handleExport}\n disabled={entries.length === 0}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <button\n onClick={openCreate}\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 New Entry\n </button>\n </div>\n }\n >\n {loading && entries.length === 0 ? (\n <LoadingSkeleton variant=\"table\" count={5} />\n ) : entries.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No time entries found\"\n description=\"Track time with a timer or add a manual entry — your entries will appear here.\"\n action={\n <button\n onClick={openCreate}\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 New Entry\n </button>\n }\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Entry\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Project\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Duration\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Billable\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Status\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {entries.map((entry: TimeEntryFieldsFragment) => (\n <tr\n key={entry.id}\n className=\"hover:bg-bg-sunken/50 transition-colors\"\n >\n <td className=\"px-4 py-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.task?.title ??\n entry.description ??\n \"No description\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(entry.startTime)}\n </p>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-action-primary-bg/10 text-xs font-semibold text-primary\">\n {getInitials(entry.project?.name ?? \"No Project\")}\n </span>\n <span className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ?? \"No Project\"}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge\n label={entry.billable ? \"Billable\" : \"Non-billable\"}\n variant={entry.billable ? \"success\" : \"muted\"}\n />\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge\n status={entry.isRunning ? \"running\" : \"completed\"}\n dot={entry.isRunning}\n label={entry.isRunning ? \"running\" : \"completed\"}\n />\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-1\">\n <button\n onClick={() => openEdit(entry)}\n title=\"Edit entry\"\n aria-label=\"Edit entry\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleDuplicate(entry)}\n disabled={duplicating}\n title=\"Duplicate entry\"\n aria-label=\"Duplicate entry\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-50\"\n >\n <Copy className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteEntry(entry)}\n title=\"Delete entry\"\n aria-label=\"Delete entry\"\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </GlassCard>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Showing {entries.length} of {total}\n </p>\n {hasMore && (\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"inline-flex items-center gap-1.5 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 )}\n </div>\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title=\"New Time Entry\"\n description=\"Manually log a block of work.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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={handleCreate}\n disabled={creating || !createForm.startTime}\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 {creating ? \"Saving...\" : \"Create Entry\"}\n </button>\n </>\n }\n >\n {createError && (\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 create entry. Please try again.\n </div>\n )}\n <EntryFormFields\n form={createForm}\n onChange={(patch) => setCreateForm((f) => ({ ...f, ...patch }))}\n projects={projects}\n />\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={Boolean(editEntry)}\n onClose={() => setEditEntry(null)}\n title=\"Edit Time Entry\"\n description=\"Correct the details of this logged block.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setEditEntry(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={handleUpdate}\n disabled={updating || !editForm.startTime}\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 {updating ? \"Saving...\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateError && (\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 update entry. Please try again.\n </div>\n )}\n <EntryFormFields\n form={editForm}\n onChange={(patch) => setEditForm((f) => ({ ...f, ...patch }))}\n projects={projects}\n />\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={Boolean(deleteEntry)}\n onClose={() => setDeleteEntry(null)}\n title=\"Delete time entry?\"\n description=\"This permanently removes the entry. It cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setDeleteEntry(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-action-danger-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Delete the entry for{\" \"}\n <span className=\"font-medium text-text-primary\">\n {deleteEntry?.project?.name ??\n deleteEntry?.description ??\n \"this block of work\"}\n </span>\n ?\n </p>\n </Modal>\n\n {/* Filter modal */}\n <Modal\n open={filterOpen}\n onClose={() => setFilterOpen(false)}\n title=\"Filter entries\"\n description=\"Narrow the list to what you need.\"\n footer={\n <>\n <button\n onClick={clearFilter}\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 Clear\n </button>\n <button\n onClick={applyFilter}\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\"\n >\n Apply\n </button>\n </>\n }\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Search\n </label>\n <input\n type=\"text\"\n placeholder=\"Description contains...\"\n value={draftSearch}\n onChange={(e) => setDraftSearch(e.target.value)}\n className=\"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\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project\n </label>\n <select\n value={draftProjectId}\n onChange={(e) => setDraftProjectId(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">All Projects</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Billable\n </label>\n <select\n value={draftBillable}\n onChange={(e) =>\n setDraftBillable(e.target.value as \"all\" | \"yes\" | \"no\")\n }\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"all\">All</option>\n <option value=\"yes\">Billable only</option>\n <option value=\"no\">Non-billable only</option>\n </select>\n </div>\n </div>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeEntriesPage.js","names":[],"sources":["../../../../src/timecampus/pages/time/TimeEntriesPage.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Plus, Filter, Download, Pencil, Copy, Trash2 } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { Modal } from \"../../components/Modal\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { formatHours, formatDate, getInitials } from \"../../utils/helpers\";\nimport {\n useListTimeEntriesQuery,\n useCreateTimeEntryMutation,\n useUpdateTimeEntryMutation,\n useDeleteTimeEntryMutation,\n useDuplicateTimeEntryMutation,\n useListProjectsQuery,\n type TimeEntryFieldsFragment,\n type ListProjectsQuery,\n type ListTimeEntriesQuery,\n} from \"@/generated/wspace-operations\";\nimport type { TimeEntryFilterInput } from \"@/generated/wspace-types\";\n\ntype ProjectOption = ListProjectsQuery[\"projects\"][\"items\"][number];\n\nconst PAGE_SIZE = 50;\n\n/** Convert an ISO datetime to a value usable by <input type=\"datetime-local\">. */\nfunction toLocalInput(iso?: string | null): string {\n if (!iso) return \"\";\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 16);\n}\n\n/** Convert a datetime-local value to an ISO string, or undefined if empty. */\nfunction toISO(local: string): string | undefined {\n if (!local) return undefined;\n const d = new Date(local);\n if (Number.isNaN(d.getTime())) return undefined;\n return d.toISOString();\n}\n\ninterface EntryFormState {\n description: string;\n projectId: string;\n startTime: string;\n endTime: string;\n billable: boolean;\n}\n\nfunction emptyForm(): EntryFormState {\n const now = new Date();\n const local = new Date(now.getTime() - now.getTimezoneOffset() * 60000);\n return {\n description: \"\",\n projectId: \"\",\n startTime: local.toISOString().slice(0, 16),\n endTime: \"\",\n billable: false,\n };\n}\n\ninterface EntryFormFieldsProps {\n form: EntryFormState;\n onChange: (patch: Partial<EntryFormState>) => void;\n projects: ProjectOption[];\n}\n\nfunction EntryFormFields({ form, onChange, projects }: EntryFormFieldsProps) {\n return (\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Description\n </label>\n <input\n type=\"text\"\n placeholder=\"What did you work on?\"\n value={form.description}\n onChange={(e) => onChange({ description: e.target.value })}\n className=\"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\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project\n </label>\n <select\n value={form.projectId}\n onChange={(e) => onChange({ projectId: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">No Project</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}\n </option>\n ))}\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Start\n </label>\n <input\n type=\"datetime-local\"\n value={form.startTime}\n onChange={(e) => onChange({ startTime: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n End\n </label>\n <input\n type=\"datetime-local\"\n value={form.endTime}\n onChange={(e) => onChange({ endTime: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={form.billable}\n onChange={(e) => onChange({ billable: e.target.checked })}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n Billable\n </label>\n </div>\n );\n}\n\nexport function TimeEntriesPage() {\n // Filters\n const [filter, setFilter] = useState<TimeEntryFilterInput>({});\n const [filterOpen, setFilterOpen] = useState(false);\n const [draftBillable, setDraftBillable] = useState<\"all\" | \"yes\" | \"no\">(\n \"all\",\n );\n const [draftProjectId, setDraftProjectId] = useState(\"\");\n const [draftSearch, setDraftSearch] = useState(\"\");\n\n // Create / edit / delete modal state\n const [createOpen, setCreateOpen] = useState(false);\n const [createForm, setCreateForm] = useState<EntryFormState>(emptyForm);\n const [editEntry, setEditEntry] = useState<TimeEntryFieldsFragment | null>(\n null,\n );\n const [editForm, setEditForm] = useState<EntryFormState>(emptyForm);\n const [deleteEntry, setDeleteEntry] =\n useState<TimeEntryFieldsFragment | null>(null);\n\n const hasFilter = Object.keys(filter).length > 0;\n\n const { data, loading, error, fetchMore, refetch } = useListTimeEntriesQuery({\n variables: {\n pagination: { limit: PAGE_SIZE, offset: 0 },\n filter: hasFilter ? filter : undefined,\n },\n notifyOnNetworkStatusChange: true,\n });\n\n const { data: projectsData } = useListProjectsQuery({\n variables: { pagination: { limit: 100 } },\n });\n const projects: ProjectOption[] = projectsData?.projects?.items ?? [];\n\n const entries: TimeEntryFieldsFragment[] = useMemo(\n () => data?.timeEntries?.items ?? [],\n [data?.timeEntries?.items],\n );\n const hasMore = data?.timeEntries?.hasMore ?? false;\n const total = data?.timeEntries?.total ?? 0;\n\n const [createEntry, { loading: creating, error: createError }] =\n useCreateTimeEntryMutation();\n const [updateEntry, { loading: updating, error: updateError }] =\n useUpdateTimeEntryMutation();\n const [removeEntry, { loading: deleting }] = useDeleteTimeEntryMutation();\n const [duplicateEntry, { loading: duplicating }] =\n useDuplicateTimeEntryMutation();\n\n const [loadingMore, setLoadingMore] = useState(false);\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { limit: PAGE_SIZE, offset: entries.length } },\n updateQuery: (\n prev: ListTimeEntriesQuery,\n { fetchMoreResult }: { fetchMoreResult?: ListTimeEntriesQuery },\n ) => {\n if (!fetchMoreResult) return prev;\n return {\n ...fetchMoreResult,\n timeEntries: {\n ...fetchMoreResult.timeEntries,\n items: [\n ...prev.timeEntries.items,\n ...fetchMoreResult.timeEntries.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const openCreate = () => {\n setCreateForm(emptyForm());\n setCreateOpen(true);\n };\n\n const handleCreate = async () => {\n const startTime = toISO(createForm.startTime);\n if (!startTime) return;\n await createEntry({\n variables: {\n input: {\n description: createForm.description.trim() || undefined,\n projectId: createForm.projectId || undefined,\n startTime,\n endTime: toISO(createForm.endTime),\n billable: createForm.billable,\n source: \"manual\",\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const openEdit = (entry: TimeEntryFieldsFragment) => {\n setEditEntry(entry);\n setEditForm({\n description: entry.description ?? \"\",\n projectId: entry.projectId ?? \"\",\n startTime: toLocalInput(entry.startTime),\n endTime: toLocalInput(entry.endTime),\n billable: entry.billable,\n });\n };\n\n const handleUpdate = async () => {\n if (!editEntry) return;\n const startTime = toISO(editForm.startTime);\n if (!startTime) return;\n await updateEntry({\n variables: {\n id: editEntry.id,\n input: {\n description: editForm.description.trim() || undefined,\n projectId: editForm.projectId || undefined,\n startTime,\n endTime: toISO(editForm.endTime),\n billable: editForm.billable,\n },\n },\n });\n setEditEntry(null);\n await refetch();\n };\n\n const handleDuplicate = async (entry: TimeEntryFieldsFragment) => {\n await duplicateEntry({ variables: { id: entry.id } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteEntry) return;\n await removeEntry({ variables: { id: deleteEntry.id } });\n setDeleteEntry(null);\n await refetch();\n };\n\n const applyFilter = () => {\n const next: TimeEntryFilterInput = {};\n if (draftBillable !== \"all\") next.billable = draftBillable === \"yes\";\n if (draftProjectId) next.projectId = draftProjectId;\n if (draftSearch.trim()) next.search = draftSearch.trim();\n setFilter(next);\n setFilterOpen(false);\n };\n\n const clearFilter = () => {\n setDraftBillable(\"all\");\n setDraftProjectId(\"\");\n setDraftSearch(\"\");\n setFilter({});\n setFilterOpen(false);\n };\n\n // Client-side CSV export of the currently loaded rows. There is no backend\n // export operation, so this exports what has been fetched into the table.\n const handleExport = () => {\n const header = [\n \"Date\",\n \"Project\",\n \"Task/Description\",\n \"Duration (hours)\",\n \"Billable\",\n \"Status\",\n ];\n const rows = entries.map((entry) => [\n formatDate(entry.startTime),\n entry.project?.name ?? \"No Project\",\n entry.task?.title ?? entry.description ?? \"\",\n ((entry.duration ?? 0) / 3600).toFixed(2),\n entry.billable ? \"Yes\" : \"No\",\n entry.isRunning ? \"running\" : \"completed\",\n ]);\n const escape = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\n const csv = [header, ...rows]\n .map((row) => row.map((cell: string) => escape(String(cell))).join(\",\"))\n .join(\"\\n\");\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8;\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `time-entries-${new Date().toISOString().slice(0, 10)}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n };\n\n if (error) {\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load time entries. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Entries\"\n description=\"View and manage all time entries\"\n purpose=\"Every tracked block of work in one auditable list — filter, correct and confirm entries here before they roll into timesheets, approvals and client invoices.\"\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => {\n setDraftBillable(\n filter.billable === undefined\n ? \"all\"\n : filter.billable\n ? \"yes\"\n : \"no\",\n );\n setDraftProjectId(filter.projectId ?? \"\");\n setDraftSearch(filter.search ?? \"\");\n setFilterOpen(true);\n }}\n className={`inline-flex items-center gap-1.5 rounded-md border px-3 py-2 text-sm font-medium hover:bg-bg-sunken/50 ${hasFilter ? \"border-action-primary-border text-primary\" : \"border-border-subtle text-text-primary\"}`}\n >\n <Filter className=\"h-4 w-4\" />\n Filter{hasFilter ? ` (${Object.keys(filter).length})` : \"\"}\n </button>\n <button\n onClick={handleExport}\n disabled={entries.length === 0}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n <button\n onClick={openCreate}\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 New Entry\n </button>\n </div>\n }\n >\n {loading && entries.length === 0 ? (\n <LoadingSkeleton variant=\"table\" count={5} />\n ) : entries.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No time entries found\"\n description=\"Track time with a timer or add a manual entry — your entries will appear here.\"\n action={\n <button\n onClick={openCreate}\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 New Entry\n </button>\n }\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Entry\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Project\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Duration\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Billable\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary\">\n Status\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {entries.map((entry: TimeEntryFieldsFragment) => (\n <tr\n key={entry.id}\n className=\"hover:bg-bg-sunken/50 transition-colors\"\n >\n <td className=\"px-4 py-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {entry.task?.title ??\n entry.description ??\n \"No description\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(entry.startTime)}\n </p>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-action-primary-bg/10 text-xs font-semibold text-primary\">\n {getInitials(entry.project?.name ?? \"No Project\")}\n </span>\n <span className=\"text-sm font-medium text-text-primary\">\n {entry.project?.name ?? \"No Project\"}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm font-mono text-text-primary\">\n {formatHours((entry.duration ?? 0) / 3600)}\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge\n label={entry.billable ? \"Billable\" : \"Non-billable\"}\n variant={entry.billable ? \"success\" : \"muted\"}\n />\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge\n status={entry.isRunning ? \"running\" : \"completed\"}\n dot={entry.isRunning}\n label={entry.isRunning ? \"running\" : \"completed\"}\n />\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-1\">\n <button\n onClick={() => openEdit(entry)}\n title=\"Edit entry\"\n aria-label=\"Edit entry\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleDuplicate(entry)}\n disabled={duplicating}\n title=\"Duplicate entry\"\n aria-label=\"Duplicate entry\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-50\"\n >\n <Copy className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteEntry(entry)}\n title=\"Delete entry\"\n aria-label=\"Delete entry\"\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </GlassCard>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Showing {entries.length} of {total}\n </p>\n {hasMore && (\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"inline-flex items-center gap-1.5 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 )}\n </div>\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title=\"New Time Entry\"\n description=\"Manually log a block of work.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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={handleCreate}\n disabled={creating || !createForm.startTime}\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 {creating ? \"Saving...\" : \"Create Entry\"}\n </button>\n </>\n }\n >\n {createError && (\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 create entry. Please try again.\n </div>\n )}\n <EntryFormFields\n form={createForm}\n onChange={(patch) => setCreateForm((f) => ({ ...f, ...patch }))}\n projects={projects}\n />\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={Boolean(editEntry)}\n onClose={() => setEditEntry(null)}\n title=\"Edit Time Entry\"\n description=\"Correct the details of this logged block.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setEditEntry(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={handleUpdate}\n disabled={updating || !editForm.startTime}\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 {updating ? \"Saving...\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateError && (\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 update entry. Please try again.\n </div>\n )}\n <EntryFormFields\n form={editForm}\n onChange={(patch) => setEditForm((f) => ({ ...f, ...patch }))}\n projects={projects}\n />\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={Boolean(deleteEntry)}\n onClose={() => setDeleteEntry(null)}\n title=\"Delete time entry?\"\n description=\"This permanently removes the entry. It cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setDeleteEntry(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-action-danger-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-danger-bg/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Delete the entry for{\" \"}\n <span className=\"font-medium text-text-primary\">\n {deleteEntry?.project?.name ??\n deleteEntry?.description ??\n \"this block of work\"}\n </span>\n ?\n </p>\n </Modal>\n\n {/* Filter modal */}\n <Modal\n open={filterOpen}\n onClose={() => setFilterOpen(false)}\n title=\"Filter entries\"\n description=\"Narrow the list to what you need.\"\n footer={\n <>\n <button\n onClick={clearFilter}\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 Clear\n </button>\n <button\n onClick={applyFilter}\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\"\n >\n Apply\n </button>\n </>\n }\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Search\n </label>\n <input\n type=\"text\"\n placeholder=\"Description contains...\"\n value={draftSearch}\n onChange={(e) => setDraftSearch(e.target.value)}\n className=\"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\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project\n </label>\n <select\n value={draftProjectId}\n onChange={(e) => setDraftProjectId(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"\">All Projects</option>\n {projects.map((project) => (\n <option key={project.id} value={project.id}>\n {project.name}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Billable\n </label>\n <select\n value={draftBillable}\n onChange={(e) =>\n setDraftBillable(e.target.value as \"all\" | \"yes\" | \"no\")\n }\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"all\">All</option>\n <option value=\"yes\">Billable only</option>\n <option value=\"no\">Non-billable only</option>\n </select>\n </div>\n </div>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|