@elevasis/ui 2.10.1 → 2.11.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 (43) hide show
  1. package/dist/app/index.js +2 -2
  2. package/dist/{chunk-TSX4I3NW.js → chunk-23PZ57GB.js} +1 -1
  3. package/dist/{chunk-6PNHW4X2.js → chunk-24UMQV5B.js} +51 -95
  4. package/dist/chunk-3ZMAGTWF.js +18 -0
  5. package/dist/{chunk-WHQXDETX.js → chunk-AQDBRRZD.js} +124 -5
  6. package/dist/{chunk-CLXMNMIS.js → chunk-BDENEI4Q.js} +536 -39
  7. package/dist/{chunk-GJVGV7QZ.js → chunk-BLQLWIOW.js} +276 -5
  8. package/dist/chunk-BRXELOHC.js +47 -0
  9. package/dist/{chunk-YQLE5HR5.js → chunk-BSCTPKXM.js} +2 -2
  10. package/dist/{chunk-N5CLIRBS.js → chunk-C7BX547M.js} +4 -4
  11. package/dist/{chunk-LPSBID5V.js → chunk-DJBORKTR.js} +1 -1
  12. package/dist/{chunk-AEBKE4IX.js → chunk-DKQQK3WX.js} +1 -1
  13. package/dist/{chunk-E3IFHX6A.js → chunk-DOFVHWAP.js} +483 -321
  14. package/dist/{chunk-XA34RETF.js → chunk-GHIPBT5V.js} +1 -14
  15. package/dist/{chunk-KYOF6NYW.js → chunk-KDAOCM66.js} +1 -1
  16. package/dist/{chunk-LW5NKRI7.js → chunk-L34DFR2K.js} +8 -52
  17. package/dist/{chunk-XB4NWSI3.js → chunk-TNOIOBYI.js} +2 -2
  18. package/dist/{chunk-M6ZZ2FW5.js → chunk-XYSMBMAR.js} +3 -3
  19. package/dist/components/index.d.ts +69 -3
  20. package/dist/components/index.js +22 -20
  21. package/dist/execution/index.js +2 -1
  22. package/dist/features/crm/index.d.ts +7 -2
  23. package/dist/features/crm/index.js +8 -6
  24. package/dist/features/dashboard/index.js +9 -7
  25. package/dist/features/delivery/index.d.ts +14 -1
  26. package/dist/features/delivery/index.js +8 -6
  27. package/dist/features/lead-gen/index.js +12 -10
  28. package/dist/features/monitoring/index.js +10 -8
  29. package/dist/features/operations/index.js +11 -9
  30. package/dist/features/settings/index.js +9 -7
  31. package/dist/hooks/index.d.ts +230 -51
  32. package/dist/hooks/index.js +7 -5
  33. package/dist/hooks/published.d.ts +230 -51
  34. package/dist/hooks/published.js +7 -5
  35. package/dist/index.d.ts +191 -22
  36. package/dist/index.js +9 -7
  37. package/dist/provider/index.js +4 -4
  38. package/dist/provider/published.js +1 -1
  39. package/dist/supabase/index.js +2 -47
  40. package/dist/theme/index.js +2 -2
  41. package/dist/zustand/index.d.ts +0 -4
  42. package/dist/zustand/index.js +0 -10
  43. package/package.json +2 -2
@@ -5,16 +5,125 @@ import { SubshellNavItem } from './chunk-CEWTOKE7.js';
5
5
  import { SubshellSidebarSection } from './chunk-IIMU5YAJ.js';
6
6
  import { FilterBar } from './chunk-PDHTXPSF.js';
7
7
  import { CustomModal } from './chunk-GBMNCNHX.js';
8
- import { useProjects, useDeleteProject, useTableSort, sortData, usePaginationState, useTableSelection, useProject, useProjectNotes, useUpdateMilestone, useCreateNote } from './chunk-E3IFHX6A.js';
8
+ import { useCreateTask, useCreateMilestone, useProjectMilestones, useProjects, useDeleteProject, useTableSort, sortData, usePaginationState, useTableSelection, useProjectRealtime, useProject, useProjectTasks, useProjectNotes, useUpdateMilestone, useProjectActivities, useCreateNote, showApiErrorNotification, projectKeys } from './chunk-DOFVHWAP.js';
9
9
  import { SubshellContentContainer } from './chunk-RX4UWZZR.js';
10
10
  import { StatusBadge, EmptyState, PageTitleCaption, CenteredErrorState, StatCard } from './chunk-SQQGLGHW.js';
11
- import { PAGE_SIZE_DEFAULT, formatTimeAgo, formatDate } from './chunk-IOKL7BKE.js';
11
+ import { PAGE_SIZE_DEFAULT, formatTimeAgo, formatDate, formatRelativeTime } from './chunk-IOKL7BKE.js';
12
+ import { useElevasisServices } from './chunk-QEPXAWE2.js';
12
13
  import { useRouterContext } from './chunk-Q7DJKLEN.js';
