@elevasis/ui 2.30.0 → 2.31.0

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 (109) hide show
  1. package/dist/{CoreAuthKitInner-QC62UHTZ.js → CoreAuthKitInner-KSEGSB67.js} +1 -1
  2. package/dist/api/index.js +3 -3
  3. package/dist/app/index.d.ts +122 -1
  4. package/dist/app/index.js +7 -7
  5. package/dist/auth/context.js +1 -1
  6. package/dist/auth/index.js +1 -1
  7. package/dist/charts/index.js +4 -4
  8. package/dist/{chunk-T5Z7G2J2.js → chunk-3BAPR3KA.js} +1 -1
  9. package/dist/{chunk-4VQ2PXMI.js → chunk-3FV6HBXS.js} +4 -4
  10. package/dist/{chunk-2DIYILF7.js → chunk-542WPQU2.js} +2 -2
  11. package/dist/{chunk-SBCIB5TZ.js → chunk-5LJAEZMA.js} +5 -5
  12. package/dist/{chunk-T2PAD63Y.js → chunk-7HMCB26R.js} +1 -1
  13. package/dist/chunk-7KC4P3AU.js +357 -0
  14. package/dist/{chunk-AKOD52HS.js → chunk-CQZ3DNQY.js} +4 -3
  15. package/dist/{chunk-I2KLQ2HA.js → chunk-DZTG5IAC.js} +7 -1
  16. package/dist/{chunk-JCGD4GM6.js → chunk-GRDLB6LM.js} +1 -0
  17. package/dist/{chunk-JKTPRYGV.js → chunk-HQGF4ATG.js} +9 -55
  18. package/dist/{chunk-SKXXT3E2.js → chunk-HYNYEBHM.js} +4 -4
  19. package/dist/{chunk-6EFVZV6X.js → chunk-JKSUN5GN.js} +718 -64
  20. package/dist/{chunk-LRZFLK2F.js → chunk-L2NVFLXU.js} +3 -3
  21. package/dist/{chunk-6WXDE5LZ.js → chunk-L3BVJWML.js} +1 -1
  22. package/dist/{chunk-3MDNBHVB.js → chunk-MVFCLZSK.js} +690 -221
  23. package/dist/{chunk-HYLERWRO.js → chunk-ND42LPY4.js} +6 -6
  24. package/dist/{chunk-CLUP5H3C.js → chunk-O2QOPJI5.js} +360 -126
  25. package/dist/{chunk-X2SUMO3P.js → chunk-P55BJZZW.js} +2 -1
  26. package/dist/{chunk-4FZYEEPK.js → chunk-Q6OYNEGR.js} +5 -5
  27. package/dist/{chunk-4SY6BTVZ.js → chunk-QDEETKYT.js} +4 -1
  28. package/dist/{chunk-IKQ42WHU.js → chunk-QHEWXU7I.js} +1 -1
  29. package/dist/chunk-R2XR4FCV.js +48 -0
  30. package/dist/chunk-R66W5UDG.js +26 -0
  31. package/dist/{chunk-3GV5NHSS.js → chunk-SHQXMW4F.js} +39 -211
  32. package/dist/{chunk-A7B7HLDF.js → chunk-T3IPHEYJ.js} +1889 -301
  33. package/dist/{chunk-7E3FUTND.js → chunk-TOIXUWR6.js} +1 -1
  34. package/dist/{chunk-NITGGYH2.js → chunk-TVRQ6AQI.js} +1 -1
  35. package/dist/{chunk-CN2HC4D4.js → chunk-UFTM5SZZ.js} +2 -2
  36. package/dist/{chunk-KVJ3LFH2.js → chunk-VNFR57DF.js} +4 -24
  37. package/dist/{chunk-P5WYW2GI.js → chunk-Y4FWCG7Y.js} +150 -305
  38. package/dist/components/chat/index.js +1 -1
  39. package/dist/components/index.d.ts +205 -11
  40. package/dist/components/index.js +38 -35
  41. package/dist/components/navigation/index.js +1 -1
  42. package/dist/execution/index.d.ts +2 -1
  43. package/dist/execution/index.js +1 -1
  44. package/dist/features/auth/index.d.ts +121 -0
  45. package/dist/features/auth/index.js +1 -1
  46. package/dist/features/clients/index.css +611 -0
  47. package/dist/features/clients/index.d.ts +86 -0
  48. package/dist/features/clients/index.js +719 -0
  49. package/dist/features/crm/index.d.ts +148 -2
  50. package/dist/features/crm/index.js +18 -16
  51. package/dist/features/dashboard/index.d.ts +36 -1
  52. package/dist/features/dashboard/index.js +14 -14
  53. package/dist/features/delivery/index.d.ts +121 -0
  54. package/dist/features/delivery/index.js +18 -16
  55. package/dist/features/knowledge/index.js +43 -20
  56. package/dist/features/lead-gen/index.d.ts +17 -11
  57. package/dist/features/lead-gen/index.js +19 -17
  58. package/dist/features/monitoring/index.js +17 -16
  59. package/dist/features/monitoring/requests/index.js +14 -13
  60. package/dist/features/operations/index.d.ts +38 -2
  61. package/dist/features/operations/index.js +22 -21
  62. package/dist/features/seo/index.js +1 -1
  63. package/dist/features/settings/index.d.ts +121 -0
  64. package/dist/features/settings/index.js +16 -15
  65. package/dist/graph/index.js +1 -1
  66. package/dist/hooks/delivery/index.d.ts +140 -0
  67. package/dist/hooks/delivery/index.js +3 -3
  68. package/dist/hooks/index.d.ts +583 -19
  69. package/dist/hooks/index.js +12 -12
  70. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +82 -1
  71. package/dist/hooks/operations/command-view/utils/transformCommandViewData.js +1 -1
  72. package/dist/hooks/published.d.ts +583 -19
  73. package/dist/hooks/published.js +12 -12
  74. package/dist/index.d.ts +680 -21
  75. package/dist/index.js +13 -13
  76. package/dist/initialization/index.d.ts +121 -0
  77. package/dist/initialization/index.js +1 -1
  78. package/dist/knowledge/index.d.ts +97 -1
  79. package/dist/knowledge/index.js +1692 -1039
  80. package/dist/layout/index.d.ts +6 -0
  81. package/dist/layout/index.js +4 -4
  82. package/dist/organization/index.js +1 -1
  83. package/dist/profile/index.d.ts +121 -0
  84. package/dist/profile/index.js +1 -1
  85. package/dist/provider/ElevasisServiceContext.js +1 -1
  86. package/dist/provider/index.d.ts +218 -2
  87. package/dist/provider/index.js +10 -10
  88. package/dist/provider/published.d.ts +218 -2
  89. package/dist/provider/published.js +7 -7
  90. package/dist/router/context.js +1 -1
  91. package/dist/router/index.js +1 -1
  92. package/dist/sse/index.js +1 -1
  93. package/dist/supabase/index.d.ts +232 -0
  94. package/dist/supabase/index.js +1 -1
  95. package/dist/test-utils/index.js +3 -3
  96. package/dist/test-utils/setup-integration.js +1 -1
  97. package/dist/test-utils/setup.js +2 -2
  98. package/dist/theme/index.js +4 -4
  99. package/dist/theme/presets/index.js +2 -2
  100. package/dist/typeform/index.js +1 -1
  101. package/dist/typeform/schemas.js +1 -1
  102. package/dist/types/index.d.ts +204 -1
  103. package/dist/utils/index.d.ts +36 -1
  104. package/dist/utils/index.js +2 -2
  105. package/dist/vite/index.js +3 -3
  106. package/dist/vite-plugin-knowledge/index.js +2 -2
  107. package/dist/zustand/index.js +1 -1
  108. package/package.json +13 -4
  109. /package/dist/{chunk-HXZQWMKE.js → chunk-XQHZBA65.js} +0 -0
