@burdenoff/microfe-proserviceworld 2026.709.3 → 2026.709.4
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/proserviceworld/i18n/index.d.ts.map +1 -1
- package/dist/proserviceworld/i18n/index.js +30 -1
- package/dist/proserviceworld/i18n/index.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.js +123 -122
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.js +124 -123
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
- 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 { 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":"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"}
|
|
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';\nimport { t } from '../i18n';\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={t('psw.engagements.title')}\n subtitle={t('psw.engagements.subtitle')}\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 {t('psw.engagements.new')}\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={t('psw.engagements.searchPlaceholder')}\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=\"\">{t('psw.engagements.allStatuses')}</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=\"\">{t('psw.engagements.allClients')}</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={t('psw.engagements.emptyTitle')}\n description={t('psw.engagements.emptyDescription')}\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 {t('psw.engagements.new')}\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} engagement${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>{t('psw.engagements.tableEngagement')}</Th>\n <Th>{t('psw.engagements.tableClient')}</Th>\n <Th>{t('psw.engagements.tableFee')}</Th>\n <Th>{t('psw.engagements.tableStatus')}</Th>\n <Th>{t('psw.engagements.tableStarted')}</Th>\n <Th>{t('psw.engagements.tableTags')}</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={t('psw.engagements.newTitle')}\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":"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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;AAgEhC,eAAO,MAAM,WAAW,EAAE,EAgOzB,CAAC"}
|
|
@@ -1,67 +1,68 @@
|
|
|
1
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
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 {
|
|
4
|
-
import {
|
|
3
|
+
import { t as w } from "../i18n/index.js";
|
|
4
|
+
import { useAnalytics as T } from "../hooks/useAnalytics.js";
|
|
5
|
+
import { impactFeedback as ee } from "../utils/native.js";
|
|
5
6
|
import { TagPicker as E } from "../components/TagPicker.js";
|
|
6
|
-
import { LoadMore as
|
|
7
|
-
import { useMemo as
|
|
8
|
-
import { AlertTriangle as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import { Button as
|
|
7
|
+
import { LoadMore as D } from "../components/LoadMore.js";
|
|
8
|
+
import { useMemo as O, useState as k } from "react";
|
|
9
|
+
import { AlertTriangle as te, CheckCircle2 as A, CircleDot as j, FolderKanban as M, Lock as N, Plus as P, Search as F } from "lucide-react";
|
|
10
|
+
import { Fragment as I, jsx as L, jsxs as R } from "react/jsx-runtime";
|
|
11
|
+
import { Button as z } from "@burdenoff/fe-libs/ui";
|
|
11
12
|
//#region src/proserviceworld/pages/MattersPage.tsx
|
|
12
|
-
var
|
|
13
|
+
var B = [
|
|
13
14
|
"OPEN",
|
|
14
15
|
"ON_HOLD",
|
|
15
16
|
"CLOSED",
|
|
16
17
|
"ARCHIVED"
|
|
17
|
-
],
|
|
18
|
+
], V = [
|
|
18
19
|
"LOW",
|
|
19
20
|
"MEDIUM",
|
|
20
21
|
"HIGH",
|
|
21
22
|
"CRITICAL"
|
|
22
|
-
], ne = () => /* @__PURE__ */
|
|
23
|
+
], ne = () => /* @__PURE__ */ L("span", {
|
|
23
24
|
className: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md bg-status-warning-bg-subtle text-status-warning-text",
|
|
24
25
|
title: "Privileged / confidential",
|
|
25
26
|
"aria-label": "Privileged",
|
|
26
|
-
children: /* @__PURE__ */
|
|
27
|
-
}),
|
|
28
|
-
let { emitIntent: e, emitFact: n } =
|
|
29
|
-
limit:
|
|
27
|
+
children: /* @__PURE__ */ L(N, { className: "h-3 w-3" })
|
|
28
|
+
}), H = 25, U = () => {
|
|
29
|
+
let { emitIntent: e, emitFact: n } = T(), [i, l] = k(""), [f, y] = k(""), [V, U] = k(!1), [W, G] = k(null), [ie, K] = k({
|
|
30
|
+
limit: H,
|
|
30
31
|
offset: 0
|
|
31
32
|
}), { data: q, isLoading: J, error: Y, refetch: X } = r({
|
|
32
33
|
search: i.trim() || void 0,
|
|
33
34
|
status: f || void 0,
|
|
34
|
-
pagination:
|
|
35
|
-
}), Z = t(),
|
|
36
|
-
return /* @__PURE__ */
|
|
35
|
+
pagination: ie
|
|
36
|
+
}), Z = t(), ae = a(), Q = O(() => q?.items ?? [], [q]), oe = Q.filter((e) => e.status === "OPEN").length, se = Q.filter((e) => e.isPrivileged).length, $ = Q.filter((e) => e.priority === "HIGH" || e.priority === "CRITICAL").length;
|
|
37
|
+
return /* @__PURE__ */ R("div", {
|
|
37
38
|
className: "space-y-6",
|
|
38
39
|
children: [
|
|
39
|
-
/* @__PURE__ */
|
|
40
|
-
title: "
|
|
41
|
-
subtitle: "
|
|
42
|
-
icon: /* @__PURE__ */
|
|
43
|
-
actions: /* @__PURE__ */
|
|
40
|
+
/* @__PURE__ */ L(m, {
|
|
41
|
+
title: w("psw.matters.title"),
|
|
42
|
+
subtitle: w("psw.matters.subtitle"),
|
|
43
|
+
icon: /* @__PURE__ */ L(M, { className: "h-6 w-6" }),
|
|
44
|
+
actions: /* @__PURE__ */ R(z, {
|
|
44
45
|
onClick: () => {
|
|
45
|
-
|
|
46
|
+
ee("light"), e("psw.matter.create_clicked"), U(!0);
|
|
46
47
|
},
|
|
47
|
-
children: [/* @__PURE__ */
|
|
48
|
+
children: [/* @__PURE__ */ L(P, { className: "mr-1.5 h-4 w-4" }), w("psw.matters.new")]
|
|
48
49
|
})
|
|
49
50
|
}),
|
|
50
|
-
|
|
51
|
-
tone:
|
|
52
|
-
onClose: () =>
|
|
53
|
-
children:
|
|
51
|
+
W && /* @__PURE__ */ L(o, {
|
|
52
|
+
tone: W.tone,
|
|
53
|
+
onClose: () => G(null),
|
|
54
|
+
children: W.text
|
|
54
55
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ R("div", {
|
|
56
57
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
57
|
-
children: [/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ R("div", {
|
|
58
59
|
className: "relative flex-1",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
60
|
-
placeholder: "
|
|
60
|
+
children: [/* @__PURE__ */ L(F, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ L(b, {
|
|
61
|
+
placeholder: w("psw.matters.searchPlaceholder"),
|
|
61
62
|
value: i,
|
|
62
63
|
onChange: (e) => {
|
|
63
64
|
l(e.target.value), K({
|
|
64
|
-
limit:
|
|
65
|
+
limit: H,
|
|
65
66
|
offset: 0
|
|
66
67
|
});
|
|
67
68
|
},
|
|
@@ -73,143 +74,143 @@ var z = [
|
|
|
73
74
|
},
|
|
74
75
|
className: "pl-9"
|
|
75
76
|
})]
|
|
76
|
-
}), /* @__PURE__ */
|
|
77
|
+
}), /* @__PURE__ */ R(p, {
|
|
77
78
|
value: f,
|
|
78
79
|
onChange: (e) => {
|
|
79
80
|
y(e.target.value), K({
|
|
80
|
-
limit:
|
|
81
|
+
limit: H,
|
|
81
82
|
offset: 0
|
|
82
83
|
});
|
|
83
84
|
},
|
|
84
85
|
className: "sm:w-48",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ L("option", {
|
|
86
87
|
value: "",
|
|
87
|
-
children: "
|
|
88
|
-
}), S(
|
|
88
|
+
children: w("psw.matters.allStatuses")
|
|
89
|
+
}), S(B)]
|
|
89
90
|
})]
|
|
90
91
|
}),
|
|
91
|
-
J ? /* @__PURE__ */
|
|
92
|
+
J ? /* @__PURE__ */ L(u, {}) : Y ? /* @__PURE__ */ L(c, {
|
|
92
93
|
error: Y,
|
|
93
94
|
onRetry: () => void X()
|
|
94
|
-
}) : !q || q.items.length === 0 ? /* @__PURE__ */
|
|
95
|
-
title: "
|
|
96
|
-
description: "
|
|
97
|
-
icon: /* @__PURE__ */
|
|
98
|
-
action: /* @__PURE__ */
|
|
99
|
-
onClick: () =>
|
|
100
|
-
children: [/* @__PURE__ */
|
|
95
|
+
}) : !q || q.items.length === 0 ? /* @__PURE__ */ L(s, {
|
|
96
|
+
title: w("psw.matters.emptyTitle"),
|
|
97
|
+
description: w("psw.matters.emptyDescription"),
|
|
98
|
+
icon: /* @__PURE__ */ L(M, { className: "h-8 w-8" }),
|
|
99
|
+
action: /* @__PURE__ */ R(z, {
|
|
100
|
+
onClick: () => U(!0),
|
|
101
|
+
children: [/* @__PURE__ */ L(P, { className: "mr-1.5 h-4 w-4" }), w("psw.matters.new")]
|
|
101
102
|
})
|
|
102
|
-
}) : /* @__PURE__ */
|
|
103
|
+
}) : /* @__PURE__ */ R(I, { children: [/* @__PURE__ */ R("div", {
|
|
103
104
|
className: "grid grid-cols-2 gap-3 lg:grid-cols-4",
|
|
104
105
|
children: [
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ L(d, {
|
|
106
107
|
label: "Matters",
|
|
107
108
|
value: q.totalCount,
|
|
108
|
-
icon: /* @__PURE__ */
|
|
109
|
+
icon: /* @__PURE__ */ L(M, { className: "h-4 w-4" })
|
|
109
110
|
}),
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
+
/* @__PURE__ */ L(d, {
|
|
111
112
|
label: "Open",
|
|
112
|
-
value:
|
|
113
|
-
icon: /* @__PURE__ */
|
|
113
|
+
value: oe,
|
|
114
|
+
icon: /* @__PURE__ */ L(j, { className: "h-4 w-4" }),
|
|
114
115
|
tone: "accent"
|
|
115
116
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ L(d, {
|
|
117
118
|
label: "Privileged",
|
|
118
|
-
value:
|
|
119
|
-
icon: /* @__PURE__ */
|
|
119
|
+
value: se,
|
|
120
|
+
icon: /* @__PURE__ */ L(N, { className: "h-4 w-4" }),
|
|
120
121
|
tone: "warning"
|
|
121
122
|
}),
|
|
122
|
-
/* @__PURE__ */
|
|
123
|
+
/* @__PURE__ */ L(d, {
|
|
123
124
|
label: "High priority",
|
|
124
125
|
value: $,
|
|
125
|
-
icon: /* @__PURE__ */
|
|
126
|
+
icon: /* @__PURE__ */ L(te, { className: "h-4 w-4" }),
|
|
126
127
|
tone: $ ? "danger" : "neutral"
|
|
127
128
|
})
|
|
128
129
|
]
|
|
129
|
-
}), /* @__PURE__ */
|
|
130
|
+
}), /* @__PURE__ */ R(h, {
|
|
130
131
|
title: `${q.totalCount} matter${q.totalCount === 1 ? "" : "s"}`,
|
|
131
|
-
children: [/* @__PURE__ */
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
-
/* @__PURE__ */
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
-
/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ R(_, { children: [/* @__PURE__ */ L("thead", { children: /* @__PURE__ */ R("tr", { children: [
|
|
133
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tableMatter") }),
|
|
134
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tableClient") }),
|
|
135
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tablePriority") }),
|
|
136
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tableStatus") }),
|
|
137
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tableDue") }),
|
|
138
|
+
/* @__PURE__ */ L(x, { children: w("psw.matters.tableTags") }),
|
|
139
|
+
/* @__PURE__ */ L(x, {
|
|
139
140
|
className: "text-right",
|
|
140
|
-
children: "
|
|
141
|
+
children: w("psw.common.actions")
|
|
141
142
|
})
|
|
142
|
-
] }) }), /* @__PURE__ */
|
|
143
|
+
] }) }), /* @__PURE__ */ L("tbody", { children: q.items.map((e) => /* @__PURE__ */ R("tr", {
|
|
143
144
|
className: "transition-colors hover:bg-bg-sunken/60",
|
|
144
145
|
children: [
|
|
145
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ R(v, { children: [/* @__PURE__ */ R("span", {
|
|
146
147
|
className: "flex items-center gap-2 font-medium text-text-primary",
|
|
147
|
-
children: [e.isPrivileged && /* @__PURE__ */
|
|
148
|
+
children: [e.isPrivileged && /* @__PURE__ */ L(ne, {}), /* @__PURE__ */ L("span", {
|
|
148
149
|
className: "truncate",
|
|
149
150
|
children: e.title
|
|
150
151
|
})]
|
|
151
|
-
}), e.reference ? /* @__PURE__ */
|
|
152
|
+
}), e.reference ? /* @__PURE__ */ R("div", {
|
|
152
153
|
className: "mt-0.5 pl-0 text-xs text-text-tertiary",
|
|
153
154
|
children: ["Ref: ", e.reference]
|
|
154
155
|
}) : null] }),
|
|
155
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ L(v, {
|
|
156
157
|
className: "text-text-primary",
|
|
157
158
|
children: e.client?.name ?? "—"
|
|
158
159
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
-
/* @__PURE__ */
|
|
162
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ L(v, { children: /* @__PURE__ */ L(g, { status: e.priority }) }),
|
|
161
|
+
/* @__PURE__ */ L(v, { children: /* @__PURE__ */ L(g, { status: e.status }) }),
|
|
162
|
+
/* @__PURE__ */ L(v, { children: C(e.dueDate) }),
|
|
163
|
+
/* @__PURE__ */ L(v, { children: /* @__PURE__ */ L(E, {
|
|
163
164
|
entityType: "PswMatter",
|
|
164
165
|
entityId: e.id,
|
|
165
166
|
tags: e.tags ?? [],
|
|
166
167
|
onChange: async (t) => {
|
|
167
|
-
await
|
|
168
|
+
await ae.mutateAsync({
|
|
168
169
|
id: e.id,
|
|
169
170
|
input: { tags: t }
|
|
170
171
|
}), X();
|
|
171
172
|
},
|
|
172
173
|
maxTags: 5
|
|
173
174
|
}) }),
|
|
174
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ L(v, {
|
|
175
176
|
className: "text-right",
|
|
176
|
-
children: e.status !== "CLOSED" && e.status !== "ARCHIVED" ? /* @__PURE__ */
|
|
177
|
+
children: e.status !== "CLOSED" && e.status !== "ARCHIVED" ? /* @__PURE__ */ R(z, {
|
|
177
178
|
variant: "ghost",
|
|
178
179
|
size: "sm",
|
|
179
180
|
disabled: Z.isPending,
|
|
180
181
|
onClick: () => Z.mutate(e.id, {
|
|
181
182
|
onSuccess: () => {
|
|
182
|
-
n("psw.matter.closed", { matterId: e.id }),
|
|
183
|
+
n("psw.matter.closed", { matterId: e.id }), G({
|
|
183
184
|
tone: "success",
|
|
184
185
|
text: `${e.title} closed.`
|
|
185
186
|
});
|
|
186
187
|
},
|
|
187
|
-
onError: (e) =>
|
|
188
|
+
onError: (e) => G({
|
|
188
189
|
tone: "danger",
|
|
189
190
|
text: e instanceof Error ? e.message : "Failed."
|
|
190
191
|
})
|
|
191
192
|
}),
|
|
192
|
-
children: [/* @__PURE__ */
|
|
193
|
-
}) : /* @__PURE__ */
|
|
193
|
+
children: [/* @__PURE__ */ L(A, { className: "mr-1 h-4 w-4" }), " Close"]
|
|
194
|
+
}) : /* @__PURE__ */ L("span", {
|
|
194
195
|
className: "text-xs text-text-tertiary",
|
|
195
196
|
children: e.closedAt ? `Closed ${C(e.closedAt)}` : "—"
|
|
196
197
|
})
|
|
197
198
|
})
|
|
198
199
|
]
|
|
199
|
-
}, e.id)) })] }), /* @__PURE__ */
|
|
200
|
+
}, e.id)) })] }), /* @__PURE__ */ L(D, {
|
|
200
201
|
hasMore: q?.pageInfo?.hasMore ?? !1,
|
|
201
202
|
loading: J,
|
|
202
203
|
loadedCount: Q.length,
|
|
203
204
|
totalCount: q?.totalCount ?? Q.length,
|
|
204
205
|
onLoadMore: () => K((e) => ({
|
|
205
|
-
limit:
|
|
206
|
-
offset: e.offset +
|
|
206
|
+
limit: H,
|
|
207
|
+
offset: e.offset + H
|
|
207
208
|
}))
|
|
208
209
|
})]
|
|
209
210
|
})] }),
|
|
210
|
-
|
|
211
|
-
onClose: () =>
|
|
212
|
-
onSaved: (e) =>
|
|
211
|
+
V && /* @__PURE__ */ L(re, {
|
|
212
|
+
onClose: () => U(!1),
|
|
213
|
+
onSaved: (e) => G({
|
|
213
214
|
tone: "success",
|
|
214
215
|
text: `${e} opened.`
|
|
215
216
|
})
|
|
@@ -217,7 +218,7 @@ var z = [
|
|
|
217
218
|
]
|
|
218
219
|
});
|
|
219
220
|
}, re = ({ onClose: t, onSaved: r }) => {
|
|
220
|
-
let a = n(), { emitFact: s } =
|
|
221
|
+
let a = n(), { emitFact: s } = T(), c = e({ pagination: { limit: 100 } }), u = i(), [d, m] = k({
|
|
221
222
|
clientId: "",
|
|
222
223
|
practiceAreaId: "",
|
|
223
224
|
title: "",
|
|
@@ -226,20 +227,20 @@ var z = [
|
|
|
226
227
|
priority: "MEDIUM",
|
|
227
228
|
dueDate: "",
|
|
228
229
|
isPrivileged: !1
|
|
229
|
-
}), [h, g] =
|
|
230
|
+
}), [h, g] = k(null), _ = (e) => (t) => m((n) => ({
|
|
230
231
|
...n,
|
|
231
232
|
[e]: t.target.value
|
|
232
233
|
}));
|
|
233
|
-
return /* @__PURE__ */
|
|
234
|
+
return /* @__PURE__ */ L(f, {
|
|
234
235
|
open: !0,
|
|
235
236
|
onClose: t,
|
|
236
|
-
title: "
|
|
237
|
-
footer: /* @__PURE__ */
|
|
237
|
+
title: w("psw.matters.newTitle"),
|
|
238
|
+
footer: /* @__PURE__ */ R(I, { children: [/* @__PURE__ */ L(z, {
|
|
238
239
|
variant: "outline",
|
|
239
240
|
onClick: t,
|
|
240
241
|
disabled: a.isPending,
|
|
241
242
|
children: "Cancel"
|
|
242
|
-
}), /* @__PURE__ */
|
|
243
|
+
}), /* @__PURE__ */ L(z, {
|
|
243
244
|
onClick: () => {
|
|
244
245
|
if (g(null), !d.clientId) return g("Select a client.");
|
|
245
246
|
if (!d.title.trim()) return g("Matter title is required.");
|
|
@@ -266,98 +267,98 @@ var z = [
|
|
|
266
267
|
disabled: a.isPending,
|
|
267
268
|
children: a.isPending ? "Opening…" : "Open matter"
|
|
268
269
|
})] }),
|
|
269
|
-
children: /* @__PURE__ */
|
|
270
|
+
children: /* @__PURE__ */ R("div", {
|
|
270
271
|
className: "space-y-3",
|
|
271
272
|
children: [
|
|
272
|
-
h && /* @__PURE__ */
|
|
273
|
+
h && /* @__PURE__ */ L(o, {
|
|
273
274
|
tone: "danger",
|
|
274
275
|
children: h
|
|
275
276
|
}),
|
|
276
|
-
/* @__PURE__ */
|
|
277
|
+
/* @__PURE__ */ L(l, {
|
|
277
278
|
label: "Client",
|
|
278
279
|
required: !0,
|
|
279
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ R(p, {
|
|
280
281
|
value: d.clientId,
|
|
281
282
|
onChange: _("clientId"),
|
|
282
|
-
children: [/* @__PURE__ */
|
|
283
|
+
children: [/* @__PURE__ */ L("option", {
|
|
283
284
|
value: "",
|
|
284
285
|
children: "Select a client…"
|
|
285
|
-
}), c.data?.items.map((e) => /* @__PURE__ */
|
|
286
|
+
}), c.data?.items.map((e) => /* @__PURE__ */ L("option", {
|
|
286
287
|
value: e.id,
|
|
287
288
|
children: e.name
|
|
288
289
|
}, e.id))]
|
|
289
290
|
})
|
|
290
291
|
}),
|
|
291
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ L(l, {
|
|
292
293
|
label: "Title",
|
|
293
294
|
required: !0,
|
|
294
|
-
children: /* @__PURE__ */
|
|
295
|
+
children: /* @__PURE__ */ L(b, {
|
|
295
296
|
value: d.title,
|
|
296
297
|
onChange: _("title"),
|
|
297
298
|
placeholder: "Regulatory investigation — SEC inquiry"
|
|
298
299
|
})
|
|
299
300
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ R("div", {
|
|
301
302
|
className: "grid grid-cols-2 gap-3",
|
|
302
|
-
children: [/* @__PURE__ */
|
|
303
|
+
children: [/* @__PURE__ */ L(l, {
|
|
303
304
|
label: "Reference",
|
|
304
|
-
children: /* @__PURE__ */
|
|
305
|
+
children: /* @__PURE__ */ L(b, {
|
|
305
306
|
value: d.reference,
|
|
306
307
|
onChange: _("reference"),
|
|
307
308
|
placeholder: "MAT-2026-014"
|
|
308
309
|
})
|
|
309
|
-
}), /* @__PURE__ */
|
|
310
|
+
}), /* @__PURE__ */ L(l, {
|
|
310
311
|
label: "Priority",
|
|
311
|
-
children: /* @__PURE__ */
|
|
312
|
+
children: /* @__PURE__ */ L(p, {
|
|
312
313
|
value: d.priority,
|
|
313
314
|
onChange: _("priority"),
|
|
314
|
-
children: S(
|
|
315
|
+
children: S(V)
|
|
315
316
|
})
|
|
316
317
|
})]
|
|
317
318
|
}),
|
|
318
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ R("div", {
|
|
319
320
|
className: "grid grid-cols-2 gap-3",
|
|
320
|
-
children: [/* @__PURE__ */
|
|
321
|
+
children: [/* @__PURE__ */ L(l, {
|
|
321
322
|
label: "Practice area",
|
|
322
|
-
children: /* @__PURE__ */
|
|
323
|
+
children: /* @__PURE__ */ R(p, {
|
|
323
324
|
value: d.practiceAreaId,
|
|
324
325
|
onChange: _("practiceAreaId"),
|
|
325
|
-
children: [/* @__PURE__ */
|
|
326
|
+
children: [/* @__PURE__ */ L("option", {
|
|
326
327
|
value: "",
|
|
327
328
|
children: "Unassigned"
|
|
328
|
-
}), u.data?.map((e) => /* @__PURE__ */
|
|
329
|
+
}), u.data?.map((e) => /* @__PURE__ */ L("option", {
|
|
329
330
|
value: e.id,
|
|
330
331
|
children: e.name
|
|
331
332
|
}, e.id))]
|
|
332
333
|
})
|
|
333
|
-
}), /* @__PURE__ */
|
|
334
|
+
}), /* @__PURE__ */ L(l, {
|
|
334
335
|
label: "Due date",
|
|
335
|
-
children: /* @__PURE__ */
|
|
336
|
+
children: /* @__PURE__ */ L(b, {
|
|
336
337
|
type: "date",
|
|
337
338
|
value: d.dueDate,
|
|
338
339
|
onChange: _("dueDate")
|
|
339
340
|
})
|
|
340
341
|
})]
|
|
341
342
|
}),
|
|
342
|
-
/* @__PURE__ */
|
|
343
|
+
/* @__PURE__ */ L(l, {
|
|
343
344
|
label: "Description",
|
|
344
|
-
children: /* @__PURE__ */
|
|
345
|
+
children: /* @__PURE__ */ L(y, {
|
|
345
346
|
value: d.description,
|
|
346
347
|
onChange: _("description")
|
|
347
348
|
})
|
|
348
349
|
}),
|
|
349
|
-
/* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ R("label", {
|
|
350
351
|
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",
|
|
351
|
-
children: [/* @__PURE__ */
|
|
352
|
+
children: [/* @__PURE__ */ L("input", {
|
|
352
353
|
type: "checkbox",
|
|
353
354
|
checked: d.isPrivileged,
|
|
354
355
|
onChange: (e) => m((t) => ({
|
|
355
356
|
...t,
|
|
356
357
|
isPrivileged: e.target.checked
|
|
357
358
|
}))
|
|
358
|
-
}), /* @__PURE__ */
|
|
359
|
+
}), /* @__PURE__ */ R("span", {
|
|
359
360
|
className: "flex items-center gap-1.5",
|
|
360
|
-
children: [/* @__PURE__ */
|
|
361
|
+
children: [/* @__PURE__ */ L(N, { className: "h-3.5 w-3.5 text-status-warning-text" }), "Privileged / confidential matter"]
|
|
361
362
|
})]
|
|
362
363
|
})
|
|
363
364
|
]
|
|
@@ -365,6 +366,6 @@ var z = [
|
|
|
365
366
|
});
|
|
366
367
|
};
|
|
367
368
|
//#endregion
|
|
368
|
-
export {
|
|
369
|
+
export { U as MattersPage };
|
|
369
370
|
|
|
370
371
|
//# sourceMappingURL=MattersPage.js.map
|