13
- import { SimpleGrid, Card, Stack, Group, ThemeIcon, Text, RingProgress, Paper, Timeline, Badge, Collapse, ActionIcon, Select, TextInput, Center, Loader, Table, Checkbox, Pagination, Title, Button, Alert, CopyButton, Tooltip, Divider, Tabs, Textarea } from '@mantine/core';
14
- import { IconNotes, IconBriefcase, IconChecklist, IconFlag, IconHeartbeat, IconFileText, IconCalendar, IconInbox, IconLock, IconAlertTriangle, IconCircleCheck, IconClock, IconChevronDown, IconChevronRight, IconDownload, IconListCheck, IconMessageCircle, IconSearch, IconArrowLeft, IconCheck, IconCopy, IconPlus, IconX } from '@tabler/icons-react';
15
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
16
- import { useState, useMemo } from 'react';
14
+ import { useState, useMemo, useCallback } from 'react';
15
+ import { Modal, Stack, Title, TextInput, Textarea, Select, Group, Button, SimpleGrid, Card, ThemeIcon, Text, RingProgress, Paper, Timeline, Badge, Collapse, ActionIcon, Center, Loader, Table, Checkbox, Pagination, Alert, CopyButton, Tooltip, Divider, Tabs, Anchor, Chip } from '@mantine/core';
16
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
+ import { IconNotes, IconBriefcase, IconChecklist, IconFlag, IconHeartbeat, IconFileText, IconCalendar, IconInbox, IconLock, IconAlertTriangle, IconCircleCheck, IconClock, IconChevronDown, IconChevronRight, IconDownload, IconListCheck, IconMessageCircle, IconSearch, IconArrowLeft, IconCheck, IconCopy, IconX, IconPlus, IconTerminal2, IconCurrencyDollar, IconBuilding } from '@tabler/icons-react';
18
+ import { useDisclosure, useClipboard } from '@mantine/hooks';
19
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
17
20
 
21
+ var TASK_TYPE_OPTIONS = [
22
+ { value: "documentation", label: "Documentation" },
23
+ { value: "code", label: "Code" },
24
+ { value: "report", label: "Report" },
25
+ { value: "design", label: "Design" },
26
+ { value: "other", label: "Other" }
27
+ ];
28
+ function CreateDeliveryEntityModal({ opened, onClose, projectId, entityType }) {
29
+ const [name, setName] = useState("");
30
+ const [description, setDescription] = useState("");
31
+ const [taskType, setTaskType] = useState("other");
32
+ const [milestoneId, setMilestoneId] = useState(null);
33
+ const { mutate: createTask, isPending: isCreatingTask } = useCreateTask();
34
+ const { mutate: createMilestone, isPending: isCreatingMilestone } = useCreateMilestone();
35
+ const { data: milestones = [] } = useProjectMilestones(entityType === "task" ? projectId : "");
36
+ const isPending = entityType === "task" ? isCreatingTask : isCreatingMilestone;
37
+ const milestoneOptions = milestones.map((m) => ({ value: m.id, label: m.name }));
38
+ function resetForm() {
39
+ setName("");
40
+ setDescription("");
41
+ setTaskType("other");
42
+ setMilestoneId(null);
43
+ }
44
+ function handleClose() {
45
+ resetForm();
46
+ onClose();
47
+ }
48
+ function handleSubmit() {
49
+ const trimmedName = name.trim();
50
+ if (!trimmedName) return;
51
+ if (entityType === "task") {
52
+ createTask(
53
+ {
54
+ project_id: projectId,
55
+ name: trimmedName,
56
+ description: description.trim() || null,
57
+ type: taskType ?? "other",
58
+ milestone_id: milestoneId ?? null
59
+ },
60
+ {
61
+ onSuccess: () => {
62
+ resetForm();
63
+ onClose();
64
+ }
65
+ }
66
+ );
67
+ } else {
68
+ createMilestone(
69
+ {
70
+ project_id: projectId,
71
+ name: trimmedName,
72
+ description: description.trim() || null
73
+ },
74
+ {
75
+ onSuccess: () => {
76
+ resetForm();
77
+ onClose();
78
+ }
79
+ }
80
+ );
81
+ }
82
+ }
83
+ const title = entityType === "task" ? "Create Task" : "Add Milestone";
84
+ return /* @__PURE__ */ jsx(Modal, { opened, onClose: handleClose, size: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
85
+ /* @__PURE__ */ jsx(Title, { order: 3, children: title }),
86
+ /* @__PURE__ */ jsx(
87
+ TextInput,
88
+ {
89
+ label: "Name",
90
+ placeholder: entityType === "task" ? "Task name..." : "Milestone name...",
91
+ value: name,
92
+ onChange: (e) => setName(e.target.value),
93
+ required: true,
94
+ "data-autofocus": true
95
+ }
96
+ ),
97
+ /* @__PURE__ */ jsx(
98
+ Textarea,
99
+ {
100
+ label: "Description",
101
+ placeholder: "Optional description...",
102
+ minRows: 3,
103
+ value: description,
104
+ onChange: (e) => setDescription(e.target.value)
105
+ }
106
+ ),
107
+ entityType === "task" && /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ /* @__PURE__ */ jsx(Select, { label: "Type", data: TASK_TYPE_OPTIONS, value: taskType, onChange: setTaskType }),
109
+ milestoneOptions.length > 0 && /* @__PURE__ */ jsx(
110
+ Select,
111
+ {
112
+ label: "Milestone",
113
+ placeholder: "Assign to milestone (optional)",
114
+ data: milestoneOptions,
115
+ value: milestoneId,
116
+ onChange: setMilestoneId,
117
+ clearable: true
118
+ }
119
+ )
120
+ ] }),
121
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
122
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isPending, children: "Cancel" }),
123
+ /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: isPending, disabled: !name.trim(), children: title })
124
+ ] })
125
+ ] }) });
126
+ }
18
127
  function ringColor(completed, total) {
19
128
  if (total === 0) return "gray";
20
129
  const pct = completed / total;
@@ -554,6 +663,252 @@ function ProjectsListPage({ onProjectClick } = {}) {
554
663
  )
555
664
  ] });
