@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.
- package/dist/generated/wspace-types.js +28 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/timecampus/components/Modal.js +65 -0
- package/dist/timecampus/components/Modal.js.map +1 -0
- package/dist/timecampus/components/StatusBadge.js +28 -0
- package/dist/timecampus/components/StatusBadge.js.map +1 -0
- package/dist/timecampus/components/Toast.js +87 -0
- package/dist/timecampus/components/Toast.js.map +1 -0
- package/dist/timecampus/index.js +1 -1
- package/dist/timecampus/pages/HomePage.js +143 -135
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +432 -118
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +562 -85
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +208 -0
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +367 -65
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +4 -3
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +229 -97
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +339 -110
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/dist/timecampus/store/timecampusStore.js +13 -1
- package/dist/timecampus/store/timecampusStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,150 +1,464 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { getInitials as e } from "../../utils/helpers.js";
|
|
2
|
+
import { StatCard as t } from "../../components/StatCard.js";
|
|
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";
|
|
11
16
|
//#region src/timecampus/pages/client-portal/ClientPortalPage.tsx
|
|
12
|
-
|
|
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`
|
|
18
|
+
query TimeCampus_GetFileDownloadUrl($id: ID!, $expiresIn: Int) {
|
|
19
|
+
getFileDownloadUrl(id: $id, expiresIn: $expiresIn)
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
function _e(e) {
|
|
13
23
|
return `${typeof window < "u" ? window.location.origin : ""}/portal/${e}`;
|
|
14
24
|
}
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
var b = {
|
|
26
|
+
clientName: "",
|
|
27
|
+
contactEmail: "",
|
|
28
|
+
contactPhone: "",
|
|
29
|
+
description: "",
|
|
30
|
+
logoUrl: ""
|
|
31
|
+
};
|
|
32
|
+
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({
|
|
34
|
+
variables: {
|
|
35
|
+
limit: v,
|
|
36
|
+
offset: 0
|
|
37
|
+
},
|
|
38
|
+
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) => {
|
|
17
42
|
if (e.shareToken) try {
|
|
18
|
-
await navigator.clipboard.writeText(
|
|
19
|
-
} catch {
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
await navigator.clipboard.writeText(_e(e.shareToken)), w(e.id), window.setTimeout(() => w(null), 2e3);
|
|
44
|
+
} catch {
|
|
45
|
+
x({
|
|
46
|
+
title: "Clipboard unavailable",
|
|
47
|
+
variant: "error"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}, De = async (e) => {
|
|
51
|
+
X(e.id);
|
|
52
|
+
try {
|
|
53
|
+
await P({ variables: { id: e.id } }), x({
|
|
54
|
+
title: "Share link regenerated",
|
|
55
|
+
variant: "success"
|
|
56
|
+
}), await A();
|
|
57
|
+
} catch {
|
|
58
|
+
x({
|
|
59
|
+
title: "Failed to regenerate link",
|
|
60
|
+
variant: "error"
|
|
61
|
+
});
|
|
62
|
+
} finally {
|
|
63
|
+
X(null);
|
|
64
|
+
}
|
|
65
|
+
}, Oe = () => {
|
|
66
|
+
G(null), q(b), U(!0);
|
|
67
|
+
}, ke = (e) => {
|
|
68
|
+
G(e), q({
|
|
69
|
+
clientName: e.clientName,
|
|
70
|
+
contactEmail: e.contactEmail ?? "",
|
|
71
|
+
contactPhone: e.contactPhone ?? "",
|
|
72
|
+
description: e.description ?? "",
|
|
73
|
+
logoUrl: e.logoUrl ?? ""
|
|
74
|
+
}), U(!0);
|
|
75
|
+
}, Ae = async (e) => {
|
|
76
|
+
E(!0);
|
|
77
|
+
try {
|
|
78
|
+
let t = await me({
|
|
79
|
+
client: S,
|
|
80
|
+
blob: e,
|
|
81
|
+
filename: e.name,
|
|
82
|
+
mimeType: e.type || "application/octet-stream",
|
|
83
|
+
folderId: ""
|
|
84
|
+
}), { data: n } = await S.query({
|
|
85
|
+
query: ge,
|
|
86
|
+
variables: {
|
|
87
|
+
id: t.id,
|
|
88
|
+
expiresIn: 604800
|
|
89
|
+
},
|
|
90
|
+
fetchPolicy: "no-cache"
|
|
91
|
+
}), r = n?.getFileDownloadUrl;
|
|
92
|
+
if (!r) throw Error("No download URL returned");
|
|
93
|
+
q((e) => ({
|
|
94
|
+
...e,
|
|
95
|
+
logoUrl: r
|
|
96
|
+
}));
|
|
97
|
+
} catch {
|
|
98
|
+
x({
|
|
99
|
+
title: "Failed to upload logo",
|
|
100
|
+
variant: "error"
|
|
101
|
+
});
|
|
102
|
+
} finally {
|
|
103
|
+
E(!1);
|
|
104
|
+
}
|
|
105
|
+
}, Q = () => {
|
|
106
|
+
U(!1), G(null);
|
|
107
|
+
}, je = async () => {
|
|
108
|
+
if (K.clientName.trim()) try {
|
|
109
|
+
W ? (await be({ variables: {
|
|
110
|
+
id: W.id,
|
|
111
|
+
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
|
|
117
|
+
}
|
|
118
|
+
} }), x({
|
|
119
|
+
title: "Client updated",
|
|
120
|
+
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,
|
|
127
|
+
isActive: !0
|
|
128
|
+
} } }), x({
|
|
129
|
+
title: "Client added",
|
|
130
|
+
variant: "success"
|
|
131
|
+
})), Q(), await Z();
|
|
132
|
+
} catch {
|
|
133
|
+
x({
|
|
134
|
+
title: W ? "Failed to update client" : "Failed to add client",
|
|
135
|
+
variant: "error"
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}, Me = async () => {
|
|
139
|
+
if (J) try {
|
|
140
|
+
await xe({ variables: { id: J.id } }), x({
|
|
141
|
+
title: "Client deleted",
|
|
142
|
+
variant: "success"
|
|
143
|
+
}), Y(null), await Z();
|
|
144
|
+
} catch {
|
|
145
|
+
x({
|
|
146
|
+
title: "Failed to delete client",
|
|
147
|
+
variant: "error"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}, Ne = async (e) => {
|
|
151
|
+
X(e.id);
|
|
152
|
+
try {
|
|
153
|
+
await Se({ variables: {
|
|
154
|
+
id: e.id,
|
|
155
|
+
isActive: !e.isActive
|
|
156
|
+
} }), x({
|
|
157
|
+
title: e.isActive ? "Client deactivated" : "Client activated",
|
|
158
|
+
variant: "success"
|
|
159
|
+
}), await Z();
|
|
160
|
+
} catch {
|
|
161
|
+
x({
|
|
162
|
+
title: "Failed to toggle client",
|
|
163
|
+
variant: "error"
|
|
164
|
+
});
|
|
165
|
+
} finally {
|
|
166
|
+
X(null);
|
|
167
|
+
}
|
|
168
|
+
}, Pe = async () => {
|
|
169
|
+
H(!0);
|
|
170
|
+
try {
|
|
171
|
+
await j({
|
|
172
|
+
variables: {
|
|
173
|
+
limit: v,
|
|
174
|
+
offset: z.length
|
|
175
|
+
},
|
|
176
|
+
updateQuery: (e, { fetchMoreResult: t }) => t ? {
|
|
177
|
+
...e,
|
|
178
|
+
clientPortalConfigs: {
|
|
179
|
+
...t.clientPortalConfigs,
|
|
180
|
+
items: [...e.clientPortalConfigs.items, ...t.clientPortalConfigs.items]
|
|
181
|
+
}
|
|
182
|
+
} : e
|
|
183
|
+
});
|
|
184
|
+
} finally {
|
|
185
|
+
H(!1);
|
|
186
|
+
}
|
|
22
187
|
};
|
|
23
|
-
|
|
188
|
+
if (O && z.length === 0) return /* @__PURE__ */ g(a, {
|
|
24
189
|
title: "Client Portal",
|
|
25
190
|
description: "Manage clients and their projects",
|
|
26
|
-
children: /* @__PURE__ */
|
|
191
|
+
children: /* @__PURE__ */ g(n, {
|
|
27
192
|
variant: "list",
|
|
28
193
|
count: 4
|
|
29
194
|
})
|
|
30
|
-
})
|
|
195
|
+
});
|
|
196
|
+
if (k) return /* @__PURE__ */ g(a, {
|
|
31
197
|
title: "Client Portal",
|
|
32
198
|
description: "Manage clients and their projects",
|
|
33
|
-
children: /* @__PURE__ */
|
|
199
|
+
children: /* @__PURE__ */ g("div", {
|
|
34
200
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
35
201
|
children: "Failed to load client portal data. Please try again later."
|
|
36
202
|
})
|
|
37
|
-
})
|
|
203
|
+
});
|
|
204
|
+
let $ = /* @__PURE__ */ _("button", {
|
|
205
|
+
onClick: Oe,
|
|
206
|
+
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"]
|
|
208
|
+
});
|
|
209
|
+
return /* @__PURE__ */ _(a, {
|
|
38
210
|
title: "Client Portal",
|
|
39
211
|
description: "Manage clients and their projects",
|
|
40
212
|
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.",
|
|
41
|
-
actions:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
className: "
|
|
79
|
-
children:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
213
|
+
actions: $,
|
|
214
|
+
children: [
|
|
215
|
+
/* @__PURE__ */ _("div", {
|
|
216
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
217
|
+
children: [
|
|
218
|
+
/* @__PURE__ */ g(t, {
|
|
219
|
+
featured: !0,
|
|
220
|
+
title: "Active Clients",
|
|
221
|
+
value: String(B?.activeClients ?? 0),
|
|
222
|
+
icon: /* @__PURE__ */ g(c, { className: "h-5 w-5" })
|
|
223
|
+
}),
|
|
224
|
+
/* @__PURE__ */ g(t, {
|
|
225
|
+
title: "Total Projects",
|
|
226
|
+
value: String(B?.totalProjectsShared ?? 0),
|
|
227
|
+
icon: /* @__PURE__ */ g(d, { className: "h-5 w-5" })
|
|
228
|
+
}),
|
|
229
|
+
/* @__PURE__ */ g(t, {
|
|
230
|
+
title: "Total Clients",
|
|
231
|
+
value: String(B?.totalClients ?? 0),
|
|
232
|
+
icon: /* @__PURE__ */ g(u, { className: "h-5 w-5" })
|
|
233
|
+
}),
|
|
234
|
+
/* @__PURE__ */ g(t, {
|
|
235
|
+
title: "Active",
|
|
236
|
+
value: String(B?.activeClients ?? 0),
|
|
237
|
+
trend: {
|
|
238
|
+
value: 0,
|
|
239
|
+
label: "clients"
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
]
|
|
243
|
+
}),
|
|
244
|
+
z.length === 0 ? /* @__PURE__ */ g(ae, {
|
|
245
|
+
illustration: "empty-people",
|
|
246
|
+
title: "No clients configured yet",
|
|
247
|
+
description: "Add your first client to share projects and tracked hours with them.",
|
|
248
|
+
action: $
|
|
249
|
+
}) : /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
|
|
250
|
+
className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
|
|
251
|
+
children: /* @__PURE__ */ g("div", {
|
|
252
|
+
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", {
|
|
256
|
+
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", {
|
|
258
|
+
className: "flex items-center gap-3",
|
|
259
|
+
children: [/* @__PURE__ */ g("div", {
|
|
260
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-sm font-medium text-primary",
|
|
261
|
+
children: e(t.clientName)
|
|
262
|
+
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
263
|
+
className: "text-sm font-medium text-text-primary",
|
|
264
|
+
children: t.clientName
|
|
265
|
+
}), /* @__PURE__ */ _("p", {
|
|
266
|
+
className: "text-xs text-text-secondary",
|
|
267
|
+
children: [
|
|
268
|
+
t.projects.length,
|
|
269
|
+
" projects ·",
|
|
270
|
+
" ",
|
|
271
|
+
n,
|
|
272
|
+
"h tracked",
|
|
273
|
+
t.contactEmail && /* @__PURE__ */ _(h, { children: [" · ", t.contactEmail] })
|
|
274
|
+
]
|
|
275
|
+
})] })]
|
|
276
|
+
}), /* @__PURE__ */ _("div", {
|
|
277
|
+
className: "flex flex-wrap items-center gap-2",
|
|
99
278
|
children: [
|
|
100
|
-
|
|
101
|
-
" projects ·",
|
|
102
|
-
" ",
|
|
103
|
-
r,
|
|
104
|
-
"h tracked"
|
|
105
|
-
]
|
|
106
|
-
})] })]
|
|
107
|
-
}), /* @__PURE__ */ b("div", {
|
|
108
|
-
className: "flex items-center gap-3",
|
|
109
|
-
children: [
|
|
110
|
-
e.contactEmail && /* @__PURE__ */ y("span", {
|
|
111
|
-
className: "hidden text-xs text-text-secondary sm:inline",
|
|
112
|
-
children: e.contactEmail
|
|
113
|
-
}),
|
|
114
|
-
e.isActive && e.shareToken && /* @__PURE__ */ b("div", {
|
|
115
|
-
className: "flex items-center gap-1",
|
|
116
|
-
children: [/* @__PURE__ */ y("button", {
|
|
279
|
+
t.isActive && t.shareToken && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
|
|
117
280
|
type: "button",
|
|
118
|
-
onClick: (
|
|
119
|
-
t.stopPropagation(), P(e);
|
|
120
|
-
},
|
|
281
|
+
onClick: () => void Ee(t),
|
|
121
282
|
title: "Copy the client's private share link",
|
|
122
283
|
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",
|
|
123
|
-
children: C ===
|
|
124
|
-
}), /* @__PURE__ */
|
|
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", {
|
|
125
286
|
type: "button",
|
|
126
|
-
disabled:
|
|
127
|
-
onClick: (
|
|
128
|
-
t.stopPropagation(), F(e);
|
|
129
|
-
},
|
|
287
|
+
disabled: r && F,
|
|
288
|
+
onClick: () => void De(t),
|
|
130
289
|
title: "Regenerate the link (invalidates the old one)",
|
|
131
|
-
className: "inline-flex items-center rounded-md border border-border-seam bg-bg-surface p-1 text-text-secondary hover:bg-bg-sunken/50 disabled:opacity-50",
|
|
132
|
-
children: /* @__PURE__ */
|
|
133
|
-
})]
|
|
290
|
+
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" : ""}` })
|
|
292
|
+
})] }),
|
|
293
|
+
/* @__PURE__ */ _("button", {
|
|
294
|
+
type: "button",
|
|
295
|
+
disabled: r,
|
|
296
|
+
onClick: () => void Ne(t),
|
|
297
|
+
title: t.isActive ? "Deactivate client portal" : "Activate client portal",
|
|
298
|
+
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"]
|
|
300
|
+
}),
|
|
301
|
+
/* @__PURE__ */ g(oe, {
|
|
302
|
+
label: t.isActive ? "active" : "inactive",
|
|
303
|
+
status: t.isActive ? "active" : "inactive"
|
|
304
|
+
}),
|
|
305
|
+
/* @__PURE__ */ g("button", {
|
|
306
|
+
type: "button",
|
|
307
|
+
onClick: () => ke(t),
|
|
308
|
+
title: "Edit client",
|
|
309
|
+
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" })
|
|
311
|
+
}),
|
|
312
|
+
/* @__PURE__ */ g("button", {
|
|
313
|
+
type: "button",
|
|
314
|
+
onClick: () => Y(t),
|
|
315
|
+
title: "Delete client",
|
|
316
|
+
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" })
|
|
318
|
+
})
|
|
319
|
+
]
|
|
320
|
+
})]
|
|
321
|
+
}, t.id);
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
}), Ce && /* @__PURE__ */ g("div", {
|
|
325
|
+
className: "mt-4 flex justify-center",
|
|
326
|
+
children: /* @__PURE__ */ g("button", {
|
|
327
|
+
onClick: Pe,
|
|
328
|
+
disabled: V,
|
|
329
|
+
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"
|
|
331
|
+
})
|
|
332
|
+
})] }),
|
|
333
|
+
/* @__PURE__ */ g(o, {
|
|
334
|
+
open: we,
|
|
335
|
+
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", {
|
|
339
|
+
onClick: Q,
|
|
340
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
341
|
+
children: "Cancel"
|
|
342
|
+
}), /* @__PURE__ */ g("button", {
|
|
343
|
+
onClick: je,
|
|
344
|
+
disabled: I || L || !K.clientName.trim(),
|
|
345
|
+
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"
|
|
347
|
+
})] }),
|
|
348
|
+
children: /* @__PURE__ */ _("div", {
|
|
349
|
+
className: "space-y-4",
|
|
350
|
+
children: [
|
|
351
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
352
|
+
className: "block text-sm font-medium text-text-primary",
|
|
353
|
+
children: "Client Name"
|
|
354
|
+
}), /* @__PURE__ */ g("input", {
|
|
355
|
+
type: "text",
|
|
356
|
+
placeholder: "e.g. Acme Corp",
|
|
357
|
+
value: K.clientName,
|
|
358
|
+
onChange: (e) => q({
|
|
359
|
+
...K,
|
|
360
|
+
clientName: e.target.value
|
|
361
|
+
}),
|
|
362
|
+
className: y
|
|
363
|
+
})] }),
|
|
364
|
+
/* @__PURE__ */ _("div", {
|
|
365
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
366
|
+
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
367
|
+
className: "block text-sm font-medium text-text-primary",
|
|
368
|
+
children: "Contact Email"
|
|
369
|
+
}), /* @__PURE__ */ g("input", {
|
|
370
|
+
type: "email",
|
|
371
|
+
placeholder: "contact@client.com",
|
|
372
|
+
value: K.contactEmail,
|
|
373
|
+
onChange: (e) => q({
|
|
374
|
+
...K,
|
|
375
|
+
contactEmail: e.target.value
|
|
376
|
+
}),
|
|
377
|
+
className: y
|
|
378
|
+
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
379
|
+
className: "block text-sm font-medium text-text-primary",
|
|
380
|
+
children: "Contact Phone"
|
|
381
|
+
}), /* @__PURE__ */ g("input", {
|
|
382
|
+
type: "tel",
|
|
383
|
+
placeholder: "Optional",
|
|
384
|
+
value: K.contactPhone,
|
|
385
|
+
onChange: (e) => q({
|
|
386
|
+
...K,
|
|
387
|
+
contactPhone: e.target.value
|
|
134
388
|
}),
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
389
|
+
className: y
|
|
390
|
+
})] })]
|
|
391
|
+
}),
|
|
392
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
393
|
+
className: "block text-sm font-medium text-text-primary",
|
|
394
|
+
children: "Description"
|
|
395
|
+
}), /* @__PURE__ */ g("textarea", {
|
|
396
|
+
rows: 3,
|
|
397
|
+
placeholder: "Notes about this client (optional)",
|
|
398
|
+
value: K.description,
|
|
399
|
+
onChange: (e) => q({
|
|
400
|
+
...K,
|
|
401
|
+
description: e.target.value
|
|
402
|
+
}),
|
|
403
|
+
className: y
|
|
404
|
+
})] }),
|
|
405
|
+
/* @__PURE__ */ _("div", { children: [
|
|
406
|
+
/* @__PURE__ */ g("label", {
|
|
407
|
+
className: "block text-sm font-medium text-text-primary",
|
|
408
|
+
children: "Logo"
|
|
409
|
+
}),
|
|
410
|
+
/* @__PURE__ */ g("input", {
|
|
411
|
+
type: "file",
|
|
412
|
+
accept: "image/*",
|
|
413
|
+
disabled: T,
|
|
414
|
+
onChange: (e) => {
|
|
415
|
+
let t = e.target.files?.[0];
|
|
416
|
+
t && Ae(t), e.target.value = "";
|
|
417
|
+
},
|
|
418
|
+
className: he
|
|
419
|
+
}),
|
|
420
|
+
T && /* @__PURE__ */ g("p", {
|
|
421
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
422
|
+
children: "Uploading…"
|
|
423
|
+
}),
|
|
424
|
+
K.logoUrl && /* @__PURE__ */ g("img", {
|
|
425
|
+
src: K.logoUrl,
|
|
426
|
+
alt: "Client logo preview",
|
|
427
|
+
className: "mt-2 h-16 w-16 rounded-md border border-border-seam object-cover"
|
|
428
|
+
})
|
|
429
|
+
] })
|
|
430
|
+
]
|
|
431
|
+
})
|
|
432
|
+
}),
|
|
433
|
+
/* @__PURE__ */ g(o, {
|
|
434
|
+
open: J !== null,
|
|
435
|
+
onClose: () => Y(null),
|
|
436
|
+
title: "Delete client?",
|
|
437
|
+
description: J ? `"${J.clientName}" and its share link will be removed.` : void 0,
|
|
438
|
+
size: "sm",
|
|
439
|
+
footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
|
|
440
|
+
onClick: () => Y(null),
|
|
441
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
442
|
+
children: "Cancel"
|
|
443
|
+
}), /* @__PURE__ */ g("button", {
|
|
444
|
+
onClick: Me,
|
|
445
|
+
disabled: R,
|
|
446
|
+
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"
|
|
448
|
+
})] }),
|
|
449
|
+
children: /* @__PURE__ */ g("p", {
|
|
450
|
+
className: "text-sm text-text-secondary",
|
|
451
|
+
children: "The client will lose access to their shared portal immediately. This cannot be undone."
|
|
142
452
|
})
|
|
453
|
+
}),
|
|
454
|
+
/* @__PURE__ */ g(se, {
|
|
455
|
+
toasts: m,
|
|
456
|
+
onDismiss: ve
|
|
143
457
|
})
|
|
144
|
-
|
|
458
|
+
]
|
|
145
459
|
});
|
|
146
460
|
}
|
|
147
461
|
//#endregion
|
|
148
|
-
export {
|
|
462
|
+
export { x as ClientPortalPage };
|
|
149
463
|
|
|
150
464
|
//# sourceMappingURL=ClientPortalPage.js.map
|