@elevasis/ui 2.67.0 → 2.68.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 (98) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +3 -3
  3. package/dist/app/index.d.ts +1 -6
  4. package/dist/app/index.js +15 -12
  5. package/dist/auth/index.js +10 -12
  6. package/dist/charts/index.js +10 -12
  7. package/dist/chunk-2MTP3H3A.js +425 -0
  8. package/dist/chunk-3KMDHCAR.js +52 -0
  9. package/dist/chunk-3MEXPLWT.js +265 -0
  10. package/dist/{chunk-XNW3O6QW.js → chunk-5NRJ2BLQ.js} +1 -1
  11. package/dist/chunk-75NOC536.js +2162 -0
  12. package/dist/chunk-76IVMVI3.js +1764 -0
  13. package/dist/chunk-AH52XQB7.js +1 -0
  14. package/dist/chunk-BZZCNLT6.js +12 -0
  15. package/dist/chunk-DLR4SDZW.js +51 -0
  16. package/dist/chunk-FK3K2VK2.js +2103 -0
  17. package/dist/chunk-GBXTRSAU.js +37 -0
  18. package/dist/chunk-HJVPVS57.js +1934 -0
  19. package/dist/{chunk-M535KHLW.js → chunk-JC5W55J3.js} +1 -1
  20. package/dist/{chunk-I7BZVVVU.js → chunk-L3HFJFYF.js} +89 -382
  21. package/dist/chunk-LGKLC5MG.js +44 -0
  22. package/dist/{chunk-4DV4ECMG.js → chunk-N7U24M2J.js} +4 -0
  23. package/dist/chunk-NDHMZT2G.js +383 -0
  24. package/dist/chunk-OOACTYRA.js +10 -0
  25. package/dist/chunk-PDHTXPSF.js +12 -0
  26. package/dist/chunk-QYI5BZKM.js +85 -0
  27. package/dist/{chunk-4OEVLV66.js → chunk-R6NMRXNO.js} +112 -147
  28. package/dist/chunk-SIVUCZXQ.js +1194 -0
  29. package/dist/{chunk-JFBZ6XDW.js → chunk-SNZDFCOQ.js} +3 -49
  30. package/dist/chunk-TGPJNKDU.js +1595 -0
  31. package/dist/chunk-TUMSNGTX.js +35 -0
  32. package/dist/chunk-TYKAKJGW.js +67 -0
  33. package/dist/chunk-UD657ICS.js +589 -0
  34. package/dist/chunk-WPIASV3H.js +194 -0
  35. package/dist/chunk-ZFISGKYW.js +39 -0
  36. package/dist/chunk-ZGCWTWBD.js +16127 -0
  37. package/dist/chunk-ZNKZMK2E.js +4528 -0
  38. package/dist/chunk-ZWS3TCQR.js +11032 -0
  39. package/dist/components/chat/index.js +2 -1
  40. package/dist/components/index.d.ts +23 -11
  41. package/dist/components/index.js +2773 -14
  42. package/dist/components/navigation/index.js +11 -12
  43. package/dist/features/auth/index.d.ts +0 -4
  44. package/dist/features/auth/index.js +12 -13
  45. package/dist/features/clients/index.js +14 -12
  46. package/dist/features/crm/index.d.ts +1371 -15
  47. package/dist/features/crm/index.js +15 -12
  48. package/dist/features/dashboard/index.d.ts +1 -2
  49. package/dist/features/dashboard/index.js +15 -12
  50. package/dist/features/delivery/index.d.ts +0 -4
  51. package/dist/features/delivery/index.js +15 -12
  52. package/dist/features/knowledge/index.js +10 -12
  53. package/dist/features/lead-gen/index.d.ts +35 -15
  54. package/dist/features/lead-gen/index.js +16 -12
  55. package/dist/features/monitoring/index.js +15 -12
  56. package/dist/features/monitoring/requests/index.js +13 -13
  57. package/dist/features/operations/index.d.ts +1 -2
  58. package/dist/features/operations/index.js +21 -12
  59. package/dist/features/public-agent-chat/index.js +2 -1
  60. package/dist/features/seo/index.js +2 -1
  61. package/dist/features/settings/index.d.ts +0 -4
  62. package/dist/features/settings/index.js +14 -12
  63. package/dist/hooks/access/index.js +10 -12
  64. package/dist/hooks/delivery/index.d.ts +0 -4
  65. package/dist/hooks/delivery/index.js +2 -23
  66. package/dist/hooks/index.d.ts +13 -7
  67. package/dist/hooks/index.js +10 -12
  68. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +1 -2
  69. package/dist/hooks/published.d.ts +13 -7
  70. package/dist/hooks/published.js +10 -12
  71. package/dist/initialization/index.d.ts +1 -5
  72. package/dist/knowledge/index.d.ts +1 -2
  73. package/dist/knowledge/index.js +15 -13
  74. package/dist/layout/index.js +14 -12
  75. package/dist/organization/index.d.ts +3 -3
  76. package/dist/organization/index.js +1 -21
  77. package/dist/profile/index.d.ts +0 -4
  78. package/dist/provider/index.d.ts +2 -7
  79. package/dist/provider/index.js +10 -12
  80. package/dist/provider/published.d.ts +2 -7
  81. package/dist/provider/published.js +10 -12
  82. package/dist/supabase/index.d.ts +0 -8
  83. package/dist/test-utils/index.d.ts +5 -0
  84. package/dist/test-utils/index.js +5 -5
  85. package/dist/test-utils/setup-integration.js +1 -1
  86. package/dist/test-utils/setup.js +1 -1
  87. package/dist/theme/index.js +2 -1
  88. package/dist/typeform/index.js +2049 -1
  89. package/dist/types/index.d.ts +13 -7
  90. package/dist/utils/index.d.ts +1 -2
  91. package/dist/utils/index.js +1 -1
  92. package/package.json +4 -4
  93. package/dist/chunk-5TERYXOO.js +0 -51390
  94. package/dist/chunk-AUDNF2Q7.js +0 -2050
  95. package/dist/index.css +0 -659
  96. package/dist/index.d.ts +0 -17888
  97. package/dist/index.js +0 -30
  98. package/dist/{chunk-GMXGDO3I.js → chunk-MISHPEJX.js} +1 -1
