@burdenoff/microfe-proserviceworld 2026.706.3 → 2026.709.1

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 (91) hide show
  1. package/dist/dev/main.d.ts +2 -0
  2. package/dist/dev/main.d.ts.map +1 -0
  3. package/dist/generated/global-operations.d.ts +2 -0
  4. package/dist/generated/global-operations.d.ts.map +1 -0
  5. package/dist/generated/global-types.d.ts +18199 -0
  6. package/dist/generated/global-types.d.ts.map +1 -0
  7. package/dist/generated/wspace-operations.d.ts +2 -0
  8. package/dist/generated/wspace-operations.d.ts.map +1 -0
  9. package/dist/generated/wspace-types.d.ts +43046 -0
  10. package/dist/generated/wspace-types.d.ts.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +2 -1
  14. package/dist/proserviceworld/ProServiceWorldRoot.d.ts +5 -0
  15. package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +1 -0
  16. package/dist/proserviceworld/ProServiceWorldRoutes.d.ts +2 -0
  17. package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -0
  18. package/dist/proserviceworld/components/DiscussionThread.d.ts +12 -0
  19. package/dist/proserviceworld/components/DiscussionThread.d.ts.map +1 -0
  20. package/dist/proserviceworld/components/ui.d.ts +82 -0
  21. package/dist/proserviceworld/components/ui.d.ts.map +1 -0
  22. package/dist/proserviceworld/hooks/useAnalytics.d.ts +18 -0
  23. package/dist/proserviceworld/hooks/useAnalytics.d.ts.map +1 -0
  24. package/dist/proserviceworld/hooks/useAnalytics.js +21 -0
  25. package/dist/proserviceworld/hooks/useAnalytics.js.map +1 -0
  26. package/dist/proserviceworld/hooks/useEntityComments.d.ts +27 -0
  27. package/dist/proserviceworld/hooks/useEntityComments.d.ts.map +1 -0
  28. package/dist/proserviceworld/hooks/useFiles.d.ts +23 -0
  29. package/dist/proserviceworld/hooks/useFiles.d.ts.map +1 -0
  30. package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts +351 -0
  31. package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts.map +1 -0
  32. package/dist/proserviceworld/index.d.ts +5 -0
  33. package/dist/proserviceworld/index.d.ts.map +1 -0
  34. package/dist/proserviceworld/index.js +1 -0
  35. package/dist/proserviceworld/pages/AnalyticsPage.d.ts +4 -0
  36. package/dist/proserviceworld/pages/AnalyticsPage.d.ts.map +1 -0
  37. package/dist/proserviceworld/pages/ClientDetailPage.d.ts +4 -0
  38. package/dist/proserviceworld/pages/ClientDetailPage.d.ts.map +1 -0
  39. package/dist/proserviceworld/pages/ClientsPage.d.ts +9 -0
  40. package/dist/proserviceworld/pages/ClientsPage.d.ts.map +1 -0
  41. package/dist/proserviceworld/pages/ClientsPage.js +157 -146
  42. package/dist/proserviceworld/pages/ClientsPage.js.map +1 -1
  43. package/dist/proserviceworld/pages/CompliancePage.d.ts +4 -0
  44. package/dist/proserviceworld/pages/CompliancePage.d.ts.map +1 -0
  45. package/dist/proserviceworld/pages/ConflictsPage.d.ts +4 -0
  46. package/dist/proserviceworld/pages/ConflictsPage.d.ts.map +1 -0
  47. package/dist/proserviceworld/pages/DashboardPage.d.ts +4 -0
  48. package/dist/proserviceworld/pages/DashboardPage.d.ts.map +1 -0
  49. package/dist/proserviceworld/pages/DocumentsPage.d.ts +4 -0
  50. package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +1 -0
  51. package/dist/proserviceworld/pages/EngagementDetailPage.d.ts +4 -0
  52. package/dist/proserviceworld/pages/EngagementDetailPage.d.ts.map +1 -0
  53. package/dist/proserviceworld/pages/EngagementDetailPage.js +162 -156
  54. package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -1
  55. package/dist/proserviceworld/pages/EngagementsPage.d.ts +4 -0
  56. package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -0
  57. package/dist/proserviceworld/pages/EngagementsPage.js +141 -128
  58. package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
  59. package/dist/proserviceworld/pages/InvoicesPage.d.ts +4 -0
  60. package/dist/proserviceworld/pages/InvoicesPage.d.ts.map +1 -0
  61. package/dist/proserviceworld/pages/MattersPage.d.ts +4 -0
  62. package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -0
  63. package/dist/proserviceworld/pages/MattersPage.js +165 -150
  64. package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
  65. package/dist/proserviceworld/pages/PracticeAreasPage.d.ts +4 -0
  66. package/dist/proserviceworld/pages/PracticeAreasPage.d.ts.map +1 -0
  67. package/dist/proserviceworld/pages/TimePage.d.ts +4 -0
  68. package/dist/proserviceworld/pages/TimePage.d.ts.map +1 -0
  69. package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts +6 -0
  70. package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts.map +1 -0
  71. package/dist/proserviceworld/services/assistantApi.d.ts +74 -0
  72. package/dist/proserviceworld/services/assistantApi.d.ts.map +1 -0
  73. package/dist/proserviceworld/services/assistantApi.js +203 -0
  74. package/dist/proserviceworld/services/assistantApi.js.map +1 -0
  75. package/dist/proserviceworld/services/createSandboxAssistantTransport.d.ts +28 -0
  76. package/dist/proserviceworld/services/createSandboxAssistantTransport.d.ts.map +1 -0
  77. package/dist/proserviceworld/services/createSandboxAssistantTransport.js +120 -0
  78. package/dist/proserviceworld/services/createSandboxAssistantTransport.js.map +1 -0
  79. package/dist/proserviceworld/types/index.d.ts +57 -0
  80. package/dist/proserviceworld/types/index.d.ts.map +1 -0
  81. package/dist/proserviceworld/utils/cn.d.ts +4 -0
  82. package/dist/proserviceworld/utils/cn.d.ts.map +1 -0
  83. package/dist/proserviceworld/utils/nav.d.ts +6 -0
  84. package/dist/proserviceworld/utils/nav.d.ts.map +1 -0
  85. package/dist/proserviceworld/utils/navigation.d.ts +2 -0
  86. package/dist/proserviceworld/utils/navigation.d.ts.map +1 -0
  87. package/dist/proserviceworld/utils/pageContext.d.ts +12 -0
  88. package/dist/proserviceworld/utils/pageContext.d.ts.map +1 -0
  89. package/dist/proserviceworld/utils/pageContext.js +74 -0
  90. package/dist/proserviceworld/utils/pageContext.js.map +1 -0
  91. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EngagementDetailPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport {\n ArrowLeft,\n ArrowRight,\n Plus,\n CheckCircle2,\n FileText,\n Flag,\n Clock,\n DollarSign,\n} from 'lucide-react';\nimport {\n useEngagement,\n useTransitionEngagement,\n useCreateDeliverable,\n useTransitionDeliverable,\n useCreateMilestone,\n useCompleteMilestone,\n useTimeEntries,\n type PswEngagementStatus,\n type PswDeliverableStatus,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { DiscussionThread } from '../components/DiscussionThread';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n NativeSelect,\n Banner,\n MetricCard,\n fmtDate,\n fmtMoney,\n fmtMinutesAsHours,\n humanize,\n} from '../components/ui';\n\n// Mirrors the svc ENGAGEMENT_TRANSITIONS state machine so we only offer valid moves.\nconst NEXT: Record<PswEngagementStatus, PswEngagementStatus[]> = {\n PROPOSAL: ['ACCEPTED', 'CANCELLED'],\n ACCEPTED: ['IN_PROGRESS', 'CANCELLED'],\n IN_PROGRESS: ['REVIEW', 'CANCELLED'],\n REVIEW: ['DELIVERED', 'IN_PROGRESS', 'CANCELLED'],\n DELIVERED: ['INVOICED', 'CANCELLED'],\n INVOICED: ['CLOSED'],\n CLOSED: [],\n CANCELLED: [],\n};\nconst DELIVERABLE_STATUSES: PswDeliverableStatus[] = [\n 'DRAFT',\n 'INTERNAL_REVIEW',\n 'CLIENT_REVIEW',\n 'ACCEPTED',\n 'REJECTED',\n];\n\nexport const EngagementDetailPage: FC = () => {\n const { id = '' } = useParams();\n const nav = useNav();\n const { data: e, isLoading, error, refetch } = useEngagement(id);\n const time = useTimeEntries({ engagementId: id });\n const transition = useTransitionEngagement();\n const [addDeliverable, setAddDeliverable] = useState(false);\n const [addMilestone, setAddMilestone] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n\n if (isLoading) return <LoadingBlock />;\n if (error) return <ErrorState error={error} onRetry={() => void refetch()} />;\n if (!e) return <Empty title=\"Engagement not found\" />;\n\n const nextStates = NEXT[e.status] ?? [];\n const loggedMinutes = (time.data?.items ?? []).reduce((sum, t) => sum + t.minutes, 0);\n\n return (\n <div className=\"space-y-6\">\n <button\n type=\"button\"\n onClick={() => nav('engagements')}\n className=\"flex items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to engagements\n </button>\n\n <PageHeader\n title={e.name}\n subtitle={\n e.client ? `${e.client.name} · ${humanize(e.feeArrangement)}` : humanize(e.feeArrangement)\n }\n actions={<StatusPill status={e.status} />}\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <MetricCard\n label=\"Fee\"\n value={fmtMoney(e.feeAmount, e.currency)}\n hint={humanize(e.feeArrangement)}\n icon={<DollarSign className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n <MetricCard\n label=\"Deliverables\"\n value={e.deliverables.length}\n icon={<FileText className=\"h-4 w-4\" />}\n tone=\"info\"\n />\n <MetricCard\n label=\"Milestones\"\n value={e.milestones.length}\n icon={<Flag className=\"h-4 w-4\" />}\n tone=\"accent\"\n />\n <MetricCard\n label=\"Time logged\"\n value={fmtMinutesAsHours(loggedMinutes)}\n hint={`${time.data?.totalCount ?? 0} entries`}\n icon={<Clock className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n </div>\n\n {nextStates.length > 0 && (\n <SectionCard title=\"Advance lifecycle\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Currently</span>\n <StatusPill status={e.status} />\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {nextStates.map((s) => {\n const isCancel = s === 'CANCELLED';\n return (\n <Button\n key={s}\n size=\"sm\"\n variant={isCancel ? 'outline' : 'default'}\n disabled={transition.isPending}\n onClick={() =>\n transition.mutate(\n { id: e.id, status: s },\n {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `Moved to ${humanize(s)}.` }),\n onError: (err) =>\n setBanner({\n tone: 'danger',\n text: err instanceof Error ? err.message : 'Transition failed.',\n }),\n }\n )\n }\n >\n {humanize(s)}\n {!isCancel && <ArrowRight className=\"ml-1.5 h-4 w-4\" />}\n </Button>\n );\n })}\n </div>\n </div>\n </SectionCard>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <SectionCard\n title=\"Deliverables\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddDeliverable(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add\n </Button>\n }\n >\n {e.deliverables.length === 0 ? (\n <Empty\n title=\"No deliverables\"\n description=\"Track the tangible outputs of this engagement.\"\n icon={<FileText className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {e.deliverables.map((d) => (\n <li\n key={d.id}\n className=\"rounded-xl border border-border-seam bg-bg-canvas p-3.5 transition-colors hover:border-accent/40\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <span className=\"truncate\">{d.name}</span>\n <span className=\"shrink-0 rounded-md bg-bg-sunken px-1.5 py-0.5 text-[10px] font-semibold text-text-tertiary\">\n v{d.version}\n </span>\n </div>\n {d.dueDate && (\n <div className=\"mt-0.5 text-xs text-text-tertiary\">\n Due {fmtDate(d.dueDate)}\n </div>\n )}\n </div>\n <DeliverableStatusSelect\n id={d.id}\n engagementId={e.id}\n status={d.status}\n onDone={() => void refetch()}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n\n <SectionCard\n title=\"Milestones\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddMilestone(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add\n </Button>\n }\n >\n {e.milestones.length === 0 ? (\n <Empty\n title=\"No milestones\"\n description=\"Add checkpoints — some can trigger billing.\"\n icon={<Flag className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {e.milestones.map((m) => (\n <li\n key={m.id}\n className=\"flex items-center justify-between gap-3 rounded-xl border border-border-seam bg-bg-canvas p-3.5 transition-colors hover:border-accent/40\"\n >\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{m.name}</div>\n <div className=\"mt-0.5 text-xs text-text-tertiary\">\n {m.triggersInvoice ? `Bills ${fmtMoney(m.triggerAmount, e.currency)} · ` : ''}\n Due {fmtDate(m.dueDate)}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusPill status={m.status} />\n {m.status !== 'COMPLETED' && m.status !== 'CANCELLED' && (\n <CompleteMilestoneButton\n id={m.id}\n engagementId={e.id}\n onDone={() => void refetch()}\n />\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <DiscussionThread entityType=\"proserviceworld-engagement\" entityId={e.id} />\n </div>\n\n <SectionCard\n title=\"Time logged\"\n description={fmtMinutesAsHours(loggedMinutes)}\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => nav('time')}>\n <Clock className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n >\n {time.isLoading ? (\n <LoadingBlock label=\"Loading…\" />\n ) : !time.data || time.data.items.length === 0 ? (\n <Empty\n title=\"No time logged\"\n description=\"Time logged against this engagement will appear here.\"\n icon={<Clock className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2\">\n {time.data.items.slice(0, 8).map((t) => (\n <li\n key={t.id}\n className=\"rounded-lg border border-border-seam bg-bg-canvas p-3 text-sm\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"whitespace-nowrap text-xs text-text-tertiary\">\n {fmtDate(t.date)}\n </span>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary\">\n {fmtMinutesAsHours(t.minutes)}\n </span>\n <StatusPill status={t.status} />\n </span>\n </div>\n <div className=\"mt-1 truncate text-text-secondary\">\n {t.narrative || 'Time entry'}\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n </div>\n\n {addDeliverable && (\n <DeliverableModal\n engagementId={e.id}\n onClose={() => setAddDeliverable(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Deliverable added.' });\n }}\n />\n )}\n {addMilestone && (\n <MilestoneModal\n engagementId={e.id}\n onClose={() => setAddMilestone(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Milestone added.' });\n }}\n />\n )}\n </div>\n );\n};\n\nconst DeliverableStatusSelect: FC<{\n id: string;\n engagementId: string;\n status: PswDeliverableStatus;\n onDone: () => void;\n}> = ({ id, engagementId, status, onDone }) => {\n const transition = useTransitionDeliverable();\n return (\n <NativeSelect\n value={status}\n disabled={transition.isPending}\n onChange={(ev) =>\n transition.mutate(\n { id, status: ev.target.value as PswDeliverableStatus, engagementId },\n { onSuccess: onDone }\n )\n }\n className=\"w-40 shrink-0\"\n >\n {DELIVERABLE_STATUSES.map((s) => (\n <option key={s} value={s}>\n {humanize(s)}\n </option>\n ))}\n </NativeSelect>\n );\n};\n\nconst CompleteMilestoneButton: FC<{ id: string; engagementId: string; onDone: () => void }> = ({\n id,\n engagementId,\n onDone,\n}) => {\n const complete = useCompleteMilestone();\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Complete milestone\"\n disabled={complete.isPending}\n onClick={() => complete.mutate({ id, engagementId }, { onSuccess: onDone })}\n >\n <CheckCircle2 className=\"h-4 w-4\" />\n </Button>\n );\n};\n\nconst DeliverableModal: FC<{ engagementId: string; onClose: () => void; onDone: () => void }> = ({\n engagementId,\n onClose,\n onDone,\n}) => {\n const create = useCreateDeliverable();\n const [form, setForm] = useState({\n name: '',\n description: '',\n dueDate: '',\n acceptanceCriteria: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const submit = () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Name is required.');\n create.mutate(\n {\n engagementId,\n name: form.name.trim(),\n description: form.description.trim() || null,\n acceptanceCriteria: form.acceptanceCriteria.trim() || null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add deliverable\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Adding…' : 'Add'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"Draft audit report\" />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n <Field label=\"Acceptance criteria\">\n <TextArea value={form.acceptanceCriteria} onChange={set('acceptanceCriteria')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n\nconst MilestoneModal: FC<{ engagementId: string; onClose: () => void; onDone: () => void }> = ({\n engagementId,\n onClose,\n onDone,\n}) => {\n const create = useCreateMilestone();\n const [form, setForm] = useState({\n name: '',\n dueDate: '',\n triggerAmount: '',\n triggersInvoice: false,\n });\n const [err, setErr] = useState<string | null>(null);\n const submit = () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Name is required.');\n create.mutate(\n {\n engagementId,\n name: form.name.trim(),\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n triggerAmount: form.triggerAmount ? Number(form.triggerAmount) : 0,\n triggersInvoice: form.triggersInvoice,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add milestone\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Adding…' : 'Add'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Phase 1 sign-off\"\n />\n </Field>\n <Field label=\"Due date\">\n <TextInput\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm((f) => ({ ...f, dueDate: e.target.value }))}\n />\n </Field>\n <Field\n label=\"Trigger amount\"\n hint=\"Amount billed when this milestone completes (if it triggers an invoice).\"\n >\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.triggerAmount}\n onChange={(e) => setForm((f) => ({ ...f, triggerAmount: e.target.value }))}\n />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.triggersInvoice}\n onChange={(e) => setForm((f) => ({ ...f, triggersInvoice: e.target.checked }))}\n />\n Completing this milestone triggers an invoice\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"EngagementDetailPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport {\n ArrowLeft,\n ArrowRight,\n Plus,\n CheckCircle2,\n FileText,\n Flag,\n Clock,\n DollarSign,\n} from 'lucide-react';\nimport {\n useEngagement,\n useTransitionEngagement,\n useCreateDeliverable,\n useTransitionDeliverable,\n useCreateMilestone,\n useCompleteMilestone,\n useTimeEntries,\n type PswEngagementStatus,\n type PswDeliverableStatus,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { DiscussionThread } from '../components/DiscussionThread';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n NativeSelect,\n Banner,\n MetricCard,\n fmtDate,\n fmtMoney,\n fmtMinutesAsHours,\n humanize,\n} from '../components/ui';\n\n// Mirrors the svc ENGAGEMENT_TRANSITIONS state machine so we only offer valid moves.\nconst NEXT: Record<PswEngagementStatus, PswEngagementStatus[]> = {\n PROPOSAL: ['ACCEPTED', 'CANCELLED'],\n ACCEPTED: ['IN_PROGRESS', 'CANCELLED'],\n IN_PROGRESS: ['REVIEW', 'CANCELLED'],\n REVIEW: ['DELIVERED', 'IN_PROGRESS', 'CANCELLED'],\n DELIVERED: ['INVOICED', 'CANCELLED'],\n INVOICED: ['CLOSED'],\n CLOSED: [],\n CANCELLED: [],\n};\nconst DELIVERABLE_STATUSES: PswDeliverableStatus[] = [\n 'DRAFT',\n 'INTERNAL_REVIEW',\n 'CLIENT_REVIEW',\n 'ACCEPTED',\n 'REJECTED',\n];\n\nexport const EngagementDetailPage: FC = () => {\n const { id = '' } = useParams();\n const nav = useNav();\n const { emitFact } = useAnalytics();\n const { data: e, isLoading, error, refetch } = useEngagement(id);\n const time = useTimeEntries({ engagementId: id });\n const transition = useTransitionEngagement();\n const [addDeliverable, setAddDeliverable] = useState(false);\n const [addMilestone, setAddMilestone] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n\n if (isLoading) return <LoadingBlock />;\n if (error) return <ErrorState error={error} onRetry={() => void refetch()} />;\n if (!e) return <Empty title=\"Engagement not found\" />;\n\n const nextStates = NEXT[e.status] ?? [];\n const loggedMinutes = (time.data?.items ?? []).reduce((sum, t) => sum + t.minutes, 0);\n\n return (\n <div className=\"space-y-6\">\n <button\n type=\"button\"\n onClick={() => nav('engagements')}\n className=\"flex items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to engagements\n </button>\n\n <PageHeader\n title={e.name}\n subtitle={\n e.client ? `${e.client.name} · ${humanize(e.feeArrangement)}` : humanize(e.feeArrangement)\n }\n actions={<StatusPill status={e.status} />}\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <MetricCard\n label=\"Fee\"\n value={fmtMoney(e.feeAmount, e.currency)}\n hint={humanize(e.feeArrangement)}\n icon={<DollarSign className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n <MetricCard\n label=\"Deliverables\"\n value={e.deliverables.length}\n icon={<FileText className=\"h-4 w-4\" />}\n tone=\"info\"\n />\n <MetricCard\n label=\"Milestones\"\n value={e.milestones.length}\n icon={<Flag className=\"h-4 w-4\" />}\n tone=\"accent\"\n />\n <MetricCard\n label=\"Time logged\"\n value={fmtMinutesAsHours(loggedMinutes)}\n hint={`${time.data?.totalCount ?? 0} entries`}\n icon={<Clock className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n </div>\n\n {nextStates.length > 0 && (\n <SectionCard title=\"Advance lifecycle\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Currently</span>\n <StatusPill status={e.status} />\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {nextStates.map((s) => {\n const isCancel = s === 'CANCELLED';\n return (\n <Button\n key={s}\n size=\"sm\"\n variant={isCancel ? 'outline' : 'default'}\n disabled={transition.isPending}\n onClick={() =>\n transition.mutate(\n { id: e.id, status: s },\n {\n onSuccess: () => {\n emitFact('psw.engagement.transitioned', {\n engagementId: e.id,\n status: s,\n });\n setBanner({ tone: 'success', text: `Moved to ${humanize(s)}.` });\n },\n onError: (err) =>\n setBanner({\n tone: 'danger',\n text: err instanceof Error ? err.message : 'Transition failed.',\n }),\n }\n )\n }\n >\n {humanize(s)}\n {!isCancel && <ArrowRight className=\"ml-1.5 h-4 w-4\" />}\n </Button>\n );\n })}\n </div>\n </div>\n </SectionCard>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <SectionCard\n title=\"Deliverables\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddDeliverable(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add\n </Button>\n }\n >\n {e.deliverables.length === 0 ? (\n <Empty\n title=\"No deliverables\"\n description=\"Track the tangible outputs of this engagement.\"\n icon={<FileText className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {e.deliverables.map((d) => (\n <li\n key={d.id}\n className=\"rounded-xl border border-border-seam bg-bg-canvas p-3.5 transition-colors hover:border-accent/40\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <span className=\"truncate\">{d.name}</span>\n <span className=\"shrink-0 rounded-md bg-bg-sunken px-1.5 py-0.5 text-[10px] font-semibold text-text-tertiary\">\n v{d.version}\n </span>\n </div>\n {d.dueDate && (\n <div className=\"mt-0.5 text-xs text-text-tertiary\">\n Due {fmtDate(d.dueDate)}\n </div>\n )}\n </div>\n <DeliverableStatusSelect\n id={d.id}\n engagementId={e.id}\n status={d.status}\n onDone={() => void refetch()}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n\n <SectionCard\n title=\"Milestones\"\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAddMilestone(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add\n </Button>\n }\n >\n {e.milestones.length === 0 ? (\n <Empty\n title=\"No milestones\"\n description=\"Add checkpoints — some can trigger billing.\"\n icon={<Flag className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {e.milestones.map((m) => (\n <li\n key={m.id}\n className=\"flex items-center justify-between gap-3 rounded-xl border border-border-seam bg-bg-canvas p-3.5 transition-colors hover:border-accent/40\"\n >\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{m.name}</div>\n <div className=\"mt-0.5 text-xs text-text-tertiary\">\n {m.triggersInvoice ? `Bills ${fmtMoney(m.triggerAmount, e.currency)} · ` : ''}\n Due {fmtDate(m.dueDate)}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusPill status={m.status} />\n {m.status !== 'COMPLETED' && m.status !== 'CANCELLED' && (\n <CompleteMilestoneButton\n id={m.id}\n engagementId={e.id}\n onDone={() => void refetch()}\n />\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <DiscussionThread entityType=\"proserviceworld-engagement\" entityId={e.id} />\n </div>\n\n <SectionCard\n title=\"Time logged\"\n description={fmtMinutesAsHours(loggedMinutes)}\n action={\n <Button size=\"sm\" variant=\"outline\" onClick={() => nav('time')}>\n <Clock className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n >\n {time.isLoading ? (\n <LoadingBlock label=\"Loading…\" />\n ) : !time.data || time.data.items.length === 0 ? (\n <Empty\n title=\"No time logged\"\n description=\"Time logged against this engagement will appear here.\"\n icon={<Clock className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2\">\n {time.data.items.slice(0, 8).map((t) => (\n <li\n key={t.id}\n className=\"rounded-lg border border-border-seam bg-bg-canvas p-3 text-sm\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"whitespace-nowrap text-xs text-text-tertiary\">\n {fmtDate(t.date)}\n </span>\n <span className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary\">\n {fmtMinutesAsHours(t.minutes)}\n </span>\n <StatusPill status={t.status} />\n </span>\n </div>\n <div className=\"mt-1 truncate text-text-secondary\">\n {t.narrative || 'Time entry'}\n </div>\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n </div>\n\n {addDeliverable && (\n <DeliverableModal\n engagementId={e.id}\n onClose={() => setAddDeliverable(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Deliverable added.' });\n }}\n />\n )}\n {addMilestone && (\n <MilestoneModal\n engagementId={e.id}\n onClose={() => setAddMilestone(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Milestone added.' });\n }}\n />\n )}\n </div>\n );\n};\n\nconst DeliverableStatusSelect: FC<{\n id: string;\n engagementId: string;\n status: PswDeliverableStatus;\n onDone: () => void;\n}> = ({ id, engagementId, status, onDone }) => {\n const transition = useTransitionDeliverable();\n return (\n <NativeSelect\n value={status}\n disabled={transition.isPending}\n onChange={(ev) =>\n transition.mutate(\n { id, status: ev.target.value as PswDeliverableStatus, engagementId },\n { onSuccess: onDone }\n )\n }\n className=\"w-40 shrink-0\"\n >\n {DELIVERABLE_STATUSES.map((s) => (\n <option key={s} value={s}>\n {humanize(s)}\n </option>\n ))}\n </NativeSelect>\n );\n};\n\nconst CompleteMilestoneButton: FC<{ id: string; engagementId: string; onDone: () => void }> = ({\n id,\n engagementId,\n onDone,\n}) => {\n const complete = useCompleteMilestone();\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Complete milestone\"\n disabled={complete.isPending}\n onClick={() => complete.mutate({ id, engagementId }, { onSuccess: onDone })}\n >\n <CheckCircle2 className=\"h-4 w-4\" />\n </Button>\n );\n};\n\nconst DeliverableModal: FC<{ engagementId: string; onClose: () => void; onDone: () => void }> = ({\n engagementId,\n onClose,\n onDone,\n}) => {\n const create = useCreateDeliverable();\n const [form, setForm] = useState({\n name: '',\n description: '',\n dueDate: '',\n acceptanceCriteria: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const submit = () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Name is required.');\n create.mutate(\n {\n engagementId,\n name: form.name.trim(),\n description: form.description.trim() || null,\n acceptanceCriteria: form.acceptanceCriteria.trim() || null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add deliverable\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Adding…' : 'Add'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"Draft audit report\" />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n <Field label=\"Acceptance criteria\">\n <TextArea value={form.acceptanceCriteria} onChange={set('acceptanceCriteria')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n\nconst MilestoneModal: FC<{ engagementId: string; onClose: () => void; onDone: () => void }> = ({\n engagementId,\n onClose,\n onDone,\n}) => {\n const create = useCreateMilestone();\n const [form, setForm] = useState({\n name: '',\n dueDate: '',\n triggerAmount: '',\n triggersInvoice: false,\n });\n const [err, setErr] = useState<string | null>(null);\n const submit = () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Name is required.');\n create.mutate(\n {\n engagementId,\n name: form.name.trim(),\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n triggerAmount: form.triggerAmount ? Number(form.triggerAmount) : 0,\n triggersInvoice: form.triggersInvoice,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add milestone\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Adding…' : 'Add'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Phase 1 sign-off\"\n />\n </Field>\n <Field label=\"Due date\">\n <TextInput\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm((f) => ({ ...f, dueDate: e.target.value }))}\n />\n </Field>\n <Field\n label=\"Trigger amount\"\n hint=\"Amount billed when this milestone completes (if it triggers an invoice).\"\n >\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.triggerAmount}\n onChange={(e) => setForm((f) => ({ ...f, triggerAmount: e.target.value }))}\n />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.triggersInvoice}\n onChange={(e) => setForm((f) => ({ ...f, triggersInvoice: e.target.checked }))}\n />\n Completing this milestone triggers an invoice\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const EngagementsPage: FC;