@@ -0,0 +1,719 @@
1
+ import '../../chunk-R2XR4FCV.js';
2
+ import { PageContainer } from '../../chunk-BZZCNLT6.js';
3
+ import '../../chunk-TUMSNGTX.js';
4
+ import { FilterBar } from '../../chunk-PDHTXPSF.js';
5
+ import { CustomModal } from '../../chunk-R66W5UDG.js';
6
+ import '../../chunk-7M2VOCYN.js';
7
+ import { useClientStatus, useCreateClient, useUpdateClient, useDeleteClient, usePaginationState, useClients, useClient } from '../../chunk-JKSUN5GN.js';
8
+ import '../../chunk-6YT4IKJ7.js';
9
+ import { showApiErrorNotification } from '../../chunk-7HMCB26R.js';
10
+ import '../../chunk-BRXELOHC.js';
11
+ import '../../chunk-3ZMAGTWF.js';
12
+ import '../../chunk-OAVTMITG.js';
13
+ import '../../chunk-HYNYEBHM.js';
14
+ import '../../chunk-3BAPR3KA.js';
15
+ import '../../chunk-3FV6HBXS.js';
16
+ import '../../chunk-WLOQ4IBG.js';
17
+ import '../../chunk-QDEETKYT.js';
18
+ import '../../chunk-6IXOKUBC.js';
19
+ import { StatCard, CardHeader, EmptyState, PageTitleCaption, CenteredErrorState } from '../../chunk-L3BVJWML.js';
20
+ import '../../chunk-3KMDHCAR.js';
21
+ import '../../chunk-CQZ3DNQY.js';
22
+ import '../../chunk-SZHARWKU.js';
23
+ import { SubshellContentContainer } from '../../chunk-TKAYX2SP.js';
24
+ import '../../chunk-NYBEU5TE.js';
25
+ import '../../chunk-TOIXUWR6.js';
26
+ import '../../chunk-2IFYDILW.js';
27
+ import '../../chunk-Q7DJKLEN.js';
28
+ import '../../chunk-JA5ECJJB.js';
29
+ import '../../chunk-HUJCU55S.js';
30
+ import '../../chunk-E565XMTQ.js';
31
+ import '../../chunk-JBWJ6WHZ.js';
32
+ import '../../chunk-DT3QYZVU.js';
33
+ import '../../chunk-RNP5R5I3.js';
34
+ import { UuidSchema, PAGE_SIZE_DEFAULT, formatTimeAgo } from '../../chunk-XQHZBA65.js';
35
+ import '../../chunk-KRWALB24.js';
36
+ import '../../chunk-VKIZUUPM.js';
37
+ import '../../chunk-533DUEQY.js';
38
+ import '../../chunk-DD3CCMCZ.js';
39
+ import '../../chunk-2Q2JQSQO.js';
40
+ import '../../chunk-KJ3QUBNU.js';
41
+ import '../../chunk-BRJ3QZ4E.js';
42
+ import '../../chunk-DZTG5IAC.js';
43
+ import { Badge, SimpleGrid, Paper, Stack, Table, Title, TextInput, Select, Group, Button, Text, Alert, Center, Loader, Pagination } from '@mantine/core';
44
+ import { jsx, jsxs } from 'react/jsx-runtime';
45
+ import { IconUsers, IconCircleCheck, IconRocket, IconBriefcase, IconAlertTriangle, IconPlus, IconSearch, IconInbox, IconCash, IconUser, IconBuilding, IconArrowLeft, IconEdit, IconRefresh, IconTrash } from '@tabler/icons-react';
46
+ import { z } from 'zod';
47
+ import { useForm } from '@mantine/form';
48
+ import { zod4Resolver } from 'mantine-form-zod-resolver';
49
+ import { useEffect, useState } from 'react';
50
+ import { useDisclosure, useDebouncedValue } from '@mantine/hooks';
51
+ import { useNavigate } from '@tanstack/react-router';
52
+
53
+ // src/features/clients/_shared.ts
54
+ var CLIENT_STATUS_COLORS = {
55
+ active: "green",
56
+ onboarding: "blue",
57
+ paused: "yellow",
58
+ completed: "gray",
59
+ churned: "red"
60
+ };
61
+ function formatClientStatusLabel(status) {
62
+ return status.charAt(0).toUpperCase() + status.slice(1);
63
+ }
64
+ function ClientStatusBadge({ status }) {
65
+ return /* @__PURE__ */ jsx(Badge, { variant: "light", color: CLIENT_STATUS_COLORS[status] ?? "gray", size: "sm", children: formatClientStatusLabel(status) });
66
+ }
67
+ var ClientStatusSchema = z.enum(["active", "onboarding", "paused", "completed", "churned"]);
68
+ z.object({
69
+ clientId: UuidSchema
70
+ }).strict();
71
+ z.object({
72
+ status: ClientStatusSchema.optional(),
73
+ search: z.string().trim().min(1).max(255).optional(),
74
+ limit: z.coerce.number().int().min(1).max(100).default(50),
75
+ offset: z.coerce.number().int().min(0).default(0)
76
+ }).strict();
77
+ z.object({
78
+ id: z.string(),
79
+ name: z.string(),
80
+ status: ClientStatusSchema
81
+ });
82
+ var ClientResponseSchema = z.object({
83
+ id: z.string(),
84
+ organizationId: z.string(),
85
+ name: z.string(),
86
+ status: ClientStatusSchema,
87
+ sourceDealId: z.string().nullable(),
88
+ primaryCompanyId: z.string().nullable(),
89
+ primaryContactId: z.string().nullable(),
90
+ convertedAt: z.string().nullable(),
91
+ metadata: z.record(z.string(), z.unknown()),
92
+ createdAt: z.string(),
93
+ updatedAt: z.string()
94
+ });
95
+ var ClientDealRefSchema = z.object({
96
+ id: z.string(),
97
+ contactEmail: z.string(),
98
+ stageKey: z.string().nullable(),
99
+ stateKey: z.string().nullable(),
100
+ updatedAt: z.string()
101
+ });
102
+ var ClientProjectRefSchema = z.object({
103
+ id: z.string(),
104
+ name: z.string(),
105
+ kind: z.string(),
106
+ status: z.string(),
107
+ updatedAt: z.string()
108
+ });
109
+ var ClientCompanyRefSchema = z.object({
110
+ id: z.string(),
111
+ name: z.string(),
112
+ domain: z.string().nullable(),
113
+ status: z.string()
114
+ });
115
+ var ClientContactRefSchema = z.object({
116
+ id: z.string(),
117
+ email: z.string(),
118
+ firstName: z.string().nullable(),
119
+ lastName: z.string().nullable(),
120
+ title: z.string().nullable(),
121
+ companyId: z.string().nullable(),
122
+ status: z.string()
123
+ });
124
+ var ClientLineageSchema = z.object({
125
+ deals: z.array(ClientDealRefSchema),
126
+ projects: z.array(ClientProjectRefSchema),
127
+ companies: z.array(ClientCompanyRefSchema),
128
+ contacts: z.array(ClientContactRefSchema)
129
+ });
130
+ ClientResponseSchema.extend({
131
+ lineage: ClientLineageSchema
132
+ });
133
+ z.object({
134
+ data: z.array(ClientResponseSchema),
135
+ total: z.number().int().min(0),
136
+ limit: z.number().int().min(1),
137
+ offset: z.number().int().min(0)
138
+ });
139
+ z.object({
140
+ totalClients: z.number().int().min(0),
141
+ byStatus: z.record(ClientStatusSchema, z.number().int().min(0)),
142
+ linkedDeals: z.number().int().min(0),
143
+ linkedProjects: z.number().int().min(0),
144
+ linkedCompanies: z.number().int().min(0),
145
+ linkedContacts: z.number().int().min(0)
146
+ });
147
+ var CreateClientRequestSchema = z.object({
148
+ name: z.string().trim().min(1).max(255),
149
+ status: ClientStatusSchema.optional(),
150
+ sourceDealId: UuidSchema.nullable().optional(),
151
+ primaryCompanyId: UuidSchema.nullable().optional(),
152
+ primaryContactId: UuidSchema.nullable().optional(),
153
+ metadata: z.record(z.string(), z.unknown()).nullable().optional()
154
+ }).strict();
155
+ z.object({
156
+ name: z.string().trim().min(1).max(255).optional(),
157
+ status: ClientStatusSchema.optional(),
158
+ sourceDealId: UuidSchema.nullable().optional(),
159
+ primaryCompanyId: UuidSchema.nullable().optional(),
160
+ primaryContactId: UuidSchema.nullable().optional(),
161
+ metadata: z.record(z.string(), z.unknown()).nullable().optional()
162
+ }).strict().refine((data) => Object.keys(data).length > 0, {
163
+ message: "At least one field must be provided"
164
+ });
165
+ function ClientsMetricsStrip() {
166
+ const { data, isLoading } = useClientStatus();
167
+ return /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
168
+ /* @__PURE__ */ jsx(
169
+ StatCard,
170
+ {
171
+ variant: "hero",
172
+ icon: IconUsers,
173
+ value: String(data?.totalClients ?? 0),
174
+ label: "Total Clients",
175
+ isLoading
176
+ }
177
+ ),
178
+ /* @__PURE__ */ jsx(
179
+ StatCard,
180
+ {
181
+ variant: "hero",
182
+ icon: IconCircleCheck,
183
+ value: String(data?.byStatus?.active ?? 0),
184
+ label: "Active",
185
+ isLoading
186
+ }
187
+ ),
188
+ /* @__PURE__ */ jsx(
189
+ StatCard,
190
+ {
191
+ variant: "hero",
192
+ icon: IconRocket,
193
+ value: String(data?.byStatus?.onboarding ?? 0),
194
+ label: "Onboarding",
195
+ isLoading
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsx(
199
+ StatCard,
200
+ {
201
+ variant: "hero",
202
+ icon: IconBriefcase,
203
+ value: String(data?.linkedProjects ?? 0),
204
+ label: "Linked Projects",
205
+ isLoading
206
+ }
207
+ )
208
+ ] });
209
+ }
210
+ function ClientLineageSection({
211
+ title,
212
+ icon,
213
+ rows,
214
+ columns,
215
+ emptyText,
216
+ emptyIcon,
217
+ rowKey,
218
+ onRowClick
219
+ }) {
220
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
221
+ /* @__PURE__ */ jsx(CardHeader, { icon, title, mb: 0 }),
222
+ rows.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { icon: emptyIcon, title: emptyText }) : /* @__PURE__ */ jsxs(Table, { children: [
223
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsx(Table.Tr, { children: columns.map((c) => /* @__PURE__ */ jsx(Table.Th, { children: c.header }, c.header)) }) }),
224
+ /* @__PURE__ */ jsx(Table.Tbody, { children: rows.map((row) => /* @__PURE__ */ jsx(
225
+ Table.Tr,
226
+ {
227
+ style: onRowClick ? { cursor: "pointer" } : void 0,
228
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
229
+ children: columns.map((c) => /* @__PURE__ */ jsx(Table.Td, { children: c.render(row) }, c.header))
230
+ },
231
+ rowKey(row)
232
+ )) })
233
+ ] })
234
+ ] }) });
235
+ }
236
+ var STATUS_OPTIONS = [
237
+ { value: "active", label: "Active" },
238
+ { value: "onboarding", label: "Onboarding" },
239
+ { value: "paused", label: "Paused" },
240
+ { value: "completed", label: "Completed" },
241
+ { value: "churned", label: "Churned" }
242
+ ];
243
+ function CreateClientModal({ opened, onClose }) {
244
+ const createClient = useCreateClient();
245
+ const form = useForm({
246
+ initialValues: {
247
+ name: "",
248
+ status: void 0
249
+ },
250
+ validate: zod4Resolver(CreateClientRequestSchema)
251
+ });
252
+ const handleSubmit = async (values) => {
253
+ try {
254
+ await createClient.mutateAsync(values);
255
+ form.reset();
256
+ onClose();
257
+ } catch (error) {
258
+ showApiErrorNotification(error);
259
+ }
260
+ };
261
+ const handleClose = () => {
262
+ if (!createClient.isPending) {
263
+ form.reset();
264
+ onClose();
265
+ }
266
+ };
267
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "md", loading: createClient.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: /* @__PURE__ */ jsxs(Stack, { p: "md", children: [
268
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "New Client" }),
269
+ /* @__PURE__ */ jsx(
270
+ TextInput,
271
+ {
272
+ label: "Name",
273
+ placeholder: "Enter client name",
274
+ required: true,
275
+ ...form.getInputProps("name"),
276
+ disabled: createClient.isPending
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsx(
280
+ Select,
281
+ {
282
+ label: "Status",
283
+ placeholder: "Select status (optional)",
284
+ data: STATUS_OPTIONS,
285
+ clearable: true,
286
+ ...form.getInputProps("status"),
287
+ disabled: createClient.isPending
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "xs", children: [
291
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: createClient.isPending, children: "Cancel" }),
292
+ /* @__PURE__ */ jsx(Button, { type: "submit", loading: createClient.isPending, children: "Create Client" })
293
+ ] })
294
+ ] }) }) });
295
+ }
296
+ var EditNameSchema = z.object({ name: z.string().trim().min(1).max(255) });
297
+ function EditClientNameModal({ opened, onClose, clientId, currentName }) {
298
+ const updateClient = useUpdateClient();
299
+ const form = useForm({
300
+ initialValues: { name: currentName },
301
+ validate: zod4Resolver(EditNameSchema)
302
+ });
303
+ useEffect(() => {
304
+ if (opened) {
305
+ form.setValues({ name: currentName });
306
+ }
307
+ }, [opened, currentName]);
308
+ const handleSubmit = async (values) => {
309
+ try {
310
+ await updateClient.mutateAsync({ clientId, updates: { name: values.name } });
311
+ onClose();
312
+ } catch (error) {
313
+ showApiErrorNotification(error);
314
+ }
315
+ };
316
+ const handleClose = () => {
317
+ if (!updateClient.isPending) {
318
+ form.reset();
319
+ onClose();
320
+ }
321
+ };
322
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "sm", loading: updateClient.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: /* @__PURE__ */ jsxs(Stack, { p: "md", children: [
323
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Edit Client Name" }),
324
+ /* @__PURE__ */ jsx(
325
+ TextInput,
326
+ {
327
+ label: "Name",
328
+ placeholder: "Enter client name",
329
+ required: true,
330
+ ...form.getInputProps("name"),
331
+ disabled: updateClient.isPending
332
+ }
333
+ ),
334
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "xs", children: [
335
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: updateClient.isPending, children: "Cancel" }),
336
+ /* @__PURE__ */ jsx(Button, { type: "submit", loading: updateClient.isPending, children: "Save" })
337
+ ] })
338
+ ] }) }) });
339
+ }
340
+ var STATUS_OPTIONS2 = [
341
+ { value: "active", label: "Active" },
342
+ { value: "onboarding", label: "Onboarding" },
343
+ { value: "paused", label: "Paused" },
344
+ { value: "completed", label: "Completed" },
345
+ { value: "churned", label: "Churned" }
346
+ ];
347
+ function UpdateClientStatusModal({ opened, onClose, clientId, currentStatus }) {
348
+ const updateClient = useUpdateClient();
349
+ const form = useForm({
350
+ initialValues: { status: currentStatus }
351
+ });
352
+ useEffect(() => {
353
+ if (opened) {
354
+ form.setValues({ status: currentStatus });
355
+ }
356
+ }, [opened, currentStatus]);
357
+ const handleSubmit = async (values) => {
358
+ try {
359
+ await updateClient.mutateAsync({ clientId, updates: { status: values.status } });
360
+ onClose();
361
+ } catch (error) {
362
+ showApiErrorNotification(error);
363
+ }
364
+ };
365
+ const handleClose = () => {
366
+ if (!updateClient.isPending) {
367
+ onClose();
368
+ }
369
+ };
370
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "sm", loading: updateClient.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: /* @__PURE__ */ jsxs(Stack, { p: "md", children: [
371
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Change Status" }),
372
+ /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
373
+ "Current status: ",
374
+ formatClientStatusLabel(currentStatus)
375
+ ] }),
376
+ /* @__PURE__ */ jsx(
377
+ Select,
378
+ {
379
+ label: "New Status",
380
+ data: STATUS_OPTIONS2,
381
+ ...form.getInputProps("status"),
382
+ disabled: updateClient.isPending
383
+ }
384
+ ),
385
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "xs", children: [
386
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: updateClient.isPending, children: "Cancel" }),
387
+ /* @__PURE__ */ jsx(Button, { type: "submit", loading: updateClient.isPending, children: "Update Status" })
388
+ ] })
389
+ ] }) }) });
390
+ }
391
+ function DeleteClientConfirm({ opened, onClose, clientId, clientName, onDeleted }) {
392
+ const deleteClient = useDeleteClient();
393
+ const [inlineError, setInlineError] = useState(null);
394
+ const handleConfirm = async () => {
395
+ setInlineError(null);
396
+ try {
397
+ await deleteClient.mutateAsync(clientId);
398
+ onClose();
399
+ onDeleted?.();
400
+ } catch (error) {
401
+ const message = error instanceof Error ? error.message : "Failed to delete client";
402
+ setInlineError(message);
403
+ }
404
+ };
405
+ const handleClose = () => {
406
+ if (!deleteClient.isPending) {
407
+ setInlineError(null);
408
+ onClose();
409
+ }
410
+ };
411
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "sm", loading: deleteClient.isPending, children: /* @__PURE__ */ jsxs(Stack, { p: "md", children: [
412
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete Client" }),
413
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
414
+ "Are you sure you want to delete ",
415
+ /* @__PURE__ */ jsx("strong", { children: clientName }),
416
+ "? This action cannot be undone."
417
+ ] }),
418
+ inlineError && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }), color: "red", title: "Cannot delete client", children: inlineError }),
419
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "xs", children: [
420
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: deleteClient.isPending, children: "Cancel" }),
421
+ /* @__PURE__ */ jsx(Button, { color: "red", onClick: handleConfirm, loading: deleteClient.isPending, children: "Delete" })
422
+ ] })
423
+ ] }) });
424
+ }
425
+ var STATUS_OPTIONS3 = [
426
+ { value: "active", label: "Active" },
427
+ { value: "onboarding", label: "Onboarding" },
428
+ { value: "paused", label: "Paused" },
429
+ { value: "completed", label: "Completed" },
430
+ { value: "churned", label: "Churned" }
431
+ ];
432
+ function ClientsListPage({ onClientClick } = {}) {
433
+ const [statusFilter, setStatusFilter] = useState(null);
434
+ const [searchQuery, setSearchQuery] = useState("");
435
+ const [createOpened, { open: openCreate, close: closeCreate }] = useDisclosure(false);
436
+ const [debouncedSearch] = useDebouncedValue(searchQuery, 300);
437
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT, [statusFilter, debouncedSearch]);
438
+ const { data, isLoading, error } = useClients({
439
+ status: statusFilter ?? void 0,
440
+ search: debouncedSearch.trim() || void 0,
441
+ limit: PAGE_SIZE_DEFAULT,
442
+ offset: pagination.offset
443
+ });
444
+ const rows = data?.data ?? [];
445
+ const total = data?.total ?? 0;
446
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
447
+ /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
448
+ /* @__PURE__ */ jsx(
449
+ PageTitleCaption,
450
+ {
451
+ title: "Clients",
452
+ caption: "Closed-won deals and ongoing engagements",
453
+ rightSection: /* @__PURE__ */ jsx(Button, { variant: "filled", size: "sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: openCreate, children: "New Client" })
454
+ }
455
+ ),
456
+ /* @__PURE__ */ jsx(ClientsMetricsStrip, {}),
457
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
458
+ /* @__PURE__ */ jsxs(FilterBar, { children: [
459
+ /* @__PURE__ */ jsx(
460
+ Select,
461
+ {
462
+ placeholder: "All Statuses",
463
+ data: STATUS_OPTIONS3,
464
+ value: statusFilter,
465
+ onChange: (v) => setStatusFilter(v),
466
+ clearable: true,
467
+ size: "sm",
468
+ style: { minWidth: 180 }
469
+ }
470
+ ),
471
+ /* @__PURE__ */ jsx(
472
+ TextInput,
473
+ {
474
+ placeholder: "Search clients...",
475
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
476
+ value: searchQuery,
477
+ onChange: (e) => setSearchQuery(e.currentTarget.value),
478
+ size: "sm",
479
+ style: { minWidth: 250 }
480
+ }
481
+ )
482
+ ] }),
483
+ isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : error ? /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load clients" }) : !rows.length ? /* @__PURE__ */ jsx(
484
+ EmptyState,
485
+ {
486
+ icon: IconUsers,
487
+ title: "No clients found",
488
+ description: "Clients are created when a deal is closed-won."
489
+ }
490
+ ) : /* @__PURE__ */ jsxs(Table, { children: [
491
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
492
+ /* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
493
+ /* @__PURE__ */ jsx(Table.Th, { children: "Status" }),
494
+ /* @__PURE__ */ jsx(Table.Th, { children: "Converted" }),
495
+ /* @__PURE__ */ jsx(Table.Th, { children: "Updated" })
496
+ ] }) }),
497
+ /* @__PURE__ */ jsx(Table.Tbody, { children: rows.map((client) => {
498
+ const rowProps = onClientClick ? {
499
+ style: { cursor: "pointer" },
500
+ onClick: () => onClientClick(client.id)
501
+ } : void 0;
502
+ return /* @__PURE__ */ jsxs(Table.Tr, { ...rowProps, children: [
503
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: client.name }) }),
504
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(ClientStatusBadge, { status: client.status }) }),
505
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(client.convertedAt) }) }),
506
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(client.updatedAt) }) })
507
+ ] }, client.id);
508
+ }) })
509
+ ] }),
510
+ total > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
511
+ Pagination,
512
+ {
513
+ value: pagination.page,
514
+ onChange: pagination.setPage,
515
+ total: pagination.totalPages(total),
516
+ size: "sm"
517
+ }
518
+ ) })
519
+ ] }) })
520
+ ] }) }),
521
+ /* @__PURE__ */ jsx(CreateClientModal, { opened: createOpened, onClose: closeCreate })
522
+ ] });
523
+ }
524
+ function DetailRow({ label, value }) {
525
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-start", wrap: "nowrap", children: [
526
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, style: { width: 140, flexShrink: 0 }, children: label }),
527
+ /* @__PURE__ */ jsx(Text, { size: "sm", style: { minWidth: 0, wordBreak: "break-word", color: "var(--color-text-subtle)" }, children: value ?? "N/A" })
528
+ ] });
529
+ }
530
+ function ClientDetailPage({ clientId }) {
531
+ const navigate = useNavigate();
532
+ const { data: client, isLoading, error } = useClient(clientId);
533
+ const [editNameOpened, { open: openEditName, close: closeEditName }] = useDisclosure(false);
534
+ const [updateStatusOpened, { open: openUpdateStatus, close: closeUpdateStatus }] = useDisclosure(false);
535
+ const [deleteOpened, { open: openDelete, close: closeDelete }] = useDisclosure(false);
536
+ const backButton = /* @__PURE__ */ jsx(
537
+ Button,
538
+ {
539
+ variant: "light",
540
+ size: "sm",
541
+ leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }),
542
+ onClick: () => navigate({ to: "/business/clients" }),
543
+ children: "Clients"
544
+ }
545
+ );
546
+ const headerActions = /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
547
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconEdit, { size: 16 }), onClick: openEditName, children: "Edit name" }),
548
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconRefresh, { size: 16 }), onClick: openUpdateStatus, children: "Change status" }),
549
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", color: "red", leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 16 }), onClick: openDelete, children: "Delete" }),
550
+ backButton
551
+ ] });
552
+ if (isLoading) {
553
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
554
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Client Detail", caption: "Loading client details...", rightSection: backButton }),
555
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) })
556
+ ] }) }) });
557
+ }
558
+ if (error) {
559
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
560
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Client Detail", caption: "Unable to load client details", rightSection: backButton }),
561
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load client" }) })
562
+ ] }) }) });
563
+ }
564
+ if (!client) {
565
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
566
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: "Client Detail", caption: "Client not found", rightSection: backButton }),
567
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsx(
568
+ EmptyState,
569
+ {
570
+ icon: IconInbox,
571
+ title: "Client not found",
572
+ description: "The selected client no longer exists."
573
+ }
574
+ ) })
575
+ ] }) }) });
576
+ }
577
+ const caption = client.convertedAt ? `Converted ${formatTimeAgo(client.convertedAt)}` : "Not yet converted";
578
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
579
+ /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
580
+ /* @__PURE__ */ jsx(PageTitleCaption, { title: client.name, caption, rightSection: headerActions }),
581
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
582
+ /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconUsers, { size: 18 }), title: "Client", mb: 0 }),
583
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", children: [
584
+ /* @__PURE__ */ jsx(DetailRow, { label: "Status", value: /* @__PURE__ */ jsx(ClientStatusBadge, { status: client.status }) }),
585
+ /* @__PURE__ */ jsx(
586
+ DetailRow,
587
+ {
588
+ label: "Converted",
589
+ value: client.convertedAt ? new Date(client.convertedAt).toLocaleDateString() : "N/A"
590
+ }
591
+ ),
592
+ /* @__PURE__ */ jsx(
593
+ DetailRow,
594
+ {
595
+ label: "Source Deal",
596
+ value: client.sourceDealId ? /* @__PURE__ */ jsx(
597
+ Text,
598
+ {
599
+ size: "sm",
600
+ style: { cursor: "pointer", color: "var(--color-primary)" },
601
+ onClick: () => navigate({ to: "/crm/deals/$dealId", params: { dealId: client.sourceDealId } }),
602
+ children: client.sourceDealId
603
+ }
604
+ ) : "N/A"
605
+ }
606
+ ),
607
+ /* @__PURE__ */ jsx(DetailRow, { label: "Primary Company", value: client.primaryCompanyId ?? "N/A" }),
608
+ /* @__PURE__ */ jsx(DetailRow, { label: "Primary Contact", value: client.primaryContactId ?? "N/A" }),
609
+ /* @__PURE__ */ jsx(DetailRow, { label: "Created", value: new Date(client.createdAt).toLocaleDateString() }),
610
+ /* @__PURE__ */ jsx(DetailRow, { label: "Updated", value: formatTimeAgo(client.updatedAt) })
611
+ ] })
612
+ ] }) }),
613
+ /* @__PURE__ */ jsx(
614
+ ClientLineageSection,
615
+ {
616
+ title: "Projects",
617
+ icon: /* @__PURE__ */ jsx(IconBriefcase, { size: 18 }),
618
+ rows: client.lineage.projects,
619
+ emptyText: "No projects yet",
620
+ emptyIcon: IconBriefcase,
621
+ rowKey: (row) => row.id,
622
+ onRowClick: (row) => navigate({ to: "/projects/$projectId", params: { projectId: row.id } }),
623
+ columns: [
624
+ { header: "Name", render: (row) => row.name },
625
+ { header: "Kind", render: (row) => row.kind },
626
+ { header: "Status", render: (row) => row.status },
627
+ { header: "Updated", render: (row) => formatTimeAgo(row.updatedAt) }
628
+ ]
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsx(
632
+ ClientLineageSection,
633
+ {
634
+ title: "Deals",
635
+ icon: /* @__PURE__ */ jsx(IconCash, { size: 18 }),
636
+ rows: client.lineage.deals,
637
+ emptyText: "No linked deals",
638
+ emptyIcon: IconCash,
639
+ rowKey: (row) => row.id,
640
+ onRowClick: (row) => navigate({ to: "/crm/deals/$dealId", params: { dealId: row.id } }),
641
+ columns: [
642
+ { header: "Contact Email", render: (row) => row.contactEmail },
643
+ { header: "Stage", render: (row) => row.stageKey ?? "\u2014" },
644
+ { header: "State", render: (row) => row.stateKey ?? "\u2014" },
645
+ { header: "Updated", render: (row) => formatTimeAgo(row.updatedAt) }
646
+ ]
647
+ }
648
+ ),
649
+ /* @__PURE__ */ jsx(
650
+ ClientLineageSection,
651
+ {
652
+ title: "Contacts",
653
+ icon: /* @__PURE__ */ jsx(IconUser, { size: 18 }),
654
+ rows: client.lineage.contacts,
655
+ emptyText: "No contacts",
656
+ emptyIcon: IconUser,
657
+ rowKey: (row) => row.id,
658
+ onRowClick: (row) => navigate({ to: "/crm/contacts/$contactId", params: { contactId: row.id } }),
659
+ columns: [
660
+ {
661
+ header: "Name",
662
+ render: (row) => [row.firstName, row.lastName].filter(Boolean).join(" ") || "\u2014"
663
+ },
664
+ { header: "Email", render: (row) => row.email },
665
+ { header: "Title", render: (row) => row.title ?? "\u2014" },
666
+ { header: "Status", render: (row) => row.status }
667
+ ]
668
+ }
669
+ ),
670
+ /* @__PURE__ */ jsx(
671
+ ClientLineageSection,
672
+ {
673
+ title: "Companies",
674
+ icon: /* @__PURE__ */ jsx(IconBuilding, { size: 18 }),
675
+ rows: client.lineage.companies,
676
+ emptyText: "No companies",
677
+ emptyIcon: IconBuilding,
678
+ rowKey: (row) => row.id,
679
+ onRowClick: (row) => navigate({ to: "/crm/companies/$companyId", params: { companyId: row.id } }),
680
+ columns: [
681
+ { header: "Name", render: (row) => row.name },
682
+ { header: "Domain", render: (row) => row.domain ?? "\u2014" },
683
+ { header: "Status", render: (row) => row.status }
684
+ ]
685
+ }
686
+ )
687
+ ] }) }),
688
+ /* @__PURE__ */ jsx(
689
+ EditClientNameModal,
690
+ {
691
+ opened: editNameOpened,
692
+ onClose: closeEditName,
693
+ clientId,
694
+ currentName: client.name
695
+ }
696
+ ),
697
+ /* @__PURE__ */ jsx(
698
+ UpdateClientStatusModal,
699
+ {
700
+ opened: updateStatusOpened,
701
+ onClose: closeUpdateStatus,
702
+ clientId,
703
+ currentStatus: client.status
704
+ }
705
+ ),
706
+ /* @__PURE__ */ jsx(
707
+ DeleteClientConfirm,
708
+ {
709
+ opened: deleteOpened,
710
+ onClose: closeDelete,
711
+ clientId,
712
+ clientName: client.name,
713
+ onDeleted: () => navigate({ to: "/business/clients" })
714
+ }
715
+ )
716
+ ] });
717
+ }
718
+
719
+ export { CLIENT_STATUS_COLORS, ClientDetailPage, ClientLineageSection, ClientStatusBadge, ClientsListPage, ClientsMetricsStrip, CreateClientModal, DeleteClientConfirm, EditClientNameModal, UpdateClientStatusModal, formatClientStatusLabel };