@burdenoff/microfe-timecampus 2026.703.1 → 2026.703.3

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.
Files changed (85) hide show
  1. package/dist/generated/wspace-types.js +28 -0
  2. package/dist/generated/wspace-types.js.map +1 -0
  3. package/dist/index.js +4 -4
  4. package/dist/timecampus/components/Modal.js +65 -0
  5. package/dist/timecampus/components/Modal.js.map +1 -0
  6. package/dist/timecampus/components/StatusBadge.js +28 -0
  7. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  8. package/dist/timecampus/components/Toast.js +87 -0
  9. package/dist/timecampus/components/Toast.js.map +1 -0
  10. package/dist/timecampus/index.js +1 -1
  11. package/dist/timecampus/pages/HomePage.js +143 -135
  12. package/dist/timecampus/pages/HomePage.js.map +1 -1
  13. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  14. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  15. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  16. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  17. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  18. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  19. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  20. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  21. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +432 -118
  22. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  23. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  24. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  25. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  26. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  27. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +562 -85
  28. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  30. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  31. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  32. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  33. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  34. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  35. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  36. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  37. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  38. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  40. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  41. package/dist/timecampus/pages/meetings/MeetingFormModal.js +208 -0
  42. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  43. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  44. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  46. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  48. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  49. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  50. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  51. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  52. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  54. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  56. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  57. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  58. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  59. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  60. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  61. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  62. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  64. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  65. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  66. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  67. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  68. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  69. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  70. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  71. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  72. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  74. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  75. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +339 -110
  76. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  77. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  78. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  79. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  80. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  81. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  82. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  83. package/dist/timecampus/store/timecampusStore.js +13 -1
  84. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  85. package/package.json +1 -1
@@ -1,110 +1,587 @@
1
1
  import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
- import { formatCurrency as t, formatDate as n, getStatusColor as r } from "../../utils/helpers.js";
3
- import { StatCard as i } from "../../components/StatCard.js";
4
- import { LoadingSkeleton as a } from "../../components/LoadingSkeleton.js";
5
- import { useGetExpenseStatsQuery as o, useListExpensesQuery as s } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as c } from "../../components/PageLayout.js";
7
- import { EmptyState as l } from "../../components/EmptyState.js";
8
- import { DollarSign as u, Plus as d, Receipt as f } from "lucide-react";
9
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
2
+ import { formatCurrency as t, formatDate as n } from "../../utils/helpers.js";
3
+ import { StatCard as r } from "../../components/StatCard.js";
4
+ import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
5
+ import { useApproveExpenseMutation as a, useCreateExpenseMutation as ee, useDeleteExpenseMutation as te, useGetExpenseStatsQuery as ne, useListExpensesQuery as re, useRejectExpenseMutation as ie, useSubmitExpenseForApprovalMutation as ae, useUpdateExpenseMutation as oe } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as o } from "../../components/PageLayout.js";
7
+ import { EmptyState as se } from "../../components/EmptyState.js";
8
+ import { ExpenseStatus as s } from "../../../generated/wspace-types.js";
9
+ import { Modal as c } from "../../components/Modal.js";
10
+ import { StatusBadge as ce } from "../../components/StatusBadge.js";
11
+ import { ToastContainer as le, useToast as ue } from "../../components/Toast.js";
12
+ import { useState as l } from "react";
13
+ import { Check as de, DollarSign as fe, ExternalLink as pe, MessageSquare as me, Pencil as he, Plus as ge, Receipt as u, Send as _e, Trash2 as ve, Upload as ye, X as be } from "lucide-react";
14
+ import { gql as d } from "@apollo/client";
15
+ import { useApolloClient as xe } from "@apollo/client/react";
16
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
17
+ import { uploadBlobToFiles as Se } from "@burdenoff/fe-libs/storage";
18
+ import { ContextConversationWidget as h } from "@burdenoff/fe-libs/shared";
19
+ import { useRegisterPageConversationContext as g } from "@burdenoff/fe-libs/shared/providers/shell";
10
20
  //#region src/timecampus/pages/expense-tracking/ExpenseTrackingPage.tsx