556
665
  }
666
+ function isResumeContext(val) {
667
+ return !!val && typeof val === "object" && !Array.isArray(val);
668
+ }
669
+ function ResumeContextPreview({ tasks }) {
670
+ 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];
671
+ if (!candidate) return null;
672
+ const ctx = candidate.resume_context;
673
+ return /* @__PURE__ */ jsx(
674
+ Paper,
675
+ {
676
+ p: "sm",
677
+ style: {
678
+ border: "1px solid var(--color-border)",
679
+ borderRadius: "var(--mantine-radius-default)",
680
+ backgroundColor: "color-mix(in srgb, var(--color-primary) 5%, var(--color-surface))"
681
+ },
682
+ children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
683
+ /* @__PURE__ */ jsx(Group, { gap: "xs", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
684
+ "Last worked",
685
+ " ",
686
+ /* @__PURE__ */ jsx(Text, { component: "span", size: "xs", fw: 500, c: "var(--color-text)", children: formatRelativeTime(candidate.updated_at) }),
687
+ " ",
688
+ "on",
689
+ " ",
690
+ /* @__PURE__ */ jsx(Text, { component: "span", size: "xs", fw: 500, c: "var(--color-text)", children: candidate.name })
691
+ ] }) }),
692
+ ctx.current_state && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
693
+ "Current state:",
694
+ " ",
695
+ /* @__PURE__ */ jsx(Text, { component: "span", size: "xs", c: "var(--color-text-subtle)", children: ctx.current_state })
696
+ ] }),
697
+ ctx.next_steps && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
698
+ "Next:",
699
+ " ",
700
+ /* @__PURE__ */ jsx(Text, { component: "span", size: "xs", c: "var(--color-text-subtle)", children: ctx.next_steps })
701
+ ] })
702
+ ] })
703
+ }
704
+ );
705
+ }
706
+ var buildBootstrapCommand = (projectId) => `/project work ${projectId}`;
707
+ function QuickActionsRow({
708
+ onCreateTask,
709
+ onAddMilestone,
710
+ onLogBlocker,
711
+ onResumeBootstrap
712
+ }) {
713
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
714
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "compact-sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 14 }), onClick: onCreateTask, children: "Create Task" }),
715
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "compact-sm", leftSection: /* @__PURE__ */ jsx(IconFlag, { size: 14 }), onClick: onAddMilestone, children: "Add Milestone" }),
716
+ /* @__PURE__ */ jsx(
717
+ Button,
718
+ {
719
+ variant: "light",
720
+ size: "compact-sm",
721
+ color: "orange",
722
+ leftSection: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 14 }),
723
+ onClick: onLogBlocker,
724
+ children: "Log Blocker"
725
+ }
726
+ ),
727
+ /* @__PURE__ */ jsx(Tooltip, { label: "Copies bootstrap command to clipboard", children: /* @__PURE__ */ jsx(
728
+ Button,
729
+ {
730
+ variant: "light",
731
+ size: "compact-sm",
732
+ color: "grape",
733
+ leftSection: /* @__PURE__ */ jsx(IconTerminal2, { size: 14 }),
734
+ onClick: onResumeBootstrap,
735
+ children: "Resume Bootstrap"
736
+ }
737
+ ) })
738
+ ] });
739
+ }
740
+ function LinkedRecordChips({ dealId, clientCompanyId }) {
741
+ if (!dealId && !clientCompanyId) return null;
742
+ return /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "wrap", children: [
743
+ dealId && /* @__PURE__ */ jsx(Anchor, { href: `/crm/deals/${dealId}`, underline: "never", children: /* @__PURE__ */ jsx(
744
+ Badge,
745
+ {
746
+ variant: "light",
747
+ color: "blue",
748
+ leftSection: /* @__PURE__ */ jsx(IconCurrencyDollar, { size: 12 }),
749
+ style: { cursor: "pointer" },
750
+ children: "Deal"
751
+ }
752
+ ) }),
753
+ 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" }) })
754
+ ] });
755
+ }
756
+ function ProjectActivityTimeline({ projectId }) {
757
+ const { data: activities, isLoading, error } = useProjectActivities(projectId);
758
+ if (isLoading) {
759
+ return /* @__PURE__ */ jsx(Center, { py: "xl", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) });
760
+ }
761
+ if (error) {
762
+ return /* @__PURE__ */ jsx(Alert, { color: "red", title: "Failed to load activity", children: "Could not fetch project activity. Please refresh." });
763
+ }
764
+ if (!activities?.length) {
765
+ return /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No activity recorded for this project yet." });
766
+ }
767
+ return /* @__PURE__ */ jsx(Timeline, { bulletSize: 18, lineWidth: 2, children: activities.map((activity) => /* @__PURE__ */ jsxs(
768
+ Timeline.Item,
769
+ {
770
+ title: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: activity.title }),
771
+ children: [
772
+ activity.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mt: 2, children: activity.description }),
773
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mt: 2, children: formatTimeAgo(activity.occurredAt) })
774
+ ]
775
+ },
776
+ activity.id
777
+ )) });
778
+ }
779
+ var NOTE_FILTER_OPTIONS = [
780
+ { value: "call_note", label: "Call Notes" },
781
+ { value: "status_update", label: "Status Updates" },
782
+ { value: "issue", label: "Issues" },
783
+ { value: "blocker", label: "Blockers" }
784
+ ];
785
+ function isWithinLastWeek(dateStr) {
786
+ if (!dateStr) return false;
787
+ const date = new Date(dateStr);
788
+ const cutoff = /* @__PURE__ */ new Date();
789
+ cutoff.setDate(cutoff.getDate() - 7);
790
+ return date >= cutoff;
791
+ }
792
+ function NotesTabContent({ notes, onAddNote }) {
793
+ const [activeFilter, setActiveFilter] = useState(null);
794
+ const allNotes = notes ?? [];
795
+ const thisWeek = allNotes.filter((n) => isWithinLastWeek(n.occurred_at));
796
+ const weeklyByType = NOTE_FILTER_OPTIONS.map(({ value, label }) => ({
797
+ value,
798
+ label,
799
+ count: thisWeek.filter((n) => n.type === value).length
800
+ })).filter((entry) => entry.count > 0);
801
+ const rollupText = weeklyByType.length > 0 ? weeklyByType.map((e) => `${e.count} ${e.label.toLowerCase()}`).join(", ") + " this week" : null;
802
+ const visibleNotes = activeFilter ? allNotes.filter((n) => n.type === activeFilter) : allNotes;
803
+ return /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
804
+ rollupText && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: rollupText }),
805
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", wrap: "wrap", children: [
806
+ /* @__PURE__ */ jsx(Chip.Group, { value: activeFilter ?? "", onChange: (val) => setActiveFilter(val || null), children: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
807
+ /* @__PURE__ */ jsx(Chip, { value: "", size: "xs", variant: "light", children: "All" }),
808
+ NOTE_FILTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx(Chip, { value, size: "xs", variant: "light", children: label }, value))
809
+ ] }) }),
810
+ /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: onAddNote, children: "Add Note" })
811
+ ] }),
812
+ !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: [
813
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
814
+ /* @__PURE__ */ jsx(Badge, { variant: "light", color: noteTypeColors[note.type ?? ""] ?? "gray", size: "sm", children: formatStatusLabel(note.type ?? "note") }),
815
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatRelativeTime(note.occurred_at) })
816
+ ] }),
817
+ note.summary && /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: note.summary }),
818
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: note.content })
819
+ ] }) }, note.id)) })
820
+ ] });
821
+ }
822
+ function isResumeContext2(val) {
823
+ return !!val && typeof val === "object" && !Array.isArray(val);
824
+ }
825
+ function extractContext(task) {
826
+ if (isResumeContext2(task.resume_context)) return task.resume_context;
827
+ return {};
828
+ }
829
+ function useMergeResumeContext() {
830
+ const { apiRequest } = useElevasisServices();
831
+ const queryClient = useQueryClient();
832
+ return useMutation({
833
+ mutationFn: async ({ taskId, patch }) => {
834
+ const res = await apiRequest(`/project-tasks/${taskId}/resume-context`, {
835
+ method: "PATCH",
836
+ body: JSON.stringify(patch)
837
+ });
838
+ return res.task;
839
+ },
840
+ onSuccess: (_data, variables) => {
841
+ queryClient.invalidateQueries({ queryKey: projectKeys.tasks(variables.projectId) });
842
+ },
843
+ onError: (error) => {
844
+ showApiErrorNotification(error);
845
+ }
846
+ });
847
+ }
848
+ function InlineResumeContextEditor({ task, projectId }) {
849
+ const [expanded, setExpanded] = useState(false);
850
+ const ctx = extractContext(task);
851
+ const [currentState, setCurrentState] = useState(ctx.current_state ?? "");
852
+ const [nextSteps, setNextSteps] = useState(ctx.next_steps ?? "");
853
+ const [filesModified, setFilesModified] = useState(typeof ctx.files_modified === "string" ? ctx.files_modified : "");
854
+ const { mutate: mergeContext, isPending } = useMergeResumeContext();
855
+ const save = useCallback(
856
+ (patch) => {
857
+ const nonEmpty = Object.fromEntries(Object.entries(patch).filter(([, v]) => v !== void 0));
858
+ if (Object.keys(nonEmpty).length === 0) return;
859
+ mergeContext({ taskId: task.id, projectId, patch: nonEmpty });
860
+ },
861
+ [mergeContext, task.id, projectId]
862
+ );
863
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
864
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", style: { cursor: "pointer", userSelect: "none" }, onClick: () => setExpanded((v) => !v), children: [
865
+ /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "xs", tabIndex: -1, children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 12 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 12 }) }),
866
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Resume context" }),
867
+ isPending && /* @__PURE__ */ jsx(Loader, { size: 10 })
868
+ ] }),
869
+ /* @__PURE__ */ jsx(Collapse, { in: expanded, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", pl: "md", pt: 4, children: [
870
+ /* @__PURE__ */ jsx(
871
+ Textarea,
872
+ {
873
+ label: "Current state",
874
+ placeholder: "What is the task's current state?",
875
+ size: "xs",
876
+ minRows: 2,
877
+ autosize: true,
878
+ value: currentState,
879
+ onChange: (e) => setCurrentState(e.target.value),
880
+ onBlur: () => save({ current_state: currentState || void 0 })
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsx(
884
+ Textarea,
885
+ {
886
+ label: "Next steps",
887
+ placeholder: "What should happen next?",
888
+ size: "xs",
889
+ minRows: 2,
890
+ autosize: true,
891
+ value: nextSteps,
892
+ onChange: (e) => setNextSteps(e.target.value),
893
+ onBlur: () => save({ next_steps: nextSteps || void 0 })
894
+ }
895
+ ),
896
+ /* @__PURE__ */ jsx(
897
+ Textarea,
898
+ {
899
+ label: "Files modified (one per line)",
900
+ placeholder: "src/foo.ts\nsrc/bar.ts",
901
+ size: "xs",
902
+ minRows: 2,
903
+ autosize: true,
904
+ value: filesModified,
905
+ onChange: (e) => setFilesModified(e.target.value),
906
+ onBlur: () => save({ files_modified: filesModified || void 0 })
907
+ }
908
+ )
909
+ ] }) })
910
+ ] });
911
+ }
557
912
  var noteTypeOptions = [
558
913
  { value: "call_note", label: "Call Note" },
559
914
  { value: "status_update", label: "Status Update" },
@@ -652,7 +1007,16 @@ function MilestoneChecklist({ milestone }) {
652
1007
  autoFocus: true,
653
1008
  style: { maxWidth: 250 }
654
1009
  }
655
- ) : /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(Button, { variant: "subtle", size: "compact-xs", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 12 }), onClick: () => setAdding(true), children: "Add item" }) })
1010
+ ) : /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(
1011
+ Button,
1012
+ {
1013
+ variant: "subtle",
1014
+ size: "compact-xs",
1015
+ leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 12 }),
1016
+ onClick: () => setAdding(true),
1017
+ children: "Add item"
1018
+ }
1019
+ ) })
656
1020
  ] });
