@burdenoff/microfe-proserviceworld 2026.709.1 → 2026.709.3
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.
- package/dist/index.js +2 -1
- package/dist/node_modules/clsx/dist/clsx.js +1 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +130 -130
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoot.js +4 -2
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +95 -42
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/DiscussionThread.js.map +1 -1
- package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts +8 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts.map +1 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.js +17 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -0
- package/dist/proserviceworld/components/LoadMore.d.ts +12 -0
- package/dist/proserviceworld/components/LoadMore.d.ts.map +1 -0
- package/dist/proserviceworld/components/LoadMore.js +30 -0
- package/dist/proserviceworld/components/LoadMore.js.map +1 -0
- package/dist/proserviceworld/components/TagPicker.d.ts +11 -0
- package/dist/proserviceworld/components/TagPicker.d.ts.map +1 -0
- package/dist/proserviceworld/components/TagPicker.js +31 -0
- package/dist/proserviceworld/components/TagPicker.js.map +1 -0
- package/dist/proserviceworld/components/ui.js.map +1 -1
- package/dist/proserviceworld/hooks/useAnalytics.js.map +1 -1
- package/dist/proserviceworld/hooks/useEntityComments.js.map +1 -1
- package/dist/proserviceworld/hooks/useFiles.js.map +1 -1
- package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts +10 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts.map +1 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.js +24 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +47 -27
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/i18n/index.d.ts +13 -0
- package/dist/proserviceworld/i18n/index.d.ts.map +1 -0
- package/dist/proserviceworld/i18n/index.js +171 -0
- package/dist/proserviceworld/i18n/index.js.map +1 -0
- package/dist/proserviceworld/index.d.ts +1 -0
- package/dist/proserviceworld/index.d.ts.map +1 -1
- package/dist/proserviceworld/index.js +5 -3
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/ClientsPage.js +173 -121
- package/dist/proserviceworld/pages/ClientsPage.js.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.js +168 -120
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.js +183 -144
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
- package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -1
- package/dist/proserviceworld/pages/TimePage.js.map +1 -1
- package/dist/proserviceworld/providers/ProServiceWorldProvider.js.map +1 -1
- package/dist/proserviceworld/services/assistantApi.js.map +1 -1
- package/dist/proserviceworld/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/proserviceworld/utils/cn.js +1 -1
- package/dist/proserviceworld/utils/cn.js.map +1 -1
- package/dist/proserviceworld/utils/native.d.ts +23 -0
- package/dist/proserviceworld/utils/native.d.ts.map +1 -0
- package/dist/proserviceworld/utils/native.js +14 -0
- package/dist/proserviceworld/utils/native.js.map +1 -0
- package/dist/proserviceworld/utils/nav.js.map +1 -1
- package/dist/proserviceworld/utils/navigation.js.map +1 -1
- package/dist/proserviceworld/utils/pageContext.js +14 -16
- package/dist/proserviceworld/utils/pageContext.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Receipt, CheckCircle2, AlertCircle, Wallet } from 'lucide-react';\nimport {\n useInvoices,\n useCreateInvoice,\n useMarkInvoicePaid,\n useClients,\n useEngagements,\n type PswInvoiceStatus,\n} from '../hooks/useProServiceWorldApi';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMoney,\n} from '../components/ui';\nimport { cn } from '../utils/cn';\n\nconst STATUSES: PswInvoiceStatus[] = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'];\n\nexport const InvoicesPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswInvoiceStatus | ''>('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useInvoices({ status: statusFilter || undefined });\n const markPaid = useMarkInvoicePaid();\n const clients = useClients({ pagination: { limit: 100 } });\n const clientName = useMemo(\n () => new Map((clients.data?.items ?? []).map((c) => [c.id, c.name])),\n [clients.data]\n );\n\n const outstandingInvoices = (data?.items ?? []).filter(\n (i) => i.status === 'SENT' || i.status === 'OVERDUE'\n );\n const paidInvoices = (data?.items ?? []).filter((i) => i.status === 'PAID');\n const outstanding = outstandingInvoices.reduce((s, i) => s + i.total, 0);\n const collected = paidInvoices.reduce((s, i) => s + i.total, 0);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Invoices\"\n subtitle=\"Assemble invoices from time, milestones and fixed-fee schedules — and track what's outstanding.\"\n icon={<Receipt className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\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-1 gap-3 sm:grid-cols-3\">\n <MetricCard\n label=\"Invoices\"\n value={data?.totalCount ?? 0}\n icon={<Receipt className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Outstanding\"\n value={fmtMoney(outstanding)}\n hint={`${outstandingInvoices.length} sent + overdue`}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n <MetricCard\n label=\"Collected\"\n value={fmtMoney(collected)}\n hint={`${paidInvoices.length} paid`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n </div>\n\n <div className=\"flex justify-end\">\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswInvoiceStatus | '')}\n className=\"sm:w-48\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\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 invoices yet\"\n description=\"Raise an invoice for a client to start tracking receivables.\"\n icon={<Receipt className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Invoices\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Number</Th>\n <Th>Client</Th>\n <Th>Total</Th>\n <Th>Status</Th>\n <Th>Due</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((i) => (\n <tr key={i.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td>\n <span className=\"font-medium text-text-primary\">{i.number}</span>\n </Td>\n <Td className=\"text-text-primary\">{clientName.get(i.clientId) ?? '—'}</Td>\n <Td className=\"whitespace-nowrap font-semibold text-text-primary\">\n {fmtMoney(i.total, i.currency)}\n </Td>\n <Td>\n <StatusPill status={i.status} />\n </Td>\n <Td\n className={cn(\n 'whitespace-nowrap',\n i.status === 'OVERDUE' && 'font-medium text-status-error-text'\n )}\n >\n {fmtDate(i.dueDate)}\n </Td>\n <Td className=\"text-right\">\n {i.status !== 'PAID' && i.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={markPaid.isPending}\n onClick={() =>\n markPaid.mutate(i.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${i.number} marked paid.` }),\n onError: (e) =>\n setBanner({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Failed.',\n }),\n })\n }\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" />\n Mark paid\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <InvoiceModal\n onClose={() => setCreating(false)}\n onSaved={(n) => setBanner({ tone: 'success', text: `${n} created.` })}\n />\n )}\n </div>\n );\n};\n\nconst InvoiceModal: FC<{ onClose: () => void; onSaved: (number: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateInvoice();\n const clients = useClients({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n clientId: '',\n engagementId: '',\n number: '',\n currency: 'USD',\n subtotal: '',\n taxAmount: '',\n issueDate: new Date().toISOString().slice(0, 10),\n dueDate: '',\n notes: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const engagements = useEngagements({\n clientId: form.clientId || undefined,\n pagination: { limit: 100 },\n });\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const total = (Number(form.subtotal) || 0) + (Number(form.taxAmount) || 0);\n\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.number.trim()) return setErr('Invoice number is required.');\n create.mutate(\n {\n clientId: form.clientId,\n engagementId: form.engagementId || null,\n number: form.number.trim(),\n currency: form.currency || 'USD',\n subtotal: Number(form.subtotal) || 0,\n taxAmount: Number(form.taxAmount) || 0,\n total,\n issueDate: form.issueDate ? new Date(form.issueDate).toISOString() : null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n notes: form.notes.trim() || null,\n },\n {\n onSuccess: () => {\n onSaved(form.number.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 invoice\"\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 invoice'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select…</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=\"Number\" required>\n <TextInput value={form.number} onChange={set('number')} placeholder=\"INV-2026-014\" />\n </Field>\n </div>\n <Field label=\"Engagement (optional)\">\n <NativeSelect\n value={form.engagementId}\n onChange={set('engagementId')}\n disabled={!form.clientId}\n >\n <option value=\"\">None</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Subtotal\">\n <TextInput type=\"number\" min=\"0\" value={form.subtotal} onChange={set('subtotal')} />\n </Field>\n <Field label=\"Tax\">\n <TextInput type=\"number\" min=\"0\" value={form.taxAmount} onChange={set('taxAmount')} />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <div className=\"rounded-lg bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n Total:{' '}\n <span className=\"font-semibold text-text-primary\">{fmtMoney(total, form.currency)}</span>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Issue date\">\n <TextInput type=\"date\" value={form.issueDate} onChange={set('issueDate')} />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n </div>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Receipt, CheckCircle2, AlertCircle, Wallet } from 'lucide-react';\nimport {\n useInvoices,\n useCreateInvoice,\n useMarkInvoicePaid,\n useClients,\n useEngagements,\n type PswInvoiceStatus,\n} from '../hooks/useProServiceWorldApi';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMoney,\n} from '../components/ui';\nimport { cn } from '../utils/cn';\n\nconst STATUSES: PswInvoiceStatus[] = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'];\n\nexport const InvoicesPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswInvoiceStatus | ''>('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useInvoices({ status: statusFilter || undefined });\n const markPaid = useMarkInvoicePaid();\n const clients = useClients({ pagination: { limit: 100 } });\n const clientName = useMemo(\n () => new Map((clients.data?.items ?? []).map((c) => [c.id, c.name])),\n [clients.data]\n );\n\n const outstandingInvoices = (data?.items ?? []).filter(\n (i) => i.status === 'SENT' || i.status === 'OVERDUE'\n );\n const paidInvoices = (data?.items ?? []).filter((i) => i.status === 'PAID');\n const outstanding = outstandingInvoices.reduce((s, i) => s + i.total, 0);\n const collected = paidInvoices.reduce((s, i) => s + i.total, 0);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Invoices\"\n subtitle=\"Assemble invoices from time, milestones and fixed-fee schedules — and track what's outstanding.\"\n icon={<Receipt className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\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-1 gap-3 sm:grid-cols-3\">\n <MetricCard\n label=\"Invoices\"\n value={data?.totalCount ?? 0}\n icon={<Receipt className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Outstanding\"\n value={fmtMoney(outstanding)}\n hint={`${outstandingInvoices.length} sent + overdue`}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n <MetricCard\n label=\"Collected\"\n value={fmtMoney(collected)}\n hint={`${paidInvoices.length} paid`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n </div>\n\n <div className=\"flex justify-end\">\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswInvoiceStatus | '')}\n className=\"sm:w-48\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\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 invoices yet\"\n description=\"Raise an invoice for a client to start tracking receivables.\"\n icon={<Receipt className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Invoices\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Number</Th>\n <Th>Client</Th>\n <Th>Total</Th>\n <Th>Status</Th>\n <Th>Due</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((i) => (\n <tr key={i.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td>\n <span className=\"font-medium text-text-primary\">{i.number}</span>\n </Td>\n <Td className=\"text-text-primary\">{clientName.get(i.clientId) ?? '—'}</Td>\n <Td className=\"whitespace-nowrap font-semibold text-text-primary\">\n {fmtMoney(i.total, i.currency)}\n </Td>\n <Td>\n <StatusPill status={i.status} />\n </Td>\n <Td\n className={cn(\n 'whitespace-nowrap',\n i.status === 'OVERDUE' && 'font-medium text-status-error-text'\n )}\n >\n {fmtDate(i.dueDate)}\n </Td>\n <Td className=\"text-right\">\n {i.status !== 'PAID' && i.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={markPaid.isPending}\n onClick={() =>\n markPaid.mutate(i.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${i.number} marked paid.` }),\n onError: (e) =>\n setBanner({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Failed.',\n }),\n })\n }\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" />\n Mark paid\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <InvoiceModal\n onClose={() => setCreating(false)}\n onSaved={(n) => setBanner({ tone: 'success', text: `${n} created.` })}\n />\n )}\n </div>\n );\n};\n\nconst InvoiceModal: FC<{ onClose: () => void; onSaved: (number: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateInvoice();\n const clients = useClients({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n clientId: '',\n engagementId: '',\n number: '',\n currency: 'USD',\n subtotal: '',\n taxAmount: '',\n issueDate: new Date().toISOString().slice(0, 10),\n dueDate: '',\n notes: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const engagements = useEngagements({\n clientId: form.clientId || undefined,\n pagination: { limit: 100 },\n });\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const total = (Number(form.subtotal) || 0) + (Number(form.taxAmount) || 0);\n\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.number.trim()) return setErr('Invoice number is required.');\n create.mutate(\n {\n clientId: form.clientId,\n engagementId: form.engagementId || null,\n number: form.number.trim(),\n currency: form.currency || 'USD',\n subtotal: Number(form.subtotal) || 0,\n taxAmount: Number(form.taxAmount) || 0,\n total,\n issueDate: form.issueDate ? new Date(form.issueDate).toISOString() : null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n notes: form.notes.trim() || null,\n },\n {\n onSuccess: () => {\n onSaved(form.number.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 invoice\"\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 invoice'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select…</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=\"Number\" required>\n <TextInput value={form.number} onChange={set('number')} placeholder=\"INV-2026-014\" />\n </Field>\n </div>\n <Field label=\"Engagement (optional)\">\n <NativeSelect\n value={form.engagementId}\n onChange={set('engagementId')}\n disabled={!form.clientId}\n >\n <option value=\"\">None</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Subtotal\">\n <TextInput type=\"number\" min=\"0\" value={form.subtotal} onChange={set('subtotal')} />\n </Field>\n <Field label=\"Tax\">\n <TextInput type=\"number\" min=\"0\" value={form.taxAmount} onChange={set('taxAmount')} />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <div className=\"rounded-lg bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n Total:{' '}\n <span className=\"font-semibold text-text-primary\">{fmtMoney(total, form.currency)}</span>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Issue date\">\n <TextInput type=\"date\" value={form.issueDate} onChange={set('issueDate')} />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n </div>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
@@ -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;
|
|
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
|
|
3
|
-
import { useAnalytics as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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
|
|
12
|
+
var z = [
|
|
10
13
|
"OPEN",
|
|
11
14
|
"ON_HOLD",
|
|
12
15
|
"CLOSED",
|
|
13
16
|
"ARCHIVED"
|
|
14
|
-
],
|
|
17
|
+
], B = [
|
|
15
18
|
"LOW",
|
|
16
19
|
"MEDIUM",
|
|
17
20
|
"HIGH",
|
|
18
21
|
"CRITICAL"
|
|
19
|
-
],
|
|
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__ */
|
|
24
|
-
}),
|
|
25
|
-
let { emitIntent: e, emitFact: n } =
|
|
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:
|
|
28
|
-
|
|
29
|
-
|
|
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__ */
|
|
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__ */
|
|
36
|
-
actions: /* @__PURE__ */
|
|
42
|
+
icon: /* @__PURE__ */ I(j, { className: "h-6 w-6" }),
|
|
43
|
+
actions: /* @__PURE__ */ L(R, {
|
|
37
44
|
onClick: () => {
|
|
38
|
-
e("psw.matter.create_clicked"),
|
|
45
|
+
T("light"), e("psw.matter.create_clicked"), H(!0);
|
|
39
46
|
},
|
|
40
|
-
children: [/* @__PURE__ */ N
|
|
47
|
+
children: [/* @__PURE__ */ I(N, { className: "mr-1.5 h-4 w-4" }), "New matter"]
|
|
41
48
|
})
|
|
42
49
|
}),
|
|
43
|
-
|
|
44
|
-
tone:
|
|
45
|
-
onClose: () =>
|
|
46
|
-
children:
|
|
50
|
+
U && /* @__PURE__ */ I(o, {
|
|
51
|
+
tone: U.tone,
|
|
52
|
+
onClose: () => W(null),
|
|
53
|
+
children: U.text
|
|
47
54
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
55
|
+
/* @__PURE__ */ L("div", {
|
|
49
56
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
57
|
+
children: [/* @__PURE__ */ L("div", {
|
|
51
58
|
className: "relative flex-1",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
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) =>
|
|
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__ */
|
|
65
|
-
value:
|
|
66
|
-
onChange: (e) =>
|
|
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__ */
|
|
85
|
+
children: [/* @__PURE__ */ I("option", {
|
|
69
86
|
value: "",
|
|
70
87
|
children: "All statuses"
|
|
71
|
-
}),
|
|
88
|
+
}), S(z)]
|
|
72
89
|
})]
|
|
73
90
|
}),
|
|
74
|
-
|
|
75
|
-
error:
|
|
76
|
-
onRetry: () => void
|
|
77
|
-
}) : !
|
|
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__ */
|
|
81
|
-
action: /* @__PURE__ */
|
|
82
|
-
onClick: () =>
|
|
83
|
-
children: [/* @__PURE__ */ N
|
|
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__ */
|
|
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__ */
|
|
105
|
+
/* @__PURE__ */ I(d, {
|
|
89
106
|
label: "Matters",
|
|
90
|
-
value:
|
|
91
|
-
icon: /* @__PURE__ */
|
|
107
|
+
value: q.totalCount,
|
|
108
|
+
icon: /* @__PURE__ */ I(j, { className: "h-4 w-4" })
|
|
92
109
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
110
|
+
/* @__PURE__ */ I(d, {
|
|
94
111
|
label: "Open",
|
|
95
|
-
value:
|
|
96
|
-
icon: /* @__PURE__ */
|
|
112
|
+
value: ae,
|
|
113
|
+
icon: /* @__PURE__ */ I(A, { className: "h-4 w-4" }),
|
|
97
114
|
tone: "accent"
|
|
98
115
|
}),
|
|
99
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ I(d, {
|
|
100
117
|
label: "Privileged",
|
|
101
|
-
value:
|
|
102
|
-
icon: /* @__PURE__ */
|
|
118
|
+
value: oe,
|
|
119
|
+
icon: /* @__PURE__ */ I(M, { className: "h-4 w-4" }),
|
|
103
120
|
tone: "warning"
|
|
104
121
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ I(d, {
|
|
106
123
|
label: "High priority",
|
|
107
|
-
value:
|
|
108
|
-
icon: /* @__PURE__ */
|
|
109
|
-
tone:
|
|
124
|
+
value: $,
|
|
125
|
+
icon: /* @__PURE__ */ I(O, { className: "h-4 w-4" }),
|
|
126
|
+
tone: $ ? "danger" : "neutral"
|
|
110
127
|
})
|
|
111
128
|
]
|
|
112
|
-
}), /* @__PURE__ */
|
|
113
|
-
title: `${
|
|
114
|
-
children: /* @__PURE__ */
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
-
/* @__PURE__ */
|
|
119
|
-
/* @__PURE__ */
|
|
120
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
155
|
+
/* @__PURE__ */ I(v, {
|
|
138
156
|
className: "text-text-primary",
|
|
139
157
|
children: e.client?.name ?? "—"
|
|
140
158
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
-
/* @__PURE__ */
|
|
144
|
-
/* @__PURE__ */
|
|
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__ */
|
|
176
|
+
children: e.status !== "CLOSED" && e.status !== "ARCHIVED" ? /* @__PURE__ */ L(R, {
|
|
147
177
|
variant: "ghost",
|
|
148
178
|
size: "sm",
|
|
149
|
-
disabled:
|
|
150
|
-
onClick: () =>
|
|
179
|
+
disabled: Z.isPending,
|
|
180
|
+
onClick: () => Z.mutate(e.id, {
|
|
151
181
|
onSuccess: () => {
|
|
152
|
-
n("psw.matter.closed", { matterId: e.id }),
|
|
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) =>
|
|
187
|
+
onError: (e) => W({
|
|
158
188
|
tone: "danger",
|
|
159
189
|
text: e instanceof Error ? e.message : "Failed."
|
|
160
190
|
})
|
|
161
191
|
}),
|
|
162
|
-
children: [/* @__PURE__ */
|
|
163
|
-
}) : /* @__PURE__ */
|
|
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 ${
|
|
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
|
-
|
|
172
|
-
onClose: () =>
|
|
173
|
-
onSaved: (e) =>
|
|
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
|
-
},
|
|
181
|
-
let
|
|
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] =
|
|
229
|
+
}), [h, g] = D(null), _ = (e) => (t) => m((n) => ({
|
|
191
230
|
...n,
|
|
192
231
|
[e]: t.target.value
|
|
193
232
|
}));
|
|
194
|
-
return /* @__PURE__ */
|
|
233
|
+
return /* @__PURE__ */ I(f, {
|
|
195
234
|
open: !0,
|
|
196
235
|
onClose: t,
|
|
197
236
|
title: "New matter",
|
|
198
|
-
footer: /* @__PURE__ */
|
|
237
|
+
footer: /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ I(R, {
|
|
199
238
|
variant: "outline",
|
|
200
239
|
onClick: t,
|
|
201
|
-
disabled:
|
|
240
|
+
disabled: a.isPending,
|
|
202
241
|
children: "Cancel"
|
|
203
|
-
}), /* @__PURE__ */
|
|
242
|
+
}), /* @__PURE__ */ I(R, {
|
|
204
243
|
onClick: () => {
|
|
205
|
-
if (g(null), !
|
|
206
|
-
if (!
|
|
207
|
-
|
|
208
|
-
clientId:
|
|
209
|
-
practiceAreaId:
|
|
210
|
-
title:
|
|
211
|
-
reference:
|
|
212
|
-
description:
|
|
213
|
-
priority:
|
|
214
|
-
isPrivileged:
|
|
215
|
-
dueDate:
|
|
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:
|
|
222
|
-
}), r(
|
|
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:
|
|
228
|
-
children:
|
|
266
|
+
disabled: a.isPending,
|
|
267
|
+
children: a.isPending ? "Opening…" : "Open matter"
|
|
229
268
|
})] }),
|
|
230
|
-
children: /* @__PURE__ */
|
|
269
|
+
children: /* @__PURE__ */ L("div", {
|
|
231
270
|
className: "space-y-3",
|
|
232
271
|
children: [
|
|
233
|
-
h && /* @__PURE__ */
|
|
272
|
+
h && /* @__PURE__ */ I(o, {
|
|
234
273
|
tone: "danger",
|
|
235
274
|
children: h
|
|
236
275
|
}),
|
|
237
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ I(l, {
|
|
238
277
|
label: "Client",
|
|
239
278
|
required: !0,
|
|
240
|
-
children: /* @__PURE__ */
|
|
241
|
-
value:
|
|
279
|
+
children: /* @__PURE__ */ L(p, {
|
|
280
|
+
value: d.clientId,
|
|
242
281
|
onChange: _("clientId"),
|
|
243
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ I("option", {
|
|
244
283
|
value: "",
|
|
245
284
|
children: "Select a client…"
|
|
246
|
-
}),
|
|
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__ */
|
|
291
|
+
/* @__PURE__ */ I(l, {
|
|
253
292
|
label: "Title",
|
|
254
293
|
required: !0,
|
|
255
|
-
children: /* @__PURE__ */
|
|
256
|
-
value:
|
|
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__ */
|
|
300
|
+
/* @__PURE__ */ L("div", {
|
|
262
301
|
className: "grid grid-cols-2 gap-3",
|
|
263
|
-
children: [/* @__PURE__ */
|
|
302
|
+
children: [/* @__PURE__ */ I(l, {
|
|
264
303
|
label: "Reference",
|
|
265
|
-
children: /* @__PURE__ */
|
|
266
|
-
value:
|
|
304
|
+
children: /* @__PURE__ */ I(b, {
|
|
305
|
+
value: d.reference,
|
|
267
306
|
onChange: _("reference"),
|
|
268
307
|
placeholder: "MAT-2026-014"
|
|
269
308
|
})
|
|
270
|
-
}), /* @__PURE__ */
|
|
309
|
+
}), /* @__PURE__ */ I(l, {
|
|
271
310
|
label: "Priority",
|
|
272
|
-
children: /* @__PURE__ */
|
|
273
|
-
value:
|
|
311
|
+
children: /* @__PURE__ */ I(p, {
|
|
312
|
+
value: d.priority,
|
|
274
313
|
onChange: _("priority"),
|
|
275
|
-
children:
|
|
314
|
+
children: S(B)
|
|
276
315
|
})
|
|
277
316
|
})]
|
|
278
317
|
}),
|
|
279
|
-
/* @__PURE__ */
|
|
318
|
+
/* @__PURE__ */ L("div", {
|
|
280
319
|
className: "grid grid-cols-2 gap-3",
|
|
281
|
-
children: [/* @__PURE__ */
|
|
320
|
+
children: [/* @__PURE__ */ I(l, {
|
|
282
321
|
label: "Practice area",
|
|
283
|
-
children: /* @__PURE__ */
|
|
284
|
-
value:
|
|
322
|
+
children: /* @__PURE__ */ L(p, {
|
|
323
|
+
value: d.practiceAreaId,
|
|
285
324
|
onChange: _("practiceAreaId"),
|
|
286
|
-
children: [/* @__PURE__ */
|
|
325
|
+
children: [/* @__PURE__ */ I("option", {
|
|
287
326
|
value: "",
|
|
288
327
|
children: "Unassigned"
|
|
289
|
-
}), u.data?.map((e) => /* @__PURE__ */
|
|
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__ */
|
|
333
|
+
}), /* @__PURE__ */ I(l, {
|
|
295
334
|
label: "Due date",
|
|
296
|
-
children: /* @__PURE__ */
|
|
335
|
+
children: /* @__PURE__ */ I(b, {
|
|
297
336
|
type: "date",
|
|
298
|
-
value:
|
|
337
|
+
value: d.dueDate,
|
|
299
338
|
onChange: _("dueDate")
|
|
300
339
|
})
|
|
301
340
|
})]
|
|
302
341
|
}),
|
|
303
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ I(l, {
|
|
304
343
|
label: "Description",
|
|
305
|
-
children: /* @__PURE__ */
|
|
306
|
-
value:
|
|
344
|
+
children: /* @__PURE__ */ I(y, {
|
|
345
|
+
value: d.description,
|
|
307
346
|
onChange: _("description")
|
|
308
347
|
})
|
|
309
348
|
}),
|
|
310
|
-
/* @__PURE__ */
|
|
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__ */
|
|
351
|
+
children: [/* @__PURE__ */ I("input", {
|
|
313
352
|
type: "checkbox",
|
|
314
|
-
checked:
|
|
353
|
+
checked: d.isPrivileged,
|
|
315
354
|
onChange: (e) => m((t) => ({
|
|
316
355
|
...t,
|
|
317
356
|
isPrivileged: e.target.checked
|
|
318
357
|
}))
|
|
319
|
-
}), /* @__PURE__ */
|
|
358
|
+
}), /* @__PURE__ */ L("span", {
|
|
320
359
|
className: "flex items-center gap-1.5",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
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 {
|
|
368
|
+
export { H as MattersPage };
|
|
330
369
|
|
|
331
370
|
//# sourceMappingURL=MattersPage.js.map
|