@burdenoff/microfe-timecampus 2026.703.3 → 2026.703.4

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.
@@ -1,25 +1,31 @@
1
1
  import { getInitials as e } from "../../utils/helpers.js";
2
2
  import { StatCard as t } from "../../components/StatCard.js";
3
3
  import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
4
- import { useCreateClientPortalConfigMutation as r, useDeleteClientPortalConfigMutation as ee, useGetClientPortalStatsQuery as te, useListClientPortalConfigsQuery as i, useRegenerateClientPortalShareTokenMutation as ne, useToggleClientPortalMutation as re, useUpdateClientPortalConfigMutation as ie } from "../../../generated/wspace-operations.js";
5
- import { PageLayout as a } from "../../components/PageLayout.js";
6
- import { EmptyState as ae } from "../../components/EmptyState.js";
7
- import { Modal as o } from "../../components/Modal.js";
8
- import { StatusBadge as oe } from "../../components/StatusBadge.js";
9
- import { ToastContainer as se, useToast as ce } from "../../components/Toast.js";
10
- import { useState as s } from "react";
11
- import { Building2 as c, Check as l, DollarSign as u, FolderOpen as d, Link2 as f, Pencil as p, Plus as le, Power as ue, RefreshCw as de, Trash2 as fe } from "lucide-react";
12
- import { gql as m } from "@apollo/client";
13
- import { useApolloClient as pe } from "@apollo/client/react";
14
- import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
15
- import { uploadBlobToFiles as me } from "@burdenoff/fe-libs/storage";
4
+ import { useCreateClientPortalConfigMutation as r, useDeleteClientPortalConfigMutation as i, useGetClientPortalStatsQuery as a, useListClientPortalConfigsQuery as o, useRegenerateClientPortalShareTokenMutation as ee, useToggleClientPortalMutation as te, useUpdateClientPortalConfigMutation as s } from "../../../generated/wspace-operations.js";
5
+ import { PageLayout as c } from "../../components/PageLayout.js";
6
+ import { EmptyState as ne } from "../../components/EmptyState.js";
7
+ import { Modal as l } from "../../components/Modal.js";
8
+ import { StatusBadge as re } from "../../components/StatusBadge.js";
9
+ import { ToastContainer as ie, useToast as ae } from "../../components/Toast.js";
10
+ import { useState as u } from "react";
11
+ import { Building2 as oe, Check as se, DollarSign as ce, FolderOpen as le, Link2 as ue, Pencil as de, Plus as fe, Power as pe, RefreshCw as me, Trash2 as he } from "lucide-react";
12
+ import { gql as d } from "@apollo/client";
13
+ import { useApolloClient as ge } from "@apollo/client/react";
14
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
15
+ import { uploadBlobToFiles as h } from "@burdenoff/fe-libs/storage";
16
16
  //#region src/timecampus/pages/client-portal/ClientPortalPage.tsx
17
- var v = 25, 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", he = "mt-1 block w-full text-sm text-text-secondary file:mr-3 file:rounded-md file:border-0 file:bg-action-primary-bg file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-action-primary-text hover:file:bg-action-primary-bg/90", ge = m`
17
+ var g = 25, _ = "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", v = "mt-1 block w-full text-sm text-text-secondary file:mr-3 file:rounded-md file:border-0 file:bg-action-primary-bg file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-action-primary-text hover:file:bg-action-primary-bg/90", y = d`
18
18
  query TimeCampus_GetFileDownloadUrl($id: ID!, $expiresIn: Int) {
19
19
  getFileDownloadUrl(id: $id, expiresIn: $expiresIn)
20
20
  }
21
+ `, _e = d`
22
+ mutation TimeCampusClient_EnsureFolder($path: String!) {
23
+ ensureFolderByPath(path: $path) {
24
+ id
25
+ }
26
+ }
21
27
  `;