657
1021
  }
658
1022
  function AddNoteModal({ opened, onClose, projectId }) {
@@ -711,12 +1075,69 @@ function AddNoteModal({ opened, onClose, projectId }) {
711
1075
  ] })
712
1076
  ] }) });
713
1077
  }
1078
+ function LogBlockerModal({ opened, onClose, projectId }) {
1079
+ const [content, setContent] = useState("");
1080
+ const [summary, setSummary] = useState("");
1081
+ const { mutate: createNote, isPending } = useCreateNote();
1082
+ function handleSubmit() {
1083
+ if (!content.trim()) return;
1084
+ createNote(
1085
+ {
1086
+ project_id: projectId,
1087
+ type: "blocker",
1088
+ content: content.trim(),
1089
+ summary: summary.trim() || null
1090
+ },
1091
+ {
1092
+ onSuccess: () => {
1093
+ setContent("");
1094
+ setSummary("");
1095
+ onClose();
1096
+ }
1097
+ }
1098
+ );
1099
+ }
1100
+ return /* @__PURE__ */ jsx(Modal, { opened, onClose, size: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1101
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Log Blocker" }),
1102
+ /* @__PURE__ */ jsx(
1103
+ TextInput,
1104
+ {
1105
+ label: "Summary",
1106
+ placeholder: "Brief blocker summary (optional)",
1107
+ value: summary,
1108
+ onChange: (e) => setSummary(e.target.value)
1109
+ }
1110
+ ),
1111
+ /* @__PURE__ */ jsx(
1112
+ Textarea,
1113
+ {
1114
+ label: "Description",
1115
+ placeholder: "Describe the blocker...",
1116
+ minRows: 4,
1117
+ value: content,
1118
+ onChange: (e) => setContent(e.target.value),
1119
+ required: true
1120
+ }
1121
+ ),
1122
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
1123
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: onClose, disabled: isPending, children: "Cancel" }),
1124
+ /* @__PURE__ */ jsx(Button, { color: "orange", onClick: handleSubmit, loading: isPending, disabled: !content.trim(), children: "Log Blocker" })
1125
+ ] })
1126
+ ] }) });
1127
+ }
714
1128
  function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