@@ -0,0 +1,1595 @@
1
+ import { milestoneStatusColors, formatStatusLabel, taskTypeColors, taskStatusColors, projectStatusColors, calculateProgress, noteTypeColors } from './chunk-DLR4SDZW.js';
2
+ import { PageContainer } from './chunk-BZZCNLT6.js';
3
+ import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
4
+ import { FilterBar } from './chunk-PDHTXPSF.js';
5
+ import { StatusBadge, EmptyState, SubshellContentContainer, PageTitleCaption, useProjectRealtime, useTableSort, sortData, usePaginationState, useTableSelection, CenteredErrorState, CustomModal, StatCard, useProjectActivities } from './chunk-ZGCWTWBD.js';
6
+ import { useCreateTask, useCreateMilestone, useProjectMilestones, useUpdateTask, useProjects, useDeleteProject, useProject, useProjectNotes, useUpdateMilestone, useCreateNote } from './chunk-NDHMZT2G.js';
7
+ import { CardHeader } from './chunk-62GVNH5U.js';
8
+ import { SubshellSidebarSection, SubshellNavItem } from './chunk-MISHPEJX.js';
9
+ import { PAGE_SIZE_DEFAULT, formatTimeAgo, formatDate, formatRelativeTime } from './chunk-L3HFJFYF.js';
10
+ import { useRouterContext } from './chunk-Q7DJKLEN.js';
11
+ import { useState, useMemo } from 'react';
12
+ import { Stack, Text, Group, Checkbox, ActionIcon, TextInput, Button, Modal, Title, Textarea, Select, SimpleGrid, Card, ThemeIcon, RingProgress, Paper, Timeline, Badge, Collapse, Center, Loader, Table, Pagination, Alert, CopyButton, Tooltip, Space, Tabs, Divider, Anchor, Chip, List } from '@mantine/core';
13
+ import { IconNotes, IconBriefcase, IconChecklist, IconFlag, IconX, IconPlus, IconHeartbeat, IconFileText, IconCalendar, IconInbox, IconLock, IconAlertTriangle, IconCircleCheck, IconClock, IconChevronDown, IconChevronRight, IconDownload, IconListCheck, IconMessageCircle, IconSearch, IconCheck, IconCopy, IconFileDescription, IconArrowLeft, IconCurrencyDollar, IconBuilding } from '@tabler/icons-react';
14
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
15
+ import { useDisclosure } from '@mantine/hooks';
16
+
17
+ function Checklist({ items, onToggle, onAdd, onRemove }) {
18
+ const [adding, setAdding] = useState(false);
19
+ const [newItemLabel, setNewItemLabel] = useState("");
20
+ function commitAdd() {
21
+ const label = newItemLabel.trim();
22
+ if (label) onAdd(label);
23
+ setNewItemLabel("");
24
+ }
25
+ const completedCount = items.filter((item) => item.completed).length;
26
+ return /* @__PURE__ */ jsxs(Stack, { gap: "xs", mt: "sm", children: [
27
+ items.length > 0 && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
28
+ completedCount,
29
+ "/",
30
+ items.length,
31
+ " complete"
32
+ ] }),
33
+ items.map((item) => /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
34
+ /* @__PURE__ */ jsx(
35
+ Checkbox,
36
+ {
37
+ size: "xs",
38
+ checked: item.completed,
39
+ onChange: () => onToggle(item.id),
40
+ label: /* @__PURE__ */ jsx(
41
+ Text,
42
+ {
43
+ size: "sm",
44
+ td: item.completed ? "line-through" : void 0,
45
+ c: item.completed ? "dimmed" : "var(--color-text-subtle)",
46
+ children: item.label
47
+ }
48
+ ),
49
+ styles: { input: { cursor: "pointer" }, label: { cursor: "pointer" } }
50
+ }
51
+ ),
52
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "xs", onClick: () => onRemove(item.id), style: { opacity: 0.4 }, children: /* @__PURE__ */ jsx(IconX, { size: 12 }) })
53
+ ] }, item.id)),
54
+ adding ? /* @__PURE__ */ jsx(
55
+ TextInput,
56
+ {
57
+ size: "xs",
58
+ placeholder: "Item label...",
59
+ value: newItemLabel,
60
+ onChange: (event) => setNewItemLabel(event.target.value),
61
+ onKeyDown: (event) => {
62
+ if (event.key === "Enter") {
63
+ commitAdd();
64
+ setAdding(false);
65
+ }
66
+ if (event.key === "Escape") {
67
+ setNewItemLabel("");
68
+ setAdding(false);
69
+ }
70
+ },
71
+ onBlur: () => {
72
+ if (newItemLabel.trim()) commitAdd();
73
+ setAdding(false);
74
+ },
75
+ autoFocus: true,
76
+ style: { maxWidth: 250 }
77
+ }
78
+ ) : /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(
79
+ Button,
80
+ {
81
+ variant: "subtle",
82
+ size: "compact-xs",
83
+ leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 12 }),
84
+ onClick: () => setAdding(true),
85
+ children: "Add item"
86
+ }
87
+ ) })
88
+ ] });
89
+ }
90
+ var TASK_TYPE_OPTIONS = [
91
+ { value: "documentation", label: "Documentation" },
92
+ { value: "code", label: "Code" },
93
+ { value: "report", label: "Report" },
94
+ { value: "design", label: "Design" },
95
+ { value: "other", label: "Other" }
96
+ ];
97
+ function CreateDeliveryEntityModal({ opened, onClose, projectId, entityType }) {
98
+ const [name, setName] = useState("");
99
+ const [description, setDescription] = useState("");
100
+ const [taskType, setTaskType] = useState("other");
101
+ const [milestoneId, setMilestoneId] = useState(null);
102
+ const { mutate: createTask, isPending: isCreatingTask } = useCreateTask();
103
+ const { mutate: createMilestone, isPending: isCreatingMilestone } = useCreateMilestone();
104
+ const { data: milestones = [] } = useProjectMilestones(entityType === "task" ? projectId : "");
105
+ const isPending = entityType === "task" ? isCreatingTask : isCreatingMilestone;
106
+ const milestoneOptions = milestones.map((m) => ({ value: m.id, label: m.name }));
107
+ function resetForm() {
108
+ setName("");
109
+ setDescription("");
110
+ setTaskType("other");
111
+ setMilestoneId(null);
112
+ }
113
+ function handleClose() {
114
+ resetForm();
115
+ onClose();
116
+ }
117
+ function handleSubmit() {
118
+ const trimmedName = name.trim();
119
+ if (!trimmedName) return;
120
+ if (entityType === "task") {
121
+ createTask(
122
+ {
123
+ project_id: projectId,
124
+ name: trimmedName,
125
+ description: description.trim() || null,
126
+ type: taskType ?? "other",
127
+ milestone_id: milestoneId ?? null
128
+ },
129
+ {
130
+ onSuccess: () => {
131
+ resetForm();
132
+ onClose();
133
+ }
134
+ }
135
+ );
136
+ } else {
137
+ createMilestone(
138
+ {
139
+ project_id: projectId,
140
+ name: trimmedName,
141
+ description: description.trim() || null
142
+ },
143
+ {
144
+ onSuccess: () => {
145
+ resetForm();
146
+ onClose();
147
+ }
148
+ }
149
+ );
150
+ }
151
+ }
152
+ const title = entityType === "task" ? "Create Task" : "Add Milestone";
153
+ return /* @__PURE__ */ jsx(Modal, { opened, onClose: handleClose, size: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
154
+ /* @__PURE__ */ jsx(Title, { order: 3, children: title }),
155
+ /* @__PURE__ */ jsx(
156
+ TextInput,
157
+ {
158
+ label: "Name",
159
+ placeholder: entityType === "task" ? "Task name..." : "Milestone name...",
160
+ value: name,
161
+ onChange: (e) => setName(e.target.value),
162
+ required: true,
163
+ "data-autofocus": true
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsx(
167
+ Textarea,
168
+ {
169
+ label: "Description",
170
+ placeholder: "Optional description...",
171
+ minRows: 3,
172
+ value: description,
173
+ onChange: (e) => setDescription(e.target.value)
174
+ }
175
+ ),
176
+ entityType === "task" && /* @__PURE__ */ jsxs(Fragment, { children: [
177
+ /* @__PURE__ */ jsx(Select, { label: "Type", data: TASK_TYPE_OPTIONS, value: taskType, onChange: setTaskType }),
178
+ milestoneOptions.length > 0 && /* @__PURE__ */ jsx(
179
+ Select,
180
+ {
181
+ label: "Milestone",
182
+ placeholder: "Assign to milestone (optional)",
183
+ data: milestoneOptions,
184
+ value: milestoneId,
185
+ onChange: setMilestoneId,
186
+ clearable: true
187
+ }
188
+ )
189
+ ] }),
190
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
191
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isPending, children: "Cancel" }),
192
+ /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: isPending, disabled: !name.trim(), children: title })
193
+ ] })
194
+ ] }) });
195
+ }
196
+ function ringColor(completed, total) {
197
+ if (total === 0) return "gray";
198
+ const pct = completed / total;
199
+ if (pct > 0.75) return "green";
200
+ if (pct > 0.5) return "yellow";
201
+ return "gray";
202
+ }
203
+ function ringValue(completed, total) {
204
+ if (total === 0) return 0;
205
+ return Math.round(completed / total * 100);
206
+ }
207
+ function formatDate2(iso) {
208
+ return new Date(iso).toLocaleDateString();
209
+ }
210
+ function daysRelative(targetEndDate) {
211
+ const now = /* @__PURE__ */ new Date();
212
+ const end = new Date(targetEndDate);
213
+ const diffMs = end.getTime() - now.getTime();
214
+ const diffDays = Math.ceil(diffMs / 864e5);
215
+ if (diffDays >= 0) {
216
+ return { label: `${diffDays} day${diffDays === 1 ? "" : "s"} remaining`, overdue: false };
217
+ }
218
+ return { label: `${Math.abs(diffDays)} day${Math.abs(diffDays) === 1 ? "" : "s"} overdue`, overdue: true };
219
+ }
220
+ function HealthStatusCard({
221
+ status,
222
+ milestoneCount,
223
+ completedMilestones,
224
+ taskCount,
225
+ completedTasks,
226
+ startDate,
227
+ targetEndDate
228
+ }) {
229
+ const milestoneRingValue = ringValue(completedMilestones, milestoneCount);
230
+ const taskRingValue = ringValue(completedTasks, taskCount);
231
+ const timeInfo = targetEndDate ? daysRelative(targetEndDate) : null;
232
+ return /* @__PURE__ */ jsxs(SimpleGrid, { cols: 4, children: [
233
+ /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: 8, children: [
234
+ /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
235
+ /* @__PURE__ */ jsx(ThemeIcon, { size: 20, variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconHeartbeat, { size: 14 }) }),
236
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: "Status" })
237
+ ] }),
238
+ /* @__PURE__ */ jsx(StatusBadge, { status, size: "md" })
239
+ ] }) }),
240
+ /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: 8, children: [
241
+ /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
242
+ /* @__PURE__ */ jsx(ThemeIcon, { size: 20, variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconFlag, { size: 14 }) }),
243
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: "Milestones" })
244
+ ] }),
245
+ /* @__PURE__ */ jsxs(Group, { gap: 10, align: "center", children: [
246
+ /* @__PURE__ */ jsx(
247
+ RingProgress,
248
+ {
249
+ size: 48,
250
+ thickness: 5,
251
+ sections: [{ value: milestoneRingValue, color: ringColor(completedMilestones, milestoneCount) }]
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
255
+ /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 600, children: [
256
+ completedMilestones,
257
+ "/",
258
+ milestoneCount
259
+ ] }),
260
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "complete" })
261
+ ] })
262
+ ] })
263
+ ] }) }),
264
+ /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: 8, children: [
265
+ /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
266
+ /* @__PURE__ */ jsx(ThemeIcon, { size: 20, variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconFileText, { size: 14 }) }),
267
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: "Tasks" })
268
+ ] }),
269
+ /* @__PURE__ */ jsxs(Group, { gap: 10, align: "center", children: [
270
+ /* @__PURE__ */ jsx(
271
+ RingProgress,
272
+ {
273
+ size: 48,
274
+ thickness: 5,
275
+ sections: [{ value: taskRingValue, color: ringColor(completedTasks, taskCount) }]
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
279
+ /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 600, children: [
280
+ completedTasks,
281
+ "/",
282
+ taskCount
283
+ ] }),
284
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "approved" })
285
+ ] })
286
+ ] })
287
+ ] }) }),
288
+ /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: 8, children: [
289
+ /* @__PURE__ */ jsxs(Group, { gap: 6, children: [
290
+ /* @__PURE__ */ jsx(ThemeIcon, { size: 20, variant: "light", color: "gray", children: /* @__PURE__ */ jsx(IconCalendar, { size: 14 }) }),
291
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: "Timeline" })
292
+ ] }),
293
+ /* @__PURE__ */ jsx(Stack, { gap: 2, children: startDate && targetEndDate ? /* @__PURE__ */ jsxs(Fragment, { children: [
294
+ /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
295
+ formatDate2(startDate),
296
+ " \u2192 ",
297
+ formatDate2(targetEndDate)
298
+ ] }),
299
+ timeInfo && /* @__PURE__ */ jsx(Text, { size: "xs", c: timeInfo.overdue ? "red" : "dimmed", children: timeInfo.label })
300
+ ] }) : startDate ? /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
301
+ "Started ",
302
+ formatDate2(startDate)
303
+ ] }) : targetEndDate ? /* @__PURE__ */ jsxs(Text, { size: "sm", fw: 500, children: [
304
+ "Due ",
305
+ formatDate2(targetEndDate)
306
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No dates set" }) })
307
+ ] }) })
308
+ ] });
309
+ }
310
+ var MILESTONE_ICONS = {
311
+ upcoming: IconClock,
312
+ in_progress: IconFlag,
313
+ completed: IconCircleCheck,
314
+ overdue: IconAlertTriangle,
315
+ blocked: IconLock
316
+ };
317
+ function MilestoneTimeline({ milestones, tasks }) {
318
+ const sorted = [...milestones].sort((a, b) => a.sequence - b.sequence);
319
+ const defaultExpanded = new Set(sorted.filter((m) => m.status === "in_progress").map((m) => m.id));
320
+ const [expanded, setExpanded] = useState(defaultExpanded);
321
+ if (sorted.length === 0) {
322
+ return /* @__PURE__ */ jsx(EmptyState, { icon: IconInbox, title: "No milestones yet" });
323
+ }
324
+ function toggleMilestone(id) {
325
+ setExpanded((prev) => {
326
+ const next = new Set(prev);
327
+ if (next.has(id)) {
328
+ next.delete(id);
329
+ } else {
330
+ next.add(id);
331
+ }
332
+ return next;
333
+ });
334
+ }
335
+ const activeIndex = sorted.reduce((last, m, i) => m.status !== "upcoming" ? i : last, -1);
336
+ return /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsx(Timeline, { active: activeIndex, bulletSize: 28, lineWidth: 2, color: "var(--color-primary)", children: sorted.map((milestone) => {
337
+ const Icon = MILESTONE_ICONS[milestone.status] || IconFlag;
338
+ const color = milestoneStatusColors[milestone.status] || "gray";
339
+ const isExpanded = expanded.has(milestone.id);
340
+ const milestoneTasks = tasks.filter((t) => t.milestone_id === milestone.id);
341
+ const ChevronIcon = isExpanded ? IconChevronDown : IconChevronRight;
342
+ return /* @__PURE__ */ jsx(
343
+ Timeline.Item,
344
+ {
345
+ bullet: /* @__PURE__ */ jsx(ThemeIcon, { size: 28, variant: "filled", color, radius: "xl", children: /* @__PURE__ */ jsx(Icon, { size: 14 }) }),
346
+ children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
347
+ /* @__PURE__ */ jsxs(
348
+ Group,
349
+ {
350
+ gap: 8,
351
+ style: { cursor: "pointer", userSelect: "none" },
352
+ onClick: () => toggleMilestone(milestone.id),
353
+ children: [
354
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: milestone.name }),
355
+ /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color, children: formatStatusLabel(milestone.status) }),
356
+ /* @__PURE__ */ jsx(ThemeIcon, { size: 16, variant: "transparent", color: "dimmed", children: /* @__PURE__ */ jsx(ChevronIcon, { size: 14 }) })
357
+ ]
358
+ }
359
+ ),
360
+ milestone.due_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
361
+ "Due ",
362
+ new Date(milestone.due_date).toLocaleDateString()
363
+ ] }),
364
+ /* @__PURE__ */ jsx(Collapse, { in: isExpanded, children: /* @__PURE__ */ jsx(Stack, { gap: 6, mt: 6, children: milestoneTasks.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No tasks" }) : milestoneTasks.map((task) => /* @__PURE__ */ jsxs(Card, { withBorder: true, p: "xs", children: [
365
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", children: [
366
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 500, style: { flex: 1, minWidth: 0 }, children: task.name }),
367
+ /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: taskTypeColors[task.type] || "gray", children: formatStatusLabel(task.type) }),
368
+ /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: taskStatusColors[task.status] || "gray", children: formatStatusLabel(task.status) })
369
+ ] }),
370
+ task.due_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", mt: 4, children: [
371
+ "Due ",
372
+ new Date(task.due_date).toLocaleDateString()
373
+ ] })
374
+ ] }, task.id)) }) })
375
+ ] })
376
+ },
377
+ milestone.id
378
+ );
379
+ }) }) });
380
+ }
381
+ function parseChecklist(task) {
382
+ const raw = task.checklist;
383
+ if (!Array.isArray(raw)) return [];
384
+ return raw;
385
+ }
386
+ function TaskCard({ task }) {
387
+ const { mutate: updateTask } = useUpdateTask();
388
+ const items = parseChecklist(task);
389
+ function saveChecklist(updated) {
390
+ const updates = {
391
+ checklist: updated
392
+ };
393
+ updateTask({ id: task.id, updates });
394
+ }
395
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "sm", p: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: 6, children: [
396
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
397
+ /* @__PURE__ */ jsxs(Group, { gap: 6, wrap: "nowrap", style: { minWidth: 0 }, children: [
398
+ /* @__PURE__ */ jsx(Text, { fw: 500, size: "sm", style: { flexShrink: 0 }, children: task.name }),
399
+ /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: taskTypeColors[task.type] || "gray", children: formatStatusLabel(task.type) }),
400
+ /* @__PURE__ */ jsx(StatusBadge, { status: task.status, size: "xs" })
401
+ ] }),
402
+ task.file_url && /* @__PURE__ */ jsx(
403
+ ActionIcon,
404
+ {
405
+ component: "a",
406
+ href: task.file_url,
407
+ target: "_blank",
408
+ rel: "noopener noreferrer",
409
+ variant: "light",
410
+ size: "sm",
411
+ "aria-label": "Download file",
412
+ children: /* @__PURE__ */ jsx(IconDownload, { size: 14 })
413
+ }
414
+ )
415
+ ] }),
416
+ task.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: task.description }),
417
+ task.due_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
418
+ "Due ",
419
+ new Date(task.due_date).toLocaleDateString()
420
+ ] }),
421
+ items.length > 0 && /* @__PURE__ */ jsx(
422
+ Checklist,
423
+ {
424
+ items,
425
+ onToggle: (itemId) => {
426
+ saveChecklist(items.map((item) => item.id === itemId ? { ...item, completed: !item.completed } : item));
427
+ },
428
+ onAdd: (label) => {
429
+ saveChecklist([...items, { id: crypto.randomUUID(), label, completed: false }]);
430
+ },
431
+ onRemove: (itemId) => {
432
+ saveChecklist(items.filter((item) => item.id !== itemId));
433
+ }
434
+ }
435
+ )
436
+ ] }) });
437
+ }
438
+ var ProjectsSidebarTop = () => {
439
+ return /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconBriefcase, label: "Projects" });
440
+ };
441
+ var DELIVERY_PROJECT_ITEMS = [
442
+ { label: "Projects", to: "/projects", icon: IconBriefcase, exact: true }
443
+ ];
444
+ var DELIVERY_WORK_ITEMS = [
445
+ { label: "Tasks", to: "/projects/tasks", icon: IconChecklist, exact: false },
446
+ { label: "Milestones", to: "/projects/milestones", icon: IconFlag, exact: false }
447
+ ];
448
+ var DELIVERY_COMMUNICATION_ITEMS = [
449
+ { label: "Notes", to: "/projects/notes", icon: IconNotes, exact: false }
450
+ ];
451
+ var ProjectsSidebarMiddle = ({
452
+ currentPath,
453
+ onNavigate,
454
+ projectItems = DELIVERY_PROJECT_ITEMS,
455
+ workItems = DELIVERY_WORK_ITEMS,
456
+ communicationItems = DELIVERY_COMMUNICATION_ITEMS
457
+ } = {}) => {
458
+ const { currentPath: routerCurrentPath, navigate } = useRouterContext();
459
+ const resolvedCurrentPath = currentPath ?? routerCurrentPath;
460
+ const resolvedNavigate = onNavigate ?? navigate;
461
+ const renderItems = (items) => items.map((item) => {
462
+ const isActive = item.exact ? resolvedCurrentPath === item.to || resolvedCurrentPath === `${item.to}/` : resolvedCurrentPath.startsWith(item.to);
463
+ return /* @__PURE__ */ jsx(
464
+ SubshellNavItem,
465
+ {
466
+ icon: item.icon,
467
+ label: item.label,
468
+ isActive,
469
+ href: item.to,
470
+ onClick: () => resolvedNavigate(item.to)
471
+ },
472
+ item.to
473
+ );
474
+ });
475
+ return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { flex: 1, overflowY: "auto" }, children: [
476
+ /* @__PURE__ */ jsx(Stack, { gap: 0, p: "sm", children: renderItems(projectItems) }),
477
+ /* @__PURE__ */ jsxs(Fragment, { children: [
478
+ /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconListCheck, label: "Work", withTopBorder: true }),
479
+ /* @__PURE__ */ jsx(Stack, { gap: 0, p: "sm", children: renderItems(workItems) })
480
+ ] }),
481
+ /* @__PURE__ */ jsxs(Fragment, { children: [
482
+ /* @__PURE__ */ jsx(SubshellSidebarSection, { icon: IconMessageCircle, label: "Communication", withTopBorder: true }),
483
+ /* @__PURE__ */ jsx(Stack, { gap: 0, p: "sm", children: renderItems(communicationItems) })
484
+ ] })
485
+ ] });
486
+ };
487
+ var ProjectsSidebar = ({ children } = {}) => {
488
+ return /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { height: "100%", display: "flex", flexDirection: "column" }, children: [
489
+ /* @__PURE__ */ jsx(ProjectsSidebarTop, {}),
490
+ children ?? /* @__PURE__ */ jsx(ProjectsSidebarMiddle, {})
491
+ ] });
492
+ };
493
+ function AllTasksPage() {
494
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
495
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "All Tasks", caption: "Project tasks are scoped to an individual project" }) }),
496
+ /* @__PURE__ */ jsxs(Paper, { children: [
497
+ /* @__PURE__ */ jsx(
498
+ EmptyState,
499
+ {
500
+ icon: IconChecklist,
501
+ title: "Project task lists are not aggregated here",
502
+ description: "Open a project detail view to inspect its tasks. The shared contract does not provide a cross-project task list.",
503
+ py: "xl"
504
+ }
505
+ ),
506
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", ta: "center", pb: "xl", children: "This page is kept for route parity only." })
507
+ ] })
508
+ ] }) });
509
+ }
510
+ function UpcomingMilestonesPage() {
511
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsxs(PageContainer, { children: [
512
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Upcoming Milestones", caption: "Milestones are scoped to an individual project" }) }),
513
+ /* @__PURE__ */ jsxs(Paper, { children: [
514
+ /* @__PURE__ */ jsx(
515
+ EmptyState,
516
+ {
517
+ icon: IconFlag,
518
+ title: "Project milestone lists are not aggregated here",
519
+ description: "Open a project detail view to inspect its milestones. The shared contract does not provide a cross-project milestone list.",
520
+ py: "xl"
521
+ }
522
+ ),
523
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", ta: "center", pb: "xl", children: "This page is kept for route parity only." })
524
+ ] })
525
+ ] }) });
526
+ }
527
+ var STATUS_OPTIONS = [
528
+ { value: "active", label: "Active" },
529
+ { value: "on_track", label: "On Track" },
530
+ { value: "at_risk", label: "At Risk" },
531
+ { value: "blocked", label: "Blocked" },
532
+ { value: "completed", label: "Completed" },
533
+ { value: "paused", label: "Paused" }
534
+ ];
535
+ function ProjectsListPage({ onProjectClick } = {}) {
536
+ useProjectRealtime();
537
+ const [statusFilter, setStatusFilter] = useState(null);
538
+ const [searchQuery, setSearchQuery] = useState("");
539
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
540
+ const { data: projects, isLoading, error } = useProjects();
541
+ const deleteProject = useDeleteProject();
542
+ const { sort, toggleSort } = useTableSort("updated");
543
+ const filteredProjects = useMemo(() => {
544
+ const source = projects ?? [];
545
+ const query = searchQuery.trim().toLowerCase();
546
+ return source.filter((project) => {
547
+ const matchesStatus = !statusFilter || project.status === statusFilter;
548
+ const matchesSearch = !query || project.name.toLowerCase().includes(query);
549
+ return matchesStatus && matchesSearch;
550
+ });
551
+ }, [projects, searchQuery, statusFilter]);
552
+ const sortAccessors = useMemo(
553
+ () => ({
554
+ name: (p) => p.name || "",
555
+ status: (p) => p.status || "",
556
+ updated: (p) => p.updated_at || ""
557
+ }),
558
+ []
559
+ );
560
+ const sortedProjects = useMemo(
561
+ () => sortData(filteredProjects, sort, sortAccessors),
562
+ [filteredProjects, sort, sortAccessors]
563
+ );
564
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT, [statusFilter, searchQuery], sortedProjects.length);
565
+ const paginatedProjects = useMemo(
566
+ () => sortedProjects.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT),
567
+ [sortedProjects, pagination.offset]
568
+ );
569
+ const selection = useTableSelection(paginatedProjects, sortedProjects);
570
+ const handleDeleteSelected = async () => {
571
+ await Promise.all([...selection.selectedIds].map((projectId) => deleteProject.mutateAsync(projectId)));
572
+ setShowBatchDelete(false);
573
+ selection.clear();
574
+ };
575
+ return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
576
+ /* @__PURE__ */ jsxs(PageContainer, { children: [
577
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "Projects", caption: "Project tracking and milestone management" }) }),
578
+ /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
579
+ /* @__PURE__ */ jsxs(
580
+ FilterBar,
581
+ {
582
+ actions: /* @__PURE__ */ jsx(
583
+ TableSelectionToolbar,
584
+ {
585
+ selectedCount: selection.selectedCount,
586
+ onDelete: () => setShowBatchDelete(true),
587
+ isDeleting: deleteProject.isPending
588
+ }
589
+ ),
590
+ children: [
591
+ /* @__PURE__ */ jsx(
592
+ Select,
593
+ {
594
+ placeholder: "All Statuses",
595
+ data: STATUS_OPTIONS,
596
+ style: { minWidth: 180 },
597
+ size: "sm",
598
+ clearable: true,
599
+ value: statusFilter,
600
+ onChange: setStatusFilter
601
+ }
602
+ ),
603
+ /* @__PURE__ */ jsx(
604
+ TextInput,
605
+ {
606
+ placeholder: "Search by name...",
607
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
608
+ style: { minWidth: 250 },
609
+ size: "sm",
610
+ value: searchQuery,
611
+ onChange: (e) => setSearchQuery(e.currentTarget.value)
612
+ }
613
+ )
614
+ ]
615
+ }
616
+ ),
617
+ isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : error ? /* @__PURE__ */ jsx(CenteredErrorState, { error, title: "Failed to load projects" }) : !sortedProjects.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconBriefcase, title: "No projects found" }) : /* @__PURE__ */ jsxs(Table, { children: [
618
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
619
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
620
+ Checkbox,
621
+ {
622
+ checked: selection.isPageAllSelected,
623
+ indeterminate: selection.isPagePartiallySelected,
624
+ onChange: selection.togglePage
625
+ }
626
+ ) }),
627
+ /* @__PURE__ */ jsx(SortableHeader, { column: "name", sort, onToggle: toggleSort, children: "Name" }),
628
+ /* @__PURE__ */ jsx(SortableHeader, { column: "status", sort, onToggle: toggleSort, children: "Status" }),
629
+ /* @__PURE__ */ jsx(Table.Th, { children: "Milestones" }),
630
+ /* @__PURE__ */ jsx(Table.Th, { children: "Tasks" }),
631
+ /* @__PURE__ */ jsx(SortableHeader, { column: "updated", sort, onToggle: toggleSort, children: "Updated" })
632
+ ] }) }),
633
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedProjects.map((project) => {
634
+ const completedMilestones = project.completedMilestones ?? 0;
635
+ const milestoneCount = project.milestoneCount ?? 0;
636
+ const completedTasks = project.completedTasks ?? 0;
637
+ const taskCount = project.taskCount ?? 0;
638
+ const rowProps = onProjectClick ? {
639
+ style: { cursor: "pointer" },
640
+ onClick: () => onProjectClick(project.id)
641
+ } : void 0;
642
+ return /* @__PURE__ */ jsxs(Table.Tr, { ...rowProps, children: [
643
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
644
+ Checkbox,
645
+ {
646
+ checked: selection.isSelected(project.id),
647
+ onChange: () => selection.toggle(project.id)
648
+ }
649
+ ) }),
650
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, children: project.name }) }),
651
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: projectStatusColors[project.status || ""] || "gray", size: "sm", children: formatStatusLabel(project.status || "unknown") }) }),
652
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
653
+ completedMilestones,
654
+ "/",
655
+ milestoneCount
656
+ ] }) }),
657
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
658
+ completedTasks,
659
+ "/",
660
+ taskCount
661
+ ] }) }),
662
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatTimeAgo(project.updated_at) }) })
663
+ ] }, project.id);
664
+ }) })
665
+ ] }),
666
+ sortedProjects.length > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
667
+ Pagination,
668
+ {
669
+ value: pagination.page,
670
+ onChange: pagination.setPage,
671
+ total: pagination.totalPages(sortedProjects.length),
672
+ size: "sm"
673
+ }
674
+ ) })
675
+ ] }) })
676
+ ] }),
677
+ /* @__PURE__ */ jsx(
678
+ CustomModal,
679
+ {
680
+ opened: showBatchDelete,
681
+ onClose: () => !deleteProject.isPending && setShowBatchDelete(false),
682
+ size: "sm",
683
+ loading: deleteProject.isPending,
684
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
685
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
686
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
687
+ /* @__PURE__ */ jsxs(Title, { order: 4, children: [
688
+ "Delete ",
689
+ selection.selectedCount,
690
+ " Projects"
691
+ ] })
692
+ ] }),
693
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
694
+ "Are you sure you want to delete",
695
+ " ",
696
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: selection.selectedCount }),
697
+ " ",
698
+ "selected projects?"
699
+ ] }),
700
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone." }),
701
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
702
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setShowBatchDelete(false), disabled: deleteProject.isPending, children: "Cancel" }),
703
+ /* @__PURE__ */ jsx(Button, { color: "red", loading: deleteProject.isPending, onClick: () => void handleDeleteSelected(), children: "Delete" })
704
+ ] })
705
+ ] })
706
+ }
707
+ )
708
+ ] });
709
+ }
710
+ function isResumeContext(val) {
711
+ return !!val && typeof val === "object" && !Array.isArray(val);
712
+ }
713
+ function ContextRow({ label, value }) {
714
+ return /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
715
+ /* @__PURE__ */ jsx(
716
+ Text,
717
+ {
718
+ size: "xs",
719
+ ff: "monospace",
720
+ c: "var(--color-text-subtle)",
721
+ style: { textTransform: "uppercase", letterSpacing: "0.06em", opacity: 0.7 },
722
+ children: label
723
+ }
724
+ ),
725
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "var(--color-text)", style: { lineHeight: 1.55 }, children: value })
726
+ ] });
727
+ }
728
+ function ResumeContextPreview({ tasks }) {
729
+ const candidate = tasks.filter((t) => isResumeContext(t.resume_context)).sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())[0];
730
+ if (!candidate) return null;
731
+ const ctx = candidate.resume_context;
732
+ const hasBody = ctx.current_state || ctx.next_steps;
733
+ return /* @__PURE__ */ jsxs(
734
+ Paper,
735
+ {
736
+ p: "md",
737
+ style: {
738
+ border: "1px solid var(--color-border)",
739
+ borderRadius: "var(--mantine-radius-default)",
740
+ backgroundColor: "color-mix(in srgb, var(--color-primary) 4%, var(--color-surface))",
741
+ position: "relative",
742
+ overflow: "hidden"
743
+ },
744
+ children: [
745
+ /* @__PURE__ */ jsx(
746
+ "div",
747
+ {
748
+ style: {
749
+ position: "absolute",
750
+ inset: "0 auto 0 0",
751
+ width: 3,
752
+ background: "linear-gradient(180deg, var(--color-primary) 0%, color-mix(in srgb, var(--color-primary) 30%, transparent) 100%)"
753
+ }
754
+ }
755
+ ),
756
+ /* @__PURE__ */ jsxs(Stack, { gap: "sm", pl: 4, children: [
757
+ /* @__PURE__ */ jsxs(Group, { gap: 6, align: "baseline", wrap: "wrap", children: [
758
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Last worked" }),
759
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "var(--color-text)", children: formatRelativeTime(candidate.updated_at) }),
760
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "on" }),
761
+ /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, c: "var(--color-text)", children: candidate.name })
762
+ ] }),
763
+ hasBody && /* @__PURE__ */ jsxs(Fragment, { children: [
764
+ /* @__PURE__ */ jsx(Divider, { opacity: 0.3 }),
765
+ /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
766
+ ctx.current_state && /* @__PURE__ */ jsx(ContextRow, { label: "Current State", value: ctx.current_state }),
767
+ ctx.next_steps && /* @__PURE__ */ jsx(ContextRow, { label: "Next Steps", value: ctx.next_steps })
768
+ ] })
769
+ ] })
770
+ ] })
771
+ ]
772
+ }
773
+ );
774
+ }
775
+ function QuickActionsRow({
776
+ onCreateTask,
777
+ onAddMilestone,
778
+ onLogBlocker,
779
+ onBack,
780
+ backLabel = "Projects"
781
+ }) {
782
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", justify: "space-between", children: [
783
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
784
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "compact-sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }), onClick: onCreateTask, children: "Create Task" }),
785
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "compact-sm", leftSection: /* @__PURE__ */ jsx(IconFlag, { size: 14 }), onClick: onAddMilestone, children: "Add Milestone" }),
786
+ /* @__PURE__ */ jsx(
787
+ Button,
788
+ {
789
+ variant: "light",
790
+ size: "compact-sm",
791
+ color: "orange",
792
+ leftSection: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 14 }),
793
+ onClick: onLogBlocker,
794
+ children: "Log Blocker"
795
+ }
796
+ )
797
+ ] }),
798
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "compact-sm", leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 14 }), onClick: onBack, children: backLabel })
799
+ ] });
800
+ }
801
+ function LinkedRecordChips({ dealId, clientCompanyId }) {
802
+ if (!dealId && !clientCompanyId) return null;
803
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
804
+ dealId && /* @__PURE__ */ jsx(Anchor, { href: `/crm/deals/${dealId}`, underline: "never", children: /* @__PURE__ */ jsx(
805
+ Badge,
806
+ {
807
+ variant: "light",
808
+ color: "blue",
809
+ leftSection: /* @__PURE__ */ jsx(IconCurrencyDollar, { size: 12 }),
810
+ style: { cursor: "pointer" },
811
+ children: "Deal"
812
+ }
813
+ ) }),
814
+ clientCompanyId && /* @__PURE__ */ jsx(Anchor, { href: `/crm/companies/${clientCompanyId}`, underline: "never", children: /* @__PURE__ */ jsx(Badge, { variant: "light", color: "teal", leftSection: /* @__PURE__ */ jsx(IconBuilding, { size: 12 }), style: { cursor: "pointer" }, children: "Company" }) })
815
+ ] });
816
+ }
817
+ function ProjectActivityTimeline({ projectId }) {
818
+ const { data: activities, isLoading, error } = useProjectActivities(projectId);
819
+ if (isLoading) {
820
+ return /* @__PURE__ */ jsx(Center, { py: "xl", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
821
+ }
822
+ if (error) {
823
+ return /* @__PURE__ */ jsx(Alert, { color: "red", title: "Failed to load activity", children: "Could not fetch project activity. Please refresh." });
824
+ }
825
+ if (!activities?.length) {
826
+ return /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No activity recorded for this project yet." });
827
+ }
828
+ return /* @__PURE__ */ jsx(Timeline, { bulletSize: 18, lineWidth: 2, children: activities.map((activity) => /* @__PURE__ */ jsxs(
829
+ Timeline.Item,
830
+ {
831
+ title: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: activity.title }),
832
+ children: [
833
+ activity.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mt: 2, children: activity.description }),
834
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mt: 2, children: formatTimeAgo(activity.occurredAt) })
835
+ ]
836
+ },
837
+ activity.id
838
+ )) });
839
+ }
840
+ var NOTE_FILTER_OPTIONS = [
841
+ { value: "call_note", label: "Call Notes" },
842
+ { value: "status_update", label: "Status Updates" },
843
+ { value: "issue", label: "Issues" },
844
+ { value: "blocker", label: "Blockers" },
845
+ { value: "agent_learning", label: "Agent Learning" }
846
+ ];
847
+ function isWithinLastWeek(dateStr) {
848
+ if (!dateStr) return false;
849
+ const date = new Date(dateStr);
850
+ const cutoff = /* @__PURE__ */ new Date();
851
+ cutoff.setDate(cutoff.getDate() - 7);
852
+ return date >= cutoff;
853
+ }
854
+ function NotesTabContent({ notes, onAddNote }) {
855
+ const [activeFilter, setActiveFilter] = useState(null);
856
+ const allNotes = notes ?? [];
857
+ const thisWeek = allNotes.filter((n) => isWithinLastWeek(n.occurred_at));
858
+ const weeklyByType = NOTE_FILTER_OPTIONS.map(({ value, label }) => ({
859
+ value,
860
+ label,
861
+ count: thisWeek.filter((n) => n.type === value).length
862
+ })).filter((entry) => entry.count > 0);
863
+ const rollupText = weeklyByType.length > 0 ? weeklyByType.map((e) => `${e.count} ${e.label.toLowerCase()}`).join(", ") + " this week" : null;
864
+ const visibleNotes = activeFilter ? allNotes.filter((n) => n.type === activeFilter) : allNotes;
865
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
866
+ rollupText && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: rollupText }),
867
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", wrap: "wrap", children: [
868
+ /* @__PURE__ */ jsx(Chip.Group, { value: activeFilter ?? "", onChange: (val) => setActiveFilter(val || null), children: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
869
+ /* @__PURE__ */ jsx(Chip, { value: "", size: "xs", variant: "light", children: "All" }),
870
+ NOTE_FILTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx(Chip, { value, size: "xs", variant: "light", children: label }, value))
871
+ ] }) }),
872
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: onAddNote, children: "Add Note" })
873
+ ] }),
874
+ !visibleNotes.length ? /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: activeFilter ? `No ${formatStatusLabel(activeFilter)} notes yet.` : "No notes yet." }) : /* @__PURE__ */ jsx(Stack, { gap: "sm", children: visibleNotes.map((note) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
875
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
876
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: noteTypeColors[note.type ?? ""] ?? "gray", size: "sm", children: formatStatusLabel(note.type ?? "note") }),
877
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatRelativeTime(note.occurred_at) })
878
+ ] }),
879
+ note.summary && /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: note.summary }),
880
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: note.content })
881
+ ] }) }, note.id)) })
882
+ ] });
883
+ }
884
+ function humanizeKey(key) {
885
+ return key.replace(/_/g, " ").toUpperCase();
886
+ }
887
+ function ContextLabel({ label }) {
888
+ return /* @__PURE__ */ jsx(
889
+ Text,
890
+ {
891
+ size: "xs",
892
+ ff: "monospace",
893
+ c: "var(--color-text-subtle)",
894
+ style: { textTransform: "uppercase", letterSpacing: "0.06em", opacity: 0.7 },
895
+ children: label
896
+ }
897
+ );
898
+ }
899
+ function ContextValue({ value }) {
900
+ if (Array.isArray(value)) {
901
+ return /* @__PURE__ */ jsx(List, { size: "xs", withPadding: true, children: value.map((item, i) => /* @__PURE__ */ jsx(List.Item, { children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "var(--color-text)", style: { lineHeight: 1.55 }, children: typeof item === "string" || typeof item === "number" || typeof item === "boolean" ? String(item) : JSON.stringify(item) }) }, i)) });
902
+ }
903
+ if (value !== null && typeof value === "object") {
904
+ return /* @__PURE__ */ jsx(Text, { size: "xs", ff: "monospace", c: "var(--color-text)", style: { lineHeight: 1.55, whiteSpace: "pre-wrap" }, children: JSON.stringify(value, null, 2) });
905
+ }
906
+ return /* @__PURE__ */ jsx(Text, { size: "xs", c: "var(--color-text)", style: { lineHeight: 1.55 }, children: String(value ?? "") });
907
+ }
908
+ function ResumeContextView({ task }) {
909
+ const ctx = task.resume_context;
910
+ if (!ctx || typeof ctx !== "object" || Array.isArray(ctx)) return null;
911
+ const entries = Object.entries(ctx);
912
+ if (entries.length === 0) return null;
913
+ return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: entries.map(([key, value]) => /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
914
+ /* @__PURE__ */ jsx(ContextLabel, { label: humanizeKey(key) }),
915
+ /* @__PURE__ */ jsx(ContextValue, { value })
916
+ ] }, key)) });
917
+ }
918
+ var noteTypeOptions = [
919
+ { value: "call_note", label: "Call Note" },
920
+ { value: "status_update", label: "Status Update" },
921
+ { value: "issue", label: "Issue" },
922
+ { value: "blocker", label: "Blocker" },
923
+ { value: "agent_learning", label: "Agent Learning" }
924
+ ];
925
+ function isTaskDone(status) {
926
+ return status === "completed" || status === "approved";
927
+ }
928
+ function parseChecklist2(milestone) {
929
+ const raw = milestone.checklist;
930
+ if (!Array.isArray(raw)) return [];
931
+ return raw;
932
+ }
933
+ function sortMilestonesForTimeline(milestones) {
934
+ return [...milestones].sort((a, b) => {
935
+ if (a.sequence !== b.sequence) return a.sequence - b.sequence;
936
+ const aCreated = a.created_at ? new Date(a.created_at).getTime() : 0;
937
+ const bCreated = b.created_at ? new Date(b.created_at).getTime() : 0;
938
+ if (aCreated !== bCreated) return aCreated - bCreated;
939
+ return a.name.localeCompare(b.name);
940
+ });
941
+ }
942
+ var bulletSize = 14;
943
+ function renderMilestoneBullet(status) {
944
+ const normalizedStatus = status ?? "upcoming";
945
+ const isCompleted = normalizedStatus === "completed";
946
+ const isInProgress = normalizedStatus === "in_progress";
947
+ if (isCompleted) {
948
+ return /* @__PURE__ */ jsx(
949
+ "div",
950
+ {
951
+ style: {
952
+ width: bulletSize,
953
+ height: bulletSize,
954
+ borderRadius: "50%",
955
+ backgroundColor: "var(--color-primary)",
956
+ boxShadow: "0 0 0 4px var(--color-surface)"
957
+ }
958
+ }
959
+ );
960
+ }
961
+ const fillColor = isInProgress ? "var(--color-primary)" : "color-mix(in srgb, var(--color-primary) 50%, transparent)";
962
+ const borderColor = isInProgress ? "var(--color-primary)" : "color-mix(in srgb, var(--color-primary) 20%, transparent)";
963
+ return /* @__PURE__ */ jsx(
964
+ "div",
965
+ {
966
+ style: {
967
+ width: bulletSize,
968
+ height: bulletSize,
969
+ borderRadius: "50%",
970
+ backgroundColor: fillColor,
971
+ border: `2px solid ${borderColor}`,
972
+ boxSizing: "border-box",
973
+ zIndex: 2,
974
+ boxShadow: "0 0 0 4px var(--color-surface)"
975
+ }
976
+ }
977
+ );
978
+ }
979
+ function MilestoneChecklist({ milestone }) {
980
+ const items = parseChecklist2(milestone);
981
+ const { mutate: updateMilestone } = useUpdateMilestone();
982
+ function saveChecklist(updated) {
983
+ const allComplete = updated.length > 0 && updated.every((item) => item.completed);
984
+ const wasComplete = milestone.status === "completed";
985
+ const updates = {
986
+ checklist: updated
987
+ };
988
+ if (allComplete && !wasComplete) {
989
+ updates.status = "completed";
990
+ updates.completed_at = (/* @__PURE__ */ new Date()).toISOString();
991
+ } else if (!allComplete && wasComplete) {
992
+ updates.status = "in_progress";
993
+ updates.completed_at = null;
994
+ }
995
+ updateMilestone({ id: milestone.id, updates });
996
+ }
997
+ return /* @__PURE__ */ jsx(
998
+ Checklist,
999
+ {
1000
+ items,
1001
+ onToggle: (itemId) => {
1002
+ const updated = items.map((item) => item.id === itemId ? { ...item, completed: !item.completed } : item);
1003
+ saveChecklist(updated);
1004
+ },
1005
+ onAdd: (label) => {
1006
+ const newItem = { id: crypto.randomUUID(), label, completed: false };
1007
+ saveChecklist([...items, newItem]);
1008
+ },
1009
+ onRemove: (itemId) => {
1010
+ saveChecklist(items.filter((item) => item.id !== itemId));
1011
+ }
1012
+ }
1013
+ );
1014
+ }
1015
+ function AddNoteModal({ opened, onClose, projectId }) {
1016
+ const [noteType, setNoteType] = useState("call_note");
1017
+ const [content, setContent] = useState("");
1018
+ const [summary, setSummary] = useState("");
1019
+ const { mutate: createNote, isPending } = useCreateNote();
1020
+ function handleSubmit() {
1021
+ if (!content.trim() || !noteType) return;
1022
+ createNote(
1023
+ {
1024
+ project_id: projectId,
1025
+ type: noteType,
1026
+ content: content.trim(),
1027
+ summary: summary.trim() || null
1028
+ },
1029
+ {
1030
+ onSuccess: () => {
1031
+ setContent("");
1032
+ setSummary("");
1033
+ setNoteType("call_note");
1034
+ onClose();
1035
+ }
1036
+ }
1037
+ );
1038
+ }
1039
+ return /* @__PURE__ */ jsx(CustomModal, { opened, onClose, size: "md", loading: isPending, children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1040
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
1041
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Add Note" }),
1042
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: onClose, disabled: isPending, children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
1043
+ ] }),
1044
+ /* @__PURE__ */ jsx(Select, { label: "Type", data: noteTypeOptions, value: noteType, onChange: setNoteType, required: true }),
1045
+ /* @__PURE__ */ jsx(
1046
+ TextInput,
1047
+ {
1048
+ label: "Summary",
1049
+ placeholder: "Brief summary (optional)",
1050
+ value: summary,
1051
+ onChange: (event) => setSummary(event.target.value)
1052
+ }
1053
+ ),
1054
+ /* @__PURE__ */ jsx(
1055
+ Textarea,
1056
+ {
1057
+ label: "Content",
1058
+ placeholder: "Note content...",
1059
+ minRows: 4,
1060
+ value: content,
1061
+ onChange: (event) => setContent(event.target.value),
1062
+ required: true
1063
+ }
1064
+ ),
1065
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
1066
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: onClose, disabled: isPending, children: "Cancel" }),
1067
+ /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: isPending, disabled: !content.trim() || !noteType, children: "Add Note" })
1068
+ ] })
1069
+ ] }) });
1070
+ }
1071
+ function LogBlockerModal({ opened, onClose, projectId }) {
1072
+ const [content, setContent] = useState("");
1073
+ const [summary, setSummary] = useState("");
1074
+ const { mutate: createNote, isPending } = useCreateNote();
1075
+ function handleSubmit() {
1076
+ if (!content.trim()) return;
1077
+ createNote(
1078
+ {
1079
+ project_id: projectId,
1080
+ type: "blocker",
1081
+ content: content.trim(),
1082
+ summary: summary.trim() || null
1083
+ },
1084
+ {
1085
+ onSuccess: () => {
1086
+ setContent("");
1087
+ setSummary("");
1088
+ onClose();
1089
+ }
1090
+ }
1091
+ );
1092
+ }
1093
+ return /* @__PURE__ */ jsx(Modal, { opened, onClose, size: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1094
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Log Blocker" }),
1095
+ /* @__PURE__ */ jsx(
1096
+ TextInput,
1097
+ {
1098
+ label: "Summary",
1099
+ placeholder: "Brief blocker summary (optional)",
1100
+ value: summary,
1101
+ onChange: (e) => setSummary(e.target.value)
1102
+ }
1103
+ ),
1104
+ /* @__PURE__ */ jsx(
1105
+ Textarea,
1106
+ {
1107
+ label: "Description",
1108
+ placeholder: "Describe the blocker...",
1109
+ minRows: 4,
1110
+ value: content,
1111
+ onChange: (e) => setContent(e.target.value),
1112
+ required: true
1113
+ }
1114
+ ),
1115
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
1116
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: onClose, disabled: isPending, children: "Cancel" }),
1117
+ /* @__PURE__ */ jsx(Button, { color: "orange", onClick: handleSubmit, loading: isPending, disabled: !content.trim(), children: "Log Blocker" })
1118
+ ] })
1119
+ ] }) });
1120
+ }
1121
+ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
1122
+ useProjectRealtime(projectId);
1123
+ const { data: project, isLoading, error } = useProject(projectId);
1124
+ const { data: notes } = useProjectNotes({ projectId });
1125
+ const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject();
1126
+ const [addNoteOpen, setAddNoteOpen] = useState(false);
1127
+ const [deleteModalOpen, setDeleteModalOpen] = useState(false);
1128
+ const [logBlockerOpen, { open: openLogBlocker, close: closeLogBlocker }] = useDisclosure(false);
1129
+ const [createTaskOpen, { open: openCreateTask, close: closeCreateTask }] = useDisclosure(false);
1130
+ const [addMilestoneOpen, { open: openAddMilestone, close: closeAddMilestone }] = useDisclosure(false);
1131
+ if (isLoading) {
1132
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Loader, {}) }) });
1133
+ }
1134
+ if (error) {
1135
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Alert, { color: "red", children: "Error loading project" }) }) });
1136
+ }
1137
+ if (!project) {
1138
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Alert, { color: "yellow", children: "Project not found" }) }) });
1139
+ }
1140
+ const milestones = sortMilestonesForTimeline(project.milestones ?? []);
1141
+ const tasks = project.tasks ?? [];
1142
+ const totalMilestones = milestones.length;
1143
+ const completedMilestones = milestones.filter((milestone) => milestone.status === "completed").length;
1144
+ const totalTasks = tasks.length;
1145
+ const approvedTasks = tasks.filter((task) => task.status === "approved").length;
1146
+ const milestoneProgress = calculateProgress(completedMilestones, totalMilestones);
1147
+ const taskProgress = calculateProgress(approvedTasks, totalTasks);
1148
+ const companyName = project.company?.name ?? null;
1149
+ const activeMilestoneIndex = milestones.reduce(
1150
+ (lastIndex, milestone, index) => milestone.status !== "upcoming" ? index : lastIndex,
1151
+ -1
1152
+ );
1153
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
1154
+ /* @__PURE__ */ jsx(
1155
+ PageTitleCaption,
1156
+ {
1157
+ title: project.name,
1158
+ caption: [companyName, formatStatusLabel(project.status || "unknown")].filter(Boolean).join(" - ")
1159
+ }
1160
+ ),
1161
+ /* @__PURE__ */ jsx(
1162
+ QuickActionsRow,
1163
+ {
1164
+ onCreateTask: openCreateTask,
1165
+ onAddMilestone: openAddMilestone,
1166
+ onLogBlocker: openLogBlocker,
1167
+ onBack,
1168
+ backLabel
1169
+ }
1170
+ ),
1171
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: 4, children: [
1172
+ /* @__PURE__ */ jsx(
1173
+ StatCard,
1174
+ {
1175
+ variant: "hero",
1176
+ icon: IconHeartbeat,
1177
+ value: formatStatusLabel(project.status || "unknown"),
1178
+ label: "Status",
1179
+ valueColor: `var(--mantine-color-${projectStatusColors[project.status || ""] || "gray"}-6)`
1180
+ }
1181
+ ),
1182
+ /* @__PURE__ */ jsx(
1183
+ StatCard,
1184
+ {
1185
+ variant: "hero",
1186
+ icon: IconFlag,
1187
+ value: `${completedMilestones}/${totalMilestones}`,
1188
+ label: "Milestones",
1189
+ children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1190
+ milestoneProgress,
1191
+ "% complete"
1192
+ ] })
1193
+ }
1194
+ ),
1195
+ /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChecklist, value: `${approvedTasks}/${totalTasks}`, label: "Tasks", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1196
+ taskProgress,
1197
+ "% complete"
1198
+ ] }) }),
1199
+ /* @__PURE__ */ jsx(
1200
+ StatCard,
1201
+ {
1202
+ variant: "hero",
1203
+ icon: IconCalendar,
1204
+ value: project.start_date ? formatDate(project.start_date) : "-",
1205
+ label: "Timeline",
1206
+ children: project.target_end_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1207
+ "to ",
1208
+ formatDate(project.target_end_date)
1209
+ ] })
1210
+ }
1211
+ )
1212
+ ] }),
1213
+ /* @__PURE__ */ jsxs(
1214
+ Paper,
1215
+ {
1216
+ p: "md",
1217
+ style: {
1218
+ border: "1px solid var(--color-border)",
1219
+ borderRadius: "var(--mantine-radius-default)"
1220
+ },
1221
+ children: [
1222
+ /* @__PURE__ */ jsx(Stack, { gap: "md", children: /* @__PURE__ */ jsx(
1223
+ CardHeader,
1224
+ {
1225
+ icon: /* @__PURE__ */ jsx(IconFileDescription, { size: 18 }),
1226
+ title: "Project Details",
1227
+ rightSection: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "wrap", justify: "flex-end", children: [
1228
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: projectStatusColors[project.status || ""] || "gray", children: formatStatusLabel(project.status || "unknown") }),
1229
+ /* @__PURE__ */ jsx(LinkedRecordChips, { dealId: project.deal_id, clientCompanyId: project.client_company_id }),
1230
+ /* @__PURE__ */ jsx(Text, { size: "sm", ff: "monospace", c: "var(--color-text-subtle)", children: project.id }),
1231
+ /* @__PURE__ */ jsx(CopyButton, { value: `/project work ${project.id}`, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx(
1232
+ ActionIcon,
1233
+ {
1234
+ onClick: copy,
1235
+ variant: "subtle",
1236
+ size: "xs",
1237
+ color: copied ? "green" : "var(--color-text-subtle)",
1238
+ children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 })
1239
+ }
1240
+ ) }) })
1241
+ ] })
1242
+ }
1243
+ ) }),
1244
+ /* @__PURE__ */ jsx(Space, { my: "xs" }),
1245
+ /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
1246
+ project.description && /* @__PURE__ */ jsxs("div", { children: [
1247
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Description" }),
1248
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: project.description })
1249
+ ] }),
1250
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: 4, children: [
1251
+ /* @__PURE__ */ jsxs("div", { children: [
1252
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Start Date" }),
1253
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.start_date) })
1254
+ ] }),
1255
+ /* @__PURE__ */ jsxs("div", { children: [
1256
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Target End Date" }),
1257
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.target_end_date) })
1258
+ ] }),
1259
+ /* @__PURE__ */ jsxs("div", { children: [
1260
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Actual End Date" }),
1261
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(project.actual_end_date) })
1262
+ ] }),
1263
+ /* @__PURE__ */ jsxs("div", { children: [
1264
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Contract Value" }),
1265
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: project.contract_value != null ? `$${project.contract_value.toLocaleString()}${project.metadata && project.metadata.rate_type === "hourly" ? "/hr" : ""}` : "-" })
1266
+ ] })
1267
+ ] }),
1268
+ project.company && /* @__PURE__ */ jsxs("div", { children: [
1269
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Company" }),
1270
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: project.company.name }),
1271
+ project.company.domain && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: project.company.domain })
1272
+ ] })
1273
+ ] }),
1274
+ /* @__PURE__ */ jsx(Space, { my: "xs" }),
1275
+ /* @__PURE__ */ jsx(ResumeContextPreview, { tasks }),
1276
+ /* @__PURE__ */ jsx(Space, { my: "md" }),
1277
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: "status", children: [
1278
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
1279
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "status", children: "Status" }),
1280
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "notes", children: "Notes" }),
1281
+ /* @__PURE__ */ jsx(Tabs.Tab, { value: "activity", children: "Activity" })
1282
+ ] }),
1283
+ /* @__PURE__ */ jsxs(Tabs.Panel, { value: "status", pt: "md", children: [
1284
+ milestones.length === 0 ? /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No milestones yet." }) : /* @__PURE__ */ jsx(
1285
+ Timeline,
1286
+ {
1287
+ active: activeMilestoneIndex,
1288
+ bulletSize: 20,
1289
+ lineWidth: 3,
1290
+ color: "var(--color-primary)",
1291
+ styles: {
1292
+ itemBullet: {
1293
+ borderColor: "transparent",
1294
+ backgroundColor: "transparent",
1295
+ padding: 0,
1296
+ zIndex: 3,
1297
+ overflow: "visible",
1298
+ display: "flex",
1299
+ alignItems: "center",
1300
+ justifyContent: "center"
1301
+ },
1302
+ item: {
1303
+ "--_item-border-color": "color-mix(in srgb, var(--color-primary) 22%, transparent)",
1304
+ paddingBottom: "var(--mantine-spacing-xl)"
1305
+ },
1306
+ itemTitle: {
1307
+ color: "var(--color-text)"
1308
+ },
1309
+ itemBody: {
1310
+ marginTop: 0
1311
+ }
1312
+ },
1313
+ children: milestones.map((milestone) => {
1314
+ const milestoneTasks = tasks.filter((task) => task.milestone_id === milestone.id);
1315
+ const checklistItems = parseChecklist2(milestone);
1316
+ const completedChecklistItems = checklistItems.filter((item) => item.completed).length;
1317
+ const completedTasks = milestoneTasks.filter(
1318
+ (t) => isTaskDone(t.status ?? "planned")
1319
+ ).length;
1320
+ const progressNumerator = milestoneTasks.length > 0 ? completedTasks : completedChecklistItems;
1321
+ const progressDenominator = milestoneTasks.length > 0 ? milestoneTasks.length : checklistItems.length;
1322
+ return /* @__PURE__ */ jsx(
1323
+ Timeline.Item,
1324
+ {
1325
+ bullet: renderMilestoneBullet(milestone.status),
1326
+ title: /* @__PURE__ */ jsx("div", {}),
1327
+ children: /* @__PURE__ */ jsx(
1328
+ Paper,
1329
+ {
1330
+ p: "lg",
1331
+ radius: "md",
1332
+ style: {
1333
+ background: "transparent",
1334
+ border: "1px solid var(--color-border)",
1335
+ boxShadow: "none",
1336
+ position: "relative",
1337
+ overflow: "hidden"
1338
+ },
1339
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1340
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
1341
+ /* @__PURE__ */ jsxs(Stack, { gap: 6, style: { flex: 1, minWidth: 260 }, children: [
1342
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
1343
+ /* @__PURE__ */ jsx(Text, { fw: 700, size: "lg", c: "var(--color-text)", children: milestone.name }),
1344
+ /* @__PURE__ */ jsx(
1345
+ Badge,
1346
+ {
1347
+ variant: "light",
1348
+ color: milestoneStatusColors[milestone.status || ""] || "gray",
1349
+ size: "sm",
1350
+ styles: {
1351
+ root: {
1352
+ letterSpacing: "0.02em"
1353
+ }
1354
+ },
1355
+ children: formatStatusLabel(milestone.status || "unknown")
1356
+ }
1357
+ )
1358
+ ] }),
1359
+ milestone.description && /* @__PURE__ */ jsx(Text, { size: "sm", c: "var(--color-text-dimmed)", maw: 900, children: milestone.description })
1360
+ ] }),
1361
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
1362
+ milestone.due_date && /* @__PURE__ */ jsxs(
1363
+ Badge,
1364
+ {
1365
+ variant: "outline",
1366
+ size: "md",
1367
+ styles: {
1368
+ root: {
1369
+ borderColor: "var(--border-primary-subtle)",
1370
+ backgroundColor: "var(--surface-primary-faint)"
1371
+ }
1372
+ },
1373
+ children: [
1374
+ "Due ",
1375
+ formatDate(milestone.due_date)
1376
+ ]
1377
+ }
1378
+ ),
1379
+ /* @__PURE__ */ jsxs(
1380
+ Badge,
1381
+ {
1382
+ variant: "outline",
1383
+ size: "md",
1384
+ styles: {
1385
+ root: {
1386
+ borderColor: "var(--border-primary-soft)",
1387
+ backgroundColor: "var(--color-surface)"
1388
+ }
1389
+ },
1390
+ children: [
1391
+ progressNumerator,
1392
+ "/",
1393
+ progressDenominator,
1394
+ " complete"
1395
+ ]
1396
+ }
1397
+ ),
1398
+ milestoneTasks.length > 0 && /* @__PURE__ */ jsxs(
1399
+ Badge,
1400
+ {
1401
+ variant: "outline",
1402
+ size: "md",
1403
+ styles: {
1404
+ root: {
1405
+ borderColor: "var(--border-primary-soft)",
1406
+ backgroundColor: "var(--color-surface)"
1407
+ }
1408
+ },
1409
+ children: [
1410
+ milestoneTasks.length,
1411
+ " tasks"
1412
+ ]
1413
+ }
1414
+ )
1415
+ ] })
1416
+ ] }),
1417
+ /* @__PURE__ */ jsx(MilestoneChecklist, { milestone }),
1418
+ milestoneTasks.length > 0 && /* @__PURE__ */ jsx(Stack, { gap: "sm", children: milestoneTasks.map((task) => /* @__PURE__ */ jsx(
1419
+ Card,
1420
+ {
1421
+ p: "sm",
1422
+ radius: "md",
1423
+ style: {
1424
+ border: "1px solid var(--border-primary-faint)",
1425
+ backgroundColor: "color-mix(in srgb, var(--color-surface-hover) 70%, var(--color-surface))",
1426
+ boxShadow: "var(--standard-box-shadow)"
1427
+ },
1428
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1429
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", align: "flex-start", wrap: "wrap", children: [
1430
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, c: "var(--color-text)", children: task.name }),
1431
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
1432
+ /* @__PURE__ */ jsx(
1433
+ Badge,
1434
+ {
1435
+ variant: "light",
1436
+ color: taskTypeColors[task.type || ""] || "gray",
1437
+ size: "xs",
1438
+ children: formatStatusLabel(task.type || "other")
1439
+ }
1440
+ ),
1441
+ /* @__PURE__ */ jsx(
1442
+ Badge,
1443
+ {
1444
+ variant: "light",
1445
+ color: taskStatusColors[task.status || ""] || "gray",
1446
+ size: "xs",
1447
+ children: formatStatusLabel(task.status || "pending")
1448
+ }
1449
+ )
1450
+ ] })
1451
+ ] }),
1452
+ /* @__PURE__ */ jsx(ResumeContextView, { task })
1453
+ ] })
1454
+ },
1455
+ task.id
1456
+ )) })
1457
+ ] })
1458
+ }
1459
+ )
1460
+ },
1461
+ milestone.id
1462
+ );
1463
+ })
1464
+ }
1465
+ ),
1466
+ (() => {
1467
+ const unassignedTasks = tasks.filter((task) => task.milestone_id == null);
1468
+ if (unassignedTasks.length === 0) return null;
1469
+ return /* @__PURE__ */ jsx(
1470
+ Paper,
1471
+ {
1472
+ p: "lg",
1473
+ radius: "md",
1474
+ mt: "xl",
1475
+ style: {
1476
+ border: "1px solid var(--color-border)",
1477
+ borderRadius: "var(--mantine-radius-default)"
1478
+ },
1479
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1480
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
1481
+ /* @__PURE__ */ jsx(Text, { fw: 700, size: "lg", c: "var(--color-text)", children: "Unassigned" }),
1482
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: "gray", size: "sm", styles: { root: { letterSpacing: "0.02em" } }, children: "No milestone" }),
1483
+ /* @__PURE__ */ jsxs(
1484
+ Badge,
1485
+ {
1486
+ variant: "outline",
1487
+ size: "md",
1488
+ styles: {
1489
+ root: {
1490
+ borderColor: "var(--border-primary-soft)",
1491
+ backgroundColor: "var(--color-surface)"
1492
+ }
1493
+ },
1494
+ children: [
1495
+ unassignedTasks.length,
1496
+ " tasks"
1497
+ ]
1498
+ }
1499
+ )
1500
+ ] }),
1501
+ /* @__PURE__ */ jsx(Stack, { gap: "sm", children: unassignedTasks.map((task) => /* @__PURE__ */ jsx(
1502
+ Card,
1503
+ {
1504
+ p: "sm",
1505
+ radius: "md",
1506
+ style: {
1507
+ border: "1px solid var(--border-primary-faint)",
1508
+ backgroundColor: "color-mix(in srgb, var(--color-surface-hover) 70%, var(--color-surface))",
1509
+ boxShadow: "var(--standard-box-shadow)"
1510
+ },
1511
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1512
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", align: "flex-start", wrap: "wrap", children: [
1513
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, c: "var(--color-text)", children: task.name }),
1514
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
1515
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: taskTypeColors[task.type || ""] || "gray", size: "xs", children: formatStatusLabel(task.type || "other") }),
1516
+ /* @__PURE__ */ jsx(
1517
+ Badge,
1518
+ {
1519
+ variant: "light",
1520
+ color: taskStatusColors[task.status || ""] || "gray",
1521
+ size: "xs",
1522
+ children: formatStatusLabel(task.status || "pending")
1523
+ }
1524
+ )
1525
+ ] })
1526
+ ] }),
1527
+ /* @__PURE__ */ jsx(ResumeContextView, { task })
1528
+ ] })
1529
+ },
1530
+ task.id
1531
+ )) })
1532
+ ] })
1533
+ }
1534
+ );
1535
+ })()
1536
+ ] }),
1537
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "notes", pt: "md", children: /* @__PURE__ */ jsx(NotesTabContent, { notes, onAddNote: () => setAddNoteOpen(true) }) }),
1538
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(ProjectActivityTimeline, { projectId }) })
1539
+ ] })
1540
+ ]
1541
+ }
1542
+ ),
1543
+ /* @__PURE__ */ jsx(AddNoteModal, { opened: addNoteOpen, onClose: () => setAddNoteOpen(false), projectId }),
1544
+ /* @__PURE__ */ jsx(LogBlockerModal, { opened: logBlockerOpen, onClose: closeLogBlocker, projectId }),
1545
+ /* @__PURE__ */ jsx(
1546
+ CreateDeliveryEntityModal,
1547
+ {
1548
+ opened: createTaskOpen,
1549
+ onClose: closeCreateTask,
1550
+ projectId,
1551
+ entityType: "task"
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ jsx(
1555
+ CreateDeliveryEntityModal,
1556
+ {
1557
+ opened: addMilestoneOpen,
1558
+ onClose: closeAddMilestone,
1559
+ projectId,
1560
+ entityType: "milestone"
1561
+ }
1562
+ ),
1563
+ /* @__PURE__ */ jsx(CustomModal, { opened: deleteModalOpen, onClose: () => setDeleteModalOpen(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1564
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
1565
+ /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete project" }),
1566
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", onClick: () => setDeleteModalOpen(false), children: /* @__PURE__ */ jsx(IconX, { size: 18 }) })
1567
+ ] }),
1568
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
1569
+ "Are you sure you want to delete ",
1570
+ /* @__PURE__ */ jsx("strong", { children: project.name }),
1571
+ "? This cannot be undone."
1572
+ ] }),
1573
+ /* @__PURE__ */ jsx(Divider, {}),
1574
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
1575
+ /* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => setDeleteModalOpen(false), disabled: isDeleting, children: "Cancel" }),
1576
+ /* @__PURE__ */ jsx(
1577
+ Button,
1578
+ {
1579
+ color: "red",
1580
+ loading: isDeleting,
1581
+ onClick: () => deleteProject(project.id, {
1582
+ onSuccess: () => {
1583
+ setDeleteModalOpen(false);
1584
+ onBack();
1585
+ }
1586
+ }),
1587
+ children: "Delete project"
1588
+ }
1589
+ )
1590
+ ] })
1591
+ ] }) })
1592
+ ] }) }) });
1593
+ }
1594
+
1595
+ export { AllTasksPage, Checklist, CreateDeliveryEntityModal, DELIVERY_COMMUNICATION_ITEMS, DELIVERY_PROJECT_ITEMS, DELIVERY_WORK_ITEMS, HealthStatusCard, MilestoneTimeline, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, TaskCard, UpcomingMilestonesPage };