11
- function h() {
12
- let { workspaceId: h = "" } = e(), { data: g, loading: _, error: v } = s({ variables: { workspaceId: h } }), { data: y, loading: b, error: x } = o({ variables: { workspaceId: h } }), S = g?.expenses ?? [], C = y?.expenseStats;
13
- return _ && b ? /* @__PURE__ */ p(c, {
21
+ var _ = 25, v = [
22
+ "Travel",
23
+ "Meals",
24
+ "Software",
25
+ "Hardware",
26
+ "Office",
27
+ "Training",
28
+ "Other"
29
+ ], y = "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", Ce = d`
30
+ query TimeCampusExpense_GetFileDownloadUrl($id: ID!, $expiresIn: Int) {
31
+ getFileDownloadUrl(id: $id, expiresIn: $expiresIn)
32
+ }
33
+ `, we = 604800;
34
+ function Te({ expense: e }) {
35
+ return g({
36
+ entityType: "TIMECAMPUS_EXPENSE",
37
+ entityId: e.id,
38
+ contextTitle: `Expense · ${e.description ?? e.id}`
39
+ }), /* @__PURE__ */ p(h, { widgetKey: "expense-review" });
40
+ }
41
+ function b() {
42
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
43
+ }
44
+ var x = {
45
+ amount: "",
46
+ category: v[0],
47
+ description: "",
48
+ projectId: "",
49
+ date: b(),
50
+ receiptUrl: ""
51
+ };
52
+ function S() {
53
+ let { workspaceId: d = "" } = e(), { toasts: h, addToast: g, dismissToast: S } = ue(), C = xe(), { data: Ee, loading: w, error: De, refetch: Oe, fetchMore: T } = re({
54
+ variables: {
55
+ workspaceId: d,
56
+ limit: _,
57
+ offset: 0
58
+ },
59
+ notifyOnNetworkStatusChange: !0
60
+ }), { data: ke, loading: Ae, error: je, refetch: Me } = ne({ variables: { workspaceId: d } }), [Ne, { loading: E }] = ee(), [Pe, { loading: D }] = oe(), [Fe, { loading: O }] = te(), [Ie] = ae(), [Le] = a(), [Re, { loading: k }] = ie(), A = Ee?.expenses ?? [], j = ke?.expenseStats, [M, N] = l(!1), [ze, Be] = l(!0), [Ve, P] = l(!1), [F, I] = l(null), [L, R] = l(x), [z, B] = l(!1), [V, H] = l(null), [U, W] = l(null), [G, K] = l(null), [q, J] = l(""), [He, Y] = l(null), X = async () => {
61
+ await Promise.all([Oe(), Me()]);
62
+ }, Ue = () => {
63
+ I(null), R({
64
+ ...x,
65
+ date: b()
66
+ }), P(!0);
67
+ }, Z = (e) => {
68
+ I(e), R({
69
+ amount: String(e.amount),
70
+ category: e.category,
71
+ description: e.description ?? "",
72
+ projectId: e.projectId ?? "",
73
+ date: e.date ? e.date.slice(0, 10) : b(),
74
+ receiptUrl: e.receiptUrl ?? ""
75
+ }), P(!0);
76
+ }, We = async (e) => {
77
+ B(!0);
78
+ try {
79
+ let t = await Se({
80
+ client: C,
81
+ blob: e,
82
+ filename: e.name,
83
+ mimeType: e.type || "application/octet-stream",
84
+ folderId: ""
85
+ }), { data: n } = await C.query({
86
+ query: Ce,
87
+ variables: {
88
+ id: t.id,
89
+ expiresIn: we
90
+ },
91
+ fetchPolicy: "no-cache"
92
+ }), r = n?.getFileDownloadUrl;
93
+ if (!r) throw Error("No download URL returned");
94
+ R((e) => ({
95
+ ...e,
96
+ receiptUrl: r
97
+ })), g({
98
+ title: "Receipt attached",
99
+ variant: "success"
100
+ });
101
+ } catch {
102
+ g({
103
+ title: "Failed to upload receipt",
104
+ variant: "error"
105
+ });
106
+ } finally {
107
+ B(!1);
108
+ }
109
+ }, Q = () => {
110
+ P(!1), I(null);
111
+ }, Ge = async () => {
112
+ let e = Number(L.amount);
113
+ if (!L.amount || Number.isNaN(e) || !L.category.trim()) return;
114
+ let t = new Date(L.date).toISOString();
115
+ try {
116
+ F ? (await Pe({ variables: {
117
+ id: F.id,
118
+ workspaceId: d,
119
+ input: {
120
+ amount: e,
121
+ category: L.category.trim(),
122
+ description: L.description.trim() || void 0,
123
+ projectId: L.projectId.trim() || void 0,
124
+ date: t,
125
+ receiptUrl: L.receiptUrl || void 0
126
+ }
127
+ } }), g({
128
+ title: "Expense updated",
129
+ variant: "success"
130
+ })) : (await Ne({ variables: { input: {
131
+ workspaceId: d,
132
+ amount: e,
133
+ category: L.category.trim(),
134
+ description: L.description.trim() || void 0,
135
+ projectId: L.projectId.trim() || void 0,
136
+ date: t,
137
+ receiptUrl: L.receiptUrl || void 0
138
+ } } }), g({
139
+ title: "Expense added",
140
+ variant: "success"
141
+ })), Q(), await X();
142
+ } catch {
143
+ g({
144
+ title: F ? "Failed to update expense" : "Failed to add expense",
145
+ variant: "error"
146
+ });
147
+ }
148
+ }, Ke = async () => {
149
+ if (U) try {
150
+ await Fe({ variables: {
151
+ id: U.id,
152
+ workspaceId: d
153
+ } }), g({
154
+ title: "Expense deleted",
155
+ variant: "success"
156
+ }), W(null), await X();
157
+ } catch {
158
+ g({
159
+ title: "Failed to delete expense",
160
+ variant: "error"
161
+ });
162
+ }
163
+ }, qe = async (e) => {
164
+ Y(e.id);
165
+ try {
166
+ await Ie({ variables: {
167
+ id: e.id,
168
+ workspaceId: d
169
+ } }), g({
170
+ title: "Submitted for approval",
171
+ variant: "success"
172
+ }), await X();
173
+ } catch {
174
+ g({
175
+ title: "Failed to submit expense",
176
+ variant: "error"
177
+ });
178
+ } finally {
179
+ Y(null);
180
+ }
181
+ }, Je = async (e) => {
182
+ Y(e.id);
183
+ try {
184
+ await Le({ variables: {
185
+ id: e.id,
186
+ workspaceId: d
187
+ } }), g({
188
+ title: "Expense approved",
189
+ variant: "success"
190
+ }), await X();
191
+ } catch {
192
+ g({
193
+ title: "Failed to approve expense",
194
+ variant: "error"
195
+ });
196
+ } finally {
197
+ Y(null);
198
+ }
199
+ }, Ye = async () => {
200
+ if (G) try {
201
+ await Re({ variables: {
202
+ id: G.id,
203
+ workspaceId: d,
204
+ reason: q.trim() || void 0
205
+ } }), g({
206
+ title: "Expense rejected",
207
+ variant: "success"
208
+ }), K(null), J(""), await X();
209
+ } catch {
210
+ g({
211
+ title: "Failed to reject expense",
212
+ variant: "error"
213
+ });
214
+ }
215
+ }, Xe = async () => {
216
+ N(!0);
217
+ try {
218
+ ((await T({
219
+ variables: {
220
+ workspaceId: d,
221
+ limit: _,
222
+ offset: A.length
223
+ },
224
+ updateQuery: (e, { fetchMoreResult: t }) => t ? {
225
+ ...e,
226
+ expenses: [...e.expenses, ...t.expenses]
227
+ } : e
228
+ })).data?.expenses?.length ?? 0) < _ && Be(!1);
229
+ } finally {
230
+ N(!1);
231
+ }
232
+ };
233
+ if (w && Ae && A.length === 0) return /* @__PURE__ */ p(o, {
14
234
  title: "Expense Tracking",
15
235
  description: "Track and manage work expenses",
16
- children: /* @__PURE__ */ p(a, {
236
+ children: /* @__PURE__ */ p(i, {
17
237
  variant: "list",
18
238
  count: 4
19
239
  })
20
- }) : v && x ? /* @__PURE__ */ p(c, {
240
+ });
241
+ if (De && je) return /* @__PURE__ */ p(o, {
21
242
  title: "Expense Tracking",
22
243
  description: "Track and manage work expenses",
23
244
  children: /* @__PURE__ */ p("div", {
24
245
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
25
246
  children: "Failed to load expense data. Please try again later."
26
247
  })
27
- }) : /* @__PURE__ */ m(c, {
248
+ });
249
+ let $ = /* @__PURE__ */ m("button", {
250
+ onClick: Ue,
251
+ 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",
252
+ children: [/* @__PURE__ */ p(ge, { className: "h-4 w-4" }), "Add Expense"]
253
+ });
254
+ return /* @__PURE__ */ m(o, {
28
255
  title: "Expense Tracking",
29
256
  description: "Track and manage work expenses",
30
257
  purpose: "Capture project-related costs alongside the hours you log — receipts, categories and approvals in one place — so the bill you send a client reflects the full cost of the work, not just time.",
31
- actions: /* @__PURE__ */ m("button", {
32
- 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",
33
- children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), "Add Expense"]
34
- }),
35
- children: [/* @__PURE__ */ m("div", {
36
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
37
- children: [
38
- /* @__PURE__ */ p(i, {
39
- featured: !0,
40
- title: "Total This Month",
41
- value: t(C?.totalAmount ?? 0),
42
- icon: /* @__PURE__ */ p(u, { className: "h-5 w-5" })
43
- }),
44
- /* @__PURE__ */ p(i, {
45
- title: "Pending",
46
- value: String(C?.pendingCount ?? 0),
47
- description: "expenses awaiting approval"
48
- }),
49
- /* @__PURE__ */ p(i, {
50
- title: "Approved",
51
- value: String(C?.approvedCount ?? 0),
52
- description: "expenses approved"
53
- }),
54
- /* @__PURE__ */ p(i, {
55
- title: "Average",
56
- value: t(C?.averageAmount ?? 0),
57
- icon: /* @__PURE__ */ p(f, { className: "h-5 w-5" }),
58
- description: "per expense"
258
+ actions: $,
259
+ children: [
260
+ /* @__PURE__ */ m("div", {
261
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
262
+ children: [
263
+ /* @__PURE__ */ p(r, {
264
+ featured: !0,
265
+ title: "Total This Month",
266
+ value: t(j?.totalAmount ?? 0),
267
+ icon: /* @__PURE__ */ p(fe, { className: "h-5 w-5" })
268
+ }),
269
+ /* @__PURE__ */ p(r, {
270
+ title: "Pending",
271
+ value: String(j?.pendingCount ?? 0),
272
+ description: "expenses awaiting approval"
273
+ }),
274
+ /* @__PURE__ */ p(r, {
275
+ title: "Approved",
276
+ value: String(j?.approvedCount ?? 0),
277
+ description: "expenses approved"
278
+ }),
279
+ /* @__PURE__ */ p(r, {
280
+ title: "Average",
281
+ value: t(j?.averageAmount ?? 0),
282
+ icon: /* @__PURE__ */ p(u, { className: "h-5 w-5" }),
283
+ description: "per expense"
284
+ })
285
+ ]
286
+ }),
287
+ A.length === 0 && !w ? /* @__PURE__ */ p(se, {
288
+ illustration: "empty-data",
289
+ title: "No expenses recorded yet",
290
+ description: "Add your first expense to track project costs and route them for approval.",
291
+ action: $
292
+ }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", {
293
+ className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
294
+ children: /* @__PURE__ */ p("div", {
295
+ className: "divide-y divide-border",
296
+ children: A.map((e) => {
297
+ let r = He === e.id, i = e.status === s.Pending, a = e.status === s.Rejected;
298
+ return /* @__PURE__ */ m("div", {
299
+ className: "flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between",
300
+ children: [/* @__PURE__ */ m("div", {
301
+ className: "flex items-center gap-3",
302
+ children: [/* @__PURE__ */ p(u, { className: "h-5 w-5 shrink-0 text-text-secondary" }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
303
+ className: "text-sm font-medium text-text-primary",
304
+ children: e.description ?? "Untitled expense"
305
+ }), /* @__PURE__ */ m("p", {
306
+ className: "text-xs text-text-secondary",
307
+ children: [
308
+ e.category,
309
+ " ·",
310
+ " ",
311
+ e.projectId ?? "No project",
312
+ " ·",
313
+ " ",
314
+ n(e.date)
315
+ ]
316
+ })] })]
317
+ }), /* @__PURE__ */ m("div", {
318
+ className: "flex items-center gap-3",
319
+ children: [
320
+ /* @__PURE__ */ p("p", {
321
+ className: "text-sm font-medium text-text-primary",
322
+ children: t(e.amount, e.currency)
323
+ }),
324
+ /* @__PURE__ */ p(ce, {
325
+ label: e.status.toLowerCase(),
326
+ status: e.status.toLowerCase()
327
+ }),
328
+ /* @__PURE__ */ m("div", {
329
+ className: "flex items-center gap-1",
330
+ children: [
331
+ i && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
332
+ type: "button",
333
+ disabled: r,
334
+ onClick: () => Je(e),
335
+ title: "Approve expense",
336
+ className: "rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50",
337
+ children: /* @__PURE__ */ p(de, { className: "h-4 w-4" })
338
+ }), /* @__PURE__ */ p("button", {
339
+ type: "button",
340
+ disabled: r,
341
+ onClick: () => {
342
+ K(e), J("");
343
+ },
344
+ title: "Reject expense",
345
+ className: "rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg disabled:opacity-50",
346
+ children: /* @__PURE__ */ p(be, { className: "h-4 w-4" })
347
+ })] }),
348
+ a && /* @__PURE__ */ p("button", {
349
+ type: "button",
350
+ disabled: r,
351
+ onClick: () => qe(e),
352
+ title: "Resubmit for approval",
353
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50",
354
+ children: /* @__PURE__ */ p(_e, { className: "h-4 w-4" })
355
+ }),
356
+ /* @__PURE__ */ p("button", {
357
+ type: "button",
358
+ onClick: () => H(e),
359
+ title: "Review expense",
360
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken",
361
+ children: /* @__PURE__ */ p(me, { className: "h-4 w-4" })
362
+ }),
363
+ /* @__PURE__ */ p("button", {
364
+ type: "button",
365
+ onClick: () => Z(e),
366
+ title: "Edit expense",
367
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken",
368
+ children: /* @__PURE__ */ p(he, { className: "h-4 w-4" })
369
+ }),
370
+ /* @__PURE__ */ p("button", {
371
+ type: "button",
372
+ onClick: () => W(e),
373
+ title: "Delete expense",
374
+ className: "rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg",
375
+ children: /* @__PURE__ */ p(ve, { className: "h-4 w-4" })
376
+ })
377
+ ]
378
+ })
379
+ ]
380
+ })]
381
+ }, e.id);
382
+ })
59
383
  })
60
- ]
61
- }), S.length === 0 && !_ ? /* @__PURE__ */ p(l, {
62
- illustration: "empty-data",
63
- title: "No expenses recorded yet",
64
- description: "Add your first expense to track project costs and route them for approval.",
65
- action: /* @__PURE__ */ m("button", {
66
- 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",
67
- children: [/* @__PURE__ */ p(d, { className: "h-4 w-4" }), "Add Expense"]
68
- })
69
- }) : /* @__PURE__ */ p("div", {
70
- className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
71
- children: /* @__PURE__ */ p("div", {
72
- className: "divide-y divide-border",
73
- children: S.map((e) => /* @__PURE__ */ m("div", {
74
- className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
75
- children: [/* @__PURE__ */ m("div", {
76
- className: "flex items-center gap-3",
77
- children: [/* @__PURE__ */ p(f, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
78
- className: "text-sm font-medium text-text-primary",
79
- children: e.description ?? "Untitled expense"
80
- }), /* @__PURE__ */ m("p", {
81
- className: "text-xs text-text-secondary",
384
+ }), ze && A.length >= _ && /* @__PURE__ */ p("div", {
385
+ className: "mt-4 flex justify-center",
386
+ children: /* @__PURE__ */ p("button", {
387
+ onClick: Xe,
388
+ disabled: M,
389
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
390
+ children: M ? "Loading..." : "Load more"
391
+ })
392
+ })] }),
393
+ /* @__PURE__ */ p(c, {
394
+ open: Ve,
395
+ onClose: Q,
396
+ title: F ? "Edit Expense" : "Add Expense",
397
+ description: F ? "Update this expense's details." : "Record a project-related cost to track alongside your logged hours.",
398
+ footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
399
+ onClick: Q,
400
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
401
+ children: "Cancel"
402
+ }), /* @__PURE__ */ p("button", {
403
+ onClick: Ge,
404
+ disabled: E || D || !L.amount || Number.isNaN(Number(L.amount)) || !L.category.trim(),
405
+ 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",
406
+ children: E || D ? "Saving..." : F ? "Save Changes" : "Add Expense"
407
+ })] }),
408
+ children: /* @__PURE__ */ m("div", {
409
+ className: "space-y-4",
410
+ children: [
411
+ /* @__PURE__ */ m("div", {
412
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
413
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
414
+ className: "block text-sm font-medium text-text-primary",
415
+ children: "Amount"
416
+ }), /* @__PURE__ */ p("input", {
417
+ type: "number",
418
+ min: 0,
419
+ step: "0.01",
420
+ placeholder: "0.00",
421
+ value: L.amount,
422
+ onChange: (e) => R({
423
+ ...L,
424
+ amount: e.target.value
425
+ }),
426
+ className: y
427
+ })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
428
+ className: "block text-sm font-medium text-text-primary",
429
+ children: "Category"
430
+ }), /* @__PURE__ */ p("select", {
431
+ value: L.category,
432
+ onChange: (e) => R({
433
+ ...L,
434
+ category: e.target.value
435
+ }),
436
+ className: y,
437
+ children: v.map((e) => /* @__PURE__ */ p("option", {
438
+ value: e,
439
+ children: e
440
+ }, e))
441
+ })] })]
442
+ }),
443
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
444
+ className: "block text-sm font-medium text-text-primary",
445
+ children: "Description"
446
+ }), /* @__PURE__ */ p("input", {
447
+ type: "text",
448
+ placeholder: "What was this expense for?",
449
+ value: L.description,
450
+ onChange: (e) => R({
451
+ ...L,
452
+ description: e.target.value
453
+ }),
454
+ className: y
455
+ })] }),
456
+ /* @__PURE__ */ m("div", {
457
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
458
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
459
+ className: "block text-sm font-medium text-text-primary",
460
+ children: "Project ID"
461
+ }), /* @__PURE__ */ p("input", {
462
+ type: "text",
463
+ placeholder: "Optional project ID",
464
+ value: L.projectId,
465
+ onChange: (e) => R({
466
+ ...L,
467
+ projectId: e.target.value
468
+ }),
469
+ className: y
470
+ })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
471
+ className: "block text-sm font-medium text-text-primary",
472
+ children: "Date"
473
+ }), /* @__PURE__ */ p("input", {
474
+ type: "date",
475
+ value: L.date,
476
+ onChange: (e) => R({
477
+ ...L,
478
+ date: e.target.value
479
+ }),
480
+ className: y
481
+ })] })]
482
+ }),
483
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
484
+ className: "block text-sm font-medium text-text-primary",
485
+ children: "Receipt"
486
+ }), /* @__PURE__ */ m("div", {
487
+ className: "mt-1 flex flex-col gap-2",
82
488
  children: [
83
- e.category,
84
- " ·",
85
- " ",
86
- e.projectId ?? "No project",
87
- " ·",
88
- " ",
89
- n(e.date)
489
+ /* @__PURE__ */ m("label", {
490
+ className: `inline-flex w-fit cursor-pointer 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 ${z ? "opacity-50" : ""}`,
491
+ children: [
492
+ /* @__PURE__ */ p(ye, { className: "h-4 w-4" }),
493
+ z ? "Uploading..." : "Upload receipt",
494
+ /* @__PURE__ */ p("input", {
495
+ type: "file",
496
+ accept: "image/*,application/pdf",
497
+ disabled: z,
498
+ className: "hidden",
499
+ onChange: (e) => {
500
+ let t = e.target.files?.[0];
501
+ e.target.value = "", t && We(t);
502
+ }
503
+ })
504
+ ]
505
+ }),
506
+ L.receiptUrl && /* @__PURE__ */ m("a", {
507
+ href: L.receiptUrl,
508
+ target: "_blank",
509
+ rel: "noopener noreferrer",
510
+ className: "inline-flex w-fit items-center gap-1.5 text-sm font-medium text-action-primary-text/90 underline decoration-border-subtle underline-offset-2 hover:text-action-primary-text",
511
+ children: [/* @__PURE__ */ p(pe, { className: "h-3.5 w-3.5" }), "View current receipt"]
512
+ }),
513
+ /* @__PURE__ */ p("p", {
514
+ className: "text-xs text-text-secondary",
515
+ children: "Attach an image or PDF. Uploading is optional and never blocks saving the expense."
516
+ })
90
517
  ]
91
- })] })]
92
- }), /* @__PURE__ */ m("div", {
93
- className: "flex items-center gap-3",
94
- children: [/* @__PURE__ */ p("p", {
95
- className: "text-sm font-medium text-text-primary",
96
- children: t(e.amount)
97
- }), /* @__PURE__ */ p("span", {
98
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${r(e.status)}`,
99
- children: e.status
100
- })]
101
- })]
102
- }, e.id))
518
+ })] })
519
+ ]
520
+ })
521
+ }),
522
+ /* @__PURE__ */ p(c, {
523
+ open: U !== null,
524
+ onClose: () => W(null),
525
+ title: "Delete expense?",
526
+ description: U ? `${t(U.amount, U.currency)} · ${U.category} will be removed.` : void 0,
527
+ size: "sm",
528
+ footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
529
+ onClick: () => W(null),
530
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
531
+ children: "Cancel"
532
+ }), /* @__PURE__ */ p("button", {
533
+ onClick: Ke,
534
+ disabled: O,
535
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
536
+ children: O ? "Deleting..." : "Delete"
537
+ })] }),
538
+ children: /* @__PURE__ */ p("p", {
539
+ className: "text-sm text-text-secondary",
540
+ children: "This permanently removes the expense. This action cannot be undone."
541
+ })
542
+ }),
543
+ /* @__PURE__ */ m(c, {
544
+ open: G !== null,
545
+ onClose: () => K(null),
546
+ title: "Reject expense",
547
+ description: "Let the submitter know why this expense was rejected.",
548
+ size: "sm",
549
+ footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
550
+ onClick: () => K(null),
551
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
552
+ children: "Cancel"
553
+ }), /* @__PURE__ */ p("button", {
554
+ onClick: Ye,
555
+ disabled: k,
556
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
557
+ children: k ? "Rejecting..." : "Reject Expense"
558
+ })] }),
559
+ children: [/* @__PURE__ */ p("label", {
560
+ className: "block text-sm font-medium text-text-primary",
561
+ children: "Reason"
562
+ }), /* @__PURE__ */ p("textarea", {
563
+ rows: 3,
564
+ placeholder: "Reason for rejection (optional)",
565
+ value: q,
566
+ onChange: (e) => J(e.target.value),
567
+ className: y
568
+ })]
569
+ }),
570
+ /* @__PURE__ */ p(c, {
571
+ open: V !== null,
572
+ onClose: () => H(null),
573
+ title: "Expense review",
574
+ description: V ? `Discuss ${V.description ?? "this expense"} with your team.` : void 0,
575
+ children: V && /* @__PURE__ */ p(Te, { expense: V })
576
+ }),
577
+ /* @__PURE__ */ p(le, {
578
+ toasts: h,
579
+ onDismiss: S
103
580
  })
104
- })]
581
+ ]
105
582
  });
106
583
  }
107
584
  //#endregion
108
- export { h as ExpenseTrackingPage };
585
+ export { S as ExpenseTrackingPage };
109
586
 
110
587
  //# sourceMappingURL=ExpenseTrackingPage.js.map