1129
+ useProjectRealtime(projectId);
715
1130
  const { data: project, isLoading, error } = useProject(projectId);
1131
+ const { data: milestones = [] } = useProjectMilestones(projectId);
1132
+ const { data: tasks = [] } = useProjectTasks(projectId);
716
1133
  const { data: notes } = useProjectNotes({ projectId });
717
1134
  const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject();
718
1135
  const [addNoteOpen, setAddNoteOpen] = useState(false);
719
1136
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
1137
+ const [logBlockerOpen, { open: openLogBlocker, close: closeLogBlocker }] = useDisclosure(false);
1138
+ const [createTaskOpen, { open: openCreateTask, close: closeCreateTask }] = useDisclosure(false);
1139
+ const [addMilestoneOpen, { open: openAddMilestone, close: closeAddMilestone }] = useDisclosure(false);
1140
+ const clipboard = useClipboard({ timeout: 2e3 });
720
1141
  if (isLoading) {
721
1142
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Loader, {}) }) });
722
1143
  }
@@ -726,8 +1147,6 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
726
1147
  if (!project) {
727
1148
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(Alert, { color: "yellow", children: "Project not found" }) }) });
728
1149
  }
729
- const milestones = project.milestones ?? [];
730
- const tasks = project.tasks ?? [];
731
1150
  const totalMilestones = milestones.length;
