@burdenoff/microfe-proserviceworld 2026.709.1 → 2026.709.2

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/index.js +2 -1
  2. package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
  3. package/dist/proserviceworld/ProServiceWorldRoutes.js +95 -42
  4. package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
  5. package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts +8 -0
  6. package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts.map +1 -0
  7. package/dist/proserviceworld/components/FeatureFlaggedPage.js +17 -0
  8. package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -0
  9. package/dist/proserviceworld/components/LoadMore.d.ts +12 -0
  10. package/dist/proserviceworld/components/LoadMore.d.ts.map +1 -0
  11. package/dist/proserviceworld/components/LoadMore.js +30 -0
  12. package/dist/proserviceworld/components/LoadMore.js.map +1 -0
  13. package/dist/proserviceworld/components/TagPicker.d.ts +11 -0
  14. package/dist/proserviceworld/components/TagPicker.d.ts.map +1 -0
  15. package/dist/proserviceworld/components/TagPicker.js +31 -0
  16. package/dist/proserviceworld/components/TagPicker.js.map +1 -0
  17. package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts +10 -0
  18. package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts.map +1 -0
  19. package/dist/proserviceworld/hooks/usePageViewAnalytics.js +24 -0
  20. package/dist/proserviceworld/hooks/usePageViewAnalytics.js.map +1 -0
  21. package/dist/proserviceworld/hooks/useProServiceWorldApi.js +47 -27
  22. package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
  23. package/dist/proserviceworld/i18n/index.d.ts +13 -0
  24. package/dist/proserviceworld/i18n/index.d.ts.map +1 -0
  25. package/dist/proserviceworld/i18n/index.js +171 -0
  26. package/dist/proserviceworld/i18n/index.js.map +1 -0
  27. package/dist/proserviceworld/index.d.ts +1 -0
  28. package/dist/proserviceworld/index.d.ts.map +1 -1
  29. package/dist/proserviceworld/index.js +1 -0
  30. package/dist/proserviceworld/pages/ClientsPage.d.ts.map +1 -1
  31. package/dist/proserviceworld/pages/ClientsPage.js +173 -121
  32. package/dist/proserviceworld/pages/ClientsPage.js.map +1 -1
  33. package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -1
  34. package/dist/proserviceworld/pages/EngagementsPage.js +168 -120
  35. package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
  36. package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -1
  37. package/dist/proserviceworld/pages/MattersPage.js +183 -144
  38. package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
  39. package/dist/proserviceworld/utils/native.d.ts +23 -0
  40. package/dist/proserviceworld/utils/native.d.ts.map +1 -0
  41. package/dist/proserviceworld/utils/native.js +14 -0
  42. package/dist/proserviceworld/utils/native.js.map +1 -0
  43. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EngagementsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Briefcase, ChevronRight } from 'lucide-react';\nimport {\n useEngagements,\n useCreateEngagement,\n useClients,\n usePracticeAreas,\n type PswEngagementStatus,\n type PswEngagement,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { useAnalytics } from '../hooks/useAnalytics';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtMoney,\n fmtDate,\n humanize,\n} from '../components/ui';\n\nconst STATUSES: PswEngagementStatus[] = [\n 'PROPOSAL',\n 'ACCEPTED',\n 'IN_PROGRESS',\n 'REVIEW',\n 'DELIVERED',\n 'INVOICED',\n 'CLOSED',\n 'CANCELLED',\n];\nconst FEE_ARRANGEMENTS = ['FIXED', 'HOURLY', 'RETAINER', 'VALUE_BASED'] as const;\n\nconst initials = (name: string): string =>\n name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?';\n\nconst ClientAvatar: FC<{ name: string }> = ({ name }) => (\n <span\n 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\"\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\nexport const EngagementsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswEngagementStatus | ''>('');\n const [clientFilter, setClientFilter] = useState('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<string | null>(null);\n const clients = useClients({ pagination: { limit: 100 } });\n const { data, isLoading, error, refetch } = useEngagements({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n clientId: clientFilter || undefined,\n });\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Engagements\"\n subtitle=\"The commercial containers of contracted work — scope, fee arrangement, delivery lifecycle, and billing.\"\n icon={<Briefcase className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n emitIntent('psw.engagement.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n\n {banner && (\n <Banner tone=\"success\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\" />\n <TextInput\n placeholder=\"Search engagements…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'engagement',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswEngagementStatus | '')}\n className=\"sm:w-44\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n <NativeSelect\n value={clientFilter}\n onChange={(e) => setClientFilter(e.target.value)}\n className=\"sm:w-48\"\n >\n <option value=\"\">All clients</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No engagements yet\"\n description=\"Scope your first engagement against a client to begin delivery and billing.\"\n icon={<Briefcase className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} engagement${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Engagement</Th>\n <Th>Client</Th>\n <Th>Fee</Th>\n <Th>Status</Th>\n <Th>Started</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((e) => (\n <tr\n key={e.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`engagements/${e.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span>{e.name}</span>\n <ChevronRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100\" />\n </div>\n {e.description ? (\n <div className=\"mt-0.5 max-w-md truncate text-xs text-text-tertiary\">\n {e.description}\n </div>\n ) : null}\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-2.5\">\n <ClientAvatar name={e.client?.name ?? '—'} />\n <span className=\"text-text-secondary\">{e.client?.name ?? '—'}</span>\n </span>\n </Td>\n <Td className=\"whitespace-nowrap\">\n <span className=\"font-medium text-text-primary\">\n {fmtMoney(e.feeAmount, e.currency)}\n </span>{' '}\n <span className=\"text-xs text-text-tertiary\">\n / {humanize(e.feeArrangement)}\n </span>\n </Td>\n <Td>\n <StatusPill status={e.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(e.startDate)}</Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <EngagementModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner(`${name} created.`)}\n />\n )}\n </div>\n );\n};\n\nconst EngagementModal: FC<{ onClose: () => void; onSaved: (name: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateEngagement();\n const { emitFact } = useAnalytics();\n const clients = useClients({ pagination: { limit: 100 } });\n const practiceAreas = usePracticeAreas();\n const [form, setForm] = useState({\n clientId: '',\n practiceAreaId: '',\n name: '',\n description: '',\n feeArrangement: 'FIXED',\n feeAmount: '',\n currency: 'USD',\n startDate: '',\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\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.name.trim()) return setErr('Engagement name is required.');\n create.mutate(\n {\n clientId: form.clientId,\n practiceAreaId: form.practiceAreaId || null,\n name: form.name.trim(),\n description: form.description.trim() || null,\n feeArrangement: form.feeArrangement,\n feeAmount: form.feeAmount ? Number(form.feeAmount) : 0,\n currency: form.currency || 'USD',\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n },\n {\n onSuccess: (created) => {\n const engagement = (created as { pswCreateEngagement: PswEngagement })\n .pswCreateEngagement;\n emitFact('psw.engagement.created', {\n engagementId: engagement.id,\n clientId: form.clientId,\n });\n onSaved(form.name.trim());\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"New engagement\"\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 ? 'Creating…' : 'Create engagement'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select a client…</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"FY26 Statutory Audit\" />\n </Field>\n <Field label=\"Practice area\">\n <NativeSelect value={form.practiceAreaId} onChange={set('practiceAreaId')}>\n <option value=\"\">Unassigned</option>\n {practiceAreas.data?.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Fee type\">\n <NativeSelect value={form.feeArrangement} onChange={set('feeArrangement')}>\n {enumOptions(FEE_ARRANGEMENTS)}\n </NativeSelect>\n </Field>\n <Field label=\"Fee amount\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.feeAmount}\n onChange={set('feeAmount')}\n placeholder=\"50000\"\n />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <Field label=\"Start date\">\n <TextInput type=\"date\" value={form.startDate} onChange={set('startDate')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":";;;;;;;;;AAoCA,IAAM,IAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EACK,IAAmB;CAAC;CAAS;CAAU;CAAY;CAAc,EAEjE,KAAY,MAChB,EACG,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,MAAM,GAAG,EAAE,CACX,KAAK,MAAM,EAAE,IAAI,aAAa,IAAI,GAAG,CACrC,KAAK,GAAG,IAAI,KAEX,KAAsC,EAAE,cAC5C,kBAAC,QAAD;CACE,WAAU;CACV,eAAA;WAEC,EAAS,EAAK;CACV,CAAA,EAGI,UAA4B;CACvC,IAAM,IAAM,GAAQ,EACd,EAAE,eAAY,gBAAa,GAAc,EACzC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAc,KAAmB,EAAmC,GAAG,EACxE,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAQ,KAAa,EAAwB,KAAK,EACnD,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,EAAE,SAAM,cAAW,UAAO,eAAY,EAAe;EACzD,QAAQ,EAAO,MAAM,IAAI,KAAA;EACzB,QAAQ,KAAgB,KAAA;EACxB,UAAU,KAAgB,KAAA;EAC3B,CAAC;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAM;IACN,UAAS;IACT,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,SACE,kBAAC,GAAD;KACE,eAAe;AAEb,MADA,EAAW,gCAAgC,EAC3C,EAAY,GAAK;;eAHrB,CAME,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA;GAED,KACC,kBAAC,GAAD;IAAQ,MAAK;IAAU,eAAe,EAAU,KAAK;cAClD;IACM,CAAA;GAGX,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,2FAA4F,CAAA,EAC9G,kBAAC,GAAD;OACE,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;OAC1C,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAO,MAAM,IACpC,EAAS,wBAAwB;SAC/B,YAAY;SACZ,aAAa,EAAO,MAAM,CAAC;SAC5B,CAAC;;OAGN,WAAU;OACV,CAAA,CACE;;KACN,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAkC;MAC5E,WAAU;gBAHZ,CAKE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAqB,CAAA,EACrC,EAAY,EAAS,CACT;;KACf,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;MAChD,WAAU;gBAHZ,CAKE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAoB,CAAA,EACpC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACX;;GAEL,IACC,kBAAC,GAAD,EAAgB,CAAA,GACd,IACF,kBAAC,GAAD;IAAmB;IAAO,eAAe,KAAK,GAAS;IAAI,CAAA,GACzD,CAAC,KAAQ,EAAK,MAAM,WAAW,IACjC,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,QACE,kBAAC,GAAD;KAAQ,eAAe,EAAY,GAAK;eAAxC,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA,GAEF,kBAAC,GAAD;IAAa,OAAO,GAAG,EAAK,WAAW,aAAa,EAAK,eAAe,IAAI,KAAK;cAC/E,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAI,cAAe,CAAA;KACnB,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,OAAQ,CAAA;KACZ,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,WAAY,CAAA;KACb,EAAA,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAK,MAAM,KAAK,MACf,kBAAC,MAAD;KAEE,WAAU;KACV,eAAe,EAAI,eAAe,EAAE,KAAK;eAH3C;MAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAE,MAAY,CAAA,EACrB,kBAAC,GAAD,EAAc,WAAU,gGAAiG,CAAA,CACrH;UACL,EAAE,cACD,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAE;OACC,CAAA,GACJ,KACD,EAAA,CAAA;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD,EAAc,MAAM,EAAE,QAAQ,QAAQ,KAAO,CAAA,EAC7C,kBAAC,QAAD;QAAM,WAAU;kBAAuB,EAAE,QAAQ,QAAQ;QAAW,CAAA,CAC/D;UACJ,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAS,EAAE,WAAW,EAAE,SAAS;SAC7B,CAAA;QAAC;QACR,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA6C,MACxC,EAAS,EAAE,eAAe,CACxB;;QACJ;;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAY,QAAQ,EAAE,QAAU,CAAA,EAC7B,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAqB,EAAQ,EAAE,UAAU;OAAM,CAAA;MAC1D;OAjCE,EAAE,GAiCJ,CACL,EACI,CAAA,CACE,EAAA,CAAA;IACA,CAAA;GAGf,KACC,kBAAC,GAAD;IACE,eAAe,EAAY,GAAM;IACjC,UAAU,MAAS,EAAU,GAAG,EAAK,WAAW;IAChD,CAAA;GAEA;;GAIJ,KAAiF,EACrF,YACA,iBACI;CACJ,IAAM,IAAS,GAAqB,EAC9B,EAAE,gBAAa,GAAc,EAC7B,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,IAAgB,GAAkB,EAClC,CAAC,GAAM,KAAW,EAAS;EAC/B,UAAU;EACV,gBAAgB;EAChB,MAAM;EACN,aAAa;EACb,gBAAgB;EAChB,WAAW;EACX,UAAU;EACV,WAAW;EACZ,CAAC,EACI,CAAC,GAAK,KAAU,EAAwB,KAAK,EAC7C,KAAO,OAA0B,MACrC,GAAS,OAAO;EAAE,GAAG;GAAI,IAAI,EAAE,OAAO;EAAO,EAAE;AAiCjD,QACE,kBAAC,GAAD;EACE,MAAA;EACS;EACT,OAAM;EACN,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,SAAS;GAAS,UAAU,EAAO;aAAW;GAE/D,CAAA,EACT,kBAAC,GAAD;GAAQ,eAzCK;AAEnB,QADA,EAAO,KAAK,EACR,CAAC,EAAK,SAAU,QAAO,EAAO,mBAAmB;AACrD,QAAI,CAAC,EAAK,KAAK,MAAM,CAAE,QAAO,EAAO,+BAA+B;AACpE,MAAO,OACL;KACE,UAAU,EAAK;KACf,gBAAgB,EAAK,kBAAkB;KACvC,MAAM,EAAK,KAAK,MAAM;KACtB,aAAa,EAAK,YAAY,MAAM,IAAI;KACxC,gBAAgB,EAAK;KACrB,WAAW,EAAK,YAAY,OAAO,EAAK,UAAU,GAAG;KACrD,UAAU,EAAK,YAAY;KAC3B,WAAW,EAAK,YAAY,IAAI,KAAK,EAAK,UAAU,CAAC,aAAa,GAAG;KACtE,EACD;KACE,YAAY,MAAY;MACtB,IAAM,IAAc,EACjB;AAMH,MALA,EAAS,0BAA0B;OACjC,cAAc,EAAW;OACzB,UAAU,EAAK;OAChB,CAAC,EACF,EAAQ,EAAK,KAAK,MAAM,CAAC,EACzB,GAAS;;KAEX,UAAU,MAAM,EAAO,aAAa,QAAQ,EAAE,UAAU,UAAU;KACnE,CACF;;GAa8B,UAAU,EAAO;aACvC,EAAO,YAAY,cAAc;GAC3B,CAAA,CACR,EAAA,CAAA;YAGL,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,KAAO,kBAAC,GAAD;KAAQ,MAAK;eAAU;KAAa,CAAA;IAC5C,kBAAC,GAAD;KAAO,OAAM;KAAS,UAAA;eACpB,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAU,UAAU,EAAI,WAAW;gBAA7D,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAyB,CAAA,EACzC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;KAAO,UAAA;eAClB,kBAAC,GAAD;MAAW,OAAO,EAAK;MAAM,UAAU,EAAI,OAAO;MAAE,aAAY;MAAyB,CAAA;KACnF,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAgB,UAAU,EAAI,iBAAiB;gBAAzE,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAmB,CAAA,EACnC,EAAc,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAc,OAAO,EAAK;QAAgB,UAAU,EAAI,iBAAiB;kBACtE,EAAY,EAAiB;QACjB,CAAA;OACT,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QACE,MAAK;QACL,KAAI;QACJ,OAAO,EAAK;QACZ,UAAU,EAAI,YAAY;QAC1B,aAAY;QACZ,CAAA;OACI,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAW,OAAO,EAAK;QAAU,UAAU,EAAI,WAAW;QAAE,WAAW;QAAK,CAAA;OACtE,CAAA;MACJ;;IACN,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAW,MAAK;MAAO,OAAO,EAAK;MAAW,UAAU,EAAI,YAAY;MAAI,CAAA;KACtE,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAU,OAAO,EAAK;MAAa,UAAU,EAAI,cAAc;MAAI,CAAA;KAC7D,CAAA;IACJ;;EACA,CAAA"}
