@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.
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +192 -181
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +60 -49
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +79 -68
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
5
|
-
import { PageLayout as
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import { Modal as
|
|
8
|
-
import { StatusBadge as
|
|
9
|
-
import { ToastContainer as
|
|
10
|
-
import { useState as
|
|
11
|
-
import { Building2 as
|
|
12
|
-
import { gql as
|
|
13
|
-
import { useApolloClient as
|
|
14
|
-
import { Fragment as
|
|
15
|
-
import { uploadBlobToFiles as
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
41
|
+
limit: g,
|
|
36
42
|
offset: 0
|
|
37
43
|
},
|
|
38
44
|
notifyOnNetworkStatusChange: !0
|
|
39
|
-
}), { data:
|
|
40
|
-
await Promise.all([
|
|
41
|
-
},
|
|
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(
|
|
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
|
-
},
|
|
56
|
+
}, Oe = async (e) => {
|
|
51
57
|
X(e.id);
|
|
52
58
|
try {
|
|
53
|
-
await
|
|
59
|
+
await N({ variables: { id: e.id } }), x({
|
|
54
60
|
title: "Share link regenerated",
|
|
55
61
|
variant: "success"
|
|
56
|
-
}), await
|
|
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
|
-
},
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
|
|
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
|
-
}),
|
|
75
|
-
},
|
|
76
|
-
|
|
80
|
+
}), H(!0);
|
|
81
|
+
}, je = async (e) => {
|
|
82
|
+
T(!0);
|
|
77
83
|
try {
|
|
78
|
-
let t = await
|
|
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:
|
|
85
|
-
query:
|
|
94
|
+
folderId: n
|
|
95
|
+
}), { data: i } = await S.query({
|
|
96
|
+
query: y,
|
|
86
97
|
variables: {
|
|
87
|
-
id:
|
|
98
|
+
id: r.id,
|
|
88
99
|
expiresIn: 604800
|
|
89
100
|
},
|
|
90
101
|
fetchPolicy: "no-cache"
|
|
91
|
-
}),
|
|
92
|
-
if (!
|
|
93
|
-
|
|
102
|
+
}), a = i?.getFileDownloadUrl;
|
|
103
|
+
if (!a) throw Error("No download URL returned");
|
|
104
|
+
K((e) => ({
|
|
94
105
|
...e,
|
|
95
|
-
logoUrl:
|
|
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
|
-
|
|
114
|
+
T(!1);
|
|
104
115
|
}
|
|
105
116
|
}, Q = () => {
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
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:
|
|
113
|
-
contactEmail:
|
|
114
|
-
contactPhone:
|
|
115
|
-
description:
|
|
116
|
-
logoUrl:
|
|
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
|
|
122
|
-
clientName:
|
|
123
|
-
contactEmail:
|
|
124
|
-
contactPhone:
|
|
125
|
-
description:
|
|
126
|
-
logoUrl:
|
|
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:
|
|
145
|
+
title: U ? "Failed to update client" : "Failed to add client",
|
|
135
146
|
variant: "error"
|
|
136
147
|
});
|
|
137
148
|
}
|
|
138
|
-
},
|
|
139
|
-
if (
|
|
140
|
-
await
|
|
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
|
-
}),
|
|
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
|
-
},
|
|
161
|
+
}, Pe = async (e) => {
|
|
151
162
|
X(e.id);
|
|
152
163
|
try {
|
|
153
|
-
await
|
|
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
|
-
},
|
|
169
|
-
|
|
179
|
+
}, Fe = async () => {
|
|
180
|
+
V(!0);
|
|
170
181
|
try {
|
|
171
|
-
await
|
|
182
|
+
await A({
|
|
172
183
|
variables: {
|
|
173
|
-
limit:
|
|
174
|
-
offset:
|
|
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
|
-
|
|
196
|
+
V(!1);
|
|
186
197
|
}
|
|
187
198
|
};
|
|
188
|
-
if (
|
|
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__ */
|
|
202
|
+
children: /* @__PURE__ */ p(n, {
|
|
192
203
|
variant: "list",
|
|
193
204
|
count: 4
|
|
194
205
|
})
|
|
195
206
|
});
|
|
196
|
-
if (
|
|
207
|
+
if (O) return /* @__PURE__ */ p(c, {
|
|
197
208
|
title: "Client Portal",
|
|
198
209
|
description: "Manage clients and their projects",
|
|
199
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
205
|
-
onClick:
|
|
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__ */
|
|
218
|
+
children: [/* @__PURE__ */ p(fe, { className: "h-4 w-4" }), "Add Client"]
|
|
208
219
|
});
|
|
209
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
229
|
+
/* @__PURE__ */ p(t, {
|
|
219
230
|
featured: !0,
|
|
220
231
|
title: "Active Clients",
|
|
221
|
-
value: String(
|
|
222
|
-
icon: /* @__PURE__ */
|
|
232
|
+
value: String(z?.activeClients ?? 0),
|
|
233
|
+
icon: /* @__PURE__ */ p(oe, { className: "h-5 w-5" })
|
|
223
234
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ p(t, {
|
|
225
236
|
title: "Total Projects",
|
|
226
|
-
value: String(
|
|
227
|
-
icon: /* @__PURE__ */
|
|
237
|
+
value: String(z?.totalProjectsShared ?? 0),
|
|
238
|
+
icon: /* @__PURE__ */ p(le, { className: "h-5 w-5" })
|
|
228
239
|
}),
|
|
229
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ p(t, {
|
|
230
241
|
title: "Total Clients",
|
|
231
|
-
value: String(
|
|
232
|
-
icon: /* @__PURE__ */
|
|
242
|
+
value: String(z?.totalClients ?? 0),
|
|
243
|
+
icon: /* @__PURE__ */ p(ce, { className: "h-5 w-5" })
|
|
233
244
|
}),
|
|
234
|
-
/* @__PURE__ */
|
|
245
|
+
/* @__PURE__ */ p(t, {
|
|
235
246
|
title: "Active",
|
|
236
|
-
value: String(
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
262
|
+
children: /* @__PURE__ */ p("div", {
|
|
252
263
|
className: "divide-y divide-border",
|
|
253
|
-
children:
|
|
254
|
-
let n = t.projects.reduce((e, t) => e + t.trackedHours, 0), r =
|
|
255
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
268
|
+
children: [/* @__PURE__ */ m("div", {
|
|
258
269
|
className: "flex items-center gap-3",
|
|
259
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
284
|
+
t.contactEmail && /* @__PURE__ */ m(f, { children: [" · ", t.contactEmail] })
|
|
274
285
|
]
|
|
275
286
|
})] })]
|
|
276
|
-
}), /* @__PURE__ */
|
|
287
|
+
}), /* @__PURE__ */ m("div", {
|
|
277
288
|
className: "flex flex-wrap items-center gap-2",
|
|
278
289
|
children: [
|
|
279
|
-
t.isActive && t.shareToken && /* @__PURE__ */
|
|
290
|
+
t.isActive && t.shareToken && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
|
|
280
291
|
type: "button",
|
|
281
|
-
onClick: () => void
|
|
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:
|
|
285
|
-
}), /* @__PURE__ */
|
|
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 &&
|
|
288
|
-
onClick: () => void
|
|
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__ */
|
|
302
|
+
children: /* @__PURE__ */ p(me, { className: `h-3.5 w-3.5 ${r && P ? "animate-spin" : ""}` })
|
|
292
303
|
})] }),
|
|
293
|
-
/* @__PURE__ */
|
|
304
|
+
/* @__PURE__ */ m("button", {
|
|
294
305
|
type: "button",
|
|
295
306
|
disabled: r,
|
|
296
|
-
onClick: () => void
|
|
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__ */
|
|
310
|
+
children: [/* @__PURE__ */ p(pe, { className: "h-3.5 w-3.5" }), t.isActive ? "Deactivate" : "Activate"]
|
|
300
311
|
}),
|
|
301
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ p(re, {
|
|
302
313
|
label: t.isActive ? "active" : "inactive",
|
|
303
314
|
status: t.isActive ? "active" : "inactive"
|
|
304
315
|
}),
|
|
305
|
-
/* @__PURE__ */
|
|
316
|
+
/* @__PURE__ */ p("button", {
|
|
306
317
|
type: "button",
|
|
307
|
-
onClick: () =>
|
|
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__ */
|
|
321
|
+
children: /* @__PURE__ */ p(de, { className: "h-3.5 w-3.5" })
|
|
311
322
|
}),
|
|
312
|
-
/* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ p("button", {
|
|
313
324
|
type: "button",
|
|
314
|
-
onClick: () =>
|
|
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__ */
|
|
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
|
-
}),
|
|
335
|
+
}), Te && /* @__PURE__ */ p("div", {
|
|
325
336
|
className: "mt-4 flex justify-center",
|
|
326
|
-
children: /* @__PURE__ */
|
|
327
|
-
onClick:
|
|
328
|
-
disabled:
|
|
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:
|
|
341
|
+
children: B ? "Loading..." : "Load more"
|
|
331
342
|
})
|
|
332
343
|
})] }),
|
|
333
|
-
/* @__PURE__ */
|
|
334
|
-
open:
|
|
344
|
+
/* @__PURE__ */ p(l, {
|
|
345
|
+
open: Ee,
|
|
335
346
|
onClose: Q,
|
|
336
|
-
title:
|
|
337
|
-
description:
|
|
338
|
-
footer: /* @__PURE__ */
|
|
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__ */
|
|
343
|
-
onClick:
|
|
344
|
-
disabled:
|
|
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:
|
|
357
|
+
children: F || I ? "Saving..." : U ? "Save Changes" : "Add Client"
|
|
347
358
|
})] }),
|
|
348
|
-
children: /* @__PURE__ */
|
|
359
|
+
children: /* @__PURE__ */ m("div", {
|
|
349
360
|
className: "space-y-4",
|
|
350
361
|
children: [
|
|
351
|
-
/* @__PURE__ */
|
|
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__ */
|
|
365
|
+
}), /* @__PURE__ */ p("input", {
|
|
355
366
|
type: "text",
|
|
356
367
|
placeholder: "e.g. Acme Corp",
|
|
357
|
-
value:
|
|
358
|
-
onChange: (e) =>
|
|
359
|
-
...
|
|
368
|
+
value: G.clientName,
|
|
369
|
+
onChange: (e) => K({
|
|
370
|
+
...G,
|
|
360
371
|
clientName: e.target.value
|
|
361
372
|
}),
|
|
362
|
-
className:
|
|
373
|
+
className: _
|
|
363
374
|
})] }),
|
|
364
|
-
/* @__PURE__ */
|
|
375
|
+
/* @__PURE__ */ m("div", {
|
|
365
376
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
366
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
380
|
+
}), /* @__PURE__ */ p("input", {
|
|
370
381
|
type: "email",
|
|
371
382
|
placeholder: "contact@client.com",
|
|
372
|
-
value:
|
|
373
|
-
onChange: (e) =>
|
|
374
|
-
...
|
|
383
|
+
value: G.contactEmail,
|
|
384
|
+
onChange: (e) => K({
|
|
385
|
+
...G,
|
|
375
386
|
contactEmail: e.target.value
|
|
376
387
|
}),
|
|
377
|
-
className:
|
|
378
|
-
})] }), /* @__PURE__ */
|
|
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__ */
|
|
392
|
+
}), /* @__PURE__ */ p("input", {
|
|
382
393
|
type: "tel",
|
|
383
394
|
placeholder: "Optional",
|
|
384
|
-
value:
|
|
385
|
-
onChange: (e) =>
|
|
386
|
-
...
|
|
395
|
+
value: G.contactPhone,
|
|
396
|
+
onChange: (e) => K({
|
|
397
|
+
...G,
|
|
387
398
|
contactPhone: e.target.value
|
|
388
399
|
}),
|
|
389
|
-
className:
|
|
400
|
+
className: _
|
|
390
401
|
})] })]
|
|
391
402
|
}),
|
|
392
|
-
/* @__PURE__ */
|
|
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__ */
|
|
406
|
+
}), /* @__PURE__ */ p("textarea", {
|
|
396
407
|
rows: 3,
|
|
397
408
|
placeholder: "Notes about this client (optional)",
|
|
398
|
-
value:
|
|
399
|
-
onChange: (e) =>
|
|
400
|
-
...
|
|
409
|
+
value: G.description,
|
|
410
|
+
onChange: (e) => K({
|
|
411
|
+
...G,
|
|
401
412
|
description: e.target.value
|
|
402
413
|
}),
|
|
403
|
-
className:
|
|
414
|
+
className: _
|
|
404
415
|
})] }),
|
|
405
|
-
/* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
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__ */
|
|
421
|
+
/* @__PURE__ */ p("input", {
|
|
411
422
|
type: "file",
|
|
412
423
|
accept: "image/*",
|
|
413
|
-
disabled:
|
|
424
|
+
disabled: w,
|
|
414
425
|
onChange: (e) => {
|
|
415
426
|
let t = e.target.files?.[0];
|
|
416
|
-
t &&
|
|
427
|
+
t && je(t), e.target.value = "";
|
|
417
428
|
},
|
|
418
|
-
className:
|
|
429
|
+
className: v
|
|
419
430
|
}),
|
|
420
|
-
|
|
431
|
+
w && /* @__PURE__ */ p("p", {
|
|
421
432
|
className: "mt-1 text-xs text-text-secondary",
|
|
422
433
|
children: "Uploading…"
|
|
423
434
|
}),
|
|
424
|
-
|
|
425
|
-
src:
|
|
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__ */
|
|
434
|
-
open:
|
|
435
|
-
onClose: () =>
|
|
444
|
+
/* @__PURE__ */ p(l, {
|
|
445
|
+
open: q !== null,
|
|
446
|
+
onClose: () => J(null),
|
|
436
447
|
title: "Delete client?",
|
|
437
|
-
description:
|
|
448
|
+
description: q ? `"${q.clientName}" and its share link will be removed.` : void 0,
|
|
438
449
|
size: "sm",
|
|
439
|
-
footer: /* @__PURE__ */
|
|
440
|
-
onClick: () =>
|
|
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__ */
|
|
444
|
-
onClick:
|
|
445
|
-
disabled:
|
|
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:
|
|
458
|
+
children: L ? "Deleting..." : "Delete"
|
|
448
459
|
})] }),
|
|
449
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
455
|
-
toasts:
|
|
456
|
-
onDismiss:
|
|
465
|
+
/* @__PURE__ */ p(ie, {
|
|
466
|
+
toasts: d,
|
|
467
|
+
onDismiss: ye
|
|
457
468
|
})
|
|
458
469
|
]
|
|
459
470
|
});
|