732
1151
  const completedMilestones = milestones.filter((milestone) => milestone.status === "completed").length;
733
1152
  const totalTasks = tasks.length;
@@ -735,6 +1154,8 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
735
1154
  const milestoneProgress = calculateProgress(completedMilestones, totalMilestones);
736
1155
  const taskProgress = calculateProgress(approvedTasks, totalTasks);
737
1156
  const companyName = project.company?.name ?? null;
1157
+ const isEmpty = milestones.length === 0 && tasks.length === 0 && !notes?.length;
1158
+ const defaultTab = isEmpty ? "details" : "status";
738
1159
  return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(PageContainer, { children: /* @__PURE__ */ jsxs(Stack, { children: [
739
1160
  /* @__PURE__ */ jsx(
740
1161
  PageTitleCaption,
@@ -744,6 +1165,16 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
744
1165
  rightSection: /* @__PURE__ */ jsx(Group, { children: /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }), onClick: onBack, children: backLabel }) })
745
1166
  }
746
1167
  ),
1168
+ /* @__PURE__ */ jsx(
1169
+ QuickActionsRow,
1170
+ {
1171
+ projectId,
1172
+ onCreateTask: openCreateTask,
1173
+ onAddMilestone: openAddMilestone,
1174
+ onLogBlocker: openLogBlocker,
1175
+ onResumeBootstrap: () => clipboard.copy(buildBootstrapCommand(projectId))
1176
+ }
1177
+ ),
747
1178
  /* @__PURE__ */ jsxs(SimpleGrid, { cols: 4, children: [
748
1179
  /* @__PURE__ */ jsx(
749
1180
  StatCard,
@@ -755,19 +1186,38 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
755
1186
  valueColor: `var(--mantine-color-${projectStatusColors[project.status || ""] || "gray"}-6)`
756
1187
  }
757
1188
  ),
758
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconFlag, value: `${completedMilestones}/${totalMilestones}`, label: "Milestones", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
759
- milestoneProgress,
760
- "% complete"
761
- ] }) }),
1189
+ /* @__PURE__ */ jsx(
1190
+ StatCard,
1191
+ {
1192
+ variant: "hero",
1193
+ icon: IconFlag,
1194
+ value: `${completedMilestones}/${totalMilestones}`,
1195
+ label: "Milestones",
1196
+ children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1197
+ milestoneProgress,
1198
+ "% complete"
1199
+ ] })
1200
+ }
1201
+ ),
762
1202
  /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconChecklist, value: `${approvedTasks}/${totalTasks}`, label: "Tasks", children: /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
763
1203
  taskProgress,
764
1204
  "% complete"
765
1205
  ] }) }),
766
- /* @__PURE__ */ jsx(StatCard, { variant: "hero", icon: IconCalendar, value: project.start_date ? formatDate(project.start_date) : "-", label: "Timeline", children: project.target_end_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
767
- "to ",
768
- formatDate(project.target_end_date)
769
- ] }) })
1206
+ /* @__PURE__ */ jsx(
1207
+ StatCard,
1208
+ {
1209
+ variant: "hero",
1210
+ icon: IconCalendar,
1211
+ value: project.start_date ? formatDate(project.start_date) : "-",
1212
+ label: "Timeline",
1213
+ children: project.target_end_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
1214
+ "to ",
1215
+ formatDate(project.target_end_date)
1216
+ ] })
1217
+ }
1218
+ )
770
1219
  ] }),
