@burdenoff/microfe-proserviceworld 2026.709.2 → 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/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.js.map +1 -1
- package/dist/proserviceworld/components/DiscussionThread.js.map +1 -1
- package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -1
- package/dist/proserviceworld/components/LoadMore.js.map +1 -1
- package/dist/proserviceworld/components/TagPicker.js.map +1 -1
- 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.js.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- 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/index.js +5 -4
- 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 +1 -1
- 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 +123 -122
- 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 +124 -123
- 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.js.map +1 -1
- 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
|
@@ -76,7 +76,8 @@ var _ = [
|
|
|
76
76
|
], v = (i) => {
|
|
77
77
|
let { registerNavItems: a, basePath: o = "/" } = i;
|
|
78
78
|
return r(() => {
|
|
79
|
-
if (a) return
|
|
79
|
+
if (!a) return;
|
|
80
|
+
let e = {
|
|
80
81
|
id: "proserviceworld",
|
|
81
82
|
label: "ProServiceWorld",
|
|
82
83
|
path: o,
|
|
@@ -90,7 +91,8 @@ var _ = [
|
|
|
90
91
|
icon: /* @__PURE__ */ g(e.icon, { size: 16 }),
|
|
91
92
|
order: t
|
|
92
93
|
}))
|
|
93
|
-
}
|
|
94
|
+
};
|
|
95
|
+
return a(e);
|
|
94
96
|
}, [a, o]), /* @__PURE__ */ g(h, {
|
|
95
97
|
workspaceId: null,
|
|
96
98
|
gateway: "global",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProServiceWorldRoot.js","names":[],"sources":["../../src/proserviceworld/ProServiceWorldRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n LayoutDashboard,\n Building2,\n Briefcase,\n FolderKanban,\n Clock,\n Receipt,\n ShieldCheck,\n GitBranch,\n FileText,\n LayoutGrid,\n BarChart2,\n} from 'lucide-react';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ProServiceWorldProvider } from './providers/ProServiceWorldProvider';\nimport type { NavItem, ProServiceWorldRootProps } from './types';\nimport { ProServiceWorldRoutes } from './ProServiceWorldRoutes';\nimport { joinPath } from './utils/navigation';\n\n/**\n * The authoritative sidebar navigation is registered by the host app shell\n * (proserviceworld-app NavRegistration). These items are only used when the MFE\n * is mounted standalone with `registerNavItems` provided — they mirror the\n * shell's canonical professional-services routes exactly.\n */\nconst NAV: { id: string; label: string; path: string; icon: FC<{ size?: number }> }[] = [\n { id: 'dashboard', label: 'Dashboard', path: '', icon: LayoutDashboard },\n { id: 'clients', label: 'Clients', path: 'clients', icon: Building2 },\n { id: 'engagements', label: 'Engagements', path: 'engagements', icon: Briefcase },\n { id: 'matters', label: 'Matters', path: 'matters', icon: FolderKanban },\n { id: 'time', label: 'Time', path: 'time', icon: Clock },\n { id: 'invoices', label: 'Invoices', path: 'invoices', icon: Receipt },\n { id: 'compliance', label: 'Compliance', path: 'compliance', icon: ShieldCheck },\n { id: 'conflicts', label: 'Conflict Checks', path: 'conflicts', icon: GitBranch },\n { id: 'documents', label: 'Documents', path: 'documents', icon: FileText },\n { id: 'practice-areas', label: 'Practice Areas', path: 'practice-areas', icon: LayoutGrid },\n { id: 'analytics', label: 'Analytics', path: 'analytics', icon: BarChart2 },\n];\n\nexport const ProServiceWorldRoot: FC<ProServiceWorldRootProps> = (props) => {\n const { registerNavItems, basePath = '/' } = props;\n\n useEffect(() => {\n if (!registerNavItems) return;\n const navItem: NavItem = {\n id: 'proserviceworld',\n label: 'ProServiceWorld',\n path: basePath,\n icon: <LayoutDashboard size={20} />,\n order: 80,\n group: 'products',\n children: NAV.map((n, i) => ({\n id: `proserviceworld-${n.id}`,\n label: n.label,\n path: joinPath(basePath, n.path),\n icon: <n.icon size={16} />,\n order: i,\n })),\n };\n return registerNavItems(navItem);\n }, [registerNavItems, basePath]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <ProServiceWorldProvider {...props}>\n <ProServiceWorldRoutes />\n </ProServiceWorldProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;AA2BA,IAAM,IAAkF;CACtF;EAAE,IAAI;EAAa,OAAO;EAAa,MAAM;EAAI,MAAM;
|
|
1
|
+
{"version":3,"file":"ProServiceWorldRoot.js","names":[],"sources":["../../src/proserviceworld/ProServiceWorldRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n LayoutDashboard,\n Building2,\n Briefcase,\n FolderKanban,\n Clock,\n Receipt,\n ShieldCheck,\n GitBranch,\n FileText,\n LayoutGrid,\n BarChart2,\n} from 'lucide-react';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ProServiceWorldProvider } from './providers/ProServiceWorldProvider';\nimport type { NavItem, ProServiceWorldRootProps } from './types';\nimport { ProServiceWorldRoutes } from './ProServiceWorldRoutes';\nimport { joinPath } from './utils/navigation';\n\n/**\n * The authoritative sidebar navigation is registered by the host app shell\n * (proserviceworld-app NavRegistration). These items are only used when the MFE\n * is mounted standalone with `registerNavItems` provided — they mirror the\n * shell's canonical professional-services routes exactly.\n */\nconst NAV: { id: string; label: string; path: string; icon: FC<{ size?: number }> }[] = [\n { id: 'dashboard', label: 'Dashboard', path: '', icon: LayoutDashboard },\n { id: 'clients', label: 'Clients', path: 'clients', icon: Building2 },\n { id: 'engagements', label: 'Engagements', path: 'engagements', icon: Briefcase },\n { id: 'matters', label: 'Matters', path: 'matters', icon: FolderKanban },\n { id: 'time', label: 'Time', path: 'time', icon: Clock },\n { id: 'invoices', label: 'Invoices', path: 'invoices', icon: Receipt },\n { id: 'compliance', label: 'Compliance', path: 'compliance', icon: ShieldCheck },\n { id: 'conflicts', label: 'Conflict Checks', path: 'conflicts', icon: GitBranch },\n { id: 'documents', label: 'Documents', path: 'documents', icon: FileText },\n { id: 'practice-areas', label: 'Practice Areas', path: 'practice-areas', icon: LayoutGrid },\n { id: 'analytics', label: 'Analytics', path: 'analytics', icon: BarChart2 },\n];\n\nexport const ProServiceWorldRoot: FC<ProServiceWorldRootProps> = (props) => {\n const { registerNavItems, basePath = '/' } = props;\n\n useEffect(() => {\n if (!registerNavItems) return;\n const navItem: NavItem = {\n id: 'proserviceworld',\n label: 'ProServiceWorld',\n path: basePath,\n icon: <LayoutDashboard size={20} />,\n order: 80,\n group: 'products',\n children: NAV.map((n, i) => ({\n id: `proserviceworld-${n.id}`,\n label: n.label,\n path: joinPath(basePath, n.path),\n icon: <n.icon size={16} />,\n order: i,\n })),\n };\n return registerNavItems(navItem);\n }, [registerNavItems, basePath]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <ProServiceWorldProvider {...props}>\n <ProServiceWorldRoutes />\n </ProServiceWorldProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;AA2BA,IAAM,IAAkF;CACtF;EAAE,IAAI;EAAa,OAAO;EAAa,MAAM;EAAI,MAAM;CAAgB;CACvE;EAAE,IAAI;EAAW,OAAO;EAAW,MAAM;EAAW,MAAM;CAAU;CACpE;EAAE,IAAI;EAAe,OAAO;EAAe,MAAM;EAAe,MAAM;CAAU;CAChF;EAAE,IAAI;EAAW,OAAO;EAAW,MAAM;EAAW,MAAM;CAAa;CACvE;EAAE,IAAI;EAAQ,OAAO;EAAQ,MAAM;EAAQ,MAAM;CAAM;CACvD;EAAE,IAAI;EAAY,OAAO;EAAY,MAAM;EAAY,MAAM;CAAQ;CACrE;EAAE,IAAI;EAAc,OAAO;EAAc,MAAM;EAAc,MAAM;CAAY;CAC/E;EAAE,IAAI;EAAa,OAAO;EAAmB,MAAM;EAAa,MAAM;CAAU;CAChF;EAAE,IAAI;EAAa,OAAO;EAAa,MAAM;EAAa,MAAM;CAAS;CACzE;EAAE,IAAI;EAAkB,OAAO;EAAkB,MAAM;EAAkB,MAAM;CAAW;CAC1F;EAAE,IAAI;EAAa,OAAO;EAAa,MAAM;EAAa,MAAM;CAAU;AAC5E,GAEa,KAAqD,MAAU;CAC1E,IAAM,EAAE,qBAAkB,cAAW,QAAQ;CAsB7C,OApBA,QAAgB;EACd,IAAI,CAAC,GAAkB;EACvB,IAAM,IAAmB;GACvB,IAAI;GACJ,OAAO;GACP,MAAM;GACN,MAAM,kBAAC,GAAD,EAAiB,MAAM,GAAK,CAAA;GAClC,OAAO;GACP,OAAO;GACP,UAAU,EAAI,KAAK,GAAG,OAAO;IAC3B,IAAI,mBAAmB,EAAE;IACzB,OAAO,EAAE;IACT,MAAM,EAAS,GAAU,EAAE,IAAI;IAC/B,MAAM,kBAAC,EAAE,MAAH,EAAQ,MAAM,GAAK,CAAA;IACzB,OAAO;GACT,EAAE;EACJ;EACA,OAAO,EAAiB,CAAO;CACjC,GAAG,CAAC,GAAkB,CAAQ,CAAC,GAG7B,kBAAC,GAAD;EAAqB,aAAa;EAAM,SAAQ;EAAS,gBAAgB;YACvE,kBAAC,GAAD;GAAyB,GAAI;aAC3B,kBAAC,GAAD,CAAwB,CAAA;EACD,CAAA;CACN,CAAA;AAEzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProServiceWorldRoutes.js","names":[],"sources":["../../src/proserviceworld/ProServiceWorldRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router-dom';\nimport { Loader2 } from 'lucide-react';\nimport { DashboardPage } from './pages/DashboardPage';\nimport { usePageViewAnalytics } from './hooks/usePageViewAnalytics';\nimport { FeatureFlaggedPage } from './components/FeatureFlaggedPage';\n\nconst ClientsPage = lazy(() =>\n import('./pages/ClientsPage').then((m) => ({ default: m.ClientsPage }))\n);\nconst ClientDetailPage = lazy(() =>\n import('./pages/ClientDetailPage').then((m) => ({ default: m.ClientDetailPage }))\n);\nconst EngagementsPage = lazy(() =>\n import('./pages/EngagementsPage').then((m) => ({ default: m.EngagementsPage }))\n);\nconst EngagementDetailPage = lazy(() =>\n import('./pages/EngagementDetailPage').then((m) => ({ default: m.EngagementDetailPage }))\n);\nconst MattersPage = lazy(() =>\n import('./pages/MattersPage').then((m) => ({ default: m.MattersPage }))\n);\nconst TimePage = lazy(() => import('./pages/TimePage').then((m) => ({ default: m.TimePage })));\nconst InvoicesPage = lazy(() =>\n import('./pages/InvoicesPage').then((m) => ({ default: m.InvoicesPage }))\n);\nconst CompliancePage = lazy(() =>\n import('./pages/CompliancePage').then((m) => ({ default: m.CompliancePage }))\n);\nconst ConflictsPage = lazy(() =>\n import('./pages/ConflictsPage').then((m) => ({ default: m.ConflictsPage }))\n);\nconst DocumentsPage = lazy(() =>\n import('./pages/DocumentsPage').then((m) => ({ default: m.DocumentsPage }))\n);\nconst PracticeAreasPage = lazy(() =>\n import('./pages/PracticeAreasPage').then((m) => ({ default: m.PracticeAreasPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-accent\" />\n </div>\n );\n}\n\nfunction PageViewTracker() {\n usePageViewAnalytics();\n return null;\n}\n\nfunction Gate({ pageId, children }: { pageId: string; children: ReactNode }) {\n return (\n <FeatureFlaggedPage pageId={pageId}>\n <Suspense fallback={<PageLoader />}>{children}</Suspense>\n </FeatureFlaggedPage>\n );\n}\n\nexport function ProServiceWorldRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <PageViewTracker />\n <Routes>\n <Route\n index\n element={\n <Gate pageId=\"dashboard\">\n <DashboardPage />\n </Gate>\n }\n />\n <Route\n path=\"clients\"\n element={\n <Gate pageId=\"clients\">\n <ClientsPage />\n </Gate>\n }\n />\n <Route\n path=\"clients/:id\"\n element={\n <Gate pageId=\"client-detail\">\n <ClientDetailPage />\n </Gate>\n }\n />\n <Route\n path=\"engagements\"\n element={\n <Gate pageId=\"engagements\">\n <EngagementsPage />\n </Gate>\n }\n />\n <Route\n path=\"engagements/:id\"\n element={\n <Gate pageId=\"engagement-detail\">\n <EngagementDetailPage />\n </Gate>\n }\n />\n <Route\n path=\"matters\"\n element={\n <Gate pageId=\"matters\">\n <MattersPage />\n </Gate>\n }\n />\n <Route\n path=\"time\"\n element={\n <Gate pageId=\"time\">\n <TimePage />\n </Gate>\n }\n />\n <Route\n path=\"invoices\"\n element={\n <Gate pageId=\"invoices\">\n <InvoicesPage />\n </Gate>\n }\n />\n <Route\n path=\"compliance\"\n element={\n <Gate pageId=\"compliance\">\n <CompliancePage />\n </Gate>\n }\n />\n <Route\n path=\"conflicts\"\n element={\n <Gate pageId=\"conflicts\">\n <ConflictsPage />\n </Gate>\n }\n />\n <Route\n path=\"documents\"\n element={\n <Gate pageId=\"documents\">\n <DocumentsPage />\n </Gate>\n }\n />\n <Route\n path=\"practice-areas\"\n element={\n <Gate pageId=\"practice-areas\">\n <PracticeAreasPage />\n </Gate>\n }\n />\n <Route\n path=\"analytics\"\n element={\n <Gate pageId=\"analytics\">\n <AnalyticsPage />\n </Gate>\n }\n />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":";;;;;;;;AAOA,IAAM,IAAc,QAClB,OAAO,
|
|
1
|
+
{"version":3,"file":"ProServiceWorldRoutes.js","names":[],"sources":["../../src/proserviceworld/ProServiceWorldRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router-dom';\nimport { Loader2 } from 'lucide-react';\nimport { DashboardPage } from './pages/DashboardPage';\nimport { usePageViewAnalytics } from './hooks/usePageViewAnalytics';\nimport { FeatureFlaggedPage } from './components/FeatureFlaggedPage';\n\nconst ClientsPage = lazy(() =>\n import('./pages/ClientsPage').then((m) => ({ default: m.ClientsPage }))\n);\nconst ClientDetailPage = lazy(() =>\n import('./pages/ClientDetailPage').then((m) => ({ default: m.ClientDetailPage }))\n);\nconst EngagementsPage = lazy(() =>\n import('./pages/EngagementsPage').then((m) => ({ default: m.EngagementsPage }))\n);\nconst EngagementDetailPage = lazy(() =>\n import('./pages/EngagementDetailPage').then((m) => ({ default: m.EngagementDetailPage }))\n);\nconst MattersPage = lazy(() =>\n import('./pages/MattersPage').then((m) => ({ default: m.MattersPage }))\n);\nconst TimePage = lazy(() => import('./pages/TimePage').then((m) => ({ default: m.TimePage })));\nconst InvoicesPage = lazy(() =>\n import('./pages/InvoicesPage').then((m) => ({ default: m.InvoicesPage }))\n);\nconst CompliancePage = lazy(() =>\n import('./pages/CompliancePage').then((m) => ({ default: m.CompliancePage }))\n);\nconst ConflictsPage = lazy(() =>\n import('./pages/ConflictsPage').then((m) => ({ default: m.ConflictsPage }))\n);\nconst DocumentsPage = lazy(() =>\n import('./pages/DocumentsPage').then((m) => ({ default: m.DocumentsPage }))\n);\nconst PracticeAreasPage = lazy(() =>\n import('./pages/PracticeAreasPage').then((m) => ({ default: m.PracticeAreasPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-accent\" />\n </div>\n );\n}\n\nfunction PageViewTracker() {\n usePageViewAnalytics();\n return null;\n}\n\nfunction Gate({ pageId, children }: { pageId: string; children: ReactNode }) {\n return (\n <FeatureFlaggedPage pageId={pageId}>\n <Suspense fallback={<PageLoader />}>{children}</Suspense>\n </FeatureFlaggedPage>\n );\n}\n\nexport function ProServiceWorldRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <PageViewTracker />\n <Routes>\n <Route\n index\n element={\n <Gate pageId=\"dashboard\">\n <DashboardPage />\n </Gate>\n }\n />\n <Route\n path=\"clients\"\n element={\n <Gate pageId=\"clients\">\n <ClientsPage />\n </Gate>\n }\n />\n <Route\n path=\"clients/:id\"\n element={\n <Gate pageId=\"client-detail\">\n <ClientDetailPage />\n </Gate>\n }\n />\n <Route\n path=\"engagements\"\n element={\n <Gate pageId=\"engagements\">\n <EngagementsPage />\n </Gate>\n }\n />\n <Route\n path=\"engagements/:id\"\n element={\n <Gate pageId=\"engagement-detail\">\n <EngagementDetailPage />\n </Gate>\n }\n />\n <Route\n path=\"matters\"\n element={\n <Gate pageId=\"matters\">\n <MattersPage />\n </Gate>\n }\n />\n <Route\n path=\"time\"\n element={\n <Gate pageId=\"time\">\n <TimePage />\n </Gate>\n }\n />\n <Route\n path=\"invoices\"\n element={\n <Gate pageId=\"invoices\">\n <InvoicesPage />\n </Gate>\n }\n />\n <Route\n path=\"compliance\"\n element={\n <Gate pageId=\"compliance\">\n <CompliancePage />\n </Gate>\n }\n />\n <Route\n path=\"conflicts\"\n element={\n <Gate pageId=\"conflicts\">\n <ConflictsPage />\n </Gate>\n }\n />\n <Route\n path=\"documents\"\n element={\n <Gate pageId=\"documents\">\n <DocumentsPage />\n </Gate>\n }\n />\n <Route\n path=\"practice-areas\"\n element={\n <Gate pageId=\"practice-areas\">\n <PracticeAreasPage />\n </Gate>\n }\n />\n <Route\n path=\"analytics\"\n element={\n <Gate pageId=\"analytics\">\n <AnalyticsPage />\n </Gate>\n }\n />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiscussionThread.js","names":[],"sources":["../../../src/proserviceworld/components/DiscussionThread.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { MessageSquare, Send } from 'lucide-react';\nimport { useEntityComments, useAddEntityComment } from '../hooks/useEntityComments';\nimport { useProServiceWorld } from '../providers/ProServiceWorldProvider';\nimport { SectionCard, TextArea, Banner, LoadingBlock, Empty, fmtDate } from './ui';\n\n/**\n * Discussion / notes for a Psw entity, backed entirely by wspace-conversations\n * (createEntityComment / getEntityComments) — no product-owned comments table.\n */\nexport const DiscussionThread: FC<{ entityType: string; entityId: string; title?: string }> = ({\n entityType,\n entityId,\n title = 'Discussion',\n}) => {\n const { currentUser } = useProServiceWorld();\n const { data, isLoading, error } = useEntityComments(entityType, entityId);\n const add = useAddEntityComment(entityType, entityId);\n const [text, setText] = useState('');\n const [err, setErr] = useState<string | null>(null);\n\n const submit = () => {\n setErr(null);\n const body = text.trim();\n if (!body) return;\n add.mutate(body, {\n onSuccess: () => setText(''),\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed to post.'),\n });\n };\n\n const authorLabel = (authorId: string) =>\n currentUser && authorId === currentUser.id ? 'You' : 'Team member';\n\n return (\n <SectionCard\n title={title}\n description=\"Notes & discussion — shared across the platform via Conversations.\"\n >\n <div className=\"space-y-3\">\n {err && (\n <Banner tone=\"danger\" onClose={() => setErr(null)}>\n {err}\n </Banner>\n )}\n <div className=\"flex items-start gap-2\">\n <TextArea\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Add a note…\"\n className=\"min-h-[44px] flex-1\"\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') submit();\n }}\n />\n <Button onClick={submit} disabled={add.isPending || !text.trim()} aria-label=\"Post note\">\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {isLoading ? (\n <LoadingBlock label=\"Loading discussion…\" />\n ) : error ? (\n <p className=\"text-sm text-status-error-text\">Could not load discussion.</p>\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No notes yet\"\n description=\"Start the discussion for this record.\"\n icon={<MessageSquare className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {data.items.map((c) => (\n <li key={c.id} className=\"rounded-lg border border-border-seam bg-bg-canvas p-3\">\n <div className=\"mb-1 flex items-center justify-between text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">{authorLabel(c.authorId)}</span>\n <span>{fmtDate(c.createdAt)}</span>\n </div>\n <p className=\"whitespace-pre-wrap text-sm text-text-primary\">\n {c.content?.text ?? ''}\n </p>\n </li>\n ))}\n </ul>\n )}\n </div>\n </SectionCard>\n );\n};\n"],"mappings":";;;;;;;;AAYA,IAAa,KAAkF,EAC7F,eACA,aACA,WAAQ,mBACJ;CACJ,IAAM,EAAE,mBAAgB,
|
|
1
|
+
{"version":3,"file":"DiscussionThread.js","names":[],"sources":["../../../src/proserviceworld/components/DiscussionThread.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { MessageSquare, Send } from 'lucide-react';\nimport { useEntityComments, useAddEntityComment } from '../hooks/useEntityComments';\nimport { useProServiceWorld } from '../providers/ProServiceWorldProvider';\nimport { SectionCard, TextArea, Banner, LoadingBlock, Empty, fmtDate } from './ui';\n\n/**\n * Discussion / notes for a Psw entity, backed entirely by wspace-conversations\n * (createEntityComment / getEntityComments) — no product-owned comments table.\n */\nexport const DiscussionThread: FC<{ entityType: string; entityId: string; title?: string }> = ({\n entityType,\n entityId,\n title = 'Discussion',\n}) => {\n const { currentUser } = useProServiceWorld();\n const { data, isLoading, error } = useEntityComments(entityType, entityId);\n const add = useAddEntityComment(entityType, entityId);\n const [text, setText] = useState('');\n const [err, setErr] = useState<string | null>(null);\n\n const submit = () => {\n setErr(null);\n const body = text.trim();\n if (!body) return;\n add.mutate(body, {\n onSuccess: () => setText(''),\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed to post.'),\n });\n };\n\n const authorLabel = (authorId: string) =>\n currentUser && authorId === currentUser.id ? 'You' : 'Team member';\n\n return (\n <SectionCard\n title={title}\n description=\"Notes & discussion — shared across the platform via Conversations.\"\n >\n <div className=\"space-y-3\">\n {err && (\n <Banner tone=\"danger\" onClose={() => setErr(null)}>\n {err}\n </Banner>\n )}\n <div className=\"flex items-start gap-2\">\n <TextArea\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Add a note…\"\n className=\"min-h-[44px] flex-1\"\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') submit();\n }}\n />\n <Button onClick={submit} disabled={add.isPending || !text.trim()} aria-label=\"Post note\">\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {isLoading ? (\n <LoadingBlock label=\"Loading discussion…\" />\n ) : error ? (\n <p className=\"text-sm text-status-error-text\">Could not load discussion.</p>\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No notes yet\"\n description=\"Start the discussion for this record.\"\n icon={<MessageSquare className=\"h-7 w-7\" />}\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {data.items.map((c) => (\n <li key={c.id} className=\"rounded-lg border border-border-seam bg-bg-canvas p-3\">\n <div className=\"mb-1 flex items-center justify-between text-xs text-text-tertiary\">\n <span className=\"font-medium text-text-secondary\">{authorLabel(c.authorId)}</span>\n <span>{fmtDate(c.createdAt)}</span>\n </div>\n <p className=\"whitespace-pre-wrap text-sm text-text-primary\">\n {c.content?.text ?? ''}\n </p>\n </li>\n ))}\n </ul>\n )}\n </div>\n </SectionCard>\n );\n};\n"],"mappings":";;;;;;;;AAYA,IAAa,KAAkF,EAC7F,eACA,aACA,WAAQ,mBACJ;CACJ,IAAM,EAAE,mBAAgB,EAAmB,GACrC,EAAE,SAAM,cAAW,aAAU,EAAkB,GAAY,CAAQ,GACnE,IAAM,EAAoB,GAAY,CAAQ,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAK,KAAU,EAAwB,IAAI,GAE5C,UAAe;EACnB,EAAO,IAAI;EACX,IAAM,IAAO,EAAK,KAAK;EAClB,KACL,EAAI,OAAO,GAAM;GACf,iBAAiB,EAAQ,EAAE;GAC3B,UAAU,MAAM,EAAO,aAAa,QAAQ,EAAE,UAAU,iBAAiB;EAC3E,CAAC;CACH,GAEM,KAAe,MACnB,KAAe,MAAa,EAAY,KAAK,QAAQ;CAEvD,OACE,kBAAC,GAAD;EACS;EACP,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,KACC,kBAAC,GAAD;KAAQ,MAAK;KAAS,eAAe,EAAO,IAAI;eAC7C;IACK,CAAA;IAEV,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAQ,EAAE,OAAO,KAAK;MACvC,aAAY;MACZ,WAAU;MACV,YAAY,MAAM;OAChB,CAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,WAAS,EAAO;MAC5D;KACD,CAAA,GACD,kBAAC,GAAD;MAAQ,SAAS;MAAQ,UAAU,EAAI,aAAa,CAAC,EAAK,KAAK;MAAG,cAAW;gBAC3E,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;KACrB,CAAA,CACL;;IAEJ,IACC,kBAAC,GAAD,EAAc,OAAM,sBAAuB,CAAA,IACzC,IACF,kBAAC,KAAD;KAAG,WAAU;eAAiC;IAA6B,CAAA,IACzE,CAAC,KAAQ,EAAK,MAAM,WAAW,IACjC,kBAAC,GAAD;KACE,OAAM;KACN,aAAY;KACZ,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;IAC3C,CAAA,IAED,kBAAC,MAAD;KAAI,WAAU;eACX,EAAK,MAAM,KAAK,MACf,kBAAC,MAAD;MAAe,WAAU;gBAAzB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAmC,EAAY,EAAE,QAAQ;OAAQ,CAAA,GACjF,kBAAC,QAAD,EAAA,UAAO,EAAQ,EAAE,SAAS,EAAQ,CAAA,CAC/B;UACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,SAAS,QAAQ;MACnB,CAAA,CACD;QARK,EAAE,EAQP,CACL;IACC,CAAA;GAEH;;CACM,CAAA;AAEjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureFlaggedPage.js","names":[],"sources":["../../../src/proserviceworld/components/FeatureFlaggedPage.tsx"],"sourcesContent":["/**\n * Per-page feature-flag gate.\n *\n * Every ProServiceWorld page is behind a flag (default-allow). The flag key\n * convention is `proserviceworld.page.{pageId}`. When a flag is explicitly\n * disabled the page renders a compact disabled state instead of the content.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Empty } from './ui';\n\nexport interface FeatureFlaggedPageProps {\n pageId: string;\n children: ReactNode;\n}\n\nexport const FeatureFlaggedPage: FC<FeatureFlaggedPageProps> = ({ pageId, children }) => (\n <FeatureGate\n flag={`proserviceworld.page.${pageId}`}\n defaultValue={true}\n fallback={\n <Empty title=\"Feature disabled\" description=\"This page is not enabled for your workspace.\" />\n }\n >\n {children}\n </FeatureGate>\n);\n"],"mappings":";;;;AAiBA,IAAa,KAAmD,EAAE,WAAQ,kBACxE,kBAAC,GAAD;CACE,MAAM,wBAAwB;CAC9B,cAAc;CACd,UACE,kBAAC,GAAD;EAAO,OAAM;EAAmB,aAAY;
|
|
1
|
+
{"version":3,"file":"FeatureFlaggedPage.js","names":[],"sources":["../../../src/proserviceworld/components/FeatureFlaggedPage.tsx"],"sourcesContent":["/**\n * Per-page feature-flag gate.\n *\n * Every ProServiceWorld page is behind a flag (default-allow). The flag key\n * convention is `proserviceworld.page.{pageId}`. When a flag is explicitly\n * disabled the page renders a compact disabled state instead of the content.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Empty } from './ui';\n\nexport interface FeatureFlaggedPageProps {\n pageId: string;\n children: ReactNode;\n}\n\nexport const FeatureFlaggedPage: FC<FeatureFlaggedPageProps> = ({ pageId, children }) => (\n <FeatureGate\n flag={`proserviceworld.page.${pageId}`}\n defaultValue={true}\n fallback={\n <Empty title=\"Feature disabled\" description=\"This page is not enabled for your workspace.\" />\n }\n >\n {children}\n </FeatureGate>\n);\n"],"mappings":";;;;AAiBA,IAAa,KAAmD,EAAE,WAAQ,kBACxE,kBAAC,GAAD;CACE,MAAM,wBAAwB;CAC9B,cAAc;CACd,UACE,kBAAC,GAAD;EAAO,OAAM;EAAmB,aAAY;CAAgD,CAAA;CAG7F;AACU,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadMore.js","names":[],"sources":["../../../src/proserviceworld/components/LoadMore.tsx"],"sourcesContent":["/**\n * Simple pagination \"Load more\" control for ProServiceWorld list pages.\n */\n\nimport type { FC } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { t } from '../i18n';\n\nexport interface LoadMoreProps {\n hasMore: boolean;\n loading: boolean;\n onLoadMore: () => void;\n loadedCount: number;\n totalCount: number;\n className?: string;\n}\n\nexport const LoadMore: FC<LoadMoreProps> = ({\n hasMore,\n loading,\n onLoadMore,\n loadedCount,\n totalCount,\n className,\n}) => {\n if (!hasMore && totalCount === 0) return null;\n\n return (\n <div className={cn('flex items-center justify-between gap-4 py-4', className)}>\n <span className=\"text-sm text-text-tertiary\">\n {t('psw.common.showing')} {loadedCount} {t('psw.common.of')} {totalCount}\n </span>\n {hasMore && (\n <Button variant=\"secondary\" onClick={onLoadMore} disabled={loading}>\n {loading ? <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" /> : null}\n {t('psw.common.loadMore')}\n </Button>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAmBA,IAAa,KAA+B,EAC1C,YACA,YACA,eACA,gBACA,eACA,mBAEI,CAAC,KAAW,MAAe,IAAU,OAGvC,kBAAC,OAAD;CAAK,WAAW,EAAG,gDAAgD,
|
|
1
|
+
{"version":3,"file":"LoadMore.js","names":[],"sources":["../../../src/proserviceworld/components/LoadMore.tsx"],"sourcesContent":["/**\n * Simple pagination \"Load more\" control for ProServiceWorld list pages.\n */\n\nimport type { FC } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport { t } from '../i18n';\n\nexport interface LoadMoreProps {\n hasMore: boolean;\n loading: boolean;\n onLoadMore: () => void;\n loadedCount: number;\n totalCount: number;\n className?: string;\n}\n\nexport const LoadMore: FC<LoadMoreProps> = ({\n hasMore,\n loading,\n onLoadMore,\n loadedCount,\n totalCount,\n className,\n}) => {\n if (!hasMore && totalCount === 0) return null;\n\n return (\n <div className={cn('flex items-center justify-between gap-4 py-4', className)}>\n <span className=\"text-sm text-text-tertiary\">\n {t('psw.common.showing')} {loadedCount} {t('psw.common.of')} {totalCount}\n </span>\n {hasMore && (\n <Button variant=\"secondary\" onClick={onLoadMore} disabled={loading}>\n {loading ? <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" /> : null}\n {t('psw.common.loadMore')}\n </Button>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAmBA,IAAa,KAA+B,EAC1C,YACA,YACA,eACA,gBACA,eACA,mBAEI,CAAC,KAAW,MAAe,IAAU,OAGvC,kBAAC,OAAD;CAAK,WAAW,EAAG,gDAAgD,CAAS;WAA5E,CACE,kBAAC,QAAD;EAAM,WAAU;YAAhB;GACG,EAAE,oBAAoB;GAAE;GAAE;GAAY;GAAE,EAAE,eAAe;GAAE;GAAE;EAC1D;KACL,KACC,kBAAC,GAAD;EAAQ,SAAQ;EAAY,SAAS;EAAY,UAAU;YAA3D,CACG,IAAU,kBAAC,GAAD,EAAS,WAAU,8BAA+B,CAAA,IAAI,MAChE,EAAE,qBAAqB,CAClB;GAEP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagPicker.js","names":[],"sources":["../../../src/proserviceworld/components/TagPicker.tsx"],"sourcesContent":["/**\n * ProServiceWorld tag picker.\n *\n * Wraps the shared fe-libs `TagsWidget` so entity pages can assign/remove\n * string tags on clients, engagements, matters, and documents. The backend\n * stores tags as `string[]` IDs/keys; we map them to the minimal `TagItem`\n * shape the widget expects.\n */\n\nimport type { FC } from 'react';\nimport { TagsWidget } from '@burdenoff/fe-libs/tag';\nimport { useProServiceWorld } from '../providers/ProServiceWorldProvider';\n\nexport interface TagPickerProps {\n entityType: string;\n entityId: string;\n tags: string[];\n onChange: (tags: string[]) => void | Promise<void>;\n maxTags?: number;\n}\n\nexport const TagPicker: FC<TagPickerProps> = ({\n entityType,\n entityId,\n tags,\n onChange,\n maxTags,\n}) => {\n const { workspaceId } = useProServiceWorld();\n\n const tagItems = tags.map((tag) => ({\n id: tag,\n key: tag,\n label: tag,\n value: tag,\n }));\n\n return (\n <TagsWidget\n entityType={entityType}\n entityId={entityId}\n workspaceId={workspaceId}\n tags={tagItems}\n onAdd={async (tag) => {\n if (tags.includes(tag.id)) return;\n await onChange([...tags, tag.id]);\n }}\n onRemove={async (tagId) => {\n await onChange(tags.filter((t) => t !== tagId));\n }}\n allowCreate\n maxTags={maxTags}\n syncGraphNotifications={false}\n />\n );\n};\n"],"mappings":";;;;AAqBA,IAAa,KAAiC,EAC5C,eACA,aACA,SACA,aACA,iBACI;CACJ,IAAM,EAAE,mBAAgB,
|
|
1
|
+
{"version":3,"file":"TagPicker.js","names":[],"sources":["../../../src/proserviceworld/components/TagPicker.tsx"],"sourcesContent":["/**\n * ProServiceWorld tag picker.\n *\n * Wraps the shared fe-libs `TagsWidget` so entity pages can assign/remove\n * string tags on clients, engagements, matters, and documents. The backend\n * stores tags as `string[]` IDs/keys; we map them to the minimal `TagItem`\n * shape the widget expects.\n */\n\nimport type { FC } from 'react';\nimport { TagsWidget } from '@burdenoff/fe-libs/tag';\nimport { useProServiceWorld } from '../providers/ProServiceWorldProvider';\n\nexport interface TagPickerProps {\n entityType: string;\n entityId: string;\n tags: string[];\n onChange: (tags: string[]) => void | Promise<void>;\n maxTags?: number;\n}\n\nexport const TagPicker: FC<TagPickerProps> = ({\n entityType,\n entityId,\n tags,\n onChange,\n maxTags,\n}) => {\n const { workspaceId } = useProServiceWorld();\n\n const tagItems = tags.map((tag) => ({\n id: tag,\n key: tag,\n label: tag,\n value: tag,\n }));\n\n return (\n <TagsWidget\n entityType={entityType}\n entityId={entityId}\n workspaceId={workspaceId}\n tags={tagItems}\n onAdd={async (tag) => {\n if (tags.includes(tag.id)) return;\n await onChange([...tags, tag.id]);\n }}\n onRemove={async (tagId) => {\n await onChange(tags.filter((t) => t !== tagId));\n }}\n allowCreate\n maxTags={maxTags}\n syncGraphNotifications={false}\n />\n );\n};\n"],"mappings":";;;;AAqBA,IAAa,KAAiC,EAC5C,eACA,aACA,SACA,aACA,iBACI;CACJ,IAAM,EAAE,mBAAgB,EAAmB;CAS3C,OACE,kBAAC,GAAD;EACc;EACF;EACG;EACb,MAZa,EAAK,KAAK,OAAS;GAClC,IAAI;GACJ,KAAK;GACL,OAAO;GACP,OAAO;EACT,EAOU;EACN,OAAO,OAAO,MAAQ;GAChB,EAAK,SAAS,EAAI,EAAE,KACxB,MAAM,EAAS,CAAC,GAAG,GAAM,EAAI,EAAE,CAAC;EAClC;EACA,UAAU,OAAO,MAAU;GACzB,MAAM,EAAS,EAAK,QAAQ,MAAM,MAAM,CAAK,CAAC;EAChD;EACA,aAAA;EACS;EACT,wBAAwB;CACzB,CAAA;AAEL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","names":[],"sources":["../../../src/proserviceworld/components/ui.tsx"],"sourcesContent":["/**\n * ProServiceWorld shared UI kit.\n *\n * Self-contained, token-driven primitives (no raw colors) shared by every page:\n * page header, metric cards, section cards, a mobile-friendly modal, token-styled\n * native form controls, status pills, inline banners, and empty/loading/error\n * states. Actions reuse the fe-libs Button (brand variants); everything else is\n * built from semantic tokens so theming works across light/dark/any theme.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useEffect } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { AlertCircle, CheckCircle2, Info, Loader2, X } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\n// ---------------------------------------------------------------------------\n// Formatters\n// ---------------------------------------------------------------------------\nexport const humanize = (v: string | null | undefined): string =>\n (v ?? '')\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\nexport const fmtDate = (iso: string | null | undefined): string => {\n if (!iso) return '—';\n const d = new Date(iso);\n return Number.isNaN(d.getTime())\n ? '—'\n : d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n};\n\nexport const fmtMoney = (amount: number | null | undefined, currency = 'USD'): string => {\n const n = amount ?? 0;\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n maximumFractionDigits: 0,\n }).format(n);\n } catch {\n return `${currency} ${n.toLocaleString()}`;\n }\n};\n\nexport const fmtHours = (h: number | null | undefined): string =>\n `${(h ?? 0).toLocaleString(undefined, { maximumFractionDigits: 1 })}h`;\nexport const fmtMinutesAsHours = (m: number | null | undefined): string => fmtHours((m ?? 0) / 60);\n\n// ---------------------------------------------------------------------------\n// Status tone mapping\n// ---------------------------------------------------------------------------\nexport type Tone = 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'accent';\n\nconst TONE_CLASS: Record<Tone, string> = {\n neutral: 'bg-bg-sunken text-text-secondary',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n danger: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-bg-sunken text-text-link',\n accent: 'bg-bg-sunken text-accent',\n};\n\nconst STATUS_TONE: Record<string, Tone> = {\n // clients / generic\n ACTIVE: 'success',\n PROSPECT: 'info',\n INACTIVE: 'neutral',\n ARCHIVED: 'neutral',\n // engagements\n PROPOSAL: 'info',\n ACCEPTED: 'accent',\n IN_PROGRESS: 'accent',\n REVIEW: 'warning',\n DELIVERED: 'success',\n INVOICED: 'success',\n CLOSED: 'neutral',\n CANCELLED: 'danger',\n // matters\n OPEN: 'accent',\n ON_HOLD: 'warning',\n // priority\n LOW: 'neutral',\n MEDIUM: 'info',\n HIGH: 'warning',\n CRITICAL: 'danger',\n // deliverable / doc\n DRAFT: 'neutral',\n INTERNAL_REVIEW: 'info',\n CLIENT_REVIEW: 'warning',\n REJECTED: 'danger',\n FINAL: 'success',\n // time\n SUBMITTED: 'info',\n APPROVED: 'success',\n BILLED: 'accent',\n // invoice\n SENT: 'info',\n PAID: 'success',\n OVERDUE: 'danger',\n // compliance / conflict\n PENDING: 'warning',\n IN_REVIEW: 'info',\n PASSED: 'success',\n FAILED: 'danger',\n WAIVED: 'neutral',\n CLEAR: 'success',\n POTENTIAL_CONFLICT: 'warning',\n CONFLICT: 'danger',\n // milestone\n COMPLETED: 'success',\n};\n\nexport const StatusPill: FC<{ status: string; className?: string }> = ({ status, className }) => (\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium whitespace-nowrap',\n TONE_CLASS[STATUS_TONE[status] ?? 'neutral'],\n className\n )}\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-70\" />\n {humanize(status)}\n </span>\n);\n\n// ---------------------------------------------------------------------------\n// Page header\n// ---------------------------------------------------------------------------\nexport const PageHeader: FC<{\n title: string;\n subtitle?: string;\n actions?: ReactNode;\n icon?: ReactNode;\n}> = ({ title, subtitle, actions, icon }) => (\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-3.5\">\n {icon ? (\n <div className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-bg-sunken text-accent\">\n {icon}\n </div>\n ) : null}\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight text-text-primary sm:text-3xl\">{title}</h1>\n {subtitle ? (\n <p className=\"mt-1.5 max-w-2xl text-sm leading-relaxed text-text-secondary\">{subtitle}</p>\n ) : null}\n </div>\n </div>\n {actions ? <div className=\"flex flex-wrap items-center gap-2\">{actions}</div> : null}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Metric card\n// ---------------------------------------------------------------------------\nexport const MetricCard: FC<{\n label: string;\n value: ReactNode;\n hint?: string;\n icon?: ReactNode;\n tone?: Tone;\n}> = ({ label, value, hint, icon, tone = 'neutral' }) => (\n <div className=\"vc-lift-on-hover rounded-2xl border border-border-seam bg-bg-surface p-5 shadow-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-tertiary\">\n {label}\n </span>\n {icon ? (\n <span\n className={cn('flex h-9 w-9 items-center justify-center rounded-full', TONE_CLASS[tone])}\n >\n {icon}\n </span>\n ) : null}\n </div>\n <div className=\"mt-3 text-2xl font-semibold tracking-tight text-text-primary sm:text-3xl\">\n {value}\n </div>\n {hint ? <div className=\"mt-1 text-xs text-text-tertiary\">{hint}</div> : null}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Section card\n// ---------------------------------------------------------------------------\nexport const SectionCard: FC<{\n title?: string;\n description?: string;\n action?: ReactNode;\n children: ReactNode;\n className?: string;\n}> = ({ title, description, action, children, className }) => (\n <section\n className={cn('rounded-2xl border border-border-seam bg-bg-surface shadow-sm', className)}\n >\n {(title || action) && (\n <header className=\"flex items-center justify-between gap-3 border-b border-border-seam px-5 py-3.5\">\n <div>\n {title ? <h2 className=\"text-sm font-semibold text-text-primary\">{title}</h2> : null}\n {description ? <p className=\"mt-0.5 text-xs text-text-tertiary\">{description}</p> : null}\n </div>\n {action}\n </header>\n )}\n <div className=\"p-5\">{children}</div>\n </section>\n);\n\n// ---------------------------------------------------------------------------\n// Form controls (token-styled native elements — reliable inside modals)\n// ---------------------------------------------------------------------------\nconst controlClass =\n 'w-full rounded-lg border border-border-seam bg-bg-canvas px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30 disabled:opacity-50';\n\nexport const Field: FC<{\n label: string;\n required?: boolean;\n hint?: string;\n children: ReactNode;\n className?: string;\n}> = ({ label, required, hint, children, className }) => (\n <label className={cn('block', className)}>\n <span className=\"mb-1 block text-xs font-medium text-text-secondary\">\n {label}\n {required ? <span className=\"text-status-error-text\"> *</span> : null}\n </span>\n {children}\n {hint ? <span className=\"mt-1 block text-xs text-text-tertiary\">{hint}</span> : null}\n </label>\n);\n\nexport const TextInput: FC<React.InputHTMLAttributes<HTMLInputElement>> = ({\n className,\n ...props\n}) => <input className={cn(controlClass, className)} {...props} />;\nexport const TextArea: FC<React.TextareaHTMLAttributes<HTMLTextAreaElement>> = ({\n className,\n ...props\n}) => <textarea className={cn(controlClass, 'min-h-[80px] resize-y', className)} {...props} />;\nexport const NativeSelect: FC<React.SelectHTMLAttributes<HTMLSelectElement>> = ({\n className,\n children,\n ...props\n}) => (\n <select className={cn(controlClass, 'appearance-none pr-8')} {...props}>\n {children}\n </select>\n);\n\n/** Turn an enum-value list into <option>s with humanized labels. */\nexport const enumOptions = (values: readonly string[]): ReactNode =>\n values.map((v) => (\n <option key={v} value={v}>\n {humanize(v)}\n </option>\n ));\n\n// ---------------------------------------------------------------------------\n// Inline banner\n// ---------------------------------------------------------------------------\nexport const Banner: FC<{\n tone?: 'success' | 'danger' | 'info';\n children: ReactNode;\n onClose?: () => void;\n}> = ({ tone = 'info', children, onClose }) => {\n const Icon = tone === 'success' ? CheckCircle2 : tone === 'danger' ? AlertCircle : Info;\n const toneClass =\n tone === 'success'\n ? TONE_CLASS.success\n : tone === 'danger'\n ? TONE_CLASS.danger\n : 'bg-bg-sunken text-text-secondary';\n return (\n <div className={cn('flex items-start gap-2 rounded-lg px-3 py-2 text-sm', toneClass)}>\n <Icon className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <div className=\"flex-1\">{children}</div>\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"opacity-70 hover:opacity-100\"\n aria-label=\"Dismiss\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Modal (centered on desktop, full-height sheet on mobile)\n// ---------------------------------------------------------------------------\nexport const Modal: FC<{\n open: boolean;\n onClose: () => void;\n title: string;\n children: ReactNode;\n footer?: ReactNode;\n wide?: boolean;\n}> = ({ open, onClose, title, children, footer, wide }) => {\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', onKey);\n document.body.style.overflow = 'hidden';\n return () => {\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = '';\n };\n }, [open, onClose]);\n\n if (!open) return null;\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-end justify-center sm:items-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n >\n <div className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\" onClick={onClose} />\n <div\n className={cn(\n 'relative flex max-h-[92vh] w-full flex-col overflow-hidden rounded-t-2xl border border-border-seam bg-bg-surface shadow-xl sm:max-h-[88vh] sm:rounded-2xl',\n wide ? 'sm:max-w-3xl' : 'sm:max-w-lg'\n )}\n >\n <header className=\"flex items-center justify-between border-b border-border-seam px-4 py-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg p-1 text-text-tertiary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Close\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </header>\n <div className=\"flex-1 overflow-y-auto px-4 py-4\">{children}</div>\n {footer ? (\n <footer className=\"flex items-center justify-end gap-2 border-t border-border-seam px-4 py-3\">\n {footer}\n </footer>\n ) : null}\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// States\n// ---------------------------------------------------------------------------\nexport const LoadingBlock: FC<{ label?: string }> = ({ label = 'Loading…' }) => (\n <div className=\"flex min-h-[220px] flex-col items-center justify-center gap-3 text-text-tertiary\">\n <Loader2 className=\"h-7 w-7 animate-spin text-accent\" />\n <span className=\"text-sm\">{label}</span>\n </div>\n);\n\nexport const ErrorState: FC<{ error: unknown; onRetry?: () => void }> = ({ error, onRetry }) => (\n <div className=\"flex min-h-[220px] flex-col items-center justify-center gap-3 text-center\">\n <AlertCircle className=\"h-8 w-8 text-status-error-text\" />\n <p className=\"max-w-md text-sm text-text-secondary\">\n {error instanceof Error ? error.message : 'Something went wrong.'}\n </p>\n {onRetry ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n Try again\n </Button>\n ) : null}\n </div>\n);\n\nexport const Empty: FC<{\n title: string;\n description?: string;\n icon?: ReactNode;\n action?: ReactNode;\n}> = ({ title, description, icon, action }) => (\n <div className=\"flex min-h-[240px] flex-col items-center justify-center gap-4 rounded-2xl border border-dashed border-border-seam bg-bg-surface/50 px-6 py-12 text-center\">\n {icon ? (\n <div className=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-bg-sunken text-text-tertiary\">\n {icon}\n </div>\n ) : null}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{title}</p>\n {description ? (\n <p className=\"mt-1 max-w-sm text-sm text-text-tertiary\">{description}</p>\n ) : null}\n </div>\n {action}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Simple responsive table wrapper (scrolls horizontally on small screens)\n// ---------------------------------------------------------------------------\nexport const TableWrap: FC<{ children: ReactNode }> = ({ children }) => (\n <div className=\"overflow-x-auto rounded-xl border border-border-seam\">\n <table className=\"w-full min-w-[560px] text-left text-sm\">{children}</table>\n </div>\n);\nexport const Th: FC<{ children?: ReactNode; className?: string }> = ({ children, className }) => (\n <th\n className={cn(\n 'bg-bg-sunken px-4 py-2.5 text-xs font-semibold uppercase tracking-wide text-text-tertiary',\n className\n )}\n >\n {children}\n </th>\n);\nexport const Td: FC<{ children?: ReactNode; className?: string }> = ({ children, className }) => (\n <td className={cn('border-t border-border-seam px-4 py-3 text-text-secondary', className)}>\n {children}\n </td>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ui.js","names":[],"sources":["../../../src/proserviceworld/components/ui.tsx"],"sourcesContent":["/**\n * ProServiceWorld shared UI kit.\n *\n * Self-contained, token-driven primitives (no raw colors) shared by every page:\n * page header, metric cards, section cards, a mobile-friendly modal, token-styled\n * native form controls, status pills, inline banners, and empty/loading/error\n * states. Actions reuse the fe-libs Button (brand variants); everything else is\n * built from semantic tokens so theming works across light/dark/any theme.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useEffect } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { AlertCircle, CheckCircle2, Info, Loader2, X } from 'lucide-react';\nimport { cn } from '../utils/cn';\n\n// ---------------------------------------------------------------------------\n// Formatters\n// ---------------------------------------------------------------------------\nexport const humanize = (v: string | null | undefined): string =>\n (v ?? '')\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\nexport const fmtDate = (iso: string | null | undefined): string => {\n if (!iso) return '—';\n const d = new Date(iso);\n return Number.isNaN(d.getTime())\n ? '—'\n : d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n};\n\nexport const fmtMoney = (amount: number | null | undefined, currency = 'USD'): string => {\n const n = amount ?? 0;\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n maximumFractionDigits: 0,\n }).format(n);\n } catch {\n return `${currency} ${n.toLocaleString()}`;\n }\n};\n\nexport const fmtHours = (h: number | null | undefined): string =>\n `${(h ?? 0).toLocaleString(undefined, { maximumFractionDigits: 1 })}h`;\nexport const fmtMinutesAsHours = (m: number | null | undefined): string => fmtHours((m ?? 0) / 60);\n\n// ---------------------------------------------------------------------------\n// Status tone mapping\n// ---------------------------------------------------------------------------\nexport type Tone = 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'accent';\n\nconst TONE_CLASS: Record<Tone, string> = {\n neutral: 'bg-bg-sunken text-text-secondary',\n success: 'bg-status-success-bg-subtle text-status-success-text',\n warning: 'bg-status-warning-bg-subtle text-status-warning-text',\n danger: 'bg-status-error-bg-subtle text-status-error-text',\n info: 'bg-bg-sunken text-text-link',\n accent: 'bg-bg-sunken text-accent',\n};\n\nconst STATUS_TONE: Record<string, Tone> = {\n // clients / generic\n ACTIVE: 'success',\n PROSPECT: 'info',\n INACTIVE: 'neutral',\n ARCHIVED: 'neutral',\n // engagements\n PROPOSAL: 'info',\n ACCEPTED: 'accent',\n IN_PROGRESS: 'accent',\n REVIEW: 'warning',\n DELIVERED: 'success',\n INVOICED: 'success',\n CLOSED: 'neutral',\n CANCELLED: 'danger',\n // matters\n OPEN: 'accent',\n ON_HOLD: 'warning',\n // priority\n LOW: 'neutral',\n MEDIUM: 'info',\n HIGH: 'warning',\n CRITICAL: 'danger',\n // deliverable / doc\n DRAFT: 'neutral',\n INTERNAL_REVIEW: 'info',\n CLIENT_REVIEW: 'warning',\n REJECTED: 'danger',\n FINAL: 'success',\n // time\n SUBMITTED: 'info',\n APPROVED: 'success',\n BILLED: 'accent',\n // invoice\n SENT: 'info',\n PAID: 'success',\n OVERDUE: 'danger',\n // compliance / conflict\n PENDING: 'warning',\n IN_REVIEW: 'info',\n PASSED: 'success',\n FAILED: 'danger',\n WAIVED: 'neutral',\n CLEAR: 'success',\n POTENTIAL_CONFLICT: 'warning',\n CONFLICT: 'danger',\n // milestone\n COMPLETED: 'success',\n};\n\nexport const StatusPill: FC<{ status: string; className?: string }> = ({ status, className }) => (\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium whitespace-nowrap',\n TONE_CLASS[STATUS_TONE[status] ?? 'neutral'],\n className\n )}\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-70\" />\n {humanize(status)}\n </span>\n);\n\n// ---------------------------------------------------------------------------\n// Page header\n// ---------------------------------------------------------------------------\nexport const PageHeader: FC<{\n title: string;\n subtitle?: string;\n actions?: ReactNode;\n icon?: ReactNode;\n}> = ({ title, subtitle, actions, icon }) => (\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-3.5\">\n {icon ? (\n <div className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-bg-sunken text-accent\">\n {icon}\n </div>\n ) : null}\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight text-text-primary sm:text-3xl\">{title}</h1>\n {subtitle ? (\n <p className=\"mt-1.5 max-w-2xl text-sm leading-relaxed text-text-secondary\">{subtitle}</p>\n ) : null}\n </div>\n </div>\n {actions ? <div className=\"flex flex-wrap items-center gap-2\">{actions}</div> : null}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Metric card\n// ---------------------------------------------------------------------------\nexport const MetricCard: FC<{\n label: string;\n value: ReactNode;\n hint?: string;\n icon?: ReactNode;\n tone?: Tone;\n}> = ({ label, value, hint, icon, tone = 'neutral' }) => (\n <div className=\"vc-lift-on-hover rounded-2xl border border-border-seam bg-bg-surface p-5 shadow-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-tertiary\">\n {label}\n </span>\n {icon ? (\n <span\n className={cn('flex h-9 w-9 items-center justify-center rounded-full', TONE_CLASS[tone])}\n >\n {icon}\n </span>\n ) : null}\n </div>\n <div className=\"mt-3 text-2xl font-semibold tracking-tight text-text-primary sm:text-3xl\">\n {value}\n </div>\n {hint ? <div className=\"mt-1 text-xs text-text-tertiary\">{hint}</div> : null}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Section card\n// ---------------------------------------------------------------------------\nexport const SectionCard: FC<{\n title?: string;\n description?: string;\n action?: ReactNode;\n children: ReactNode;\n className?: string;\n}> = ({ title, description, action, children, className }) => (\n <section\n className={cn('rounded-2xl border border-border-seam bg-bg-surface shadow-sm', className)}\n >\n {(title || action) && (\n <header className=\"flex items-center justify-between gap-3 border-b border-border-seam px-5 py-3.5\">\n <div>\n {title ? <h2 className=\"text-sm font-semibold text-text-primary\">{title}</h2> : null}\n {description ? <p className=\"mt-0.5 text-xs text-text-tertiary\">{description}</p> : null}\n </div>\n {action}\n </header>\n )}\n <div className=\"p-5\">{children}</div>\n </section>\n);\n\n// ---------------------------------------------------------------------------\n// Form controls (token-styled native elements — reliable inside modals)\n// ---------------------------------------------------------------------------\nconst controlClass =\n 'w-full rounded-lg border border-border-seam bg-bg-canvas px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30 disabled:opacity-50';\n\nexport const Field: FC<{\n label: string;\n required?: boolean;\n hint?: string;\n children: ReactNode;\n className?: string;\n}> = ({ label, required, hint, children, className }) => (\n <label className={cn('block', className)}>\n <span className=\"mb-1 block text-xs font-medium text-text-secondary\">\n {label}\n {required ? <span className=\"text-status-error-text\"> *</span> : null}\n </span>\n {children}\n {hint ? <span className=\"mt-1 block text-xs text-text-tertiary\">{hint}</span> : null}\n </label>\n);\n\nexport const TextInput: FC<React.InputHTMLAttributes<HTMLInputElement>> = ({\n className,\n ...props\n}) => <input className={cn(controlClass, className)} {...props} />;\nexport const TextArea: FC<React.TextareaHTMLAttributes<HTMLTextAreaElement>> = ({\n className,\n ...props\n}) => <textarea className={cn(controlClass, 'min-h-[80px] resize-y', className)} {...props} />;\nexport const NativeSelect: FC<React.SelectHTMLAttributes<HTMLSelectElement>> = ({\n className,\n children,\n ...props\n}) => (\n <select className={cn(controlClass, 'appearance-none pr-8')} {...props}>\n {children}\n </select>\n);\n\n/** Turn an enum-value list into <option>s with humanized labels. */\nexport const enumOptions = (values: readonly string[]): ReactNode =>\n values.map((v) => (\n <option key={v} value={v}>\n {humanize(v)}\n </option>\n ));\n\n// ---------------------------------------------------------------------------\n// Inline banner\n// ---------------------------------------------------------------------------\nexport const Banner: FC<{\n tone?: 'success' | 'danger' | 'info';\n children: ReactNode;\n onClose?: () => void;\n}> = ({ tone = 'info', children, onClose }) => {\n const Icon = tone === 'success' ? CheckCircle2 : tone === 'danger' ? AlertCircle : Info;\n const toneClass =\n tone === 'success'\n ? TONE_CLASS.success\n : tone === 'danger'\n ? TONE_CLASS.danger\n : 'bg-bg-sunken text-text-secondary';\n return (\n <div className={cn('flex items-start gap-2 rounded-lg px-3 py-2 text-sm', toneClass)}>\n <Icon className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <div className=\"flex-1\">{children}</div>\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"opacity-70 hover:opacity-100\"\n aria-label=\"Dismiss\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Modal (centered on desktop, full-height sheet on mobile)\n// ---------------------------------------------------------------------------\nexport const Modal: FC<{\n open: boolean;\n onClose: () => void;\n title: string;\n children: ReactNode;\n footer?: ReactNode;\n wide?: boolean;\n}> = ({ open, onClose, title, children, footer, wide }) => {\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', onKey);\n document.body.style.overflow = 'hidden';\n return () => {\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = '';\n };\n }, [open, onClose]);\n\n if (!open) return null;\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-end justify-center sm:items-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n >\n <div className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\" onClick={onClose} />\n <div\n className={cn(\n 'relative flex max-h-[92vh] w-full flex-col overflow-hidden rounded-t-2xl border border-border-seam bg-bg-surface shadow-xl sm:max-h-[88vh] sm:rounded-2xl',\n wide ? 'sm:max-w-3xl' : 'sm:max-w-lg'\n )}\n >\n <header className=\"flex items-center justify-between border-b border-border-seam px-4 py-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg p-1 text-text-tertiary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Close\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </header>\n <div className=\"flex-1 overflow-y-auto px-4 py-4\">{children}</div>\n {footer ? (\n <footer className=\"flex items-center justify-end gap-2 border-t border-border-seam px-4 py-3\">\n {footer}\n </footer>\n ) : null}\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// States\n// ---------------------------------------------------------------------------\nexport const LoadingBlock: FC<{ label?: string }> = ({ label = 'Loading…' }) => (\n <div className=\"flex min-h-[220px] flex-col items-center justify-center gap-3 text-text-tertiary\">\n <Loader2 className=\"h-7 w-7 animate-spin text-accent\" />\n <span className=\"text-sm\">{label}</span>\n </div>\n);\n\nexport const ErrorState: FC<{ error: unknown; onRetry?: () => void }> = ({ error, onRetry }) => (\n <div className=\"flex min-h-[220px] flex-col items-center justify-center gap-3 text-center\">\n <AlertCircle className=\"h-8 w-8 text-status-error-text\" />\n <p className=\"max-w-md text-sm text-text-secondary\">\n {error instanceof Error ? error.message : 'Something went wrong.'}\n </p>\n {onRetry ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n Try again\n </Button>\n ) : null}\n </div>\n);\n\nexport const Empty: FC<{\n title: string;\n description?: string;\n icon?: ReactNode;\n action?: ReactNode;\n}> = ({ title, description, icon, action }) => (\n <div className=\"flex min-h-[240px] flex-col items-center justify-center gap-4 rounded-2xl border border-dashed border-border-seam bg-bg-surface/50 px-6 py-12 text-center\">\n {icon ? (\n <div className=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-bg-sunken text-text-tertiary\">\n {icon}\n </div>\n ) : null}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{title}</p>\n {description ? (\n <p className=\"mt-1 max-w-sm text-sm text-text-tertiary\">{description}</p>\n ) : null}\n </div>\n {action}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Simple responsive table wrapper (scrolls horizontally on small screens)\n// ---------------------------------------------------------------------------\nexport const TableWrap: FC<{ children: ReactNode }> = ({ children }) => (\n <div className=\"overflow-x-auto rounded-xl border border-border-seam\">\n <table className=\"w-full min-w-[560px] text-left text-sm\">{children}</table>\n </div>\n);\nexport const Th: FC<{ children?: ReactNode; className?: string }> = ({ children, className }) => (\n <th\n className={cn(\n 'bg-bg-sunken px-4 py-2.5 text-xs font-semibold uppercase tracking-wide text-text-tertiary',\n className\n )}\n >\n {children}\n </th>\n);\nexport const Td: FC<{ children?: ReactNode; className?: string }> = ({ children, className }) => (\n <td className={cn('border-t border-border-seam px-4 py-3 text-text-secondary', className)}>\n {children}\n </td>\n);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAnalytics.js","names":[],"sources":["../../../src/proserviceworld/hooks/useAnalytics.ts"],"sourcesContent":["/**\n * Product-analytics helper for ProServiceWorld.\n *\n * Emits intent/fact event pairs on the shared event bus. New event names are\n * fired via `emitCustom()` until they are added to fe-libs' `BurdenoffEventName`\n * union and the Rybbit allowlist.\n */\n\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport type ProServiceWorldEventName =\n | 'psw.client.create_clicked'\n | 'psw.client.created'\n | 'psw.client.updated'\n | 'psw.client.archived'\n | 'psw.engagement.create_clicked'\n | 'psw.engagement.created'\n | 'psw.engagement.transitioned'\n | 'psw.matter.create_clicked'\n | 'psw.matter.created'\n | 'psw.matter.closed'\n | 'psw.timeentry.create_clicked'\n | 'psw.timeentry.created'\n | 'psw.invoice.create_clicked'\n | 'psw.invoice.created'\n | 'psw.compliance.create_clicked'\n | 'psw.compliance.resolved'\n | 'psw.conflictcheck.run_clicked'\n | 'psw.conflictcheck.recorded'\n | 'psw.document.create_clicked'\n | 'psw.document.created'\n | 'psw.practicearea.create_clicked'\n | 'psw.practicearea.created'\n | 'psw.search.performed'\n | 'psw.export.requested';\n\ninterface BasePayload {\n workspaceId?: string;\n}\n\nexport function useAnalytics() {\n const bus = useEventBus();\n\n const emit = useCallback(\n (name: ProServiceWorldEventName, payload: BasePayload & Record<string, unknown> = {}) => {\n bus.emitCustom(name, payload);\n },\n [bus]\n );\n\n const emitIntent = useCallback(\n (name: ProServiceWorldEventName, payload?: BasePayload & Record<string, unknown>) => {\n emit(name, payload ?? {});\n },\n [emit]\n );\n\n const emitFact = useCallback(\n (name: ProServiceWorldEventName, payload?: BasePayload & Record<string, unknown>) => {\n emit(name, payload ?? {});\n },\n [emit]\n );\n\n return { emit, emitIntent, emitFact };\n}\n"],"mappings":";;;AAyCA,SAAgB,IAAe;CAC7B,IAAM,IAAM,
|
|
1
|
+
{"version":3,"file":"useAnalytics.js","names":[],"sources":["../../../src/proserviceworld/hooks/useAnalytics.ts"],"sourcesContent":["/**\n * Product-analytics helper for ProServiceWorld.\n *\n * Emits intent/fact event pairs on the shared event bus. New event names are\n * fired via `emitCustom()` until they are added to fe-libs' `BurdenoffEventName`\n * union and the Rybbit allowlist.\n */\n\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport type ProServiceWorldEventName =\n | 'psw.client.create_clicked'\n | 'psw.client.created'\n | 'psw.client.updated'\n | 'psw.client.archived'\n | 'psw.engagement.create_clicked'\n | 'psw.engagement.created'\n | 'psw.engagement.transitioned'\n | 'psw.matter.create_clicked'\n | 'psw.matter.created'\n | 'psw.matter.closed'\n | 'psw.timeentry.create_clicked'\n | 'psw.timeentry.created'\n | 'psw.invoice.create_clicked'\n | 'psw.invoice.created'\n | 'psw.compliance.create_clicked'\n | 'psw.compliance.resolved'\n | 'psw.conflictcheck.run_clicked'\n | 'psw.conflictcheck.recorded'\n | 'psw.document.create_clicked'\n | 'psw.document.created'\n | 'psw.practicearea.create_clicked'\n | 'psw.practicearea.created'\n | 'psw.search.performed'\n | 'psw.export.requested';\n\ninterface BasePayload {\n workspaceId?: string;\n}\n\nexport function useAnalytics() {\n const bus = useEventBus();\n\n const emit = useCallback(\n (name: ProServiceWorldEventName, payload: BasePayload & Record<string, unknown> = {}) => {\n bus.emitCustom(name, payload);\n },\n [bus]\n );\n\n const emitIntent = useCallback(\n (name: ProServiceWorldEventName, payload?: BasePayload & Record<string, unknown>) => {\n emit(name, payload ?? {});\n },\n [emit]\n );\n\n const emitFact = useCallback(\n (name: ProServiceWorldEventName, payload?: BasePayload & Record<string, unknown>) => {\n emit(name, payload ?? {});\n },\n [emit]\n );\n\n return { emit, emitIntent, emitFact };\n}\n"],"mappings":";;;AAyCA,SAAgB,IAAe;CAC7B,IAAM,IAAM,EAAY,GAElB,IAAO,GACV,GAAgC,IAAiD,CAAC,MAAM;EACvF,EAAI,WAAW,GAAM,CAAO;CAC9B,GACA,CAAC,CAAG,CACN;CAgBA,OAAO;EAAE;EAAM,YAdI,GAChB,GAAgC,MAAoD;GACnF,EAAK,GAAM,KAAW,CAAC,CAAC;EAC1B,GACA,CAAC,CAAI,CAUQ;EAAY,UAPV,GACd,GAAgC,MAAoD;GACnF,EAAK,GAAM,KAAW,CAAC,CAAC;EAC1B,GACA,CAAC,CAAI,CAGoB;CAAS;AACtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEntityComments.js","names":[],"sources":["../../../src/proserviceworld/hooks/useEntityComments.ts"],"sourcesContent":["/**\n * Entity discussion via wspace-conversations-svc (NO duplication).\n * @module proserviceworld/hooks\n *\n * ProServiceWorld does NOT own a comments table. Discussion/notes on a client,\n * engagement or matter reuse the shared `wspace-conversations` entity-comment\n * API (createEntityComment / getEntityComments) — the same service that powers\n * comments everywhere on the platform — via the MFE's proven graphqlFetch\n * transport. `entityType` namespaces threads to this product\n * (\"proserviceworld-<entity>\"); `entityId` is the Psw row id.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nasync function convQuery<TData>(query: string, variables: Record<string, unknown>): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length)\n throw new Error(response.errors[0]?.message ?? 'Conversations request failed');\n if (!response.data) throw new Error('Conversations response did not include data');\n if (query.trimStart().startsWith('mutation')) clearGraphqlFetchResponseCache();\n return response.data;\n}\n\nexport interface EntityComment {\n id: string;\n authorId: string;\n content: { text: string | null } | null;\n createdAt: string;\n}\n\nconst COMMENT_F = 'id authorId content { text } createdAt';\nconst QK = 'proserviceworld-comments' as const;\n\n/** List the discussion thread for a Psw entity (most recent first). */\nexport function useEntityComments(entityType: string, entityId: string | null) {\n return useQuery({\n queryKey: [QK, entityType, entityId],\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await convQuery<{\n getEntityComments: { items: EntityComment[]; total: number };\n }>(\n `query PswGetComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) { items { ${COMMENT_F} } total }\n }`,\n { input: { entityType, entityId, take: 50, sortBy: 'createdAt', sortOrder: 'desc' } }\n );\n return data.getEntityComments;\n },\n });\n}\n\n/** Post a plain-text note onto a Psw entity's shared discussion thread. */\nexport function useAddEntityComment(entityType: string, entityId: string) {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (text: string) =>\n convQuery(\n `mutation PswAddComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { ${COMMENT_F} }\n }`,\n { input: { entityType, entityId, content: { text } } }\n ),\n onSuccess: () => qc.invalidateQueries({ queryKey: [QK, entityType, entityId] }),\n });\n}\n"],"mappings":";;;;AAiBA,eAAe,EAAiB,GAAe,GAAoD;CACjG,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA,gBAAgB;
|
|
1
|
+
{"version":3,"file":"useEntityComments.js","names":[],"sources":["../../../src/proserviceworld/hooks/useEntityComments.ts"],"sourcesContent":["/**\n * Entity discussion via wspace-conversations-svc (NO duplication).\n * @module proserviceworld/hooks\n *\n * ProServiceWorld does NOT own a comments table. Discussion/notes on a client,\n * engagement or matter reuse the shared `wspace-conversations` entity-comment\n * API (createEntityComment / getEntityComments) — the same service that powers\n * comments everywhere on the platform — via the MFE's proven graphqlFetch\n * transport. `entityType` namespaces threads to this product\n * (\"proserviceworld-<entity>\"); `entityId` is the Psw row id.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nasync function convQuery<TData>(query: string, variables: Record<string, unknown>): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length)\n throw new Error(response.errors[0]?.message ?? 'Conversations request failed');\n if (!response.data) throw new Error('Conversations response did not include data');\n if (query.trimStart().startsWith('mutation')) clearGraphqlFetchResponseCache();\n return response.data;\n}\n\nexport interface EntityComment {\n id: string;\n authorId: string;\n content: { text: string | null } | null;\n createdAt: string;\n}\n\nconst COMMENT_F = 'id authorId content { text } createdAt';\nconst QK = 'proserviceworld-comments' as const;\n\n/** List the discussion thread for a Psw entity (most recent first). */\nexport function useEntityComments(entityType: string, entityId: string | null) {\n return useQuery({\n queryKey: [QK, entityType, entityId],\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await convQuery<{\n getEntityComments: { items: EntityComment[]; total: number };\n }>(\n `query PswGetComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) { items { ${COMMENT_F} } total }\n }`,\n { input: { entityType, entityId, take: 50, sortBy: 'createdAt', sortOrder: 'desc' } }\n );\n return data.getEntityComments;\n },\n });\n}\n\n/** Post a plain-text note onto a Psw entity's shared discussion thread. */\nexport function useAddEntityComment(entityType: string, entityId: string) {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (text: string) =>\n convQuery(\n `mutation PswAddComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { ${COMMENT_F} }\n }`,\n { input: { entityType, entityId, content: { text } } }\n ),\n onSuccess: () => qc.invalidateQueries({ queryKey: [QK, entityType, entityId] }),\n });\n}\n"],"mappings":";;;;AAiBA,eAAe,EAAiB,GAAe,GAAoD;CACjG,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA,gBAAgB;CAClB,CAAC;CACD,IAAI,EAAS,QAAQ,QACnB,MAAU,MAAM,EAAS,OAAO,EAAE,EAAE,WAAW,8BAA8B;CAC/E,IAAI,CAAC,EAAS,MAAM,MAAU,MAAM,6CAA6C;CAEjF,OADI,EAAM,UAAU,CAAC,CAAC,WAAW,UAAU,KAAG,EAA+B,GACtE,EAAS;AAClB;AASA,IAAM,IAAY,0CACZ,IAAK;AAGX,SAAgB,EAAkB,GAAoB,GAAyB;CAC7E,OAAO,EAAS;EACd,UAAU;GAAC;GAAI;GAAY;EAAQ;EACnC,SAAS,EAAQ;EACjB,SAAS,aASA,MARY,EAGjB;uDAC+C,EAAU;YAEzD,EAAE,OAAO;GAAE;GAAY;GAAU,MAAM;GAAI,QAAQ;GAAa,WAAW;EAAO,EAAE,CACtF,EAAA,CACY;CAEhB,CAAC;AACH;AAGA,SAAgB,EAAoB,GAAoB,GAAkB;CACxE,IAAM,IAAK,EAAe;CAC1B,OAAO,EAAY;EACjB,aAAa,MACX,EACE;iDACyC,EAAU;YAEnD,EAAE,OAAO;GAAE;GAAY;GAAU,SAAS,EAAE,QAAK;EAAE,EAAE,CACvD;EACF,iBAAiB,EAAG,kBAAkB,EAAE,UAAU;GAAC;GAAI;GAAY;EAAQ,EAAE,CAAC;CAChF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFiles.js","names":[],"sources":["../../../src/proserviceworld/hooks/useFiles.ts"],"sourcesContent":["/**\n * File upload/download via wspace-files-svc (NO duplication).\n * @module proserviceworld/hooks\n *\n * ProServiceWorld never stores blobs. A PswDocument references a wspace-files\n * File by id; the Files service owns storage, signed URLs, and download audit.\n * Upload drives the SAME `graphqlFetch` workspace transport the rest of this\n * MFE uses, through the files 3-step presigned flow:\n * ensureFolderByPath -> initiateUpload -> HTTP PUT -> completeUpload\n * (completeUpload requires `fileSize` — omitting it fails every upload after the\n * S3 PUT). Display resolves a file id to a short-lived presigned URL.\n */\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\n/** Six hours — wspace-files rejects any expiresIn above the S3 7-day cap. */\nconst DOWNLOAD_URL_TTL_SECONDS = 21_600;\nconst DOCS_FOLDER = '/ProServiceWorld/Documents';\n\nasync function filesFetch<TData>(\n query: string,\n variables: Record<string, unknown>\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length)\n throw new Error(response.errors[0]?.message ?? 'Files request failed');\n if (!response.data) throw new Error('Files response did not include data');\n return response.data;\n}\n\n/**\n * Upload a browser File to wspace-files and return its file id. The caller\n * stores the id on a PswDocument via pswCreateDocument (metadata only).\n */\nexport async function uploadDocumentFile(file: File): Promise<{ fileId: string; name: string }> {\n await filesFetch<{ ensureFolderByPath: { id: string } }>(\n `mutation PswEnsureFolder($path: String!) { ensureFolderByPath(path: $path) { id } }`,\n { path: DOCS_FOLDER }\n );\n\n const initiated = await filesFetch<{\n initiateUpload: { uploadUrl: string; session: { id: string } };\n }>(\n `mutation PswInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n mimeType: file.type || 'application/octet-stream',\n folderPath: DOCS_FOLDER,\n },\n }\n );\n\n const putRes = await fetch(initiated.initiateUpload.uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putRes.ok) throw new Error(`Upload failed (${putRes.status})`);\n\n const completed = await filesFetch<{\n completeUpload: { fileId: string; file: { id: string; name: string } };\n }>(\n `mutation PswCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId file { id name } }\n }`,\n { input: { sessionId: initiated.initiateUpload.session.id, fileSize: String(file.size) } }\n );\n\n return { fileId: completed.completeUpload.fileId, name: completed.completeUpload.file.name };\n}\n\n/** Resolve a wspace-files file id to a short-lived presigned download URL. */\nexport async function getDocumentDownloadUrl(fileId: string): Promise<string> {\n const data = await filesFetch<{ getFileDownloadUrl: string }>(\n `query PswGetFileDownloadUrl($id: ID!, $expiresIn: Int) { getFileDownloadUrl(id: $id, expiresIn: $expiresIn) }`,\n { id: fileId, expiresIn: DOWNLOAD_URL_TTL_SECONDS }\n );\n return data.getFileDownloadUrl;\n}\n"],"mappings":";;AAgBA,IAAM,IAA2B,OAC3B,IAAc;AAEpB,eAAe,EACb,GACA,GACgB;CAChB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA,gBAAgB;
|
|
1
|
+
{"version":3,"file":"useFiles.js","names":[],"sources":["../../../src/proserviceworld/hooks/useFiles.ts"],"sourcesContent":["/**\n * File upload/download via wspace-files-svc (NO duplication).\n * @module proserviceworld/hooks\n *\n * ProServiceWorld never stores blobs. A PswDocument references a wspace-files\n * File by id; the Files service owns storage, signed URLs, and download audit.\n * Upload drives the SAME `graphqlFetch` workspace transport the rest of this\n * MFE uses, through the files 3-step presigned flow:\n * ensureFolderByPath -> initiateUpload -> HTTP PUT -> completeUpload\n * (completeUpload requires `fileSize` — omitting it fails every upload after the\n * S3 PUT). Display resolves a file id to a short-lived presigned URL.\n */\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\n/** Six hours — wspace-files rejects any expiresIn above the S3 7-day cap. */\nconst DOWNLOAD_URL_TTL_SECONDS = 21_600;\nconst DOCS_FOLDER = '/ProServiceWorld/Documents';\n\nasync function filesFetch<TData>(\n query: string,\n variables: Record<string, unknown>\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length)\n throw new Error(response.errors[0]?.message ?? 'Files request failed');\n if (!response.data) throw new Error('Files response did not include data');\n return response.data;\n}\n\n/**\n * Upload a browser File to wspace-files and return its file id. The caller\n * stores the id on a PswDocument via pswCreateDocument (metadata only).\n */\nexport async function uploadDocumentFile(file: File): Promise<{ fileId: string; name: string }> {\n await filesFetch<{ ensureFolderByPath: { id: string } }>(\n `mutation PswEnsureFolder($path: String!) { ensureFolderByPath(path: $path) { id } }`,\n { path: DOCS_FOLDER }\n );\n\n const initiated = await filesFetch<{\n initiateUpload: { uploadUrl: string; session: { id: string } };\n }>(\n `mutation PswInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n mimeType: file.type || 'application/octet-stream',\n folderPath: DOCS_FOLDER,\n },\n }\n );\n\n const putRes = await fetch(initiated.initiateUpload.uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n if (!putRes.ok) throw new Error(`Upload failed (${putRes.status})`);\n\n const completed = await filesFetch<{\n completeUpload: { fileId: string; file: { id: string; name: string } };\n }>(\n `mutation PswCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId file { id name } }\n }`,\n { input: { sessionId: initiated.initiateUpload.session.id, fileSize: String(file.size) } }\n );\n\n return { fileId: completed.completeUpload.fileId, name: completed.completeUpload.file.name };\n}\n\n/** Resolve a wspace-files file id to a short-lived presigned download URL. */\nexport async function getDocumentDownloadUrl(fileId: string): Promise<string> {\n const data = await filesFetch<{ getFileDownloadUrl: string }>(\n `query PswGetFileDownloadUrl($id: ID!, $expiresIn: Int) { getFileDownloadUrl(id: $id, expiresIn: $expiresIn) }`,\n { id: fileId, expiresIn: DOWNLOAD_URL_TTL_SECONDS }\n );\n return data.getFileDownloadUrl;\n}\n"],"mappings":";;AAgBA,IAAM,IAA2B,OAC3B,IAAc;AAEpB,eAAe,EACb,GACA,GACgB;CAChB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA,gBAAgB;CAClB,CAAC;CACD,IAAI,EAAS,QAAQ,QACnB,MAAU,MAAM,EAAS,OAAO,EAAE,EAAE,WAAW,sBAAsB;CACvE,IAAI,CAAC,EAAS,MAAM,MAAU,MAAM,qCAAqC;CACzE,OAAO,EAAS;AAClB;AAMA,eAAsB,EAAmB,GAAuD;CAC9F,MAAM,EACJ,uFACA,EAAE,MAAM,EAAY,CACtB;CAEA,IAAM,IAAY,MAAM,EAGtB,uIAGA,EACE,OAAO;EACL,UAAU,EAAK;EACf,UAAU,EAAK,QAAQ;EACvB,YAAY;CACd,EACF,CACF,GAEM,IAAS,MAAM,MAAM,EAAU,eAAe,WAAW;EAC7D,QAAQ;EACR,MAAM;EACN,SAAS,EAAE,gBAAgB,EAAK,QAAQ,2BAA2B;CACrE,CAAC;CACD,IAAI,CAAC,EAAO,IAAI,MAAU,MAAM,kBAAkB,EAAO,OAAO,EAAE;CAElE,IAAM,IAAY,MAAM,EAGtB,sIAGA,EAAE,OAAO;EAAE,WAAW,EAAU,eAAe,QAAQ;EAAI,UAAU,OAAO,EAAK,IAAI;CAAE,EAAE,CAC3F;CAEA,OAAO;EAAE,QAAQ,EAAU,eAAe;EAAQ,MAAM,EAAU,eAAe,KAAK;CAAK;AAC7F;AAGA,eAAsB,EAAuB,GAAiC;CAK5E,QAAO,MAJY,EACjB,iHACA;EAAE,IAAI;EAAQ,WAAW;CAAyB,CACpD,EAAA,CACY;AACd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePageViewAnalytics.js","names":[],"sources":["../../../src/proserviceworld/hooks/usePageViewAnalytics.ts"],"sourcesContent":["/**\n * Page-view analytics for ProServiceWorld routes.\n *\n * Emits a `psw.page.viewed` event on the shared event bus whenever the\n * location pathname changes. This lets Rybbit / analytics backends track\n * which product surfaces are visited without relying on noisy generic\n * router instrumentation.\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport function usePageViewAnalytics() {\n const location = useLocation();\n const bus = useEventBus();\n const lastPathRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (lastPathRef.current === location.pathname) return;\n lastPathRef.current = location.pathname;\n\n bus.emitCustom('psw.page.viewed', {\n path: location.pathname,\n search: location.search,\n hash: location.hash,\n product: 'proserviceworld',\n });\n }, [bus, location.hash, location.pathname, location.search]);\n}\n"],"mappings":";;;;AAaA,SAAgB,IAAuB;CACrC,IAAM,IAAW,
|
|
1
|
+
{"version":3,"file":"usePageViewAnalytics.js","names":[],"sources":["../../../src/proserviceworld/hooks/usePageViewAnalytics.ts"],"sourcesContent":["/**\n * Page-view analytics for ProServiceWorld routes.\n *\n * Emits a `psw.page.viewed` event on the shared event bus whenever the\n * location pathname changes. This lets Rybbit / analytics backends track\n * which product surfaces are visited without relying on noisy generic\n * router instrumentation.\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport function usePageViewAnalytics() {\n const location = useLocation();\n const bus = useEventBus();\n const lastPathRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (lastPathRef.current === location.pathname) return;\n lastPathRef.current = location.pathname;\n\n bus.emitCustom('psw.page.viewed', {\n path: location.pathname,\n search: location.search,\n hash: location.hash,\n product: 'proserviceworld',\n });\n }, [bus, location.hash, location.pathname, location.search]);\n}\n"],"mappings":";;;;AAaA,SAAgB,IAAuB;CACrC,IAAM,IAAW,EAAY,GACvB,IAAM,EAAY,GAClB,IAAc,EAAsB,IAAI;CAE9C,QAAgB;EACV,EAAY,YAAY,EAAS,aACrC,EAAY,UAAU,EAAS,UAE/B,EAAI,WAAW,mBAAmB;GAChC,MAAM,EAAS;GACf,QAAQ,EAAS;GACjB,MAAM,EAAS;GACf,SAAS;EACX,CAAC;CACH,GAAG;EAAC;EAAK,EAAS;EAAM,EAAS;EAAU,EAAS;CAAM,CAAC;AAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProServiceWorldApi.js","names":[],"sources":["../../../src/proserviceworld/hooks/useProServiceWorldApi.ts"],"sourcesContent":["/**\n * ProServiceWorld API hooks\n * @module proserviceworld/hooks\n *\n * Real data layer for the ProServiceWorld workspace subgraph (`proserviceworld`\n * Hive service / `wspace-proserviceworld-svc`). Every hook talks to the live\n * workspace GraphQL gateway via {@link graphqlFetch} with inline operations\n * whose fields mirror `.hive-schema.graphql` exactly. No mocks.\n *\n * Conventions (mirror microfe-hookmovies / microfe-collabkin):\n * - `pswQuery` wraps graphqlFetch({ gateway: 'workspace', workspaceToken: true })\n * and throws on GraphQL errors / missing data.\n * - One react-query `useQuery` per read op and `useMutation` per write op.\n * - Strict hand-written result interfaces — no `any`.\n * - Lists paginate by offset (the workspaces gateway strips the cursor arg);\n * `*Connection { items, pageInfo { hasMore cursor }, totalCount }` verbatim.\n * - After any successful mutation we clear fe-libs' short-lived graphqlFetch\n * read cache so the very next refetch is fresh (fleet stale-read fix).\n */\n\nimport { useMutation, useQuery, useQueryClient, type QueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\n// ============================================================================\n// Low-level runner\n// ============================================================================\n\nasync function pswQuery<TData>(\n query: string,\n variables: Record<string, unknown> = {}\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n if (query.trimStart().startsWith('mutation')) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n}\n\nconst QK = 'proserviceworld' as const;\n\n/** Invalidate a set of top-level query keys after a write. */\nfunction invalidate(qc: QueryClient, ...keys: string[]): void {\n for (const key of keys) void qc.invalidateQueries({ queryKey: [QK, key] });\n}\n\n// ============================================================================\n// Shared types (mirror .hive-schema.graphql)\n// ============================================================================\n\nexport interface PswPagination {\n limit?: number;\n offset?: number;\n}\nexport interface PswPageInfo {\n hasMore: boolean;\n cursor: string | null;\n}\nexport interface PswConnection<T> {\n items: T[];\n pageInfo: PswPageInfo;\n totalCount: number;\n}\n\nexport type PswClientType = 'INDIVIDUAL' | 'ORGANIZATION';\nexport type PswClientStatus = 'PROSPECT' | 'ACTIVE' | 'INACTIVE' | 'ARCHIVED';\nexport type PswFeeArrangement = 'FIXED' | 'HOURLY' | 'RETAINER' | 'VALUE_BASED';\nexport type PswEngagementStatus =\n | 'PROPOSAL'\n | 'ACCEPTED'\n | 'IN_PROGRESS'\n | 'REVIEW'\n | 'DELIVERED'\n | 'INVOICED'\n | 'CLOSED'\n | 'CANCELLED';\nexport type PswMatterStatus = 'OPEN' | 'ON_HOLD' | 'CLOSED' | 'ARCHIVED';\nexport type PswMatterPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\nexport type PswDeliverableStatus =\n | 'DRAFT'\n | 'INTERNAL_REVIEW'\n | 'CLIENT_REVIEW'\n | 'ACCEPTED'\n | 'REJECTED';\nexport type PswMilestoneStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';\nexport type PswDocumentStatus = 'DRAFT' | 'FINAL' | 'ARCHIVED';\nexport type PswDocumentClassification = 'PUBLIC' | 'INTERNAL' | 'CONFIDENTIAL' | 'PRIVILEGED';\nexport type PswTimeEntryStatus = 'DRAFT' | 'SUBMITTED' | 'APPROVED' | 'REJECTED' | 'BILLED';\nexport type PswInvoiceStatus = 'DRAFT' | 'SENT' | 'PAID' | 'OVERDUE' | 'CANCELLED';\nexport type PswComplianceType =\n | 'ENGAGEMENT_ACCEPTANCE'\n | 'INDEPENDENCE'\n | 'LICENSING'\n | 'CONTINUING_EDUCATION'\n | 'DATA_RETENTION'\n | 'LEGAL_HOLD';\nexport type PswComplianceStatus = 'PENDING' | 'IN_REVIEW' | 'PASSED' | 'FAILED' | 'WAIVED';\nexport type PswConflictCheckStatus =\n | 'PENDING'\n | 'CLEAR'\n | 'POTENTIAL_CONFLICT'\n | 'CONFLICT'\n | 'WAIVED';\nexport type PswTeamRole = 'PARTNER' | 'MANAGER' | 'ASSOCIATE' | 'SPECIALIST' | 'SUPPORT';\n\nexport interface PswClient {\n id: string;\n name: string;\n type: PswClientType;\n status: PswClientStatus;\n industry: string | null;\n contactName: string | null;\n contactEmail: string | null;\n contactPhone: string | null;\n address: string | null;\n relationshipOwnerId: string | null;\n notes: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\nexport interface PswClientDetail extends PswClient {\n engagements: PswEngagement[];\n matters: PswMatter[];\n}\nexport interface PswEngagement {\n id: string;\n clientId: string;\n practiceAreaId: string | null;\n name: string;\n description: string | null;\n status: PswEngagementStatus;\n feeArrangement: PswFeeArrangement;\n feeAmount: number;\n currency: string;\n startDate: string | null;\n endDate: string | null;\n responsiblePartnerId: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n client?: { id: string; name: string } | null;\n}\nexport interface PswDeliverable {\n id: string;\n engagementId: string;\n name: string;\n description: string | null;\n status: PswDeliverableStatus;\n version: number;\n acceptanceCriteria: string | null;\n dueDate: string | null;\n acceptedAt: string | null;\n createdAt: string;\n}\nexport interface PswMilestone {\n id: string;\n engagementId: string;\n name: string;\n description: string | null;\n status: PswMilestoneStatus;\n dueDate: string | null;\n completedAt: string | null;\n triggerAmount: number;\n triggersInvoice: boolean;\n createdAt: string;\n}\nexport interface PswEngagementDetail extends PswEngagement {\n deliverables: PswDeliverable[];\n milestones: PswMilestone[];\n}\nexport interface PswMatter {\n id: string;\n clientId: string;\n engagementId: string | null;\n practiceAreaId: string | null;\n reference: string | null;\n title: string;\n description: string | null;\n status: PswMatterStatus;\n priority: PswMatterPriority;\n isPrivileged: boolean;\n dueDate: string | null;\n closedAt: string | null;\n leadPractitionerId: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n client?: { id: string; name: string } | null;\n}\nexport interface PswTimeEntry {\n id: string;\n engagementId: string | null;\n matterId: string | null;\n practitionerId: string;\n date: string;\n minutes: number;\n billable: boolean;\n rate: number;\n narrative: string | null;\n status: PswTimeEntryStatus;\n invoiceId: string | null;\n createdAt: string;\n}\nexport interface PswInvoice {\n id: string;\n clientId: string;\n engagementId: string | null;\n number: string;\n status: PswInvoiceStatus;\n currency: string;\n subtotal: number;\n taxAmount: number;\n total: number;\n issueDate: string | null;\n dueDate: string | null;\n paidAt: string | null;\n notes: string | null;\n createdAt: string;\n}\nexport interface PswComplianceCheck {\n id: string;\n type: PswComplianceType;\n status: PswComplianceStatus;\n subjectType: string;\n subjectId: string;\n title: string;\n details: string | null;\n reviewerId: string | null;\n dueDate: string | null;\n resolvedAt: string | null;\n createdAt: string;\n}\nexport interface PswConflictCheck {\n id: string;\n clientId: string;\n matterId: string | null;\n status: PswConflictCheckStatus;\n searchedParties: string[];\n findings: string | null;\n waiverReason: string | null;\n resolvedAt: string | null;\n createdAt: string;\n}\nexport interface PswPracticeArea {\n id: string;\n name: string;\n code: string | null;\n description: string | null;\n isActive: boolean;\n createdAt: string;\n}\nexport interface PswDocument {\n id: string;\n name: string;\n description: string | null;\n status: PswDocumentStatus;\n classification: PswDocumentClassification;\n version: number;\n legalHold: boolean;\n retainUntil: string | null;\n matterId: string | null;\n engagementId: string | null;\n tags: string[];\n file: { id: string } | null;\n createdAt: string;\n}\nexport interface PswStatusCount {\n status: string;\n count: number;\n}\nexport interface PswMonthlyAmount {\n month: string;\n amount: number;\n}\nexport interface PswDashboardStats {\n totalClients: number;\n activeClients: number;\n activeEngagements: number;\n openMatters: number;\n outstandingInvoiceTotal: number;\n paidInvoiceTotal: number;\n unbilledHours: number;\n billedHours: number;\n pendingComplianceChecks: number;\n openConflictChecks: number;\n engagementsByStatus: PswStatusCount[];\n invoicesByStatus: PswStatusCount[];\n mattersByStatus: PswStatusCount[];\n revenueByMonth: PswMonthlyAmount[];\n}\n\n// ============================================================================\n// GraphQL fragments\n// ============================================================================\n\nconst CLIENT_F = `\n id name type status industry contactName contactEmail contactPhone address\n relationshipOwnerId notes tags createdAt updatedAt`;\nconst ENGAGEMENT_F = `\n id clientId practiceAreaId name description status feeArrangement feeAmount currency\n startDate endDate responsiblePartnerId tags createdAt updatedAt client { id name }`;\nconst DELIVERABLE_F = `id engagementId name description status version acceptanceCriteria dueDate acceptedAt createdAt`;\nconst MILESTONE_F = `id engagementId name description status dueDate completedAt triggerAmount triggersInvoice createdAt`;\nconst MATTER_F = `\n id clientId engagementId practiceAreaId reference title description status priority isPrivileged\n dueDate closedAt leadPractitionerId tags createdAt updatedAt client { id name }`;\nconst TIME_F = `id engagementId matterId practitionerId date minutes billable rate narrative status invoiceId createdAt`;\nconst INVOICE_F = `id clientId engagementId number status currency subtotal taxAmount total issueDate dueDate paidAt notes createdAt`;\nconst COMPLIANCE_F = `id type status subjectType subjectId title details reviewerId dueDate resolvedAt createdAt`;\nconst CONFLICT_F = `id clientId matterId status searchedParties findings waiverReason resolvedAt createdAt`;\nconst PRACTICE_AREA_F = `id name code description isActive createdAt`;\nconst DOCUMENT_F = `id name description status classification version legalHold retainUntil matterId engagementId tags file { id } createdAt`;\n\n// ============================================================================\n// Dashboard\n// ============================================================================\n\nexport function useDashboardStats() {\n return useQuery({\n queryKey: [QK, 'dashboard'],\n queryFn: async () => {\n const data = await pswQuery<{ pswDashboardStats: PswDashboardStats }>(`\n query PswDashboard {\n pswDashboardStats {\n totalClients activeClients activeEngagements openMatters\n outstandingInvoiceTotal paidInvoiceTotal unbilledHours billedHours\n pendingComplianceChecks openConflictChecks\n engagementsByStatus { status count }\n invoicesByStatus { status count }\n mattersByStatus { status count }\n revenueByMonth { month amount }\n }\n }`);\n return data.pswDashboardStats;\n },\n });\n}\n\n// ============================================================================\n// Clients\n// ============================================================================\n\nexport function useClients(\n args: { status?: PswClientStatus; search?: string; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'clients', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListClients: PswConnection<PswClient> }>(\n `query PswClients($status: PswClientStatus, $search: String, $pagination: PaginationInput) {\n pswListClients(status: $status, search: $search, pagination: $pagination) {\n items { ${CLIENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListClients;\n },\n });\n}\n\nexport function useClient(id: string | null) {\n return useQuery({\n queryKey: [QK, 'client', id],\n enabled: Boolean(id),\n queryFn: async () => {\n const data = await pswQuery<{ pswClient: PswClientDetail | null }>(\n `query PswClient($id: ID!) {\n pswClient(id: $id) {\n ${CLIENT_F}\n engagements { ${ENGAGEMENT_F} }\n matters { ${MATTER_F} }\n }\n }`,\n { id }\n );\n return data.pswClient;\n },\n });\n}\n\nexport function useCreateClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery<{ pswCreateClient: PswClient }>(\n `mutation PswCreateClient($input: PswCreateClientInput!) { pswCreateClient(input: $input) { ${CLIENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'clients', 'dashboard'),\n });\n}\nexport function useUpdateClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateClient($id: ID!, $input: PswUpdateClientInput!) { pswUpdateClient(id: $id, input: $input) { ${CLIENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'clients', 'dashboard');\n void qc.invalidateQueries({ queryKey: [QK, 'client', v.id] });\n },\n });\n}\nexport function useArchiveClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswArchiveClient($id: ID!) { pswArchiveClient(id: $id) }`, { id }),\n onSuccess: () => invalidate(qc, 'clients', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Engagements\n// ============================================================================\n\nexport function useEngagements(\n args: {\n clientId?: string;\n status?: PswEngagementStatus;\n search?: string;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'engagements', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListEngagements: PswConnection<PswEngagement> }>(\n `query PswEngagements($clientId: ID, $status: PswEngagementStatus, $search: String, $pagination: PaginationInput) {\n pswListEngagements(clientId: $clientId, status: $status, search: $search, pagination: $pagination) {\n items { ${ENGAGEMENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListEngagements;\n },\n });\n}\n\nexport function useEngagement(id: string | null) {\n return useQuery({\n queryKey: [QK, 'engagement', id],\n enabled: Boolean(id),\n queryFn: async () => {\n const data = await pswQuery<{ pswEngagement: PswEngagementDetail | null }>(\n `query PswEngagement($id: ID!) {\n pswEngagement(id: $id) {\n ${ENGAGEMENT_F}\n deliverables { ${DELIVERABLE_F} }\n milestones { ${MILESTONE_F} }\n }\n }`,\n { id }\n );\n return data.pswEngagement;\n },\n });\n}\n\nexport function useCreateEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateEngagement($input: PswCreateEngagementInput!) { pswCreateEngagement(input: $input) { ${ENGAGEMENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'engagements', 'dashboard'),\n });\n}\nexport function useUpdateEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateEngagement($id: ID!, $input: PswUpdateEngagementInput!) { pswUpdateEngagement(id: $id, input: $input) { ${ENGAGEMENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'engagements');\n void qc.invalidateQueries({ queryKey: [QK, 'engagement', v.id] });\n },\n });\n}\nexport function useTransitionEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswEngagementStatus }) =>\n pswQuery(\n `mutation PswTransitionEngagement($id: ID!, $status: PswEngagementStatus!) { pswTransitionEngagement(id: $id, status: $status) { ${ENGAGEMENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'engagements', 'dashboard');\n void qc.invalidateQueries({ queryKey: [QK, 'engagement', v.id] });\n },\n });\n}\n\n// ============================================================================\n// Deliverables & Milestones (engagement delivery)\n// ============================================================================\n\nexport function useCreateDeliverable() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateDeliverable($input: PswCreateDeliverableInput!) { pswCreateDeliverable(input: $input) { ${DELIVERABLE_F} } }`,\n { input }\n ),\n onSuccess: (_d, input) =>\n qc.invalidateQueries({\n queryKey: [QK, 'engagement', (input as { engagementId: string }).engagementId],\n }),\n });\n}\nexport function useTransitionDeliverable() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswDeliverableStatus; engagementId: string }) =>\n pswQuery(\n `mutation PswTransitionDeliverable($id: ID!, $status: PswDeliverableStatus!) { pswTransitionDeliverable(id: $id, status: $status) { ${DELIVERABLE_F} } }`,\n {\n id: vars.id,\n status: vars.status,\n }\n ),\n onSuccess: (_d, v) => qc.invalidateQueries({ queryKey: [QK, 'engagement', v.engagementId] }),\n });\n}\nexport function useCreateMilestone() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateMilestone($input: PswCreateMilestoneInput!) { pswCreateMilestone(input: $input) { ${MILESTONE_F} } }`,\n { input }\n ),\n onSuccess: (_d, input) =>\n qc.invalidateQueries({\n queryKey: [QK, 'engagement', (input as { engagementId: string }).engagementId],\n }),\n });\n}\nexport function useCompleteMilestone() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; engagementId: string }) =>\n pswQuery(\n `mutation PswCompleteMilestone($id: ID!) { pswCompleteMilestone(id: $id) { ${MILESTONE_F} } }`,\n { id: vars.id }\n ),\n onSuccess: (_d, v) => qc.invalidateQueries({ queryKey: [QK, 'engagement', v.engagementId] }),\n });\n}\n\n// ============================================================================\n// Matters\n// ============================================================================\n\nexport function useMatters(\n args: {\n clientId?: string;\n engagementId?: string;\n status?: PswMatterStatus;\n search?: string;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'matters', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListMatters: PswConnection<PswMatter> }>(\n `query PswMatters($clientId: ID, $engagementId: ID, $status: PswMatterStatus, $search: String, $pagination: PaginationInput) {\n pswListMatters(clientId: $clientId, engagementId: $engagementId, status: $status, search: $search, pagination: $pagination) {\n items { ${MATTER_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n engagementId: args.engagementId ?? null,\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListMatters;\n },\n });\n}\nexport function useCreateMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateMatter($input: PswCreateMatterInput!) { pswCreateMatter(input: $input) { ${MATTER_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'matters', 'dashboard'),\n });\n}\nexport function useUpdateMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateMatter($id: ID!, $input: PswUpdateMatterInput!) { pswUpdateMatter(id: $id, input: $input) { ${MATTER_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'matters'),\n });\n}\nexport function useCloseMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswCloseMatter($id: ID!) { pswCloseMatter(id: $id) { ${MATTER_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'matters', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Time entries\n// ============================================================================\n\nexport function useTimeEntries(\n args: {\n engagementId?: string;\n matterId?: string;\n status?: PswTimeEntryStatus;\n billable?: boolean;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'time', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListTimeEntries: PswConnection<PswTimeEntry> }>(\n `query PswTimeEntries($engagementId: ID, $matterId: ID, $status: PswTimeEntryStatus, $billable: Boolean, $pagination: PaginationInput) {\n pswListTimeEntries(engagementId: $engagementId, matterId: $matterId, status: $status, billable: $billable, pagination: $pagination) {\n items { ${TIME_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n engagementId: args.engagementId ?? null,\n matterId: args.matterId ?? null,\n status: args.status ?? null,\n billable: args.billable ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListTimeEntries;\n },\n });\n}\nexport function useLogTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswLogTime($input: PswCreateTimeEntryInput!) { pswLogTimeEntry(input: $input) { ${TIME_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'time', 'dashboard'),\n });\n}\nexport function useSubmitTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswSubmitTime($id: ID!) { pswSubmitTimeEntry(id: $id) { ${TIME_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'time'),\n });\n}\nexport function useApproveTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswApproveTime($id: ID!) { pswApproveTimeEntry(id: $id) { ${TIME_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'time'),\n });\n}\n\n// ============================================================================\n// Invoices\n// ============================================================================\n\nexport function useInvoices(\n args: { clientId?: string; status?: PswInvoiceStatus; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'invoices', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListInvoices: PswConnection<PswInvoice> }>(\n `query PswInvoices($clientId: ID, $status: PswInvoiceStatus, $pagination: PaginationInput) {\n pswListInvoices(clientId: $clientId, status: $status, pagination: $pagination) {\n items { ${INVOICE_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n status: args.status ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListInvoices;\n },\n });\n}\nexport function useCreateInvoice() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateInvoice($input: PswCreateInvoiceInput!) { pswCreateInvoice(input: $input) { ${INVOICE_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'invoices', 'dashboard'),\n });\n}\nexport function useMarkInvoicePaid() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswMarkPaid($id: ID!) { pswMarkInvoicePaid(id: $id) { ${INVOICE_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'invoices', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Compliance\n// ============================================================================\n\nexport function useComplianceChecks(\n args: { status?: PswComplianceStatus; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'compliance', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListComplianceChecks: PswComplianceCheck[] }>(\n `query PswCompliance($status: PswComplianceStatus, $pagination: PaginationInput) {\n pswListComplianceChecks(status: $status, pagination: $pagination) { ${COMPLIANCE_F} }\n }`,\n { status: args.status ?? null, pagination: args.pagination ?? { limit: 100 } }\n );\n return data.pswListComplianceChecks;\n },\n });\n}\nexport function useCreateComplianceCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateCompliance($input: PswCreateComplianceCheckInput!) { pswCreateComplianceCheck(input: $input) { ${COMPLIANCE_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'compliance', 'dashboard'),\n });\n}\nexport function useResolveComplianceCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswComplianceStatus; details?: string }) =>\n pswQuery(\n `mutation PswResolveCompliance($id: ID!, $status: PswComplianceStatus!, $details: String) { pswResolveComplianceCheck(id: $id, status: $status, details: $details) { ${COMPLIANCE_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'compliance', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Conflict checks\n// ============================================================================\n\nexport function useConflictChecks(args: { clientId?: string; pagination?: PswPagination } = {}) {\n return useQuery({\n queryKey: [QK, 'conflicts', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListConflictChecks: PswConflictCheck[] }>(\n `query PswConflicts($clientId: ID, $pagination: PaginationInput) {\n pswListConflictChecks(clientId: $clientId, pagination: $pagination) { ${CONFLICT_F} }\n }`,\n { clientId: args.clientId ?? null, pagination: args.pagination ?? { limit: 100 } }\n );\n return data.pswListConflictChecks;\n },\n });\n}\nexport function useCreateConflictCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateConflict($input: PswCreateConflictCheckInput!) { pswCreateConflictCheck(input: $input) { ${CONFLICT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'conflicts', 'dashboard'),\n });\n}\nexport function useResolveConflictCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: {\n id: string;\n status: PswConflictCheckStatus;\n findings?: string;\n waiverReason?: string;\n }) =>\n pswQuery(\n `mutation PswResolveConflict($id: ID!, $status: PswConflictCheckStatus!, $findings: String, $waiverReason: String) {\n pswResolveConflictCheck(id: $id, status: $status, findings: $findings, waiverReason: $waiverReason) { ${CONFLICT_F} }\n }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'conflicts', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Practice areas\n// ============================================================================\n\nexport function usePracticeAreas() {\n return useQuery({\n queryKey: [QK, 'practiceAreas'],\n queryFn: async () => {\n const data = await pswQuery<{ pswListPracticeAreas: PswPracticeArea[] }>(\n `query PswPracticeAreas { pswListPracticeAreas(pagination: { limit: 100 }) { ${PRACTICE_AREA_F} } }`\n );\n return data.pswListPracticeAreas;\n },\n });\n}\nexport function useCreatePracticeArea() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreatePracticeArea($input: PswCreatePracticeAreaInput!) { pswCreatePracticeArea(input: $input) { ${PRACTICE_AREA_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'practiceAreas'),\n });\n}\nexport function useUpdatePracticeArea() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdatePracticeArea($id: ID!, $input: PswUpdatePracticeAreaInput!) { pswUpdatePracticeArea(id: $id, input: $input) { ${PRACTICE_AREA_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'practiceAreas'),\n });\n}\n\n// ============================================================================\n// Documents (metadata over wspace-files-svc)\n// ============================================================================\n\nexport function useDocuments(\n args: {\n engagementId?: string;\n matterId?: string;\n classification?: PswDocumentClassification;\n status?: PswDocumentStatus;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'documents', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListDocuments: PswConnection<PswDocument> }>(\n `query PswDocuments($engagementId: ID, $matterId: ID, $classification: PswDocumentClassification, $status: PswDocumentStatus, $pagination: PaginationInput) {\n pswListDocuments(engagementId: $engagementId, matterId: $matterId, classification: $classification, status: $status, pagination: $pagination) {\n items { ${DOCUMENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n engagementId: args.engagementId ?? null,\n matterId: args.matterId ?? null,\n classification: args.classification ?? null,\n status: args.status ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListDocuments;\n },\n });\n}\nexport function useCreateDocument() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateDocument($input: PswCreateDocumentInput!) { pswCreateDocument(input: $input) { ${DOCUMENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'documents'),\n });\n}\nexport function useArchiveDocument() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(\n `mutation PswArchiveDocument($id: ID!) { pswArchiveDocument(id: $id) { ${DOCUMENT_F} } }`,\n { id }\n ),\n onSuccess: () => invalidate(qc, 'documents'),\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useProServiceWorldApi.js","names":[],"sources":["../../../src/proserviceworld/hooks/useProServiceWorldApi.ts"],"sourcesContent":["/**\n * ProServiceWorld API hooks\n * @module proserviceworld/hooks\n *\n * Real data layer for the ProServiceWorld workspace subgraph (`proserviceworld`\n * Hive service / `wspace-proserviceworld-svc`). Every hook talks to the live\n * workspace GraphQL gateway via {@link graphqlFetch} with inline operations\n * whose fields mirror `.hive-schema.graphql` exactly. No mocks.\n *\n * Conventions (mirror microfe-hookmovies / microfe-collabkin):\n * - `pswQuery` wraps graphqlFetch({ gateway: 'workspace', workspaceToken: true })\n * and throws on GraphQL errors / missing data.\n * - One react-query `useQuery` per read op and `useMutation` per write op.\n * - Strict hand-written result interfaces — no `any`.\n * - Lists paginate by offset (the workspaces gateway strips the cursor arg);\n * `*Connection { items, pageInfo { hasMore cursor }, totalCount }` verbatim.\n * - After any successful mutation we clear fe-libs' short-lived graphqlFetch\n * read cache so the very next refetch is fresh (fleet stale-read fix).\n */\n\nimport { useMutation, useQuery, useQueryClient, type QueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\n// ============================================================================\n// Low-level runner\n// ============================================================================\n\nasync function pswQuery<TData>(\n query: string,\n variables: Record<string, unknown> = {}\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n if (query.trimStart().startsWith('mutation')) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n}\n\nconst QK = 'proserviceworld' as const;\n\n/** Invalidate a set of top-level query keys after a write. */\nfunction invalidate(qc: QueryClient, ...keys: string[]): void {\n for (const key of keys) void qc.invalidateQueries({ queryKey: [QK, key] });\n}\n\n// ============================================================================\n// Shared types (mirror .hive-schema.graphql)\n// ============================================================================\n\nexport interface PswPagination {\n limit?: number;\n offset?: number;\n}\nexport interface PswPageInfo {\n hasMore: boolean;\n cursor: string | null;\n}\nexport interface PswConnection<T> {\n items: T[];\n pageInfo: PswPageInfo;\n totalCount: number;\n}\n\nexport type PswClientType = 'INDIVIDUAL' | 'ORGANIZATION';\nexport type PswClientStatus = 'PROSPECT' | 'ACTIVE' | 'INACTIVE' | 'ARCHIVED';\nexport type PswFeeArrangement = 'FIXED' | 'HOURLY' | 'RETAINER' | 'VALUE_BASED';\nexport type PswEngagementStatus =\n | 'PROPOSAL'\n | 'ACCEPTED'\n | 'IN_PROGRESS'\n | 'REVIEW'\n | 'DELIVERED'\n | 'INVOICED'\n | 'CLOSED'\n | 'CANCELLED';\nexport type PswMatterStatus = 'OPEN' | 'ON_HOLD' | 'CLOSED' | 'ARCHIVED';\nexport type PswMatterPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\nexport type PswDeliverableStatus =\n | 'DRAFT'\n | 'INTERNAL_REVIEW'\n | 'CLIENT_REVIEW'\n | 'ACCEPTED'\n | 'REJECTED';\nexport type PswMilestoneStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';\nexport type PswDocumentStatus = 'DRAFT' | 'FINAL' | 'ARCHIVED';\nexport type PswDocumentClassification = 'PUBLIC' | 'INTERNAL' | 'CONFIDENTIAL' | 'PRIVILEGED';\nexport type PswTimeEntryStatus = 'DRAFT' | 'SUBMITTED' | 'APPROVED' | 'REJECTED' | 'BILLED';\nexport type PswInvoiceStatus = 'DRAFT' | 'SENT' | 'PAID' | 'OVERDUE' | 'CANCELLED';\nexport type PswComplianceType =\n | 'ENGAGEMENT_ACCEPTANCE'\n | 'INDEPENDENCE'\n | 'LICENSING'\n | 'CONTINUING_EDUCATION'\n | 'DATA_RETENTION'\n | 'LEGAL_HOLD';\nexport type PswComplianceStatus = 'PENDING' | 'IN_REVIEW' | 'PASSED' | 'FAILED' | 'WAIVED';\nexport type PswConflictCheckStatus =\n | 'PENDING'\n | 'CLEAR'\n | 'POTENTIAL_CONFLICT'\n | 'CONFLICT'\n | 'WAIVED';\nexport type PswTeamRole = 'PARTNER' | 'MANAGER' | 'ASSOCIATE' | 'SPECIALIST' | 'SUPPORT';\n\nexport interface PswClient {\n id: string;\n name: string;\n type: PswClientType;\n status: PswClientStatus;\n industry: string | null;\n contactName: string | null;\n contactEmail: string | null;\n contactPhone: string | null;\n address: string | null;\n relationshipOwnerId: string | null;\n notes: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\nexport interface PswClientDetail extends PswClient {\n engagements: PswEngagement[];\n matters: PswMatter[];\n}\nexport interface PswEngagement {\n id: string;\n clientId: string;\n practiceAreaId: string | null;\n name: string;\n description: string | null;\n status: PswEngagementStatus;\n feeArrangement: PswFeeArrangement;\n feeAmount: number;\n currency: string;\n startDate: string | null;\n endDate: string | null;\n responsiblePartnerId: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n client?: { id: string; name: string } | null;\n}\nexport interface PswDeliverable {\n id: string;\n engagementId: string;\n name: string;\n description: string | null;\n status: PswDeliverableStatus;\n version: number;\n acceptanceCriteria: string | null;\n dueDate: string | null;\n acceptedAt: string | null;\n createdAt: string;\n}\nexport interface PswMilestone {\n id: string;\n engagementId: string;\n name: string;\n description: string | null;\n status: PswMilestoneStatus;\n dueDate: string | null;\n completedAt: string | null;\n triggerAmount: number;\n triggersInvoice: boolean;\n createdAt: string;\n}\nexport interface PswEngagementDetail extends PswEngagement {\n deliverables: PswDeliverable[];\n milestones: PswMilestone[];\n}\nexport interface PswMatter {\n id: string;\n clientId: string;\n engagementId: string | null;\n practiceAreaId: string | null;\n reference: string | null;\n title: string;\n description: string | null;\n status: PswMatterStatus;\n priority: PswMatterPriority;\n isPrivileged: boolean;\n dueDate: string | null;\n closedAt: string | null;\n leadPractitionerId: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n client?: { id: string; name: string } | null;\n}\nexport interface PswTimeEntry {\n id: string;\n engagementId: string | null;\n matterId: string | null;\n practitionerId: string;\n date: string;\n minutes: number;\n billable: boolean;\n rate: number;\n narrative: string | null;\n status: PswTimeEntryStatus;\n invoiceId: string | null;\n createdAt: string;\n}\nexport interface PswInvoice {\n id: string;\n clientId: string;\n engagementId: string | null;\n number: string;\n status: PswInvoiceStatus;\n currency: string;\n subtotal: number;\n taxAmount: number;\n total: number;\n issueDate: string | null;\n dueDate: string | null;\n paidAt: string | null;\n notes: string | null;\n createdAt: string;\n}\nexport interface PswComplianceCheck {\n id: string;\n type: PswComplianceType;\n status: PswComplianceStatus;\n subjectType: string;\n subjectId: string;\n title: string;\n details: string | null;\n reviewerId: string | null;\n dueDate: string | null;\n resolvedAt: string | null;\n createdAt: string;\n}\nexport interface PswConflictCheck {\n id: string;\n clientId: string;\n matterId: string | null;\n status: PswConflictCheckStatus;\n searchedParties: string[];\n findings: string | null;\n waiverReason: string | null;\n resolvedAt: string | null;\n createdAt: string;\n}\nexport interface PswPracticeArea {\n id: string;\n name: string;\n code: string | null;\n description: string | null;\n isActive: boolean;\n createdAt: string;\n}\nexport interface PswDocument {\n id: string;\n name: string;\n description: string | null;\n status: PswDocumentStatus;\n classification: PswDocumentClassification;\n version: number;\n legalHold: boolean;\n retainUntil: string | null;\n matterId: string | null;\n engagementId: string | null;\n tags: string[];\n file: { id: string } | null;\n createdAt: string;\n}\nexport interface PswStatusCount {\n status: string;\n count: number;\n}\nexport interface PswMonthlyAmount {\n month: string;\n amount: number;\n}\nexport interface PswDashboardStats {\n totalClients: number;\n activeClients: number;\n activeEngagements: number;\n openMatters: number;\n outstandingInvoiceTotal: number;\n paidInvoiceTotal: number;\n unbilledHours: number;\n billedHours: number;\n pendingComplianceChecks: number;\n openConflictChecks: number;\n engagementsByStatus: PswStatusCount[];\n invoicesByStatus: PswStatusCount[];\n mattersByStatus: PswStatusCount[];\n revenueByMonth: PswMonthlyAmount[];\n}\n\n// ============================================================================\n// GraphQL fragments\n// ============================================================================\n\nconst CLIENT_F = `\n id name type status industry contactName contactEmail contactPhone address\n relationshipOwnerId notes tags createdAt updatedAt`;\nconst ENGAGEMENT_F = `\n id clientId practiceAreaId name description status feeArrangement feeAmount currency\n startDate endDate responsiblePartnerId tags createdAt updatedAt client { id name }`;\nconst DELIVERABLE_F = `id engagementId name description status version acceptanceCriteria dueDate acceptedAt createdAt`;\nconst MILESTONE_F = `id engagementId name description status dueDate completedAt triggerAmount triggersInvoice createdAt`;\nconst MATTER_F = `\n id clientId engagementId practiceAreaId reference title description status priority isPrivileged\n dueDate closedAt leadPractitionerId tags createdAt updatedAt client { id name }`;\nconst TIME_F = `id engagementId matterId practitionerId date minutes billable rate narrative status invoiceId createdAt`;\nconst INVOICE_F = `id clientId engagementId number status currency subtotal taxAmount total issueDate dueDate paidAt notes createdAt`;\nconst COMPLIANCE_F = `id type status subjectType subjectId title details reviewerId dueDate resolvedAt createdAt`;\nconst CONFLICT_F = `id clientId matterId status searchedParties findings waiverReason resolvedAt createdAt`;\nconst PRACTICE_AREA_F = `id name code description isActive createdAt`;\nconst DOCUMENT_F = `id name description status classification version legalHold retainUntil matterId engagementId tags file { id } createdAt`;\n\n// ============================================================================\n// Dashboard\n// ============================================================================\n\nexport function useDashboardStats() {\n return useQuery({\n queryKey: [QK, 'dashboard'],\n queryFn: async () => {\n const data = await pswQuery<{ pswDashboardStats: PswDashboardStats }>(`\n query PswDashboard {\n pswDashboardStats {\n totalClients activeClients activeEngagements openMatters\n outstandingInvoiceTotal paidInvoiceTotal unbilledHours billedHours\n pendingComplianceChecks openConflictChecks\n engagementsByStatus { status count }\n invoicesByStatus { status count }\n mattersByStatus { status count }\n revenueByMonth { month amount }\n }\n }`);\n return data.pswDashboardStats;\n },\n });\n}\n\n// ============================================================================\n// Clients\n// ============================================================================\n\nexport function useClients(\n args: { status?: PswClientStatus; search?: string; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'clients', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListClients: PswConnection<PswClient> }>(\n `query PswClients($status: PswClientStatus, $search: String, $pagination: PaginationInput) {\n pswListClients(status: $status, search: $search, pagination: $pagination) {\n items { ${CLIENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListClients;\n },\n });\n}\n\nexport function useClient(id: string | null) {\n return useQuery({\n queryKey: [QK, 'client', id],\n enabled: Boolean(id),\n queryFn: async () => {\n const data = await pswQuery<{ pswClient: PswClientDetail | null }>(\n `query PswClient($id: ID!) {\n pswClient(id: $id) {\n ${CLIENT_F}\n engagements { ${ENGAGEMENT_F} }\n matters { ${MATTER_F} }\n }\n }`,\n { id }\n );\n return data.pswClient;\n },\n });\n}\n\nexport function useCreateClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery<{ pswCreateClient: PswClient }>(\n `mutation PswCreateClient($input: PswCreateClientInput!) { pswCreateClient(input: $input) { ${CLIENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'clients', 'dashboard'),\n });\n}\nexport function useUpdateClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateClient($id: ID!, $input: PswUpdateClientInput!) { pswUpdateClient(id: $id, input: $input) { ${CLIENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'clients', 'dashboard');\n void qc.invalidateQueries({ queryKey: [QK, 'client', v.id] });\n },\n });\n}\nexport function useArchiveClient() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswArchiveClient($id: ID!) { pswArchiveClient(id: $id) }`, { id }),\n onSuccess: () => invalidate(qc, 'clients', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Engagements\n// ============================================================================\n\nexport function useEngagements(\n args: {\n clientId?: string;\n status?: PswEngagementStatus;\n search?: string;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'engagements', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListEngagements: PswConnection<PswEngagement> }>(\n `query PswEngagements($clientId: ID, $status: PswEngagementStatus, $search: String, $pagination: PaginationInput) {\n pswListEngagements(clientId: $clientId, status: $status, search: $search, pagination: $pagination) {\n items { ${ENGAGEMENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListEngagements;\n },\n });\n}\n\nexport function useEngagement(id: string | null) {\n return useQuery({\n queryKey: [QK, 'engagement', id],\n enabled: Boolean(id),\n queryFn: async () => {\n const data = await pswQuery<{ pswEngagement: PswEngagementDetail | null }>(\n `query PswEngagement($id: ID!) {\n pswEngagement(id: $id) {\n ${ENGAGEMENT_F}\n deliverables { ${DELIVERABLE_F} }\n milestones { ${MILESTONE_F} }\n }\n }`,\n { id }\n );\n return data.pswEngagement;\n },\n });\n}\n\nexport function useCreateEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateEngagement($input: PswCreateEngagementInput!) { pswCreateEngagement(input: $input) { ${ENGAGEMENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'engagements', 'dashboard'),\n });\n}\nexport function useUpdateEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateEngagement($id: ID!, $input: PswUpdateEngagementInput!) { pswUpdateEngagement(id: $id, input: $input) { ${ENGAGEMENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'engagements');\n void qc.invalidateQueries({ queryKey: [QK, 'engagement', v.id] });\n },\n });\n}\nexport function useTransitionEngagement() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswEngagementStatus }) =>\n pswQuery(\n `mutation PswTransitionEngagement($id: ID!, $status: PswEngagementStatus!) { pswTransitionEngagement(id: $id, status: $status) { ${ENGAGEMENT_F} } }`,\n vars\n ),\n onSuccess: (_d, v) => {\n invalidate(qc, 'engagements', 'dashboard');\n void qc.invalidateQueries({ queryKey: [QK, 'engagement', v.id] });\n },\n });\n}\n\n// ============================================================================\n// Deliverables & Milestones (engagement delivery)\n// ============================================================================\n\nexport function useCreateDeliverable() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateDeliverable($input: PswCreateDeliverableInput!) { pswCreateDeliverable(input: $input) { ${DELIVERABLE_F} } }`,\n { input }\n ),\n onSuccess: (_d, input) =>\n qc.invalidateQueries({\n queryKey: [QK, 'engagement', (input as { engagementId: string }).engagementId],\n }),\n });\n}\nexport function useTransitionDeliverable() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswDeliverableStatus; engagementId: string }) =>\n pswQuery(\n `mutation PswTransitionDeliverable($id: ID!, $status: PswDeliverableStatus!) { pswTransitionDeliverable(id: $id, status: $status) { ${DELIVERABLE_F} } }`,\n {\n id: vars.id,\n status: vars.status,\n }\n ),\n onSuccess: (_d, v) => qc.invalidateQueries({ queryKey: [QK, 'engagement', v.engagementId] }),\n });\n}\nexport function useCreateMilestone() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateMilestone($input: PswCreateMilestoneInput!) { pswCreateMilestone(input: $input) { ${MILESTONE_F} } }`,\n { input }\n ),\n onSuccess: (_d, input) =>\n qc.invalidateQueries({\n queryKey: [QK, 'engagement', (input as { engagementId: string }).engagementId],\n }),\n });\n}\nexport function useCompleteMilestone() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; engagementId: string }) =>\n pswQuery(\n `mutation PswCompleteMilestone($id: ID!) { pswCompleteMilestone(id: $id) { ${MILESTONE_F} } }`,\n { id: vars.id }\n ),\n onSuccess: (_d, v) => qc.invalidateQueries({ queryKey: [QK, 'engagement', v.engagementId] }),\n });\n}\n\n// ============================================================================\n// Matters\n// ============================================================================\n\nexport function useMatters(\n args: {\n clientId?: string;\n engagementId?: string;\n status?: PswMatterStatus;\n search?: string;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'matters', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListMatters: PswConnection<PswMatter> }>(\n `query PswMatters($clientId: ID, $engagementId: ID, $status: PswMatterStatus, $search: String, $pagination: PaginationInput) {\n pswListMatters(clientId: $clientId, engagementId: $engagementId, status: $status, search: $search, pagination: $pagination) {\n items { ${MATTER_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n engagementId: args.engagementId ?? null,\n status: args.status ?? null,\n search: args.search ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListMatters;\n },\n });\n}\nexport function useCreateMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateMatter($input: PswCreateMatterInput!) { pswCreateMatter(input: $input) { ${MATTER_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'matters', 'dashboard'),\n });\n}\nexport function useUpdateMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdateMatter($id: ID!, $input: PswUpdateMatterInput!) { pswUpdateMatter(id: $id, input: $input) { ${MATTER_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'matters'),\n });\n}\nexport function useCloseMatter() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswCloseMatter($id: ID!) { pswCloseMatter(id: $id) { ${MATTER_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'matters', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Time entries\n// ============================================================================\n\nexport function useTimeEntries(\n args: {\n engagementId?: string;\n matterId?: string;\n status?: PswTimeEntryStatus;\n billable?: boolean;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'time', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListTimeEntries: PswConnection<PswTimeEntry> }>(\n `query PswTimeEntries($engagementId: ID, $matterId: ID, $status: PswTimeEntryStatus, $billable: Boolean, $pagination: PaginationInput) {\n pswListTimeEntries(engagementId: $engagementId, matterId: $matterId, status: $status, billable: $billable, pagination: $pagination) {\n items { ${TIME_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n engagementId: args.engagementId ?? null,\n matterId: args.matterId ?? null,\n status: args.status ?? null,\n billable: args.billable ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListTimeEntries;\n },\n });\n}\nexport function useLogTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswLogTime($input: PswCreateTimeEntryInput!) { pswLogTimeEntry(input: $input) { ${TIME_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'time', 'dashboard'),\n });\n}\nexport function useSubmitTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswSubmitTime($id: ID!) { pswSubmitTimeEntry(id: $id) { ${TIME_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'time'),\n });\n}\nexport function useApproveTimeEntry() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswApproveTime($id: ID!) { pswApproveTimeEntry(id: $id) { ${TIME_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'time'),\n });\n}\n\n// ============================================================================\n// Invoices\n// ============================================================================\n\nexport function useInvoices(\n args: { clientId?: string; status?: PswInvoiceStatus; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'invoices', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListInvoices: PswConnection<PswInvoice> }>(\n `query PswInvoices($clientId: ID, $status: PswInvoiceStatus, $pagination: PaginationInput) {\n pswListInvoices(clientId: $clientId, status: $status, pagination: $pagination) {\n items { ${INVOICE_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n clientId: args.clientId ?? null,\n status: args.status ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListInvoices;\n },\n });\n}\nexport function useCreateInvoice() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateInvoice($input: PswCreateInvoiceInput!) { pswCreateInvoice(input: $input) { ${INVOICE_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'invoices', 'dashboard'),\n });\n}\nexport function useMarkInvoicePaid() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(`mutation PswMarkPaid($id: ID!) { pswMarkInvoicePaid(id: $id) { ${INVOICE_F} } }`, {\n id,\n }),\n onSuccess: () => invalidate(qc, 'invoices', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Compliance\n// ============================================================================\n\nexport function useComplianceChecks(\n args: { status?: PswComplianceStatus; pagination?: PswPagination } = {}\n) {\n return useQuery({\n queryKey: [QK, 'compliance', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListComplianceChecks: PswComplianceCheck[] }>(\n `query PswCompliance($status: PswComplianceStatus, $pagination: PaginationInput) {\n pswListComplianceChecks(status: $status, pagination: $pagination) { ${COMPLIANCE_F} }\n }`,\n { status: args.status ?? null, pagination: args.pagination ?? { limit: 100 } }\n );\n return data.pswListComplianceChecks;\n },\n });\n}\nexport function useCreateComplianceCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateCompliance($input: PswCreateComplianceCheckInput!) { pswCreateComplianceCheck(input: $input) { ${COMPLIANCE_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'compliance', 'dashboard'),\n });\n}\nexport function useResolveComplianceCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; status: PswComplianceStatus; details?: string }) =>\n pswQuery(\n `mutation PswResolveCompliance($id: ID!, $status: PswComplianceStatus!, $details: String) { pswResolveComplianceCheck(id: $id, status: $status, details: $details) { ${COMPLIANCE_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'compliance', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Conflict checks\n// ============================================================================\n\nexport function useConflictChecks(args: { clientId?: string; pagination?: PswPagination } = {}) {\n return useQuery({\n queryKey: [QK, 'conflicts', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListConflictChecks: PswConflictCheck[] }>(\n `query PswConflicts($clientId: ID, $pagination: PaginationInput) {\n pswListConflictChecks(clientId: $clientId, pagination: $pagination) { ${CONFLICT_F} }\n }`,\n { clientId: args.clientId ?? null, pagination: args.pagination ?? { limit: 100 } }\n );\n return data.pswListConflictChecks;\n },\n });\n}\nexport function useCreateConflictCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateConflict($input: PswCreateConflictCheckInput!) { pswCreateConflictCheck(input: $input) { ${CONFLICT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'conflicts', 'dashboard'),\n });\n}\nexport function useResolveConflictCheck() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: {\n id: string;\n status: PswConflictCheckStatus;\n findings?: string;\n waiverReason?: string;\n }) =>\n pswQuery(\n `mutation PswResolveConflict($id: ID!, $status: PswConflictCheckStatus!, $findings: String, $waiverReason: String) {\n pswResolveConflictCheck(id: $id, status: $status, findings: $findings, waiverReason: $waiverReason) { ${CONFLICT_F} }\n }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'conflicts', 'dashboard'),\n });\n}\n\n// ============================================================================\n// Practice areas\n// ============================================================================\n\nexport function usePracticeAreas() {\n return useQuery({\n queryKey: [QK, 'practiceAreas'],\n queryFn: async () => {\n const data = await pswQuery<{ pswListPracticeAreas: PswPracticeArea[] }>(\n `query PswPracticeAreas { pswListPracticeAreas(pagination: { limit: 100 }) { ${PRACTICE_AREA_F} } }`\n );\n return data.pswListPracticeAreas;\n },\n });\n}\nexport function useCreatePracticeArea() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreatePracticeArea($input: PswCreatePracticeAreaInput!) { pswCreatePracticeArea(input: $input) { ${PRACTICE_AREA_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'practiceAreas'),\n });\n}\nexport function useUpdatePracticeArea() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (vars: { id: string; input: Record<string, unknown> }) =>\n pswQuery(\n `mutation PswUpdatePracticeArea($id: ID!, $input: PswUpdatePracticeAreaInput!) { pswUpdatePracticeArea(id: $id, input: $input) { ${PRACTICE_AREA_F} } }`,\n vars\n ),\n onSuccess: () => invalidate(qc, 'practiceAreas'),\n });\n}\n\n// ============================================================================\n// Documents (metadata over wspace-files-svc)\n// ============================================================================\n\nexport function useDocuments(\n args: {\n engagementId?: string;\n matterId?: string;\n classification?: PswDocumentClassification;\n status?: PswDocumentStatus;\n pagination?: PswPagination;\n } = {}\n) {\n return useQuery({\n queryKey: [QK, 'documents', args],\n queryFn: async () => {\n const data = await pswQuery<{ pswListDocuments: PswConnection<PswDocument> }>(\n `query PswDocuments($engagementId: ID, $matterId: ID, $classification: PswDocumentClassification, $status: PswDocumentStatus, $pagination: PaginationInput) {\n pswListDocuments(engagementId: $engagementId, matterId: $matterId, classification: $classification, status: $status, pagination: $pagination) {\n items { ${DOCUMENT_F} } pageInfo { hasMore cursor } totalCount\n }\n }`,\n {\n engagementId: args.engagementId ?? null,\n matterId: args.matterId ?? null,\n classification: args.classification ?? null,\n status: args.status ?? null,\n pagination: args.pagination ?? { limit: 100 },\n }\n );\n return data.pswListDocuments;\n },\n });\n}\nexport function useCreateDocument() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: Record<string, unknown>) =>\n pswQuery(\n `mutation PswCreateDocument($input: PswCreateDocumentInput!) { pswCreateDocument(input: $input) { ${DOCUMENT_F} } }`,\n { input }\n ),\n onSuccess: () => invalidate(qc, 'documents'),\n });\n}\nexport function useArchiveDocument() {\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n pswQuery(\n `mutation PswArchiveDocument($id: ID!) { pswArchiveDocument(id: $id) { ${DOCUMENT_F} } }`,\n { id }\n ),\n onSuccess: () => invalidate(qc, 'documents'),\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AA6MtE,wBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,CAGlF"}
|