22
- function _e(e) {
28
+ function ve(e) {
23
29
  return `${typeof window < "u" ? window.location.origin : ""}/portal/${e}`;
24
30
  }
25
31
  var b = {
@@ -30,30 +36,30 @@ var b = {
30
36
  logoUrl: ""
31
37
  };
32
38
  function x() {
33
- let { toasts: m, addToast: x, dismissToast: ve } = ce(), S = pe(), [C, w] = s(null), [T, E] = s(!1), { data: D, loading: O, error: k, refetch: A, fetchMore: j } = i({
39
+ let { toasts: d, addToast: x, dismissToast: ye } = ae(), S = ge(), [be, C] = u(null), [w, T] = u(!1), { data: E, loading: D, error: O, refetch: k, fetchMore: A } = o({
34
40
  variables: {
35
- limit: v,
41
+ limit: g,
36
42
  offset: 0
37
43
  },
38
44
  notifyOnNetworkStatusChange: !0
39
- }), { data: M, refetch: N } = te(), [P, { loading: F }] = ne(), [ye, { loading: I }] = r(), [be, { loading: L }] = ie(), [xe, { loading: R }] = ee(), [Se] = re(), z = D?.clientPortalConfigs?.items ?? [], Ce = D?.clientPortalConfigs?.hasMore ?? !1, B = M?.clientPortalStats, [V, H] = s(!1), [we, U] = s(!1), [W, G] = s(null), [K, q] = s(b), [J, Y] = s(null), [Te, X] = s(null), Z = async () => {
40
- await Promise.all([A(), N()]);
41
- }, Ee = async (e) => {
45
+ }), { data: j, refetch: M } = a(), [N, { loading: P }] = ee(), [xe, { loading: F }] = r(), [Se, { loading: I }] = s(), [Ce, { loading: L }] = i(), [we] = te(), R = E?.clientPortalConfigs?.items ?? [], Te = E?.clientPortalConfigs?.hasMore ?? !1, z = j?.clientPortalStats, [B, V] = u(!1), [Ee, H] = u(!1), [U, W] = u(null), [G, K] = u(b), [q, J] = u(null), [Y, X] = u(null), Z = async () => {
46
+ await Promise.all([k(), M()]);
47
+ }, De = async (e) => {
42
48
  if (e.shareToken) try {
43
- await navigator.clipboard.writeText(_e(e.shareToken)), w(e.id), window.setTimeout(() => w(null), 2e3);
49
+ await navigator.clipboard.writeText(ve(e.shareToken)), C(e.id), window.setTimeout(() => C(null), 2e3);
44
50
  } catch {
45
51
  x({
46
52
  title: "Clipboard unavailable",
47
53
  variant: "error"
48
54
  });
49
55
  }
50
- }, De = async (e) => {
56
+ }, Oe = async (e) => {
51
57
  X(e.id);
52
58
  try {
53
- await P({ variables: { id: e.id } }), x({
59
+ await N({ variables: { id: e.id } }), x({
54
60
  title: "Share link regenerated",
55
61
  variant: "success"
56
- }), await A();
62
+ }), await k();
57
63
  } catch {
58
64
  x({
59
65
  title: "Failed to regenerate link",
@@ -62,37 +68,42 @@ function x() {
62
68
  } finally {
63
69
  X(null);
64
70
  }
65
- }, Oe = () => {
66
- G(null), q(b), U(!0);
67
- }, ke = (e) => {
68
- G(e), q({
71
+ }, ke = () => {
72
+ W(null), K(b), H(!0);
73
+ }, Ae = (e) => {
74
+ W(e), K({
69
75
  clientName: e.clientName,
70
76
  contactEmail: e.contactEmail ?? "",
71
77
  contactPhone: e.contactPhone ?? "",
72
78
  description: e.description ?? "",
73
79
  logoUrl: e.logoUrl ?? ""
74
- }), U(!0);
75
- }, Ae = async (e) => {
76
- E(!0);
80
+ }), H(!0);
81
+ }, je = async (e) => {
82
+ T(!0);
77
83
  try {
78
- let t = await me({
84
+ let { data: t } = await S.mutate({
85
+ mutation: _e,
86
+ variables: { path: "/TimeCampus/ClientLogos" }
87
+ }), n = t?.ensureFolderByPath?.id;
88
+ if (!n) throw Error("Could not resolve upload folder");
89
+ let r = await h({
79
90
  client: S,
80
91
  blob: e,
81
92
  filename: e.name,
82
93
  mimeType: e.type || "application/octet-stream",
83
- folderId: ""
84
- }), { data: n } = await S.query({
85
- query: ge,
94
+ folderId: n
95
+ }), { data: i } = await S.query({
96
+ query: y,
86
97
  variables: {
87
- id: t.id,
98
+ id: r.id,
88
99
  expiresIn: 604800
89
100
  },
90
101
  fetchPolicy: "no-cache"
91
- }), r = n?.getFileDownloadUrl;
92
- if (!r) throw Error("No download URL returned");
93
- q((e) => ({
102
+ }), a = i?.getFileDownloadUrl;
103
+ if (!a) throw Error("No download URL returned");
104
+ K((e) => ({
94
105
  ...e,
95
- logoUrl: r
106
+ logoUrl: a
96
107
  }));
97
108
  } catch {
98
109
  x({
@@ -100,30 +111,30 @@ function x() {
100
111
  variant: "error"
101
112
  });
102
113
  } finally {
103
- E(!1);
114
+ T(!1);
104
115
  }
105
116
  }, Q = () => {
106
- U(!1), G(null);
107
- }, je = async () => {
108
- if (K.clientName.trim()) try {
109
- W ? (await be({ variables: {
110
- id: W.id,
117
+ H(!1), W(null);
118
+ }, Me = async () => {
119
+ if (G.clientName.trim()) try {
120
+ U ? (await Se({ variables: {
121
+ id: U.id,
111
122
  input: {
112
- clientName: K.clientName.trim(),
113
- contactEmail: K.contactEmail.trim() || void 0,
114
- contactPhone: K.contactPhone.trim() || void 0,
115
- description: K.description.trim() || void 0,
116
- logoUrl: K.logoUrl || void 0
123
+ clientName: G.clientName.trim(),
124
+ contactEmail: G.contactEmail.trim() || void 0,
125
+ contactPhone: G.contactPhone.trim() || void 0,
126
+ description: G.description.trim() || void 0,
127
+ logoUrl: G.logoUrl || void 0
117
128
  }
118
129
  } }), x({
119
130
  title: "Client updated",
120
131
  variant: "success"
121
- })) : (await ye({ variables: { input: {
122
- clientName: K.clientName.trim(),
123
- contactEmail: K.contactEmail.trim() || void 0,
124
- contactPhone: K.contactPhone.trim() || void 0,
125
- description: K.description.trim() || void 0,
126
- logoUrl: K.logoUrl || void 0,
132
+ })) : (await xe({ variables: { input: {
133
+ clientName: G.clientName.trim(),
134
+ contactEmail: G.contactEmail.trim() || void 0,
135
+ contactPhone: G.contactPhone.trim() || void 0,
136
+ description: G.description.trim() || void 0,
137
+ logoUrl: G.logoUrl || void 0,
127
138
  isActive: !0
128
139
  } } }), x({
129
140
  title: "Client added",
@@ -131,26 +142,26 @@ function x() {
131
142
  })), Q(), await Z();
132
143
  } catch {
133
144
  x({
134
- title: W ? "Failed to update client" : "Failed to add client",
145
+ title: U ? "Failed to update client" : "Failed to add client",
135
146
  variant: "error"
136
147
  });
137
148
  }
138
- }, Me = async () => {
139
- if (J) try {
140
- await xe({ variables: { id: J.id } }), x({
149
+ }, Ne = async () => {
150
+ if (q) try {
151
+ await Ce({ variables: { id: q.id } }), x({
141
152
  title: "Client deleted",
142
153
  variant: "success"
143
- }), Y(null), await Z();
154
+ }), J(null), await Z();
144
155
  } catch {
145
156
  x({
146
157
  title: "Failed to delete client",
147
158
  variant: "error"
148
159
  });
149
160
  }
150
- }, Ne = async (e) => {
161
+ }, Pe = async (e) => {
151
162
  X(e.id);
152
163
  try {
153
- await Se({ variables: {
164
+ await we({ variables: {
154
165
  id: e.id,
155
166
  isActive: !e.isActive
156
167
  } }), x({
@@ -165,13 +176,13 @@ function x() {
165
176
  } finally {
166
177
  X(null);
167
178
  }
168
- }, Pe = async () => {
169
- H(!0);
179
+ }, Fe = async () => {
180
+ V(!0);
170
181
  try {
171
- await j({
182
+ await A({
172
183
  variables: {
173
- limit: v,
174
- offset: z.length
184
+ limit: g,
185
+ offset: R.length
175
186
  },
176
187
  updateQuery: (e, { fetchMoreResult: t }) => t ? {
177
188
  ...e,
@@ -182,58 +193,58 @@ function x() {
182
193
  } : e
183
194
  });
184
195
  } finally {
185
- H(!1);
196
+ V(!1);
186
197
  }
187
198
  };
188
- if (O && z.length === 0) return /* @__PURE__ */ g(a, {
199
+ if (D && R.length === 0) return /* @__PURE__ */ p(c, {
189
200
  title: "Client Portal",
190
201
  description: "Manage clients and their projects",
191
- children: /* @__PURE__ */ g(n, {
202
+ children: /* @__PURE__ */ p(n, {
192
203
  variant: "list",
193
204
  count: 4
194
205
  })
195
206
  });
196
- if (k) return /* @__PURE__ */ g(a, {
207
+ if (O) return /* @__PURE__ */ p(c, {
197
208
  title: "Client Portal",
198
209
  description: "Manage clients and their projects",
199
- children: /* @__PURE__ */ g("div", {
210
+ children: /* @__PURE__ */ p("div", {
200
211
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
201
212
  children: "Failed to load client portal data. Please try again later."
202
213
  })
203
214
  });
204
- let $ = /* @__PURE__ */ _("button", {
205
- onClick: Oe,
215
+ let $ = /* @__PURE__ */ m("button", {
216
+ onClick: ke,
206
217
  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",
207
- children: [/* @__PURE__ */ g(le, { className: "h-4 w-4" }), "Add Client"]
218
+ children: [/* @__PURE__ */ p(fe, { className: "h-4 w-4" }), "Add Client"]
208
219
  });
209
- return /* @__PURE__ */ _(a, {
220
+ return /* @__PURE__ */ m(c, {
210
221
  title: "Client Portal",
211
222
  description: "Manage clients and their projects",
212
223
  purpose: "Give clients a transparent window into the work you do for them — the projects, hours and progress tied to each one — so billing conversations are backed by data and trust, not spreadsheets.",
213
224
  actions: $,
214
225
  children: [
215
- /* @__PURE__ */ _("div", {
226
+ /* @__PURE__ */ m("div", {
216
227
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
217
228
  children: [
218
- /* @__PURE__ */ g(t, {
229
+ /* @__PURE__ */ p(t, {
219
230
  featured: !0,
220
231
  title: "Active Clients",
221
- value: String(B?.activeClients ?? 0),
222
- icon: /* @__PURE__ */ g(c, { className: "h-5 w-5" })
232
+ value: String(z?.activeClients ?? 0),
233
+ icon: /* @__PURE__ */ p(oe, { className: "h-5 w-5" })
223
234
  }),
224
- /* @__PURE__ */ g(t, {
235
+ /* @__PURE__ */ p(t, {
225
236
  title: "Total Projects",
226
- value: String(B?.totalProjectsShared ?? 0),
227
- icon: /* @__PURE__ */ g(d, { className: "h-5 w-5" })
237
+ value: String(z?.totalProjectsShared ?? 0),
238
+ icon: /* @__PURE__ */ p(le, { className: "h-5 w-5" })
228
239
  }),
229
- /* @__PURE__ */ g(t, {
240
+ /* @__PURE__ */ p(t, {
230
241
  title: "Total Clients",
231
- value: String(B?.totalClients ?? 0),
232
- icon: /* @__PURE__ */ g(u, { className: "h-5 w-5" })
242
+ value: String(z?.totalClients ?? 0),
243
+ icon: /* @__PURE__ */ p(ce, { className: "h-5 w-5" })
233
244
  }),
234
- /* @__PURE__ */ g(t, {
245
+ /* @__PURE__ */ p(t, {
235
246
  title: "Active",
236
- value: String(B?.activeClients ?? 0),
247
+ value: String(z?.activeClients ?? 0),
237
248
  trend: {
238
249
  value: 0,
239
250
  label: "clients"
@@ -241,28 +252,28 @@ function x() {
241
252
  })
242
253
  ]
243
254
  }),
244
- z.length === 0 ? /* @__PURE__ */ g(ae, {
255
+ R.length === 0 ? /* @__PURE__ */ p(ne, {
245
256
  illustration: "empty-people",
246
257
  title: "No clients configured yet",
247
258
  description: "Add your first client to share projects and tracked hours with them.",
248
259
  action: $
249
- }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
260
+ }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", {
250
261
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
251
- children: /* @__PURE__ */ g("div", {
262
+ children: /* @__PURE__ */ p("div", {
252
263
  className: "divide-y divide-border",
253
- children: z.map((t) => {
254
- let n = t.projects.reduce((e, t) => e + t.trackedHours, 0), r = Te === t.id;
255
- return /* @__PURE__ */ _("div", {
264
+ children: R.map((t) => {
265
+ let n = t.projects.reduce((e, t) => e + t.trackedHours, 0), r = Y === t.id;
266
+ return /* @__PURE__ */ m("div", {
256
267
  className: "flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between",
257
- children: [/* @__PURE__ */ _("div", {
268
+ children: [/* @__PURE__ */ m("div", {
258
269
  className: "flex items-center gap-3",
259
- children: [/* @__PURE__ */ g("div", {
270
+ children: [/* @__PURE__ */ p("div", {
260
271
  className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-sm font-medium text-primary",
261
272
  children: e(t.clientName)
262
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
273
+ }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
263
274
  className: "text-sm font-medium text-text-primary",
264
275
  children: t.clientName
265
- }), /* @__PURE__ */ _("p", {
276
+ }), /* @__PURE__ */ m("p", {
266
277
  className: "text-xs text-text-secondary",
267
278
  children: [
268
279
  t.projects.length,
@@ -270,159 +281,159 @@ function x() {
270
281
  " ",
271
282
  n,
272
283
  "h tracked",
273
- t.contactEmail && /* @__PURE__ */ _(h, { children: [" · ", t.contactEmail] })
284
+ t.contactEmail && /* @__PURE__ */ m(f, { children: [" · ", t.contactEmail] })
274
285
  ]
275
286
  })] })]
276
- }), /* @__PURE__ */ _("div", {
287
+ }), /* @__PURE__ */ m("div", {
277
288
  className: "flex flex-wrap items-center gap-2",
278
289
  children: [
279
- t.isActive && t.shareToken && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
290
+ t.isActive && t.shareToken && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
280
291
  type: "button",
281
- onClick: () => void Ee(t),
292
+ onClick: () => void De(t),
282
293
  title: "Copy the client's private share link",
283
294
  className: "inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50",
284
- children: C === t.id ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(l, { className: "h-3.5 w-3.5" }), " Copied"] }) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(f, { className: "h-3.5 w-3.5" }), " Share link"] })
285
- }), /* @__PURE__ */ g("button", {
295
+ children: be === t.id ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(se, { className: "h-3.5 w-3.5" }), " Copied"] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(ue, { className: "h-3.5 w-3.5" }), " Share link"] })
296
+ }), /* @__PURE__ */ p("button", {
286
297
  type: "button",
287
- disabled: r && F,
288
- onClick: () => void De(t),
298
+ disabled: r && P,
299
+ onClick: () => void Oe(t),
289
300
  title: "Regenerate the link (invalidates the old one)",
290
301
  className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50",
291
- children: /* @__PURE__ */ g(de, { className: `h-3.5 w-3.5 ${r && F ? "animate-spin" : ""}` })
302
+ children: /* @__PURE__ */ p(me, { className: `h-3.5 w-3.5 ${r && P ? "animate-spin" : ""}` })
292
303
  })] }),
293
- /* @__PURE__ */ _("button", {
304
+ /* @__PURE__ */ m("button", {
294
305
  type: "button",
295
306
  disabled: r,
296
- onClick: () => void Ne(t),
307
+ onClick: () => void Pe(t),
297
308
  title: t.isActive ? "Deactivate client portal" : "Activate client portal",
298
309
  className: "inline-flex items-center gap-1 rounded-md border border-border-seam bg-bg-surface px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50",
299
- children: [/* @__PURE__ */ g(ue, { className: "h-3.5 w-3.5" }), t.isActive ? "Deactivate" : "Activate"]
310
+ children: [/* @__PURE__ */ p(pe, { className: "h-3.5 w-3.5" }), t.isActive ? "Deactivate" : "Activate"]
300
311
  }),
301
- /* @__PURE__ */ g(oe, {
312
+ /* @__PURE__ */ p(re, {
302
313
  label: t.isActive ? "active" : "inactive",
303
314
  status: t.isActive ? "active" : "inactive"
304
315
  }),
305
- /* @__PURE__ */ g("button", {
316
+ /* @__PURE__ */ p("button", {
306
317
  type: "button",
307
- onClick: () => ke(t),
318
+ onClick: () => Ae(t),
308
319
  title: "Edit client",
309
320
  className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-text-secondary hover:bg-bg-sunken/50",
310
- children: /* @__PURE__ */ g(p, { className: "h-3.5 w-3.5" })
321
+ children: /* @__PURE__ */ p(de, { className: "h-3.5 w-3.5" })
311
322
  }),
312
- /* @__PURE__ */ g("button", {
323
+ /* @__PURE__ */ p("button", {
313
324
  type: "button",
314
- onClick: () => Y(t),
325
+ onClick: () => J(t),
315
326
  title: "Delete client",
316
327
  className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1.5 text-status-error-text hover:bg-status-error-bg",
317
- children: /* @__PURE__ */ g(fe, { className: "h-3.5 w-3.5" })
328
+ children: /* @__PURE__ */ p(he, { className: "h-3.5 w-3.5" })
318
329
  })
319
330
  ]
320
331
  })]
321
332
  }, t.id);
322
333
  })
323
334
  })
324
- }), Ce && /* @__PURE__ */ g("div", {
335
+ }), Te && /* @__PURE__ */ p("div", {
325
336
  className: "mt-4 flex justify-center",
326
- children: /* @__PURE__ */ g("button", {
327
- onClick: Pe,
328
- disabled: V,
337
+ children: /* @__PURE__ */ p("button", {
338
+ onClick: Fe,
339
+ disabled: B,
329
340
  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",
330
- children: V ? "Loading..." : "Load more"
341
+ children: B ? "Loading..." : "Load more"
331
342
  })
332
343
  })] }),
333
- /* @__PURE__ */ g(o, {
334
- open: we,
344
+ /* @__PURE__ */ p(l, {
345
+ open: Ee,
335
346
  onClose: Q,
336
- title: W ? "Edit Client" : "Add Client",
337
- description: W ? "Update this client's portal details." : "Set up a client so you can share projects and tracked hours with them.",
338
- footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
347
+ title: U ? "Edit Client" : "Add Client",
348
+ description: U ? "Update this client's portal details." : "Set up a client so you can share projects and tracked hours with them.",
349
+ footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
339
350
  onClick: Q,
340
351
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
341
352
  children: "Cancel"
342
- }), /* @__PURE__ */ g("button", {
343
- onClick: je,
344
- disabled: I || L || !K.clientName.trim(),
353
+ }), /* @__PURE__ */ p("button", {
354
+ onClick: Me,
355
+ disabled: F || I || !G.clientName.trim(),
345
356
  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",
346
- children: I || L ? "Saving..." : W ? "Save Changes" : "Add Client"
357
+ children: F || I ? "Saving..." : U ? "Save Changes" : "Add Client"
347
358
  })] }),
348
- children: /* @__PURE__ */ _("div", {
359
+ children: /* @__PURE__ */ m("div", {
349
360
  className: "space-y-4",
350
361
  children: [
351
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
362
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
352
363
  className: "block text-sm font-medium text-text-primary",
353
364
  children: "Client Name"
354
- }), /* @__PURE__ */ g("input", {
365
+ }), /* @__PURE__ */ p("input", {
355
366
  type: "text",
356
367
  placeholder: "e.g. Acme Corp",
357
- value: K.clientName,
358
- onChange: (e) => q({
359
- ...K,
368
+ value: G.clientName,
369
+ onChange: (e) => K({
370
+ ...G,
360
371
  clientName: e.target.value
361
372
  }),
362
- className: y
373
+ className: _
363
374
  })] }),
364
- /* @__PURE__ */ _("div", {
375
+ /* @__PURE__ */ m("div", {
365
376
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
366
- children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
377
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
367
378
  className: "block text-sm font-medium text-text-primary",
368
379
  children: "Contact Email"
369
- }), /* @__PURE__ */ g("input", {
380
+ }), /* @__PURE__ */ p("input", {
370
381
  type: "email",
371
382
  placeholder: "contact@client.com",
372
- value: K.contactEmail,
373
- onChange: (e) => q({
374
- ...K,
383
+ value: G.contactEmail,
384
+ onChange: (e) => K({
385
+ ...G,
375
386
  contactEmail: e.target.value
376
387
  }),
377
- className: y
378
- })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
388
+ className: _
389
+ })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
379
390
  className: "block text-sm font-medium text-text-primary",
380
391
  children: "Contact Phone"
381
- }), /* @__PURE__ */ g("input", {
392
+ }), /* @__PURE__ */ p("input", {
382
393
  type: "tel",
383
394
  placeholder: "Optional",
384
- value: K.contactPhone,
385
- onChange: (e) => q({
386
- ...K,
395
+ value: G.contactPhone,
396
+ onChange: (e) => K({
397
+ ...G,
387
398
  contactPhone: e.target.value
388
399
  }),
389
- className: y
400
+ className: _
390
401
  })] })]
391
402
  }),
392
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
403
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
393
404
  className: "block text-sm font-medium text-text-primary",
394
405
  children: "Description"
395
- }), /* @__PURE__ */ g("textarea", {
406
+ }), /* @__PURE__ */ p("textarea", {
396
407
  rows: 3,
397
408
  placeholder: "Notes about this client (optional)",
398
- value: K.description,
399
- onChange: (e) => q({
400
- ...K,
409
+ value: G.description,
410
+ onChange: (e) => K({
411
+ ...G,
401
412
  description: e.target.value
402
413
  }),
403
- className: y
414
+ className: _
404
415
  })] }),
405
- /* @__PURE__ */ _("div", { children: [
406
- /* @__PURE__ */ g("label", {
416
+ /* @__PURE__ */ m("div", { children: [
417
+ /* @__PURE__ */ p("label", {
407
418
  className: "block text-sm font-medium text-text-primary",
408
419
  children: "Logo"
409
420
  }),
410
- /* @__PURE__ */ g("input", {
421
+ /* @__PURE__ */ p("input", {
411
422
  type: "file",
412
423
  accept: "image/*",
413
- disabled: T,
424
+ disabled: w,
414
425
  onChange: (e) => {
415
426
  let t = e.target.files?.[0];
416
- t && Ae(t), e.target.value = "";
427
+ t && je(t), e.target.value = "";
417
428
  },
418
- className: he
429
+ className: v
419
430
  }),
420
- T && /* @__PURE__ */ g("p", {
431
+ w && /* @__PURE__ */ p("p", {
421
432
  className: "mt-1 text-xs text-text-secondary",
422
433
  children: "Uploading…"
423
434
  }),
424
- K.logoUrl && /* @__PURE__ */ g("img", {
425
- src: K.logoUrl,
435
+ G.logoUrl && /* @__PURE__ */ p("img", {
436
+ src: G.logoUrl,
426
437
  alt: "Client logo preview",
427
438
  className: "mt-2 h-16 w-16 rounded-md border border-border-seam object-cover"
428
439
  })
@@ -430,30 +441,30 @@ function x() {
430
441
  ]
431
442
  })
432
443
  }),
433
- /* @__PURE__ */ g(o, {
434
- open: J !== null,
435
- onClose: () => Y(null),
444
+ /* @__PURE__ */ p(l, {
445
+ open: q !== null,
446
+ onClose: () => J(null),
436
447
  title: "Delete client?",
437
- description: J ? `"${J.clientName}" and its share link will be removed.` : void 0,
448
+ description: q ? `"${q.clientName}" and its share link will be removed.` : void 0,
438
449
  size: "sm",
439
- footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
440
- onClick: () => Y(null),
450
+ footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
451
+ onClick: () => J(null),
441
452
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
442
453
  children: "Cancel"
443
- }), /* @__PURE__ */ g("button", {
444
- onClick: Me,
445
- disabled: R,
454
+ }), /* @__PURE__ */ p("button", {
455
+ onClick: Ne,
456
+ disabled: L,
446
457
  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",
447
- children: R ? "Deleting..." : "Delete"
458
+ children: L ? "Deleting..." : "Delete"
448
459
  })] }),
449
- children: /* @__PURE__ */ g("p", {
460
+ children: /* @__PURE__ */ p("p", {
450
461
  className: "text-sm text-text-secondary",
451
462
  children: "The client will lose access to their shared portal immediately. This cannot be undone."
452
463
  })
453
464
  }),
454
- /* @__PURE__ */ g(se, {
455
- toasts: m,
456
- onDismiss: ve
465
+ /* @__PURE__ */ p(ie, {
466
+ toasts: d,
467
+ onDismiss: ye
457
468
  })
458
469
  ]
459
470
  });