1220
+ /* @__PURE__ */ jsx(ResumeContextPreview, { tasks }),
771
1221
  /* @__PURE__ */ jsxs(
772
1222
  Paper,
773
1223
  {
@@ -784,8 +1234,18 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
784
1234
  /* @__PURE__ */ jsx(Badge, { variant: "light", color: projectStatusColors[project.status || ""] || "gray", children: formatStatusLabel(project.status || "unknown") })
785
1235
  ] }),
786
1236
  /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
1237
+ /* @__PURE__ */ jsx(LinkedRecordChips, { dealId: project.deal_id, clientCompanyId: project.client_company_id }),
787
1238
  /* @__PURE__ */ jsx(Text, { size: "sm", ff: "monospace", c: "var(--color-text-subtle)", children: project.id }),
788
- /* @__PURE__ */ jsx(CopyButton, { value: project.id, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx(ActionIcon, { onClick: copy, variant: "subtle", size: "xs", color: copied ? "green" : "var(--color-text-subtle)", children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 }) }) }) })
1239
+ /* @__PURE__ */ jsx(CopyButton, { value: `/project work ${project.id}`, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied!" : "Copy ID", withArrow: true, children: /* @__PURE__ */ jsx(
1240
+ ActionIcon,
1241
+ {
1242
+ onClick: copy,
1243
+ variant: "subtle",
1244
+ size: "xs",
1245
+ color: copied ? "green" : "var(--color-text-subtle)",
1246
+ children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 })
1247
+ }
1248
+ ) }) })
789
1249
  ] })
790
1250
  ] }),
791
1251
  /* @__PURE__ */ jsxs(Group, { gap: "lg", children: [
@@ -812,7 +1272,7 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
812
1272
  ] })
813
1273
  ] }),
814
1274
  /* @__PURE__ */ jsx(Divider, { my: "md" }),
815
- /* @__PURE__ */ jsxs(Tabs, { defaultValue: "status", children: [
1275
+ /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultTab, children: [
816
1276
  /* @__PURE__ */ jsxs(Tabs.List, { children: [
817
1277
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "status", children: "Status" }),
818
1278
  /* @__PURE__ */ jsx(Tabs.Tab, { value: "details", children: "Details" }),
@@ -856,7 +1316,15 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
856
1316
  ) : void 0,
857
1317
  title: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
858
1318
  /* @__PURE__ */ jsx(Text, { fw: 500, children: milestone.name }),
859
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: milestoneStatusColors[milestone.status || ""] || "gray", size: "sm", children: formatStatusLabel(milestone.status || "unknown") }),
1319
+ /* @__PURE__ */ jsx(
1320
+ Badge,
1321
+ {
1322
+ variant: "light",
1323
+ color: milestoneStatusColors[milestone.status || ""] || "gray",
1324
+ size: "sm",
1325
+ children: formatStatusLabel(milestone.status || "unknown")
1326
+ }
1327
+ ),
860
1328
  milestone.due_date && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
861
1329
  "Due ",
862
1330
  formatDate(milestone.due_date)
@@ -865,12 +1333,31 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
865
1333
  children: [
866
1334
  milestone.description && /* @__PURE__ */ jsx(Text, { size: "sm", mt: "xs", children: milestone.description }),
867
1335
  /* @__PURE__ */ jsx(MilestoneChecklist, { milestone }),
868
- milestoneTasks.length > 0 && /* @__PURE__ */ jsx(Stack, { gap: "xs", mt: "sm", children: milestoneTasks.map((task) => /* @__PURE__ */ jsx(Card, { withBorder: true, p: "xs", children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", children: [
869
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: task.name }),
870
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
871
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: taskTypeColors[task.type || ""] || "gray", size: "xs", children: formatStatusLabel(task.type || "other") }),
872
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: taskStatusColors[task.status || ""] || "gray", size: "xs", children: formatStatusLabel(task.status || "pending") })
873
- ] })
1336
+ milestoneTasks.length > 0 && /* @__PURE__ */ jsx(Stack, { gap: "xs", mt: "sm", children: milestoneTasks.map((task) => /* @__PURE__ */ jsx(Card, { withBorder: true, p: "xs", children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
1337
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "space-between", children: [
1338
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: task.name }),
1339
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
1340
+ /* @__PURE__ */ jsx(
1341
+ Badge,
1342
+ {
1343
+ variant: "light",
1344
+ color: taskTypeColors[task.type || ""] || "gray",
1345
+ size: "xs",
1346
+ children: formatStatusLabel(task.type || "other")
1347
+ }
1348
+ ),
1349
+ /* @__PURE__ */ jsx(
1350
+ Badge,
1351
+ {
1352
+ variant: "light",
1353
+ color: taskStatusColors[task.status || ""] || "gray",
1354
+ size: "xs",
1355
+ children: formatStatusLabel(task.status || "pending")
1356
+ }
1357
+ )
1358
+ ] })
1359
+ ] }),
1360
+ /* @__PURE__ */ jsx(InlineResumeContextEditor, { task, projectId })
874
1361
  ] }) }, task.id)) })