1
+ {"version":3,"file":"EngagementsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Briefcase, ChevronRight } from 'lucide-react';\nimport {\n useEngagements,\n useCreateEngagement,\n useUpdateEngagement,\n useClients,\n usePracticeAreas,\n type PswEngagementStatus,\n type PswEngagement,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { impactFeedback } from '../utils/native';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtMoney,\n fmtDate,\n humanize,\n} from '../components/ui';\nimport { TagPicker } from '../components/TagPicker';\nimport { LoadMore } from '../components/LoadMore';\n\nconst STATUSES: PswEngagementStatus[] = [\n 'PROPOSAL',\n 'ACCEPTED',\n 'IN_PROGRESS',\n 'REVIEW',\n 'DELIVERED',\n 'INVOICED',\n 'CLOSED',\n 'CANCELLED',\n];\nconst FEE_ARRANGEMENTS = ['FIXED', 'HOURLY', 'RETAINER', 'VALUE_BASED'] as const;\n\nconst initials = (name: string): string =>\n name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?';\n\nconst ClientAvatar: FC<{ name: string }> = ({ name }) => (\n <span\n 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\"\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\nconst PAGE_SIZE = 25;\n\nexport const EngagementsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswEngagementStatus | ''>('');\n const [clientFilter, setClientFilter] = useState('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<string | null>(null);\n const [pagination, setPagination] = useState({ limit: PAGE_SIZE, offset: 0 });\n const clients = useClients({ pagination: { limit: 100 } });\n const update = useUpdateEngagement();\n const { data, isLoading, error, refetch } = useEngagements({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n clientId: clientFilter || undefined,\n pagination,\n });\n\n const allItems = useMemo(() => data?.items ?? [], [data]);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Engagements\"\n subtitle=\"The commercial containers of contracted work — scope, fee arrangement, delivery lifecycle, and billing.\"\n icon={<Briefcase className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n void impactFeedback('light');\n emitIntent('psw.engagement.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n\n {banner && (\n <Banner tone=\"success\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\" />\n <TextInput\n placeholder=\"Search engagements…\"\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'engagement',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as PswEngagementStatus | '');\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-44\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n <NativeSelect\n value={clientFilter}\n onChange={(e) => {\n setClientFilter(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-48\"\n >\n <option value=\"\">All clients</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No engagements yet\"\n description=\"Scope your first engagement against a client to begin delivery and billing.\"\n icon={<Briefcase className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} engagement${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Engagement</Th>\n <Th>Client</Th>\n <Th>Fee</Th>\n <Th>Status</Th>\n <Th>Started</Th>\n <Th>Tags</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((e) => (\n <tr\n key={e.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`engagements/${e.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span>{e.name}</span>\n <ChevronRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100\" />\n </div>\n {e.description ? (\n <div className=\"mt-0.5 max-w-md truncate text-xs text-text-tertiary\">\n {e.description}\n </div>\n ) : null}\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-2.5\">\n <ClientAvatar name={e.client?.name ?? '—'} />\n <span className=\"text-text-secondary\">{e.client?.name ?? '—'}</span>\n </span>\n </Td>\n <Td className=\"whitespace-nowrap\">\n <span className=\"font-medium text-text-primary\">\n {fmtMoney(e.feeAmount, e.currency)}\n </span>{' '}\n <span className=\"text-xs text-text-tertiary\">\n / {humanize(e.feeArrangement)}\n </span>\n </Td>\n <Td>\n <StatusPill status={e.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(e.startDate)}</Td>\n <Td>\n <div onClick={(ev) => ev.stopPropagation()} role=\"presentation\">\n <TagPicker\n entityType=\"PswEngagement\"\n entityId={e.id}\n tags={e.tags ?? []}\n onChange={async (tags) => {\n await update.mutateAsync({ id: e.id, input: { tags } });\n void refetch();\n }}\n maxTags={5}\n />\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n <LoadMore\n hasMore={data?.pageInfo?.hasMore ?? false}\n loading={isLoading}\n loadedCount={allItems.length}\n totalCount={data?.totalCount ?? allItems.length}\n onLoadMore={() =>\n setPagination((p) => ({ limit: PAGE_SIZE, offset: p.offset + PAGE_SIZE }))\n }\n />\n </SectionCard>\n )}\n\n {creating && (\n <EngagementModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner(`${name} created.`)}\n />\n )}\n </div>\n );\n};\n\nconst EngagementModal: FC<{ onClose: () => void; onSaved: (name: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateEngagement();\n const { emitFact } = useAnalytics();\n const clients = useClients({ pagination: { limit: 100 } });\n const practiceAreas = usePracticeAreas();\n const [form, setForm] = useState({\n clientId: '',\n practiceAreaId: '',\n name: '',\n description: '',\n feeArrangement: 'FIXED',\n feeAmount: '',\n currency: 'USD',\n startDate: '',\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\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.name.trim()) return setErr('Engagement name is required.');\n create.mutate(\n {\n clientId: form.clientId,\n practiceAreaId: form.practiceAreaId || null,\n name: form.name.trim(),\n description: form.description.trim() || null,\n feeArrangement: form.feeArrangement,\n feeAmount: form.feeAmount ? Number(form.feeAmount) : 0,\n currency: form.currency || 'USD',\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n },\n {\n onSuccess: (created) => {\n const engagement = (created as { pswCreateEngagement: PswEngagement })\n .pswCreateEngagement;\n emitFact('psw.engagement.created', {\n engagementId: engagement.id,\n clientId: form.clientId,\n });\n onSaved(form.name.trim());\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"New engagement\"\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 ? 'Creating…' : 'Create engagement'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select a client…</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"FY26 Statutory Audit\" />\n </Field>\n <Field label=\"Practice area\">\n <NativeSelect value={form.practiceAreaId} onChange={set('practiceAreaId')}>\n <option value=\"\">Unassigned</option>\n {practiceAreas.data?.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Fee type\">\n <NativeSelect value={form.feeArrangement} onChange={set('feeArrangement')}>\n {enumOptions(FEE_ARRANGEMENTS)}\n </NativeSelect>\n </Field>\n <Field label=\"Fee amount\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.feeAmount}\n onChange={set('feeAmount')}\n placeholder=\"50000\"\n />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <Field label=\"Start date\">\n <TextInput type=\"date\" value={form.startDate} onChange={set('startDate')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAwCA,IAAM,IAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EACK,IAAmB;CAAC;CAAS;CAAU;CAAY;CAAc,EAEjE,KAAY,MAChB,EACG,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,MAAM,GAAG,EAAE,CACX,KAAK,MAAM,EAAE,IAAI,aAAa,IAAI,GAAG,CACrC,KAAK,GAAG,IAAI,KAEX,MAAsC,EAAE,cAC5C,kBAAC,QAAD;CACE,WAAU;CACV,eAAA;WAEC,EAAS,EAAK;CACV,CAAA,EAGH,IAAY,IAEL,UAA4B;CACvC,IAAM,IAAM,GAAQ,EACd,EAAE,eAAY,gBAAa,GAAc,EACzC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAc,KAAmB,EAAmC,GAAG,EACxE,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAQ,KAAa,EAAwB,KAAK,EACnD,CAAC,GAAY,KAAiB,EAAS;EAAE,OAAO;EAAW,QAAQ;EAAG,CAAC,EACvE,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,IAAS,GAAqB,EAC9B,EAAE,SAAM,cAAW,UAAO,eAAY,EAAe;EACzD,QAAQ,EAAO,MAAM,IAAI,KAAA;EACzB,QAAQ,KAAgB,KAAA;EACxB,UAAU,KAAgB,KAAA;EAC1B;EACD,CAAC,EAEI,IAAW,SAAc,GAAM,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC;AAEzD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAM;IACN,UAAS;IACT,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,SACE,kBAAC,GAAD;KACE,eAAe;AAGb,MAFK,EAAe,QAAQ,EAC5B,EAAW,gCAAgC,EAC3C,EAAY,GAAK;;eAJrB,CAOE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA;GAED,KACC,kBAAC,GAAD;IAAQ,MAAK;IAAU,eAAe,EAAU,KAAK;cAClD;IACM,CAAA;GAGX,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,2FAA4F,CAAA,EAC9G,kBAAC,GAAD;OACE,aAAY;OACZ,OAAO;OACP,WAAW,MAAM;AAEf,QADA,EAAU,EAAE,OAAO,MAAM,EACzB,EAAc;SAAE,OAAO;SAAW,QAAQ;SAAG,CAAC;;OAEhD,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAO,MAAM,IACpC,EAAS,wBAAwB;SAC/B,YAAY;SACZ,aAAa,EAAO,MAAM,CAAC;SAC5B,CAAC;;OAGN,WAAU;OACV,CAAA,CACE;;KACN,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM;AAEf,OADA,EAAgB,EAAE,OAAO,MAAkC,EAC3D,EAAc;QAAE,OAAO;QAAW,QAAQ;QAAG,CAAC;;MAEhD,WAAU;gBANZ,CAQE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAqB,CAAA,EACrC,EAAY,EAAS,CACT;;KACf,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM;AAEf,OADA,EAAgB,EAAE,OAAO,MAAM,EAC/B,EAAc;QAAE,OAAO;QAAW,QAAQ;QAAG,CAAC;;MAEhD,WAAU;gBANZ,CAQE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAoB,CAAA,EACpC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACX;;GAEL,IACC,kBAAC,GAAD,EAAgB,CAAA,GACd,IACF,kBAAC,GAAD;IAAmB;IAAO,eAAe,KAAK,GAAS;IAAI,CAAA,GACzD,CAAC,KAAQ,EAAK,MAAM,WAAW,IACjC,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,QACE,kBAAC,GAAD;KAAQ,eAAe,EAAY,GAAK;eAAxC,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA,GAEF,kBAAC,GAAD;IAAa,OAAO,GAAG,EAAK,WAAW,aAAa,EAAK,eAAe,IAAI,KAAK;cAAjF,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAI,cAAe,CAAA;KACnB,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,OAAQ,CAAA;KACZ,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,WAAY,CAAA;KAChB,kBAAC,GAAD,EAAA,UAAI,QAAS,CAAA;KACV,EAAA,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAK,MAAM,KAAK,MACf,kBAAC,MAAD;KAEE,WAAU;KACV,eAAe,EAAI,eAAe,EAAE,KAAK;eAH3C;MAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAE,MAAY,CAAA,EACrB,kBAAC,GAAD,EAAc,WAAU,gGAAiG,CAAA,CACrH;UACL,EAAE,cACD,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAE;OACC,CAAA,GACJ,KACD,EAAA,CAAA;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,IAAD,EAAc,MAAM,EAAE,QAAQ,QAAQ,KAAO,CAAA,EAC7C,kBAAC,QAAD;QAAM,WAAU;kBAAuB,EAAE,QAAQ,QAAQ;QAAW,CAAA,CAC/D;UACJ,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,QAAD;SAAM,WAAU;mBACb,GAAS,EAAE,WAAW,EAAE,SAAS;SAC7B,CAAA;QAAC;QACR,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA6C,MACxC,EAAS,EAAE,eAAe,CACxB;;QACJ;;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAY,QAAQ,EAAE,QAAU,CAAA,EAC7B,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAqB,EAAQ,EAAE,UAAU;OAAM,CAAA;MAC7D,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,UAAU,MAAO,EAAG,iBAAiB;OAAE,MAAK;iBAC/C,kBAAC,GAAD;QACE,YAAW;QACX,UAAU,EAAE;QACZ,MAAM,EAAE,QAAQ,EAAE;QAClB,UAAU,OAAO,MAAS;AAEnB,SADL,MAAM,EAAO,YAAY;UAAE,IAAI,EAAE;UAAI,OAAO,EAAE,SAAM;UAAE,CAAC,EAClD,GAAS;;QAEhB,SAAS;QACT,CAAA;OACE,CAAA,EACH,CAAA;MACF;OA/CE,EAAE,GA+CJ,CACL,EACI,CAAA,CACE,EAAA,CAAA,EACZ,kBAAC,IAAD;KACE,SAAS,GAAM,UAAU,WAAW;KACpC,SAAS;KACT,aAAa,EAAS;KACtB,YAAY,GAAM,cAAc,EAAS;KACzC,kBACE,GAAe,OAAO;MAAE,OAAO;MAAW,QAAQ,EAAE,SAAS;MAAW,EAAE;KAE5E,CAAA,CACU;;GAGf,KACC,kBAAC,GAAD;IACE,eAAe,EAAY,GAAM;IACjC,UAAU,MAAS,EAAU,GAAG,EAAK,WAAW;IAChD,CAAA;GAEA;;GAIJ,KAAiF,EACrF,YACA,iBACI;CACJ,IAAM,IAAS,GAAqB,EAC9B,EAAE,gBAAa,GAAc,EAC7B,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,IAAgB,GAAkB,EAClC,CAAC,GAAM,KAAW,EAAS;EAC/B,UAAU;EACV,gBAAgB;EAChB,MAAM;EACN,aAAa;EACb,gBAAgB;EAChB,WAAW;EACX,UAAU;EACV,WAAW;EACZ,CAAC,EACI,CAAC,GAAK,KAAU,EAAwB,KAAK,EAC7C,KAAO,OAA0B,MACrC,GAAS,OAAO;EAAE,GAAG;GAAI,IAAI,EAAE,OAAO;EAAO,EAAE;AAiCjD,QACE,kBAAC,GAAD;EACE,MAAA;EACS;EACT,OAAM;EACN,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,SAAS;GAAS,UAAU,EAAO;aAAW;GAE/D,CAAA,EACT,kBAAC,GAAD;GAAQ,eAzCK;AAEnB,QADA,EAAO,KAAK,EACR,CAAC,EAAK,SAAU,QAAO,EAAO,mBAAmB;AACrD,QAAI,CAAC,EAAK,KAAK,MAAM,CAAE,QAAO,EAAO,+BAA+B;AACpE,MAAO,OACL;KACE,UAAU,EAAK;KACf,gBAAgB,EAAK,kBAAkB;KACvC,MAAM,EAAK,KAAK,MAAM;KACtB,aAAa,EAAK,YAAY,MAAM,IAAI;KACxC,gBAAgB,EAAK;KACrB,WAAW,EAAK,YAAY,OAAO,EAAK,UAAU,GAAG;KACrD,UAAU,EAAK,YAAY;KAC3B,WAAW,EAAK,YAAY,IAAI,KAAK,EAAK,UAAU,CAAC,aAAa,GAAG;KACtE,EACD;KACE,YAAY,MAAY;MACtB,IAAM,IAAc,EACjB;AAMH,MALA,EAAS,0BAA0B;OACjC,cAAc,EAAW;OACzB,UAAU,EAAK;OAChB,CAAC,EACF,EAAQ,EAAK,KAAK,MAAM,CAAC,EACzB,GAAS;;KAEX,UAAU,MAAM,EAAO,aAAa,QAAQ,EAAE,UAAU,UAAU;KACnE,CACF;;GAa8B,UAAU,EAAO;aACvC,EAAO,YAAY,cAAc;GAC3B,CAAA,CACR,EAAA,CAAA;YAGL,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,KAAO,kBAAC,GAAD;KAAQ,MAAK;eAAU;KAAa,CAAA;IAC5C,kBAAC,GAAD;KAAO,OAAM;KAAS,UAAA;eACpB,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAU,UAAU,EAAI,WAAW;gBAA7D,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAyB,CAAA,EACzC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;KAAO,UAAA;eAClB,kBAAC,GAAD;MAAW,OAAO,EAAK;MAAM,UAAU,EAAI,OAAO;MAAE,aAAY;MAAyB,CAAA;KACnF,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAgB,UAAU,EAAI,iBAAiB;gBAAzE,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAmB,CAAA,EACnC,EAAc,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAc,OAAO,EAAK;QAAgB,UAAU,EAAI,iBAAiB;kBACtE,EAAY,EAAiB;QACjB,CAAA;OACT,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QACE,MAAK;QACL,KAAI;QACJ,OAAO,EAAK;QACZ,UAAU,EAAI,YAAY;QAC1B,aAAY;QACZ,CAAA;OACI,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAW,OAAO,EAAK;QAAU,UAAU,EAAI,WAAW;QAAE,WAAW;QAAK,CAAA;OACtE,CAAA;MACJ;;IACN,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAW,MAAK;MAAO,OAAO,EAAK;MAAW,UAAU,EAAI,YAAY;MAAI,CAAA;KACtE,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAU,OAAO,EAAK;MAAa,UAAU,EAAI,cAAc;MAAI,CAAA;KAC7D,CAAA;IACJ;;EACA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"MattersPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/MattersPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAyDhC,eAAO,MAAM,WAAW,EAAE,EAgMzB,CAAC"}
1
+ {"version":3,"file":"MattersPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/MattersPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA+DhC,eAAO,MAAM,WAAW,EAAE,EAgOzB,CAAC"}
@@ -1,58 +1,70 @@
1
- import { useClients as e, useCloseMatter as t, useCreateMatter as n, useMatters as r, usePracticeAreas as i } from "../hooks/useProServiceWorldApi.js";
2
- import { Banner as a, Empty as o, ErrorState as s, Field as c, LoadingBlock as l, MetricCard as u, Modal as d, NativeSelect as f, PageHeader as p, SectionCard as m, StatusPill as h, TableWrap as g, Td as _, TextArea as v, TextInput as y, Th as b, enumOptions as x, fmtDate as S } from "../components/ui.js";
3
- import { useAnalytics as C } from "../hooks/useAnalytics.js";
4
- import { useState as w } from "react";
5
- import { AlertTriangle as T, CheckCircle2 as E, CircleDot as D, FolderKanban as O, Lock as k, Plus as A, Search as j } from "lucide-react";
6
- import { Fragment as M, jsx as N, jsxs as P } from "react/jsx-runtime";
7
- import { Button as F } from "@burdenoff/fe-libs/ui";
1
+ import { useClients as e, useCloseMatter as t, useCreateMatter as n, useMatters as r, usePracticeAreas as i, useUpdateMatter as a } from "../hooks/useProServiceWorldApi.js";
2
+ import { Banner as o, Empty as s, ErrorState as c, Field as l, LoadingBlock as u, MetricCard as d, Modal as f, NativeSelect as p, PageHeader as m, SectionCard as h, StatusPill as g, TableWrap as _, Td as v, TextArea as y, TextInput as b, Th as x, enumOptions as S, fmtDate as C } from "../components/ui.js";
3
+ import { useAnalytics as w } from "../hooks/useAnalytics.js";
4
+ import { impactFeedback as T } from "../utils/native.js";
5
+ import { TagPicker as E } from "../components/TagPicker.js";
6
+ import { LoadMore as ee } from "../components/LoadMore.js";
7
+ import { useMemo as te, useState as D } from "react";
8
+ import { AlertTriangle as O, CheckCircle2 as k, CircleDot as A, FolderKanban as j, Lock as M, Plus as N, Search as P } from "lucide-react";
9
+ import { Fragment as F, jsx as I, jsxs as L } from "react/jsx-runtime";
10
+ import { Button as R } from "@burdenoff/fe-libs/ui";
8
11
  //#region src/proserviceworld/pages/MattersPage.tsx
9
- var I = [
12
+ var z = [
10
13
  "OPEN",
11
14
  "ON_HOLD",
12
15
  "CLOSED",
13
16
  "ARCHIVED"
14
- ], L = [
17
+ ], B = [
15
18
  "LOW",
16
19
  "MEDIUM",
17
20
  "HIGH",
18
21
  "CRITICAL"
19
- ], R = () => /* @__PURE__ */ N("span", {
22
+ ], ne = () => /* @__PURE__ */ I("span", {
20
23
  className: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md bg-status-warning-bg-subtle text-status-warning-text",
21
24
  title: "Privileged / confidential",
22
25
  "aria-label": "Privileged",
23
- children: /* @__PURE__ */ N(k, { className: "h-3 w-3" })
24
- }), z = () => {
25
- let { emitIntent: e, emitFact: n } = C(), [i, c] = w(""), [d, v] = w(""), [L, z] = w(!1), [V, H] = w(null), { data: U, isLoading: W, error: G, refetch: K } = r({
26
+ children: /* @__PURE__ */ I(M, { className: "h-3 w-3" })
27
+ }), V = 25, H = () => {
28
+ let { emitIntent: e, emitFact: n } = w(), [i, l] = D(""), [f, y] = D(""), [B, H] = D(!1), [U, W] = D(null), [G, K] = D({
29
+ limit: V,
30
+ offset: 0
31
+ }), { data: q, isLoading: J, error: Y, refetch: X } = r({
26
32
  search: i.trim() || void 0,
27
- status: d || void 0
28
- }), q = t(), J = U?.items ?? [], Y = J.filter((e) => e.status === "OPEN").length, X = J.filter((e) => e.isPrivileged).length, Z = J.filter((e) => e.priority === "HIGH" || e.priority === "CRITICAL").length;
29
- return /* @__PURE__ */ P("div", {
33
+ status: f || void 0,
34
+ pagination: G
35
+ }), Z = t(), ie = a(), Q = te(() => q?.items ?? [], [q]), ae = Q.filter((e) => e.status === "OPEN").length, oe = Q.filter((e) => e.isPrivileged).length, $ = Q.filter((e) => e.priority === "HIGH" || e.priority === "CRITICAL").length;
36
+ return /* @__PURE__ */ L("div", {
30
37
  className: "space-y-6",
31
38
  children: [
32
- /* @__PURE__ */ N(p, {
39
+ /* @__PURE__ */ I(m, {
33
40
  title: "Matters",
34
41
  subtitle: "The regulated threads of work — legal matters, audits, filings — with their own deadlines and sensitive-document handling.",
35
- icon: /* @__PURE__ */ N(O, { className: "h-6 w-6" }),
36
- actions: /* @__PURE__ */ P(F, {
42
+ icon: /* @__PURE__ */ I(j, { className: "h-6 w-6" }),
43
+ actions: /* @__PURE__ */ L(R, {
37
44
  onClick: () => {
38
- e("psw.matter.create_clicked"), z(!0);
45
+ T("light"), e("psw.matter.create_clicked"), H(!0);
39
46
  },
40
- children: [/* @__PURE__ */ N(A, { className: "mr-1.5 h-4 w-4" }), "New matter"]
47
+ children: [/* @__PURE__ */ I(N, { className: "mr-1.5 h-4 w-4" }), "New matter"]
41
48
  })
42
49
  }),
43
- V && /* @__PURE__ */ N(a, {
44
- tone: V.tone,
45
- onClose: () => H(null),
46
- children: V.text
50
+ U && /* @__PURE__ */ I(o, {
51
+ tone: U.tone,
52
+ onClose: () => W(null),
53
+ children: U.text
47
54
  }),
48
- /* @__PURE__ */ P("div", {
55
+ /* @__PURE__ */ L("div", {
49
56
  className: "flex flex-col gap-2 sm:flex-row sm:items-center",
50
- children: [/* @__PURE__ */ P("div", {
57
+ children: [/* @__PURE__ */ L("div", {
51
58
  className: "relative flex-1",
52
- children: [/* @__PURE__ */ N(j, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ N(y, {
59
+ children: [/* @__PURE__ */ I(P, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ I(b, {
53
60
  placeholder: "Search matters…",
54
61
  value: i,
55
- onChange: (e) => c(e.target.value),
62
+ onChange: (e) => {
63
+ l(e.target.value), K({
64
+ limit: V,
65
+ offset: 0
66
+ });
67
+ },
56
68
  onKeyDown: (e) => {
57
69
  e.key === "Enter" && i.trim() && n("psw.search.performed", {
58
70
  entityType: "matter",
@@ -61,124 +73,151 @@ var I = [
61
73
  },
62
74
  className: "pl-9"
63
75
  })]
64
- }), /* @__PURE__ */ P(f, {
65
- value: d,
66
- onChange: (e) => v(e.target.value),
76
+ }), /* @__PURE__ */ L(p, {
77
+ value: f,
78
+ onChange: (e) => {
79
+ y(e.target.value), K({
80
+ limit: V,
81
+ offset: 0
82
+ });
83
+ },
67
84
  className: "sm:w-48",
68
- children: [/* @__PURE__ */ N("option", {
85
+ children: [/* @__PURE__ */ I("option", {
69
86
  value: "",
70
87
  children: "All statuses"
71
- }), x(I)]
88
+ }), S(z)]
72
89
  })]
73
90
  }),
74
- W ? /* @__PURE__ */ N(l, {}) : G ? /* @__PURE__ */ N(s, {
75
- error: G,
76
- onRetry: () => void K()
77
- }) : !U || U.items.length === 0 ? /* @__PURE__ */ N(o, {
91
+ J ? /* @__PURE__ */ I(u, {}) : Y ? /* @__PURE__ */ I(c, {
92
+ error: Y,
93
+ onRetry: () => void X()
94
+ }) : !q || q.items.length === 0 ? /* @__PURE__ */ I(s, {
78
95
  title: "No matters yet",
79
96
  description: "Open a matter to track a regulated thread of work for a client.",
80
- icon: /* @__PURE__ */ N(O, { className: "h-8 w-8" }),
81
- action: /* @__PURE__ */ P(F, {
82
- onClick: () => z(!0),
83
- children: [/* @__PURE__ */ N(A, { className: "mr-1.5 h-4 w-4" }), "New matter"]
97
+ icon: /* @__PURE__ */ I(j, { className: "h-8 w-8" }),
98
+ action: /* @__PURE__ */ L(R, {
99
+ onClick: () => H(!0),
100
+ children: [/* @__PURE__ */ I(N, { className: "mr-1.5 h-4 w-4" }), "New matter"]
84
101
  })
85
- }) : /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ P("div", {
102
+ }) : /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ L("div", {
86
103
  className: "grid grid-cols-2 gap-3 lg:grid-cols-4",
87
104
  children: [
88
- /* @__PURE__ */ N(u, {
105
+ /* @__PURE__ */ I(d, {
89
106
  label: "Matters",
90
- value: U.totalCount,
91
- icon: /* @__PURE__ */ N(O, { className: "h-4 w-4" })
107
+ value: q.totalCount,
108
+ icon: /* @__PURE__ */ I(j, { className: "h-4 w-4" })
92
109
  }),
93
- /* @__PURE__ */ N(u, {
110
+ /* @__PURE__ */ I(d, {
94
111
  label: "Open",
95
- value: Y,
96
- icon: /* @__PURE__ */ N(D, { className: "h-4 w-4" }),
112
+ value: ae,
113
+ icon: /* @__PURE__ */ I(A, { className: "h-4 w-4" }),
97
114
  tone: "accent"
98
115
  }),
99
- /* @__PURE__ */ N(u, {
116
+ /* @__PURE__ */ I(d, {
100
117
  label: "Privileged",
101
- value: X,
102
- icon: /* @__PURE__ */ N(k, { className: "h-4 w-4" }),
118
+ value: oe,
119
+ icon: /* @__PURE__ */ I(M, { className: "h-4 w-4" }),
103
120
  tone: "warning"
104
121
  }),
105
- /* @__PURE__ */ N(u, {
122
+ /* @__PURE__ */ I(d, {
106
123
  label: "High priority",
107
- value: Z,
108
- icon: /* @__PURE__ */ N(T, { className: "h-4 w-4" }),
109
- tone: Z ? "danger" : "neutral"
124
+ value: $,
125
+ icon: /* @__PURE__ */ I(O, { className: "h-4 w-4" }),
126
+ tone: $ ? "danger" : "neutral"
110
127
  })
111
128
  ]
112
- }), /* @__PURE__ */ N(m, {
113
- title: `${U.totalCount} matter${U.totalCount === 1 ? "" : "s"}`,
114
- children: /* @__PURE__ */ P(g, { children: [/* @__PURE__ */ N("thead", { children: /* @__PURE__ */ P("tr", { children: [
115
- /* @__PURE__ */ N(b, { children: "Matter" }),
116
- /* @__PURE__ */ N(b, { children: "Client" }),
117
- /* @__PURE__ */ N(b, { children: "Priority" }),
118
- /* @__PURE__ */ N(b, { children: "Status" }),
119
- /* @__PURE__ */ N(b, { children: "Due" }),
120
- /* @__PURE__ */ N(b, {
129
+ }), /* @__PURE__ */ L(h, {
130
+ title: `${q.totalCount} matter${q.totalCount === 1 ? "" : "s"}`,
131
+ children: [/* @__PURE__ */ L(_, { children: [/* @__PURE__ */ I("thead", { children: /* @__PURE__ */ L("tr", { children: [
132
+ /* @__PURE__ */ I(x, { children: "Matter" }),
133
+ /* @__PURE__ */ I(x, { children: "Client" }),
134
+ /* @__PURE__ */ I(x, { children: "Priority" }),
135
+ /* @__PURE__ */ I(x, { children: "Status" }),
136
+ /* @__PURE__ */ I(x, { children: "Due" }),
137
+ /* @__PURE__ */ I(x, { children: "Tags" }),
138
+ /* @__PURE__ */ I(x, {
121
139
  className: "text-right",
122
140
  children: "Actions"
123
141
  })
124
- ] }) }), /* @__PURE__ */ N("tbody", { children: U.items.map((e) => /* @__PURE__ */ P("tr", {
142
+ ] }) }), /* @__PURE__ */ I("tbody", { children: q.items.map((e) => /* @__PURE__ */ L("tr", {
125
143
  className: "transition-colors hover:bg-bg-sunken/60",
126
144
  children: [
127
- /* @__PURE__ */ P(_, { children: [/* @__PURE__ */ P("span", {
145
+ /* @__PURE__ */ L(v, { children: [/* @__PURE__ */ L("span", {
128
146
  className: "flex items-center gap-2 font-medium text-text-primary",
129
- children: [e.isPrivileged && /* @__PURE__ */ N(R, {}), /* @__PURE__ */ N("span", {
147
+ children: [e.isPrivileged && /* @__PURE__ */ I(ne, {}), /* @__PURE__ */ I("span", {
130
148
  className: "truncate",
131
149
  children: e.title
132
150
  })]
133
- }), e.reference ? /* @__PURE__ */ P("div", {
151
+ }), e.reference ? /* @__PURE__ */ L("div", {
134
152
  className: "mt-0.5 pl-0 text-xs text-text-tertiary",
135
153
  children: ["Ref: ", e.reference]
136
154
  }) : null] }),
137
- /* @__PURE__ */ N(_, {
155
+ /* @__PURE__ */ I(v, {
138
156
  className: "text-text-primary",
139
157
  children: e.client?.name ?? "—"
140
158
  }),
141
- /* @__PURE__ */ N(_, { children: /* @__PURE__ */ N(h, { status: e.priority }) }),
142
- /* @__PURE__ */ N(_, { children: /* @__PURE__ */ N(h, { status: e.status }) }),
143
- /* @__PURE__ */ N(_, { children: S(e.dueDate) }),
144
- /* @__PURE__ */ N(_, {
159
+ /* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(g, { status: e.priority }) }),
160
+ /* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(g, { status: e.status }) }),
161
+ /* @__PURE__ */ I(v, { children: C(e.dueDate) }),
162
+ /* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(E, {
163
+ entityType: "PswMatter",
164
+ entityId: e.id,
165
+ tags: e.tags ?? [],
166
+ onChange: async (t) => {
167
+ await ie.mutateAsync({
168
+ id: e.id,
169
+ input: { tags: t }
170
+ }), X();
171
+ },
172
+ maxTags: 5
173
+ }) }),
174
+ /* @__PURE__ */ I(v, {
145
175
  className: "text-right",
146
- children: e.status !== "CLOSED" && e.status !== "ARCHIVED" ? /* @__PURE__ */ P(F, {
176
+ children: e.status !== "CLOSED" && e.status !== "ARCHIVED" ? /* @__PURE__ */ L(R, {
147
177
  variant: "ghost",
148
178
  size: "sm",
149
- disabled: q.isPending,
150
- onClick: () => q.mutate(e.id, {
179
+ disabled: Z.isPending,
180
+ onClick: () => Z.mutate(e.id, {
151
181
  onSuccess: () => {
152
- n("psw.matter.closed", { matterId: e.id }), H({
182
+ n("psw.matter.closed", { matterId: e.id }), W({
153
183
  tone: "success",
154
184
  text: `${e.title} closed.`
155
185
  });
156
186
  },
157
- onError: (e) => H({
187
+ onError: (e) => W({
158
188
  tone: "danger",
159
189
  text: e instanceof Error ? e.message : "Failed."
160
190
  })
161
191
  }),
162
- children: [/* @__PURE__ */ N(E, { className: "mr-1 h-4 w-4" }), " Close"]
163
- }) : /* @__PURE__ */ N("span", {
192
+ children: [/* @__PURE__ */ I(k, { className: "mr-1 h-4 w-4" }), " Close"]
193
+ }) : /* @__PURE__ */ I("span", {
164
194
  className: "text-xs text-text-tertiary",
165
- children: e.closedAt ? `Closed ${S(e.closedAt)}` : "—"
195
+ children: e.closedAt ? `Closed ${C(e.closedAt)}` : "—"
166
196
  })
167
197
  })
168
198
  ]
169
- }, e.id)) })] })
199
+ }, e.id)) })] }), /* @__PURE__ */ I(ee, {
200
+ hasMore: q?.pageInfo?.hasMore ?? !1,
201
+ loading: J,
202
+ loadedCount: Q.length,
203
+ totalCount: q?.totalCount ?? Q.length,
204
+ onLoadMore: () => K((e) => ({
205
+ limit: V,
206
+ offset: e.offset + V
207
+ }))
208
+ })]
170
209
  })] }),
171
- L && /* @__PURE__ */ N(B, {
172
- onClose: () => z(!1),
173
- onSaved: (e) => H({
210
+ B && /* @__PURE__ */ I(re, {
211
+ onClose: () => H(!1),
212
+ onSaved: (e) => W({
174
213
  tone: "success",
175
214
  text: `${e} opened.`
176
215
  })
177
216
  })
178
217
  ]
179
218
  });
180
- }, B = ({ onClose: t, onSaved: r }) => {
181
- let o = n(), { emitFact: s } = C(), l = e({ pagination: { limit: 100 } }), u = i(), [p, m] = w({
219
+ }, re = ({ onClose: t, onSaved: r }) => {
220
+ let a = n(), { emitFact: s } = w(), c = e({ pagination: { limit: 100 } }), u = i(), [d, m] = D({
182
221
  clientId: "",
183
222
  practiceAreaId: "",
184
223
  title: "",
@@ -187,138 +226,138 @@ var I = [
187
226
  priority: "MEDIUM",
188
227
  dueDate: "",
189
228
  isPrivileged: !1
190
- }), [h, g] = w(null), _ = (e) => (t) => m((n) => ({
229
+ }), [h, g] = D(null), _ = (e) => (t) => m((n) => ({
191
230
  ...n,
192
231
  [e]: t.target.value
193
232
  }));
194
- return /* @__PURE__ */ N(d, {
233
+ return /* @__PURE__ */ I(f, {
195
234
  open: !0,
196
235
  onClose: t,
197
236
  title: "New matter",
198
- footer: /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ N(F, {
237
+ footer: /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ I(R, {
199
238
  variant: "outline",
200
239
  onClick: t,
201
- disabled: o.isPending,
240
+ disabled: a.isPending,
202
241
  children: "Cancel"
203
- }), /* @__PURE__ */ N(F, {
242
+ }), /* @__PURE__ */ I(R, {
204
243
  onClick: () => {
205
- if (g(null), !p.clientId) return g("Select a client.");
206
- if (!p.title.trim()) return g("Matter title is required.");
207
- o.mutate({
208
- clientId: p.clientId,
209
- practiceAreaId: p.practiceAreaId || null,
210
- title: p.title.trim(),
211
- reference: p.reference.trim() || null,
212
- description: p.description.trim() || null,
213
- priority: p.priority,
214
- isPrivileged: p.isPrivileged,
215
- dueDate: p.dueDate ? new Date(p.dueDate).toISOString() : null
244
+ if (g(null), !d.clientId) return g("Select a client.");
245
+ if (!d.title.trim()) return g("Matter title is required.");
246
+ a.mutate({
247
+ clientId: d.clientId,
248
+ practiceAreaId: d.practiceAreaId || null,
249
+ title: d.title.trim(),
250
+ reference: d.reference.trim() || null,
251
+ description: d.description.trim() || null,
252
+ priority: d.priority,
253
+ isPrivileged: d.isPrivileged,
254
+ dueDate: d.dueDate ? new Date(d.dueDate).toISOString() : null
216
255
  }, {
217
256
  onSuccess: (e) => {
218
257
  let n = e.pswCreateMatter;
219
258
  s("psw.matter.created", {
220
259
  matterId: n.id,
221
- clientId: p.clientId
222
- }), r(p.title.trim()), t();
260
+ clientId: d.clientId
261
+ }), r(d.title.trim()), t();
223
262
  },
224
263
  onError: (e) => g(e instanceof Error ? e.message : "Failed.")
225
264
  });
226
265
  },
227
- disabled: o.isPending,
228
- children: o.isPending ? "Opening…" : "Open matter"
266
+ disabled: a.isPending,
267
+ children: a.isPending ? "Opening…" : "Open matter"
229
268
  })] }),
230
- children: /* @__PURE__ */ P("div", {
269
+ children: /* @__PURE__ */ L("div", {
231
270
  className: "space-y-3",
232
271
  children: [
233
- h && /* @__PURE__ */ N(a, {
272
+ h && /* @__PURE__ */ I(o, {
234
273
  tone: "danger",
235
274
  children: h
236
275
  }),
237
- /* @__PURE__ */ N(c, {
276
+ /* @__PURE__ */ I(l, {
238
277
  label: "Client",
239
278
  required: !0,
240
- children: /* @__PURE__ */ P(f, {
241
- value: p.clientId,
279
+ children: /* @__PURE__ */ L(p, {
280
+ value: d.clientId,
242
281
  onChange: _("clientId"),
243
- children: [/* @__PURE__ */ N("option", {
282
+ children: [/* @__PURE__ */ I("option", {
244
283
  value: "",
245
284
  children: "Select a client…"
246
- }), l.data?.items.map((e) => /* @__PURE__ */ N("option", {
285
+ }), c.data?.items.map((e) => /* @__PURE__ */ I("option", {
247
286
  value: e.id,
248
287
  children: e.name
249
288
  }, e.id))]
250
289
  })
251
290
  }),
252
- /* @__PURE__ */ N(c, {
291
+ /* @__PURE__ */ I(l, {
253
292
  label: "Title",
254
293
  required: !0,
255
- children: /* @__PURE__ */ N(y, {
256
- value: p.title,
294
+ children: /* @__PURE__ */ I(b, {
295
+ value: d.title,
257
296
  onChange: _("title"),
258
297
  placeholder: "Regulatory investigation — SEC inquiry"
259
298
  })
260
299
  }),
261
- /* @__PURE__ */ P("div", {
300
+ /* @__PURE__ */ L("div", {
262
301
  className: "grid grid-cols-2 gap-3",
263
- children: [/* @__PURE__ */ N(c, {
302
+ children: [/* @__PURE__ */ I(l, {
264
303
  label: "Reference",
265
- children: /* @__PURE__ */ N(y, {
266
- value: p.reference,
304
+ children: /* @__PURE__ */ I(b, {
305
+ value: d.reference,
267
306
  onChange: _("reference"),
268
307
  placeholder: "MAT-2026-014"
269
308
  })
270
- }), /* @__PURE__ */ N(c, {
309
+ }), /* @__PURE__ */ I(l, {
271
310
  label: "Priority",
272
- children: /* @__PURE__ */ N(f, {
273
- value: p.priority,
311
+ children: /* @__PURE__ */ I(p, {
312
+ value: d.priority,
274
313
  onChange: _("priority"),
275
- children: x(L)
314
+ children: S(B)
276
315
  })
277
316
  })]
278
317
  }),
279
- /* @__PURE__ */ P("div", {
318
+ /* @__PURE__ */ L("div", {
280
319
  className: "grid grid-cols-2 gap-3",
281
- children: [/* @__PURE__ */ N(c, {
320
+ children: [/* @__PURE__ */ I(l, {
282
321
  label: "Practice area",
283
- children: /* @__PURE__ */ P(f, {
284
- value: p.practiceAreaId,
322
+ children: /* @__PURE__ */ L(p, {
323
+ value: d.practiceAreaId,
285
324
  onChange: _("practiceAreaId"),
286
- children: [/* @__PURE__ */ N("option", {
325
+ children: [/* @__PURE__ */ I("option", {
287
326
  value: "",
288
327
  children: "Unassigned"
289
- }), u.data?.map((e) => /* @__PURE__ */ N("option", {
328
+ }), u.data?.map((e) => /* @__PURE__ */ I("option", {
290
329
  value: e.id,
291
330
  children: e.name
292
331
  }, e.id))]
293
332
  })
294
- }), /* @__PURE__ */ N(c, {
333
+ }), /* @__PURE__ */ I(l, {
295
334
  label: "Due date",
296
- children: /* @__PURE__ */ N(y, {
335
+ children: /* @__PURE__ */ I(b, {
297
336
  type: "date",
298
- value: p.dueDate,
337
+ value: d.dueDate,
299
338
  onChange: _("dueDate")
300
339
  })
301
340
  })]
302
341
  }),
303
- /* @__PURE__ */ N(c, {
342
+ /* @__PURE__ */ I(l, {
304
343
  label: "Description",
305
- children: /* @__PURE__ */ N(v, {
306
- value: p.description,
344
+ children: /* @__PURE__ */ I(y, {
345
+ value: d.description,
307
346
  onChange: _("description")
308
347
  })
309
348
  }),
310
- /* @__PURE__ */ P("label", {
349
+ /* @__PURE__ */ L("label", {
311
350
  className: "flex items-center gap-2.5 rounded-lg border border-border-seam bg-bg-canvas px-3 py-2.5 text-sm text-text-secondary",
312
- children: [/* @__PURE__ */ N("input", {
351
+ children: [/* @__PURE__ */ I("input", {
313
352
  type: "checkbox",
314
- checked: p.isPrivileged,
353
+ checked: d.isPrivileged,
315
354
  onChange: (e) => m((t) => ({
316
355
  ...t,
317
356
  isPrivileged: e.target.checked
318
357
  }))
319
- }), /* @__PURE__ */ P("span", {
358
+ }), /* @__PURE__ */ L("span", {
320
359
  className: "flex items-center gap-1.5",
321
- children: [/* @__PURE__ */ N(k, { className: "h-3.5 w-3.5 text-status-warning-text" }), "Privileged / confidential matter"]
360
+ children: [/* @__PURE__ */ I(M, { className: "h-3.5 w-3.5 text-status-warning-text" }), "Privileged / confidential matter"]
322
361
  })]
323
362
  })
324
363
  ]
@@ -326,6 +365,6 @@ var I = [
326
365
  });
327
366
  };
328
367
  //#endregion
329
- export { z as MattersPage };
368
+ export { H as MattersPage };
330
369
 
331
370
  //# sourceMappingURL=MattersPage.js.map