4
+ //# sourceMappingURL=EngagementsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EngagementsPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/EngagementsPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAiEhC,eAAO,MAAM,eAAe,EAAE,EA+J7B,CAAC"}
@@ -1,12 +1,13 @@
1
1
  import { useClients as e, useCreateEngagement as t, useEngagements as n, usePracticeAreas as r } from "../hooks/useProServiceWorldApi.js";
2
2
  import { useNav as i } from "../utils/nav.js";
3
3
  import { Banner as a, Empty as o, ErrorState as s, Field as c, LoadingBlock as l, Modal as u, NativeSelect as d, PageHeader as f, SectionCard as p, StatusPill as m, TableWrap as h, Td as g, TextArea as _, TextInput as v, Th as y, enumOptions as b, fmtDate as x, fmtMoney as S, humanize as C } from "../components/ui.js";
4
- import { useState as w } from "react";
5
- import { Briefcase as T, ChevronRight as E, Plus as D, Search as O } from "lucide-react";
6
- import { Fragment as k, jsx as A, jsxs as j } from "react/jsx-runtime";
7
- import { Button as M } from "@burdenoff/fe-libs/ui";
4
+ import { useAnalytics as w } from "../hooks/useAnalytics.js";
5
+ import { useState as T } from "react";
6
+ import { Briefcase as E, ChevronRight as D, Plus as O, Search as k } from "lucide-react";
7
+ import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
8
+ import { Button as N } from "@burdenoff/fe-libs/ui";
8
9
  //#region src/proserviceworld/pages/EngagementsPage.tsx