875
1362
  ]
876
1363
  },
@@ -880,6 +1367,7 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
880
1367
  }
881
1368
  ) }),
882
1369
  /* @__PURE__ */ jsx(Tabs.Panel, { value: "details", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
1370
+ isEmpty && /* @__PURE__ */ jsx(Alert, { color: "blue", title: "Get started", children: "This project has no milestones, tasks, or notes yet. Add a milestone or create a task to begin tracking progress." }),
883
1371
  /* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
884
1372
  /* @__PURE__ */ jsx(Title, { order: 3, children: "Project Details" }),
885
1373
  project.description && /* @__PURE__ */ jsxs("div", { children: [
@@ -914,23 +1402,32 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
914
1402
  project.company.domain && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: project.company.domain })
915
1403
  ] }) })
916
1404
  ] }) }),
917
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "notes", pt: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
918
- /* @__PURE__ */ jsx(Group, { justify: "flex-end", children: /* @__PURE__ */ jsx(Button, { variant: "light", size: "sm", leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: () => setAddNoteOpen(true), children: "Add Note" }) }),
919
- !notes?.length ? /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "No notes yet." }) : /* @__PURE__ */ jsx(Stack, { gap: "sm", children: notes.map((note) => /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
920
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
921
- /* @__PURE__ */ jsx(Badge, { variant: "light", color: noteTypeColors[note.type || ""] || "gray", size: "sm", children: formatStatusLabel(note.type || "note") }),
922
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatTimeAgo(note.occurred_at) })
923
- ] }),
924
- note.summary && /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: note.summary }),
925
- /* @__PURE__ */ jsx(Text, { size: "sm", children: note.content })
926
- ] }) }, note.id)) })
927
- ] }) }),
928
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(Text, { c: "dimmed", size: "sm", children: "Activity feed coming soon." }) })
1405
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "notes", pt: "md", children: /* @__PURE__ */ jsx(NotesTabContent, { notes, onAddNote: () => setAddNoteOpen(true) }) }),
1406
+ /* @__PURE__ */ jsx(Tabs.Panel, { value: "activity", pt: "md", children: /* @__PURE__ */ jsx(ProjectActivityTimeline, { projectId }) })
929
1407
  ] })
930
1408
  ]
931
1409
  }
932
1410
  ),
933
1411
  /* @__PURE__ */ jsx(AddNoteModal, { opened: addNoteOpen, onClose: () => setAddNoteOpen(false), projectId }),
1412
+ /* @__PURE__ */ jsx(LogBlockerModal, { opened: logBlockerOpen, onClose: closeLogBlocker, projectId }),
1413
+ /* @__PURE__ */ jsx(
1414
+ CreateDeliveryEntityModal,
1415
+ {
1416
+ opened: createTaskOpen,
1417
+ onClose: closeCreateTask,
1418
+ projectId,
1419
+ entityType: "task"
1420
+ }
1421
+ ),
1422
+ /* @__PURE__ */ jsx(
1423
+ CreateDeliveryEntityModal,
1424
+ {
1425
+ opened: addMilestoneOpen,
1426
+ onClose: closeAddMilestone,
1427
+ projectId,
1428
+ entityType: "milestone"
1429
+ }
1430
+ ),
934
1431
  /* @__PURE__ */ jsx(CustomModal, { opened: deleteModalOpen, onClose: () => setDeleteModalOpen(false), size: "sm", children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
935
1432
  /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", children: [
936
1433
  /* @__PURE__ */ jsx(Title, { order: 4, children: "Delete project" }),
@@ -963,4 +1460,4 @@ function ProjectDetailPage({ projectId, onBack, backLabel = "Projects" }) {
963
1460
  ] }) }) });
964
1461
  }
965
1462
 
966
- export { AllTasksPage, DELIVERY_COMMUNICATION_ITEMS, DELIVERY_PROJECT_ITEMS, DELIVERY_WORK_ITEMS, HealthStatusCard, MilestoneTimeline, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, TaskCard, UpcomingMilestonesPage, calculateProgress, deliveryManifest, formatStatusLabel, milestoneStatusColors, noteTypeColors, projectStatusColors, taskStatusColors, taskTypeColors };
1463
+ export { AllTasksPage, CreateDeliveryEntityModal, DELIVERY_COMMUNICATION_ITEMS, DELIVERY_PROJECT_ITEMS, DELIVERY_WORK_ITEMS, HealthStatusCard, MilestoneTimeline, ProjectDetailPage, ProjectsListPage, ProjectsSidebar, ProjectsSidebarMiddle, ProjectsSidebarTop, TaskCard, UpcomingMilestonesPage, calculateProgress, deliveryManifest, formatStatusLabel, milestoneStatusColors, noteTypeColors, projectStatusColors, taskStatusColors, taskTypeColors };