9
- var N = [
10
+ var P = [
10
11
  "PROPOSAL",
11
12
  "ACCEPTED",
12
13
  "IN_PROGRESS",
@@ -15,140 +16,148 @@ var N = [
15
16
  "INVOICED",
16
17
  "CLOSED",
17
18
  "CANCELLED"
18
- ], P = [
19
+ ], F = [
19
20
  "FIXED",
20
21
  "HOURLY",
21
22
  "RETAINER",
22
23
  "VALUE_BASED"
23
- ], F = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", I = ({ name: e }) => /* @__PURE__ */ A("span", {
24
+ ], I = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", L = ({ name: e }) => /* @__PURE__ */ j("span", {
24
25
  className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-[10px] font-semibold text-accent ring-1 ring-inset ring-accent/15",
25
26
  "aria-hidden": !0,
26
- children: F(e)
27
- }), L = () => {
28
- let t = i(), [r, c] = w(""), [u, _] = w(""), [k, P] = w(""), [F, L] = w(!1), [z, B] = w(null), V = e({ pagination: { limit: 100 } }), { data: H, isLoading: U, error: W, refetch: G } = n({
29
- search: r.trim() || void 0,
30
- status: u || void 0,
31
- clientId: k || void 0
27
+ children: I(e)
28
+ }), R = () => {
29
+ let t = i(), { emitIntent: r, emitFact: c } = w(), [u, _] = T(""), [A, F] = T(""), [I, R] = T(""), [B, V] = T(!1), [H, U] = T(null), W = e({ pagination: { limit: 100 } }), { data: G, isLoading: K, error: q, refetch: J } = n({
30
+ search: u.trim() || void 0,
31
+ status: A || void 0,
32
+ clientId: I || void 0
32
33
  });
33
- return /* @__PURE__ */ j("div", {
34
+ return /* @__PURE__ */ M("div", {
34
35
  className: "space-y-6",
35
36
  children: [
36
- /* @__PURE__ */ A(f, {
37
+ /* @__PURE__ */ j(f, {
37
38
  title: "Engagements",
38
39
  subtitle: "The commercial containers of contracted work — scope, fee arrangement, delivery lifecycle, and billing.",
39
- icon: /* @__PURE__ */ A(T, { className: "h-6 w-6" }),
40
- actions: /* @__PURE__ */ j(M, {
41
- onClick: () => L(!0),
42
- children: [/* @__PURE__ */ A(D, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
40
+ icon: /* @__PURE__ */ j(E, { className: "h-6 w-6" }),
41
+ actions: /* @__PURE__ */ M(N, {
42
+ onClick: () => {
43
+ r("psw.engagement.create_clicked"), V(!0);
44
+ },
45
+ children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
43
46
  })
44
47
  }),
45
- z && /* @__PURE__ */ A(a, {
48
+ H && /* @__PURE__ */ j(a, {
46
49
  tone: "success",
47
- onClose: () => B(null),
48
- children: z
50
+ onClose: () => U(null),
51
+ children: H
49
52
  }),
50
- /* @__PURE__ */ j("div", {
53
+ /* @__PURE__ */ M("div", {
51
54
  className: "flex flex-col gap-2 sm:flex-row sm:items-center",
52
55
  children: [
53
- /* @__PURE__ */ j("div", {
56
+ /* @__PURE__ */ M("div", {
54
57
  className: "relative flex-1",
55
- children: [/* @__PURE__ */ A(O, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ A(v, {
58
+ children: [/* @__PURE__ */ j(k, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ j(v, {
56
59
  placeholder: "Search engagements…",
57
- value: r,
58
- onChange: (e) => c(e.target.value),
60
+ value: u,
61
+ onChange: (e) => _(e.target.value),
62
+ onKeyDown: (e) => {
63
+ e.key === "Enter" && u.trim() && c("psw.search.performed", {
64
+ entityType: "engagement",
65
+ queryLength: u.trim().length
66
+ });
67
+ },
59
68
  className: "pl-9"
60
69
  })]
61
70
  }),
62
- /* @__PURE__ */ j(d, {
63
- value: u,
64
- onChange: (e) => _(e.target.value),
71
+ /* @__PURE__ */ M(d, {
72
+ value: A,
73
+ onChange: (e) => F(e.target.value),
65
74
  className: "sm:w-44",
66
- children: [/* @__PURE__ */ A("option", {
75
+ children: [/* @__PURE__ */ j("option", {
67
76
  value: "",
68
77
  children: "All statuses"
69
- }), b(N)]
78
+ }), b(P)]
70
79
  }),
71
- /* @__PURE__ */ j(d, {
72
- value: k,
73
- onChange: (e) => P(e.target.value),
80
+ /* @__PURE__ */ M(d, {
81
+ value: I,
82
+ onChange: (e) => R(e.target.value),
74
83
  className: "sm:w-48",
75
- children: [/* @__PURE__ */ A("option", {
84
+ children: [/* @__PURE__ */ j("option", {
76
85
  value: "",
77
86
  children: "All clients"
78
- }), V.data?.items.map((e) => /* @__PURE__ */ A("option", {
87
+ }), W.data?.items.map((e) => /* @__PURE__ */ j("option", {
79
88
  value: e.id,
80
89
  children: e.name
81
90
  }, e.id))]
82
91
  })
83
92
  ]
84
93
  }),
85
- U ? /* @__PURE__ */ A(l, {}) : W ? /* @__PURE__ */ A(s, {
86
- error: W,
87
- onRetry: () => void G()
88
- }) : !H || H.items.length === 0 ? /* @__PURE__ */ A(o, {
94
+ K ? /* @__PURE__ */ j(l, {}) : q ? /* @__PURE__ */ j(s, {
95
+ error: q,
96
+ onRetry: () => void J()
97
+ }) : !G || G.items.length === 0 ? /* @__PURE__ */ j(o, {
89
98
  title: "No engagements yet",
90
99
  description: "Scope your first engagement against a client to begin delivery and billing.",
91
- icon: /* @__PURE__ */ A(T, { className: "h-8 w-8" }),
92
- action: /* @__PURE__ */ j(M, {
93
- onClick: () => L(!0),
94
- children: [/* @__PURE__ */ A(D, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
100
+ icon: /* @__PURE__ */ j(E, { className: "h-8 w-8" }),
101
+ action: /* @__PURE__ */ M(N, {
102
+ onClick: () => V(!0),
103
+ children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
95
104
  })
96
- }) : /* @__PURE__ */ A(p, {
97
- title: `${H.totalCount} engagement${H.totalCount === 1 ? "" : "s"}`,
98
- children: /* @__PURE__ */ j(h, { children: [/* @__PURE__ */ A("thead", { children: /* @__PURE__ */ j("tr", { children: [
99
- /* @__PURE__ */ A(y, { children: "Engagement" }),
100
- /* @__PURE__ */ A(y, { children: "Client" }),
101
- /* @__PURE__ */ A(y, { children: "Fee" }),
102
- /* @__PURE__ */ A(y, { children: "Status" }),
103
- /* @__PURE__ */ A(y, { children: "Started" })
104
- ] }) }), /* @__PURE__ */ A("tbody", { children: H.items.map((e) => /* @__PURE__ */ j("tr", {
105
+ }) : /* @__PURE__ */ j(p, {
106
+ title: `${G.totalCount} engagement${G.totalCount === 1 ? "" : "s"}`,
107
+ children: /* @__PURE__ */ M(h, { children: [/* @__PURE__ */ j("thead", { children: /* @__PURE__ */ M("tr", { children: [
108
+ /* @__PURE__ */ j(y, { children: "Engagement" }),
109
+ /* @__PURE__ */ j(y, { children: "Client" }),
110
+ /* @__PURE__ */ j(y, { children: "Fee" }),
111
+ /* @__PURE__ */ j(y, { children: "Status" }),
112
+ /* @__PURE__ */ j(y, { children: "Started" })
113
+ ] }) }), /* @__PURE__ */ j("tbody", { children: G.items.map((e) => /* @__PURE__ */ M("tr", {
105
114
  className: "group cursor-pointer transition-colors hover:bg-bg-sunken/60",
106
115
  onClick: () => t(`engagements/${e.id}`),
107
116
  children: [
108
- /* @__PURE__ */ j(g, { children: [/* @__PURE__ */ j("div", {
117
+ /* @__PURE__ */ M(g, { children: [/* @__PURE__ */ M("div", {
109
118
  className: "flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent",
110
- children: [/* @__PURE__ */ A("span", { children: e.name }), /* @__PURE__ */ A(E, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
111
- }), e.description ? /* @__PURE__ */ A("div", {
119
+ children: [/* @__PURE__ */ j("span", { children: e.name }), /* @__PURE__ */ j(D, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
120
+ }), e.description ? /* @__PURE__ */ j("div", {
112
121
  className: "mt-0.5 max-w-md truncate text-xs text-text-tertiary",
113
122
  children: e.description
114
123
  }) : null] }),
115
- /* @__PURE__ */ A(g, { children: /* @__PURE__ */ j("span", {
124
+ /* @__PURE__ */ j(g, { children: /* @__PURE__ */ M("span", {
116
125
  className: "inline-flex items-center gap-2.5",
117
- children: [/* @__PURE__ */ A(I, { name: e.client?.name ?? "—" }), /* @__PURE__ */ A("span", {
126
+ children: [/* @__PURE__ */ j(L, { name: e.client?.name ?? "—" }), /* @__PURE__ */ j("span", {
118
127
  className: "text-text-secondary",
119
128
  children: e.client?.name ?? "—"
120
129
  })]
121
130
  }) }),
122
- /* @__PURE__ */ j(g, {
131
+ /* @__PURE__ */ M(g, {
123
132
  className: "whitespace-nowrap",
124
133
  children: [
125
- /* @__PURE__ */ A("span", {
134
+ /* @__PURE__ */ j("span", {
126
135
  className: "font-medium text-text-primary",
127
136
  children: S(e.feeAmount, e.currency)
128
137
  }),
129
138
  " ",
130
- /* @__PURE__ */ j("span", {
139
+ /* @__PURE__ */ M("span", {
131
140
  className: "text-xs text-text-tertiary",
132
141
  children: ["/ ", C(e.feeArrangement)]
133
142
  })
134
143
  ]
135
144
  }),
136
- /* @__PURE__ */ A(g, { children: /* @__PURE__ */ A(m, { status: e.status }) }),
137
- /* @__PURE__ */ A(g, {
145
+ /* @__PURE__ */ j(g, { children: /* @__PURE__ */ j(m, { status: e.status }) }),
146
+ /* @__PURE__ */ j(g, {
138
147
  className: "whitespace-nowrap",
139
148
  children: x(e.startDate)
140
149
  })
141
150
  ]
142
151
  }, e.id)) })] })
143
152
  }),
144
- F && /* @__PURE__ */ A(R, {
145
- onClose: () => L(!1),
146
- onSaved: (e) => B(`${e} created.`)
153
+ B && /* @__PURE__ */ j(z, {
154
+ onClose: () => V(!1),
155
+ onSaved: (e) => U(`${e} created.`)
147
156
  })
148
157
  ]
149
158
  });
150
- }, R = ({ onClose: n, onSaved: i }) => {
151
- let o = t(), s = e({ pagination: { limit: 100 } }), l = r(), [f, p] = w({
159
+ }, z = ({ onClose: n, onSaved: i }) => {
160
+ let o = t(), { emitFact: s } = w(), l = e({ pagination: { limit: 100 } }), f = r(), [p, m] = T({
152
161
  clientId: "",
153
162
  practiceAreaId: "",
154
163
  name: "",
@@ -157,131 +166,135 @@ var N = [
157
166
  feeAmount: "",
158
167
  currency: "USD",
159
168
  startDate: ""
160
- }), [m, h] = w(null), g = (e) => (t) => p((n) => ({
169
+ }), [h, g] = T(null), y = (e) => (t) => m((n) => ({
161
170
  ...n,
162
171
  [e]: t.target.value
163
172
  }));
164
- return /* @__PURE__ */ A(u, {
173
+ return /* @__PURE__ */ j(u, {
165
174
  open: !0,
166
175
  onClose: n,
167
176
  title: "New engagement",
168
- footer: /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(M, {
177
+ footer: /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j(N, {
169
178
  variant: "outline",
170
179
  onClick: n,
171
180
  disabled: o.isPending,
172
181
  children: "Cancel"
173
- }), /* @__PURE__ */ A(M, {
182
+ }), /* @__PURE__ */ j(N, {
174
183
  onClick: () => {
175
- if (h(null), !f.clientId) return h("Select a client.");
176
- if (!f.name.trim()) return h("Engagement name is required.");
184
+ if (g(null), !p.clientId) return g("Select a client.");
185
+ if (!p.name.trim()) return g("Engagement name is required.");
177
186
  o.mutate({
178
- clientId: f.clientId,
179
- practiceAreaId: f.practiceAreaId || null,
180
- name: f.name.trim(),
181
- description: f.description.trim() || null,
182
- feeArrangement: f.feeArrangement,
183
- feeAmount: f.feeAmount ? Number(f.feeAmount) : 0,
184
- currency: f.currency || "USD",
185
- startDate: f.startDate ? new Date(f.startDate).toISOString() : null
187
+ clientId: p.clientId,
188
+ practiceAreaId: p.practiceAreaId || null,
189
+ name: p.name.trim(),
190
+ description: p.description.trim() || null,
191
+ feeArrangement: p.feeArrangement,
192
+ feeAmount: p.feeAmount ? Number(p.feeAmount) : 0,
193
+ currency: p.currency || "USD",
194
+ startDate: p.startDate ? new Date(p.startDate).toISOString() : null
186
195
  }, {
187
- onSuccess: () => {
188
- i(f.name.trim()), n();
196
+ onSuccess: (e) => {
197
+ let t = e.pswCreateEngagement;
198
+ s("psw.engagement.created", {
199
+ engagementId: t.id,
200
+ clientId: p.clientId
201
+ }), i(p.name.trim()), n();
189
202
  },
190
- onError: (e) => h(e instanceof Error ? e.message : "Failed.")
203
+ onError: (e) => g(e instanceof Error ? e.message : "Failed.")
191
204
  });
192
205
  },
193
206
  disabled: o.isPending,
194
207
  children: o.isPending ? "Creating…" : "Create engagement"
195
208
  })] }),
196
- children: /* @__PURE__ */ j("div", {
209
+ children: /* @__PURE__ */ M("div", {
197
210
  className: "space-y-3",
198
211
  children: [
199
- m && /* @__PURE__ */ A(a, {
212
+ h && /* @__PURE__ */ j(a, {
200
213
  tone: "danger",
201
- children: m
214
+ children: h
202
215
  }),
203
- /* @__PURE__ */ A(c, {
216
+ /* @__PURE__ */ j(c, {
204
217
  label: "Client",
205
218
  required: !0,
206
- children: /* @__PURE__ */ j(d, {
207
- value: f.clientId,
208
- onChange: g("clientId"),
209
- children: [/* @__PURE__ */ A("option", {
219
+ children: /* @__PURE__ */ M(d, {
220
+ value: p.clientId,
221
+ onChange: y("clientId"),
222
+ children: [/* @__PURE__ */ j("option", {
210
223
  value: "",
211
224
  children: "Select a client…"
212
- }), s.data?.items.map((e) => /* @__PURE__ */ A("option", {
225
+ }), l.data?.items.map((e) => /* @__PURE__ */ j("option", {
213
226
  value: e.id,
214
227
  children: e.name
215
228
  }, e.id))]
216
229
  })
217
230
  }),
218
- /* @__PURE__ */ A(c, {
231
+ /* @__PURE__ */ j(c, {
219
232
  label: "Name",
220
233
  required: !0,
221
- children: /* @__PURE__ */ A(v, {
222
- value: f.name,
223
- onChange: g("name"),
234
+ children: /* @__PURE__ */ j(v, {
235
+ value: p.name,
236
+ onChange: y("name"),
224
237
  placeholder: "FY26 Statutory Audit"
225
238
  })
226
239
  }),
227
- /* @__PURE__ */ A(c, {
240
+ /* @__PURE__ */ j(c, {
228
241
  label: "Practice area",
229
- children: /* @__PURE__ */ j(d, {
230
- value: f.practiceAreaId,
231
- onChange: g("practiceAreaId"),
232
- children: [/* @__PURE__ */ A("option", {
242
+ children: /* @__PURE__ */ M(d, {
243
+ value: p.practiceAreaId,
244
+ onChange: y("practiceAreaId"),
245
+ children: [/* @__PURE__ */ j("option", {
233
246
  value: "",
234
247
  children: "Unassigned"
235
- }), l.data?.map((e) => /* @__PURE__ */ A("option", {
248
+ }), f.data?.map((e) => /* @__PURE__ */ j("option", {
236
249
  value: e.id,
237
250
  children: e.name
238
251
  }, e.id))]
239
252
  })
240
253
  }),
241
- /* @__PURE__ */ j("div", {
254
+ /* @__PURE__ */ M("div", {
242
255
  className: "grid grid-cols-3 gap-3",
243
256
  children: [
244
- /* @__PURE__ */ A(c, {
257
+ /* @__PURE__ */ j(c, {
245
258
  label: "Fee type",
246
- children: /* @__PURE__ */ A(d, {
247
- value: f.feeArrangement,
248
- onChange: g("feeArrangement"),
249
- children: b(P)
259
+ children: /* @__PURE__ */ j(d, {
260
+ value: p.feeArrangement,
261
+ onChange: y("feeArrangement"),
262
+ children: b(F)
250
263
  })
251
264
  }),
252
- /* @__PURE__ */ A(c, {
265
+ /* @__PURE__ */ j(c, {
253
266
  label: "Fee amount",
254
- children: /* @__PURE__ */ A(v, {
267
+ children: /* @__PURE__ */ j(v, {
255
268
  type: "number",
256
269
  min: "0",
257
- value: f.feeAmount,
258
- onChange: g("feeAmount"),
270
+ value: p.feeAmount,
271
+ onChange: y("feeAmount"),
259
272
  placeholder: "50000"
260
273
  })
261
274
  }),
262
- /* @__PURE__ */ A(c, {
275
+ /* @__PURE__ */ j(c, {
263
276
  label: "Currency",
264
- children: /* @__PURE__ */ A(v, {
265
- value: f.currency,
266
- onChange: g("currency"),
277
+ children: /* @__PURE__ */ j(v, {
278
+ value: p.currency,
279
+ onChange: y("currency"),
267
280
  maxLength: 3
268
281
  })
269
282
  })
270
283
  ]
271
284
  }),
272
- /* @__PURE__ */ A(c, {
285
+ /* @__PURE__ */ j(c, {
273
286
  label: "Start date",
274
- children: /* @__PURE__ */ A(v, {
287
+ children: /* @__PURE__ */ j(v, {
275
288
  type: "date",
276
- value: f.startDate,
277
- onChange: g("startDate")
289
+ value: p.startDate,
290
+ onChange: y("startDate")
278
291
  })
279
292
  }),
280
- /* @__PURE__ */ A(c, {
293
+ /* @__PURE__ */ j(c, {
281
294
  label: "Description",
282
- children: /* @__PURE__ */ A(_, {
283
- value: f.description,
284
- onChange: g("description")
295
+ children: /* @__PURE__ */ j(_, {
296
+ value: p.description,
297
+ onChange: y("description")
285
298
  })
286
299
  })
287
300
  ]
@@ -289,6 +302,6 @@ var N = [
289
302
  });
290
303
  };
291
304
  //#endregion
292
- export { L as EngagementsPage };
305
+ export { R as EngagementsPage };
293
306
 
294
307
  //# sourceMappingURL=EngagementsPage.js.map