@multiplatform.one/backoffice 6.4.0 → 6.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/chrome/CommandPalette.cjs +20 -9
- package/dist/cjs/chrome/CommandPalette.native.js +18 -10
- package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
- package/dist/cjs/i18n/en.cjs +21 -0
- package/dist/cjs/i18n/en.native.js +21 -0
- package/dist/cjs/i18n/en.native.js.map +1 -1
- package/dist/cjs/index.cjs +49 -1
- package/dist/cjs/index.native.js +48 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/types.cjs +18 -0
- package/dist/cjs/shared/types.native.js +21 -0
- package/dist/cjs/shared/types.native.js.map +1 -0
- package/dist/cjs/shared/useDocinfo.cjs +116 -0
- package/dist/cjs/shared/useDocinfo.native.js +167 -0
- package/dist/cjs/shared/useDocinfo.native.js.map +1 -0
- package/dist/cjs/shared/useDoctypeMeta.cjs +124 -0
- package/dist/cjs/shared/useDoctypeMeta.native.js +158 -0
- package/dist/cjs/shared/useDoctypeMeta.native.js.map +1 -0
- package/dist/cjs/shared/useRecentVisit.cjs +40 -0
- package/dist/cjs/shared/useRecentVisit.native.js +43 -0
- package/dist/cjs/shared/useRecentVisit.native.js.map +1 -0
- package/dist/cjs/shared/useSessionUserId.cjs +54 -0
- package/dist/cjs/shared/useSessionUserId.native.js +59 -0
- package/dist/cjs/shared/useSessionUserId.native.js.map +1 -0
- package/dist/cjs/shared/useSlugTarget.cjs +138 -0
- package/dist/cjs/shared/useSlugTarget.native.js +166 -0
- package/dist/cjs/shared/useSlugTarget.native.js.map +1 -0
- package/dist/cjs/shared/useUserRoles.cjs +72 -0
- package/dist/cjs/shared/useUserRoles.native.js +79 -0
- package/dist/cjs/shared/useUserRoles.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceChartBlock.cjs +136 -0
- package/dist/cjs/workspace/WorkspaceChartBlock.native.js +144 -0
- package/dist/cjs/workspace/WorkspaceChartBlock.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceGrid.cjs +91 -0
- package/dist/cjs/workspace/WorkspaceGrid.native.js +126 -0
- package/dist/cjs/workspace/WorkspaceGrid.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceLinkCard.cjs +139 -0
- package/dist/cjs/workspace/WorkspaceLinkCard.native.js +155 -0
- package/dist/cjs/workspace/WorkspaceLinkCard.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceLinkDirectory.cjs +110 -0
- package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js +127 -0
- package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceNumberCard.cjs +133 -0
- package/dist/cjs/workspace/WorkspaceNumberCard.native.js +138 -0
- package/dist/cjs/workspace/WorkspaceNumberCard.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceQuickList.cjs +112 -0
- package/dist/cjs/workspace/WorkspaceQuickList.native.js +130 -0
- package/dist/cjs/workspace/WorkspaceQuickList.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceRecents.cjs +116 -0
- package/dist/cjs/workspace/WorkspaceRecents.native.js +132 -0
- package/dist/cjs/workspace/WorkspaceRecents.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceScreen.cjs +291 -0
- package/dist/cjs/workspace/WorkspaceScreen.native.js +323 -0
- package/dist/cjs/workspace/WorkspaceScreen.native.js.map +1 -0
- package/dist/cjs/workspace/WorkspaceShortcut.cjs +185 -0
- package/dist/cjs/workspace/WorkspaceShortcut.native.js +202 -0
- package/dist/cjs/workspace/WorkspaceShortcut.native.js.map +1 -0
- package/dist/cjs/workspace/parseContent.cjs +180 -0
- package/dist/cjs/workspace/parseContent.native.js +299 -0
- package/dist/cjs/workspace/parseContent.native.js.map +1 -0
- package/dist/cjs/workspace/types.cjs +18 -0
- package/dist/cjs/workspace/types.native.js +21 -0
- package/dist/cjs/workspace/types.native.js.map +1 -0
- package/dist/cjs/workspace/useDashboardChart.cjs +96 -0
- package/dist/cjs/workspace/useDashboardChart.native.js +110 -0
- package/dist/cjs/workspace/useDashboardChart.native.js.map +1 -0
- package/dist/cjs/workspace/useWorkspaceDoc.cjs +103 -0
- package/dist/cjs/workspace/useWorkspaceDoc.native.js +120 -0
- package/dist/cjs/workspace/useWorkspaceDoc.native.js.map +1 -0
- package/dist/esm/chrome/CommandPalette.mjs +20 -9
- package/dist/esm/chrome/CommandPalette.mjs.map +1 -1
- package/dist/esm/chrome/CommandPalette.native.js +18 -10
- package/dist/esm/chrome/CommandPalette.native.js.map +1 -1
- package/dist/esm/i18n/en.mjs +21 -0
- package/dist/esm/i18n/en.mjs.map +1 -1
- package/dist/esm/i18n/en.native.js +21 -0
- package/dist/esm/i18n/en.native.js.map +1 -1
- package/dist/esm/index.mjs +19 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +19 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/types.mjs +2 -0
- package/dist/esm/shared/types.mjs.map +1 -0
- package/dist/esm/shared/types.native.js +2 -0
- package/dist/esm/shared/types.native.js.map +1 -0
- package/dist/esm/shared/useDocinfo.mjs +90 -0
- package/dist/esm/shared/useDocinfo.mjs.map +1 -0
- package/dist/esm/shared/useDocinfo.native.js +138 -0
- package/dist/esm/shared/useDocinfo.native.js.map +1 -0
- package/dist/esm/shared/useDoctypeMeta.mjs +99 -0
- package/dist/esm/shared/useDoctypeMeta.mjs.map +1 -0
- package/dist/esm/shared/useDoctypeMeta.native.js +130 -0
- package/dist/esm/shared/useDoctypeMeta.native.js.map +1 -0
- package/dist/esm/shared/useRecentVisit.mjs +15 -0
- package/dist/esm/shared/useRecentVisit.mjs.map +1 -0
- package/dist/esm/shared/useRecentVisit.native.js +15 -0
- package/dist/esm/shared/useRecentVisit.native.js.map +1 -0
- package/dist/esm/shared/useSessionUserId.mjs +29 -0
- package/dist/esm/shared/useSessionUserId.mjs.map +1 -0
- package/dist/esm/shared/useSessionUserId.native.js +31 -0
- package/dist/esm/shared/useSessionUserId.native.js.map +1 -0
- package/dist/esm/shared/useSlugTarget.mjs +113 -0
- package/dist/esm/shared/useSlugTarget.mjs.map +1 -0
- package/dist/esm/shared/useSlugTarget.native.js +138 -0
- package/dist/esm/shared/useSlugTarget.native.js.map +1 -0
- package/dist/esm/shared/useUserRoles.mjs +47 -0
- package/dist/esm/shared/useUserRoles.mjs.map +1 -0
- package/dist/esm/shared/useUserRoles.native.js +51 -0
- package/dist/esm/shared/useUserRoles.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceChartBlock.mjs +111 -0
- package/dist/esm/workspace/WorkspaceChartBlock.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceChartBlock.native.js +116 -0
- package/dist/esm/workspace/WorkspaceChartBlock.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceGrid.mjs +66 -0
- package/dist/esm/workspace/WorkspaceGrid.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceGrid.native.js +98 -0
- package/dist/esm/workspace/WorkspaceGrid.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceLinkCard.mjs +113 -0
- package/dist/esm/workspace/WorkspaceLinkCard.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceLinkCard.native.js +126 -0
- package/dist/esm/workspace/WorkspaceLinkCard.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceLinkDirectory.mjs +84 -0
- package/dist/esm/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceLinkDirectory.native.js +98 -0
- package/dist/esm/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceNumberCard.mjs +108 -0
- package/dist/esm/workspace/WorkspaceNumberCard.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceNumberCard.native.js +110 -0
- package/dist/esm/workspace/WorkspaceNumberCard.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceQuickList.mjs +87 -0
- package/dist/esm/workspace/WorkspaceQuickList.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceQuickList.native.js +102 -0
- package/dist/esm/workspace/WorkspaceQuickList.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceRecents.mjs +91 -0
- package/dist/esm/workspace/WorkspaceRecents.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceRecents.native.js +104 -0
- package/dist/esm/workspace/WorkspaceRecents.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceScreen.mjs +266 -0
- package/dist/esm/workspace/WorkspaceScreen.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceScreen.native.js +295 -0
- package/dist/esm/workspace/WorkspaceScreen.native.js.map +1 -0
- package/dist/esm/workspace/WorkspaceShortcut.mjs +159 -0
- package/dist/esm/workspace/WorkspaceShortcut.mjs.map +1 -0
- package/dist/esm/workspace/WorkspaceShortcut.native.js +173 -0
- package/dist/esm/workspace/WorkspaceShortcut.native.js.map +1 -0
- package/dist/esm/workspace/parseContent.mjs +148 -0
- package/dist/esm/workspace/parseContent.mjs.map +1 -0
- package/dist/esm/workspace/parseContent.native.js +264 -0
- package/dist/esm/workspace/parseContent.native.js.map +1 -0
- package/dist/esm/workspace/types.mjs +2 -0
- package/dist/esm/workspace/types.mjs.map +1 -0
- package/dist/esm/workspace/types.native.js +2 -0
- package/dist/esm/workspace/types.native.js.map +1 -0
- package/dist/esm/workspace/useDashboardChart.mjs +71 -0
- package/dist/esm/workspace/useDashboardChart.mjs.map +1 -0
- package/dist/esm/workspace/useDashboardChart.native.js +82 -0
- package/dist/esm/workspace/useDashboardChart.native.js.map +1 -0
- package/dist/esm/workspace/useWorkspaceDoc.mjs +77 -0
- package/dist/esm/workspace/useWorkspaceDoc.mjs.map +1 -0
- package/dist/esm/workspace/useWorkspaceDoc.native.js +91 -0
- package/dist/esm/workspace/useWorkspaceDoc.native.js.map +1 -0
- package/dist/jsx/chrome/CommandPalette.mjs +20 -9
- package/dist/jsx/chrome/CommandPalette.mjs.map +1 -1
- package/dist/jsx/chrome/CommandPalette.native.js +18 -10
- package/dist/jsx/chrome/CommandPalette.native.js.map +1 -1
- package/dist/jsx/i18n/en.mjs +21 -0
- package/dist/jsx/i18n/en.mjs.map +1 -1
- package/dist/jsx/i18n/en.native.js +21 -0
- package/dist/jsx/i18n/en.native.js.map +1 -1
- package/dist/jsx/index.js +19 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +19 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +48 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/types.mjs +2 -0
- package/dist/jsx/shared/types.mjs.map +1 -0
- package/dist/jsx/shared/types.native.js +21 -0
- package/dist/jsx/shared/types.native.js.map +1 -0
- package/dist/jsx/shared/useDocinfo.mjs +90 -0
- package/dist/jsx/shared/useDocinfo.mjs.map +1 -0
- package/dist/jsx/shared/useDocinfo.native.js +167 -0
- package/dist/jsx/shared/useDocinfo.native.js.map +1 -0
- package/dist/jsx/shared/useDoctypeMeta.mjs +99 -0
- package/dist/jsx/shared/useDoctypeMeta.mjs.map +1 -0
- package/dist/jsx/shared/useDoctypeMeta.native.js +158 -0
- package/dist/jsx/shared/useDoctypeMeta.native.js.map +1 -0
- package/dist/jsx/shared/useRecentVisit.mjs +15 -0
- package/dist/jsx/shared/useRecentVisit.mjs.map +1 -0
- package/dist/jsx/shared/useRecentVisit.native.js +43 -0
- package/dist/jsx/shared/useRecentVisit.native.js.map +1 -0
- package/dist/jsx/shared/useSessionUserId.mjs +29 -0
- package/dist/jsx/shared/useSessionUserId.mjs.map +1 -0
- package/dist/jsx/shared/useSessionUserId.native.js +59 -0
- package/dist/jsx/shared/useSessionUserId.native.js.map +1 -0
- package/dist/jsx/shared/useSlugTarget.mjs +113 -0
- package/dist/jsx/shared/useSlugTarget.mjs.map +1 -0
- package/dist/jsx/shared/useSlugTarget.native.js +166 -0
- package/dist/jsx/shared/useSlugTarget.native.js.map +1 -0
- package/dist/jsx/shared/useUserRoles.mjs +47 -0
- package/dist/jsx/shared/useUserRoles.mjs.map +1 -0
- package/dist/jsx/shared/useUserRoles.native.js +79 -0
- package/dist/jsx/shared/useUserRoles.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceChartBlock.mjs +111 -0
- package/dist/jsx/workspace/WorkspaceChartBlock.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceChartBlock.native.js +144 -0
- package/dist/jsx/workspace/WorkspaceChartBlock.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceGrid.mjs +66 -0
- package/dist/jsx/workspace/WorkspaceGrid.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceGrid.native.js +126 -0
- package/dist/jsx/workspace/WorkspaceGrid.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceLinkCard.mjs +113 -0
- package/dist/jsx/workspace/WorkspaceLinkCard.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceLinkCard.native.js +155 -0
- package/dist/jsx/workspace/WorkspaceLinkCard.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs +84 -0
- package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js +127 -0
- package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceNumberCard.mjs +108 -0
- package/dist/jsx/workspace/WorkspaceNumberCard.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceNumberCard.native.js +138 -0
- package/dist/jsx/workspace/WorkspaceNumberCard.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceQuickList.mjs +87 -0
- package/dist/jsx/workspace/WorkspaceQuickList.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceQuickList.native.js +130 -0
- package/dist/jsx/workspace/WorkspaceQuickList.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceRecents.mjs +91 -0
- package/dist/jsx/workspace/WorkspaceRecents.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceRecents.native.js +132 -0
- package/dist/jsx/workspace/WorkspaceRecents.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceScreen.mjs +266 -0
- package/dist/jsx/workspace/WorkspaceScreen.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceScreen.native.js +323 -0
- package/dist/jsx/workspace/WorkspaceScreen.native.js.map +1 -0
- package/dist/jsx/workspace/WorkspaceShortcut.mjs +159 -0
- package/dist/jsx/workspace/WorkspaceShortcut.mjs.map +1 -0
- package/dist/jsx/workspace/WorkspaceShortcut.native.js +202 -0
- package/dist/jsx/workspace/WorkspaceShortcut.native.js.map +1 -0
- package/dist/jsx/workspace/parseContent.mjs +148 -0
- package/dist/jsx/workspace/parseContent.mjs.map +1 -0
- package/dist/jsx/workspace/parseContent.native.js +299 -0
- package/dist/jsx/workspace/parseContent.native.js.map +1 -0
- package/dist/jsx/workspace/types.mjs +2 -0
- package/dist/jsx/workspace/types.mjs.map +1 -0
- package/dist/jsx/workspace/types.native.js +21 -0
- package/dist/jsx/workspace/types.native.js.map +1 -0
- package/dist/jsx/workspace/useDashboardChart.mjs +71 -0
- package/dist/jsx/workspace/useDashboardChart.mjs.map +1 -0
- package/dist/jsx/workspace/useDashboardChart.native.js +110 -0
- package/dist/jsx/workspace/useDashboardChart.native.js.map +1 -0
- package/dist/jsx/workspace/useWorkspaceDoc.mjs +77 -0
- package/dist/jsx/workspace/useWorkspaceDoc.mjs.map +1 -0
- package/dist/jsx/workspace/useWorkspaceDoc.native.js +120 -0
- package/dist/jsx/workspace/useWorkspaceDoc.native.js.map +1 -0
- package/package.json +12 -12
- package/src/chrome/CommandPalette.tsx +35 -15
- package/src/i18n/en.ts +21 -0
- package/src/index.ts +69 -0
- package/src/shared/types.ts +14 -0
- package/src/shared/useDocinfo.ts +163 -0
- package/src/shared/useDoctypeMeta.ts +243 -0
- package/src/shared/useRecentVisit.ts +33 -0
- package/src/shared/useSessionUserId.ts +42 -0
- package/src/shared/useSlugTarget.ts +143 -0
- package/src/shared/useUserRoles.ts +66 -0
- package/src/workspace/WorkspaceChartBlock.tsx +115 -0
- package/src/workspace/WorkspaceGrid.tsx +82 -0
- package/src/workspace/WorkspaceLinkCard.tsx +134 -0
- package/src/workspace/WorkspaceLinkDirectory.tsx +126 -0
- package/src/workspace/WorkspaceNumberCard.tsx +126 -0
- package/src/workspace/WorkspaceQuickList.tsx +100 -0
- package/src/workspace/WorkspaceRecents.tsx +100 -0
- package/src/workspace/WorkspaceScreen.tsx +322 -0
- package/src/workspace/WorkspaceShortcut.tsx +223 -0
- package/src/workspace/parseContent.spec.ts +327 -0
- package/src/workspace/parseContent.ts +240 -0
- package/src/workspace/types.ts +135 -0
- package/src/workspace/useDashboardChart.ts +115 -0
- package/src/workspace/useWorkspaceDoc.ts +111 -0
- package/types/chrome/CommandPalette.d.ts.map +1 -1
- package/types/i18n/en.d.ts +19 -0
- package/types/i18n/en.d.ts.map +1 -1
- package/types/index.d.ts +34 -0
- package/types/index.d.ts.map +1 -1
- package/types/shared/types.d.ts +21 -0
- package/types/shared/types.d.ts.map +1 -0
- package/types/shared/useDocinfo.d.ts +61 -0
- package/types/shared/useDocinfo.d.ts.map +1 -0
- package/types/shared/useDoctypeMeta.d.ts +118 -0
- package/types/shared/useDoctypeMeta.d.ts.map +1 -0
- package/types/shared/useRecentVisit.d.ts +10 -0
- package/types/shared/useRecentVisit.d.ts.map +1 -0
- package/types/shared/useSessionUserId.d.ts +6 -0
- package/types/shared/useSessionUserId.d.ts.map +1 -0
- package/types/shared/useSlugTarget.d.ts +18 -0
- package/types/shared/useSlugTarget.d.ts.map +1 -0
- package/types/shared/useUserRoles.d.ts +15 -0
- package/types/shared/useUserRoles.d.ts.map +1 -0
- package/types/workspace/WorkspaceChartBlock.d.ts +6 -0
- package/types/workspace/WorkspaceChartBlock.d.ts.map +1 -0
- package/types/workspace/WorkspaceGrid.d.ts +23 -0
- package/types/workspace/WorkspaceGrid.d.ts.map +1 -0
- package/types/workspace/WorkspaceLinkCard.d.ts +20 -0
- package/types/workspace/WorkspaceLinkCard.d.ts.map +1 -0
- package/types/workspace/WorkspaceLinkDirectory.d.ts +19 -0
- package/types/workspace/WorkspaceLinkDirectory.d.ts.map +1 -0
- package/types/workspace/WorkspaceNumberCard.d.ts +19 -0
- package/types/workspace/WorkspaceNumberCard.d.ts.map +1 -0
- package/types/workspace/WorkspaceQuickList.d.ts +6 -0
- package/types/workspace/WorkspaceQuickList.d.ts.map +1 -0
- package/types/workspace/WorkspaceRecents.d.ts +2 -0
- package/types/workspace/WorkspaceRecents.d.ts.map +1 -0
- package/types/workspace/WorkspaceScreen.d.ts +24 -0
- package/types/workspace/WorkspaceScreen.d.ts.map +1 -0
- package/types/workspace/WorkspaceShortcut.d.ts +27 -0
- package/types/workspace/WorkspaceShortcut.d.ts.map +1 -0
- package/types/workspace/parseContent.d.ts +77 -0
- package/types/workspace/parseContent.d.ts.map +1 -0
- package/types/workspace/types.d.ts +112 -0
- package/types/workspace/types.d.ts.map +1 -0
- package/types/workspace/useDashboardChart.d.ts +29 -0
- package/types/workspace/useDashboardChart.d.ts.map +1 -0
- package/types/workspace/useWorkspaceDoc.d.ts +18 -0
- package/types/workspace/useWorkspaceDoc.d.ts.map +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// Shared contract — consumed by the list, form, views, and quick-entry
|
|
2
|
+
// surfaces; keep signatures stable.
|
|
3
|
+
// This is the ONE meta loader for backoffice surfaces (form/deskMeta.ts and
|
|
4
|
+
// views/viewMeta.ts loaders were consolidated here): getdoctype RPC on live
|
|
5
|
+
// benches, the FrappeProvider fixture branch for stories/tests, one
|
|
6
|
+
// module-wide cache either way.
|
|
7
|
+
import type { FixtureProvider } from "@multiplatform.one/frappe";
|
|
8
|
+
import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
9
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
10
|
+
import { useFrappeApp } from "./useFrappeApp";
|
|
11
|
+
|
|
12
|
+
/** A single DocField row from a DocType meta (loose — server is source of truth). */
|
|
13
|
+
export interface DocFieldMeta {
|
|
14
|
+
fieldname: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
fieldtype: string;
|
|
17
|
+
options?: string;
|
|
18
|
+
reqd?: 0 | 1;
|
|
19
|
+
hidden?: 0 | 1;
|
|
20
|
+
read_only?: 0 | 1;
|
|
21
|
+
bold?: 0 | 1;
|
|
22
|
+
in_list_view?: 0 | 1;
|
|
23
|
+
in_standard_filter?: 0 | 1;
|
|
24
|
+
in_global_search?: 0 | 1;
|
|
25
|
+
allow_in_quick_entry?: 0 | 1;
|
|
26
|
+
allow_on_submit?: 0 | 1;
|
|
27
|
+
no_copy?: 0 | 1;
|
|
28
|
+
collapsible?: 0 | 1;
|
|
29
|
+
collapsible_depends_on?: string;
|
|
30
|
+
depends_on?: string;
|
|
31
|
+
mandatory_depends_on?: string;
|
|
32
|
+
read_only_depends_on?: string;
|
|
33
|
+
fetch_from?: string;
|
|
34
|
+
permlevel?: number;
|
|
35
|
+
length?: number;
|
|
36
|
+
default?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
precision?: string | number;
|
|
39
|
+
idx?: number;
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** DocType Link child row (Connections tab). Probed: `group` arrives null when unset. */
|
|
44
|
+
export interface DocLinkMeta {
|
|
45
|
+
name?: string;
|
|
46
|
+
link_doctype: string;
|
|
47
|
+
link_fieldname: string;
|
|
48
|
+
group?: string | null;
|
|
49
|
+
hidden?: 0 | 1;
|
|
50
|
+
is_child_table?: 0 | 1;
|
|
51
|
+
parent_doctype?: string;
|
|
52
|
+
table_fieldname?: string;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Role permission row from the DocType meta. */
|
|
57
|
+
export interface DocPermissionRule {
|
|
58
|
+
role?: string;
|
|
59
|
+
permlevel?: number;
|
|
60
|
+
read?: 0 | 1;
|
|
61
|
+
write?: 0 | 1;
|
|
62
|
+
create?: 0 | 1;
|
|
63
|
+
delete?: 0 | 1;
|
|
64
|
+
submit?: 0 | 1;
|
|
65
|
+
cancel?: 0 | 1;
|
|
66
|
+
amend?: 0 | 1;
|
|
67
|
+
print?: 0 | 1;
|
|
68
|
+
email?: 0 | 1;
|
|
69
|
+
share?: 0 | 1;
|
|
70
|
+
if_owner?: 0 | 1;
|
|
71
|
+
[key: string]: unknown;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** DocType meta as returned by frappe.desk.form.load.getdoctype (loose). */
|
|
75
|
+
export interface DoctypeMeta {
|
|
76
|
+
doctype?: string;
|
|
77
|
+
name: string;
|
|
78
|
+
module?: string;
|
|
79
|
+
issingle?: 0 | 1;
|
|
80
|
+
istable?: 0 | 1;
|
|
81
|
+
is_submittable?: 0 | 1;
|
|
82
|
+
is_tree?: 0 | 1;
|
|
83
|
+
is_virtual?: 0 | 1;
|
|
84
|
+
editable_grid?: 0 | 1;
|
|
85
|
+
track_changes?: 0 | 1;
|
|
86
|
+
quick_entry?: 0 | 1;
|
|
87
|
+
autoname?: string;
|
|
88
|
+
naming_rule?: string;
|
|
89
|
+
allow_rename?: 0 | 1;
|
|
90
|
+
allow_copy?: 0 | 1;
|
|
91
|
+
read_only?: 0 | 1;
|
|
92
|
+
in_create?: 0 | 1;
|
|
93
|
+
hide_toolbar?: 0 | 1;
|
|
94
|
+
max_attachments?: number;
|
|
95
|
+
title_field?: string;
|
|
96
|
+
image_field?: string;
|
|
97
|
+
search_fields?: string;
|
|
98
|
+
sort_field?: string;
|
|
99
|
+
sort_order?: string;
|
|
100
|
+
icon?: string;
|
|
101
|
+
document_type?: string;
|
|
102
|
+
fields: DocFieldMeta[];
|
|
103
|
+
permissions?: DocPermissionRule[];
|
|
104
|
+
links?: DocLinkMeta[];
|
|
105
|
+
states?: Array<{ title?: string; color?: string } & Record<string, unknown>>;
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface DoctypeMetaBundle {
|
|
110
|
+
/** Parent doctype meta (docs[0]) */
|
|
111
|
+
meta: DoctypeMeta;
|
|
112
|
+
/** Child table doctype metas keyed by doctype name (docs[1..]) */
|
|
113
|
+
childMetas: Record<string, DoctypeMeta>;
|
|
114
|
+
/** Parsed user_settings (list view settings etc.), when present */
|
|
115
|
+
userSettings?: Record<string, unknown>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface GetDoctypeResponse {
|
|
119
|
+
docs?: DoctypeMeta[];
|
|
120
|
+
user_settings?: string | Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const metaCache = new Map<string, Promise<DoctypeMetaBundle>>();
|
|
124
|
+
|
|
125
|
+
function parseUserSettings(raw: GetDoctypeResponse["user_settings"]) {
|
|
126
|
+
if (!raw) return undefined;
|
|
127
|
+
if (typeof raw !== "string") return raw;
|
|
128
|
+
try {
|
|
129
|
+
return JSON.parse(raw) as Record<string, unknown>;
|
|
130
|
+
} catch {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Fixture path (stories / tests): the raw getdoctype-shaped meta doc lives in
|
|
137
|
+
* the fixture provider's "DocType" collection under the doctype's name
|
|
138
|
+
* (fields / links / permissions arrays inline).
|
|
139
|
+
*/
|
|
140
|
+
async function loadBundleFromFixtures(
|
|
141
|
+
fixtures: FixtureProvider,
|
|
142
|
+
doctype: string,
|
|
143
|
+
): Promise<DoctypeMetaBundle> {
|
|
144
|
+
const doc = await fixtures.getDoc("DocType", doctype);
|
|
145
|
+
if (!doc) throw new Error(`No fixture meta for DocType "${doctype}"`);
|
|
146
|
+
const raw = doc as Record<string, unknown>;
|
|
147
|
+
const meta = { ...raw, fields: (raw.fields as DocFieldMeta[] | undefined) ?? [] } as DoctypeMeta;
|
|
148
|
+
return { meta, childMetas: {} };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Load (and cache, module-wide) the meta bundle for a doctype via
|
|
153
|
+
* `frappe.desk.form.load.getdoctype` — or, when the ambient FrappeProvider
|
|
154
|
+
* carries a fixture provider, from its "DocType" collection. All backoffice
|
|
155
|
+
* surfaces share this cache, so meta is fetched once per doctype per session.
|
|
156
|
+
*/
|
|
157
|
+
export function useDoctypeMeta(doctype?: string): {
|
|
158
|
+
bundle?: DoctypeMetaBundle;
|
|
159
|
+
meta?: DoctypeMeta;
|
|
160
|
+
loading: boolean;
|
|
161
|
+
error?: Error;
|
|
162
|
+
reload: () => void;
|
|
163
|
+
} {
|
|
164
|
+
const app = useFrappeApp();
|
|
165
|
+
const config = useFrappeConfig();
|
|
166
|
+
const fixtures = config?.fixtures;
|
|
167
|
+
const [bundle, setBundle] = useState<DoctypeMetaBundle | undefined>(undefined);
|
|
168
|
+
const [loading, setLoading] = useState<boolean>(Boolean(doctype));
|
|
169
|
+
const [error, setError] = useState<Error | undefined>(undefined);
|
|
170
|
+
const [generation, setGeneration] = useState(0);
|
|
171
|
+
|
|
172
|
+
// Fixture and live providers must not share cache entries (storybook mounts
|
|
173
|
+
// both kinds in one session); baseURL keeps multi-bench setups apart.
|
|
174
|
+
const cacheKey = doctype
|
|
175
|
+
? `${config?.baseURL ?? ""}\u0000${fixtures ? "fixtures" : "live"}\u0000${doctype}`
|
|
176
|
+
: undefined;
|
|
177
|
+
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if ((!app && !fixtures) || !doctype || !cacheKey) {
|
|
180
|
+
setBundle(undefined);
|
|
181
|
+
setLoading(Boolean(doctype));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
let cancelled = false;
|
|
185
|
+
let promise = metaCache.get(cacheKey);
|
|
186
|
+
if (!promise) {
|
|
187
|
+
if (fixtures) {
|
|
188
|
+
promise = loadBundleFromFixtures(fixtures, doctype);
|
|
189
|
+
} else if (app) {
|
|
190
|
+
promise = app
|
|
191
|
+
.call()
|
|
192
|
+
.get<GetDoctypeResponse>("frappe.desk.form.load.getdoctype", {
|
|
193
|
+
doctype,
|
|
194
|
+
with_parent: 1,
|
|
195
|
+
})
|
|
196
|
+
.then((res) => {
|
|
197
|
+
const docs = res.payload?.docs ?? [];
|
|
198
|
+
const meta = docs.find((d) => d.name === doctype) ?? docs[0];
|
|
199
|
+
if (!meta) throw new Error(`No meta returned for ${doctype}`);
|
|
200
|
+
const childMetas: Record<string, DoctypeMeta> = {};
|
|
201
|
+
for (const doc of docs) {
|
|
202
|
+
if (doc !== meta) childMetas[doc.name] = doc;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
meta,
|
|
206
|
+
childMetas,
|
|
207
|
+
userSettings: parseUserSettings(res.payload?.user_settings),
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
metaCache.set(cacheKey, promise);
|
|
214
|
+
promise.catch(() => metaCache.delete(cacheKey));
|
|
215
|
+
}
|
|
216
|
+
setLoading(true);
|
|
217
|
+
setError(undefined);
|
|
218
|
+
promise
|
|
219
|
+
.then((value) => {
|
|
220
|
+
if (cancelled) return;
|
|
221
|
+
setBundle(value);
|
|
222
|
+
setLoading(false);
|
|
223
|
+
})
|
|
224
|
+
.catch((err: Error) => {
|
|
225
|
+
if (cancelled) return;
|
|
226
|
+
setError(err);
|
|
227
|
+
setLoading(false);
|
|
228
|
+
});
|
|
229
|
+
return () => {
|
|
230
|
+
cancelled = true;
|
|
231
|
+
};
|
|
232
|
+
}, [app, fixtures, doctype, cacheKey, generation]);
|
|
233
|
+
|
|
234
|
+
const reload = useCallback(() => {
|
|
235
|
+
if (cacheKey) metaCache.delete(cacheKey);
|
|
236
|
+
setGeneration((g) => g + 1);
|
|
237
|
+
}, [cacheKey]);
|
|
238
|
+
|
|
239
|
+
return useMemo(
|
|
240
|
+
() => ({ bundle, meta: bundle?.meta, loading, error, reload }),
|
|
241
|
+
[bundle, loading, error, reload],
|
|
242
|
+
);
|
|
243
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write side of the shared desk recents store ("frappe-desk-recent") for
|
|
3
|
+
* plain navigation. The awesomebar/command palette already writes recents
|
|
4
|
+
* when it navigates, but sidebar/list navigation never did — so the
|
|
5
|
+
* workspace "Continue where you left off" strip stayed empty for anyone who
|
|
6
|
+
* doesn't use the palette. Screens call this on mount (and whenever the
|
|
7
|
+
* visited path changes) so every doctype-list and document visit lands in
|
|
8
|
+
* the same store the palette, sidebar recents, and the workspace strip read.
|
|
9
|
+
*
|
|
10
|
+
* Label vocabulary follows the existing writers: doctype lists use the bare
|
|
11
|
+
* doctype (command-palette navigation items), documents use
|
|
12
|
+
* "{doctype}: {name}" (frappe-ui DeskFormPage precedent).
|
|
13
|
+
*/
|
|
14
|
+
import { addToRecent } from "@multiplatform.one/frappe-ui";
|
|
15
|
+
import { useEffect } from "react";
|
|
16
|
+
|
|
17
|
+
export interface RecentVisit {
|
|
18
|
+
label: string;
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Record a visit in the shared recents store. Pass `undefined` to skip
|
|
24
|
+
* (create mode, fixture/story mode). Dedup/cap is owned by `addToRecent`.
|
|
25
|
+
*/
|
|
26
|
+
export function useRecentVisit(visit: RecentVisit | undefined): void {
|
|
27
|
+
const label = visit?.label;
|
|
28
|
+
const path = visit?.path;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!label || !path) return;
|
|
31
|
+
addToRecent({ label, path });
|
|
32
|
+
}, [label, path]);
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Session user id (for liked-hearts + "assigned to me"), cached per app.
|
|
2
|
+
// (The shell's useSessionUser in shell/useWorkspaces.ts is the HttpClient
|
|
3
|
+
// variant used by the sidebar; this one rides the FrappeApp auth module.)
|
|
4
|
+
import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { useFrappeApp } from "./useFrappeApp";
|
|
7
|
+
|
|
8
|
+
const userCache = new Map<string, Promise<string>>();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The logged-in user's id (`frappe.auth.get_logged_user`), or undefined
|
|
12
|
+
* while loading / in fixture mode (no session exists against fixtures).
|
|
13
|
+
*/
|
|
14
|
+
export function useSessionUserId(): string | undefined {
|
|
15
|
+
const config = useFrappeConfig();
|
|
16
|
+
const app = useFrappeApp();
|
|
17
|
+
const fixtureMode = Boolean(config?.fixtures);
|
|
18
|
+
const [user, setUser] = useState<string | undefined>(undefined);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!app || fixtureMode || !config?.baseURL) return;
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
let promise = userCache.get(config.baseURL);
|
|
24
|
+
if (!promise) {
|
|
25
|
+
promise = app.auth().getLoggedInUser();
|
|
26
|
+
userCache.set(config.baseURL, promise);
|
|
27
|
+
promise.catch(() => userCache.delete(config.baseURL));
|
|
28
|
+
}
|
|
29
|
+
promise
|
|
30
|
+
.then((value) => {
|
|
31
|
+
if (!cancelled && value) setUser(value);
|
|
32
|
+
})
|
|
33
|
+
.catch(() => {
|
|
34
|
+
// No session — hearts render unfilled, "me" facets hidden.
|
|
35
|
+
});
|
|
36
|
+
return () => {
|
|
37
|
+
cancelled = true;
|
|
38
|
+
};
|
|
39
|
+
}, [app, fixtureMode, config?.baseURL]);
|
|
40
|
+
|
|
41
|
+
return user;
|
|
42
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Contract: resolve a {basePath}/[slug] segment to a workspace or doctype.
|
|
2
|
+
// Every backoffice route relies on this hook; keep the signature stable.
|
|
3
|
+
import { HttpClient, type AuthConfig, type FixtureProvider } from "@multiplatform.one/frappe";
|
|
4
|
+
import { useFrappeConfig, type FrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
5
|
+
import { useEffect, useMemo, useState } from "react";
|
|
6
|
+
import { slugToDisplayName, toSlug } from "../navigation/paths";
|
|
7
|
+
import type { SlugTarget } from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Slug → proper-cased-name indexes, fetched once per session.
|
|
11
|
+
*
|
|
12
|
+
* Workspaces win over doctypes on collision (desk parity: `/app/tools` is the
|
|
13
|
+
* Tools workspace even though a "Tools" doctype could exist).
|
|
14
|
+
*/
|
|
15
|
+
interface SlugIndex {
|
|
16
|
+
workspaces: Map<string, string>;
|
|
17
|
+
doctypes: Map<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SlugIndexSource {
|
|
21
|
+
baseURL: string;
|
|
22
|
+
auth?: FrappeConfig["auth"];
|
|
23
|
+
fixtures?: FixtureProvider;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Module-level cache: one fetch per (baseURL, fixture-mode) per session. */
|
|
27
|
+
const slugIndexCache = new Map<string, Promise<SlugIndex>>();
|
|
28
|
+
|
|
29
|
+
function toSlugMap(names: readonly { name?: string }[]): Map<string, string> {
|
|
30
|
+
const map = new Map<string, string>();
|
|
31
|
+
for (const row of names) {
|
|
32
|
+
if (row.name) map.set(toSlug(row.name), row.name);
|
|
33
|
+
}
|
|
34
|
+
return map;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function fetchSlugIndex(source: SlugIndexSource): Promise<SlugIndex> {
|
|
38
|
+
if (source.fixtures) {
|
|
39
|
+
const [workspaces, doctypes] = await Promise.all([
|
|
40
|
+
source.fixtures.getList("Workspace", { fields: ["name"] }),
|
|
41
|
+
source.fixtures.getList("DocType", { fields: ["name"] }),
|
|
42
|
+
]);
|
|
43
|
+
return {
|
|
44
|
+
workspaces: toSlugMap(workspaces as { name?: string }[]),
|
|
45
|
+
doctypes: toSlugMap(doctypes as { name?: string }[]),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const client = new HttpClient(source.baseURL, source.auth as AuthConfig | undefined);
|
|
49
|
+
const [workspacesRes, doctypesRes] = await Promise.all([
|
|
50
|
+
client.get<{ data?: { name: string }[] }>("/api/resource/Workspace", {
|
|
51
|
+
fields: '["name"]',
|
|
52
|
+
limit_page_length: "0",
|
|
53
|
+
}),
|
|
54
|
+
client.get<{ message?: { name: string }[] }>("/api/method/frappe.client.get_list", {
|
|
55
|
+
doctype: "DocType",
|
|
56
|
+
fields: '["name"]',
|
|
57
|
+
limit_page_length: "0",
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
return {
|
|
61
|
+
workspaces: toSlugMap(workspacesRes.payload?.data ?? []),
|
|
62
|
+
doctypes: toSlugMap(doctypesRes.payload?.message ?? []),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getSlugIndex(source: SlugIndexSource): Promise<SlugIndex> {
|
|
67
|
+
const key = `${source.baseURL}:${source.fixtures ? "fixtures" : "live"}`;
|
|
68
|
+
let promise = slugIndexCache.get(key);
|
|
69
|
+
if (!promise) {
|
|
70
|
+
promise = fetchSlugIndex(source);
|
|
71
|
+
// Failed fetches are evicted so a later mount can retry.
|
|
72
|
+
promise.catch(() => {
|
|
73
|
+
if (slugIndexCache.get(key) === promise) slugIndexCache.delete(key);
|
|
74
|
+
});
|
|
75
|
+
slugIndexCache.set(key, promise);
|
|
76
|
+
}
|
|
77
|
+
return promise;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve a URL slug ("sales-order") to its target:
|
|
82
|
+
* - a Workspace whose slugged name matches → workspace page (workspaces win)
|
|
83
|
+
* - otherwise a DocType whose slugged name matches → doctype list
|
|
84
|
+
* - otherwise not-found
|
|
85
|
+
*
|
|
86
|
+
* The Workspace + DocType name indexes are fetched once per session (module
|
|
87
|
+
* cache) via the frappe client, honoring FrappeProvider fixtures in stories
|
|
88
|
+
* and tests. Connection comes exclusively from the ambient FrappeProvider
|
|
89
|
+
* (mounted by BackofficeProvider); without one the hook degrades the same
|
|
90
|
+
* way a failed fetch does. SSR-safe: the fetch runs in an effect, so the
|
|
91
|
+
* server render (and first client paint) reports `loading`. If the fetch
|
|
92
|
+
* fails the hook degrades gracefully by treating the slug as a title-cased
|
|
93
|
+
* doctype.
|
|
94
|
+
*/
|
|
95
|
+
export function useSlugTarget(slug: string | undefined): SlugTarget {
|
|
96
|
+
const config = useFrappeConfig();
|
|
97
|
+
const baseURL = config?.baseURL;
|
|
98
|
+
const fixtures = config?.fixtures;
|
|
99
|
+
const auth = config?.auth;
|
|
100
|
+
|
|
101
|
+
const [index, setIndex] = useState<SlugIndex | undefined>(undefined);
|
|
102
|
+
const [failed, setFailed] = useState(false);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
let cancelled = false;
|
|
106
|
+
// No provider config → no bench to ask; degrade like a failed fetch.
|
|
107
|
+
if (!baseURL && !fixtures) {
|
|
108
|
+
setFailed(true);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
setFailed(false);
|
|
112
|
+
getSlugIndex({ baseURL: baseURL ?? "", auth, fixtures })
|
|
113
|
+
.then((result) => {
|
|
114
|
+
if (!cancelled) setIndex(result);
|
|
115
|
+
})
|
|
116
|
+
.catch(() => {
|
|
117
|
+
if (!cancelled) setFailed(true);
|
|
118
|
+
});
|
|
119
|
+
return () => {
|
|
120
|
+
cancelled = true;
|
|
121
|
+
};
|
|
122
|
+
// `auth` is intentionally not a dependency: the cache is keyed by
|
|
123
|
+
// baseURL/fixtures and provider auth is stable for the session.
|
|
124
|
+
// oxlint-disable-next-line exhaustive-deps
|
|
125
|
+
}, [baseURL, fixtures]);
|
|
126
|
+
|
|
127
|
+
return useMemo<SlugTarget>(() => {
|
|
128
|
+
if (!slug) return { status: "loading" };
|
|
129
|
+
if (index) {
|
|
130
|
+
const workspace = index.workspaces.get(slug);
|
|
131
|
+
if (workspace) return { status: "resolved", kind: "workspace", workspace };
|
|
132
|
+
const doctype = index.doctypes.get(slug);
|
|
133
|
+
if (doctype) return { status: "resolved", kind: "doctype", doctype };
|
|
134
|
+
return { status: "not-found", slug };
|
|
135
|
+
}
|
|
136
|
+
if (failed) {
|
|
137
|
+
// Degrade gracefully: guess a title-cased doctype so deep links still
|
|
138
|
+
// land somewhere useful when the metadata fetch is unavailable.
|
|
139
|
+
return { status: "resolved", kind: "doctype", doctype: slugToDisplayName(slug) };
|
|
140
|
+
}
|
|
141
|
+
return { status: "loading" };
|
|
142
|
+
}, [slug, index, failed]);
|
|
143
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session user's role list — the client-side half of desk's
|
|
3
|
+
* `frappe.user_roles` (bootinfo). Fetched once per session via the
|
|
4
|
+
* whitelisted `frappe.core.doctype.user.user.get_roles` RPC (probed:
|
|
5
|
+
* `?uid=<user>` → `{"message": ["Role", ...]}`), keyed off
|
|
6
|
+
* `frappe.auth.get_logged_user`.
|
|
7
|
+
*
|
|
8
|
+
* `roles` stays undefined while loading, in fixture mode, or when the RPC
|
|
9
|
+
* fails — consumers (permlevel matrix) fall back to the conservative
|
|
10
|
+
* "permlevel > 0 → read-only" rule in that case.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
14
|
+
import { useEffect, useState } from "react";
|
|
15
|
+
import { useFrappeApp } from "./useFrappeApp";
|
|
16
|
+
|
|
17
|
+
const rolesCache = new Map<string, Promise<string[]>>();
|
|
18
|
+
|
|
19
|
+
export function useUserRoles(): { roles?: string[] } {
|
|
20
|
+
const app = useFrappeApp();
|
|
21
|
+
const config = useFrappeConfig();
|
|
22
|
+
const fixtures = config?.fixtures;
|
|
23
|
+
const baseURL = config?.baseURL;
|
|
24
|
+
const [roles, setRoles] = useState<string[] | undefined>(undefined);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// Fixture mode has no session — permlevel falls back to the coarse rule.
|
|
28
|
+
if (!app || fixtures || !baseURL) {
|
|
29
|
+
setRoles(undefined);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let cancelled = false;
|
|
33
|
+
let promise = rolesCache.get(baseURL);
|
|
34
|
+
if (!promise) {
|
|
35
|
+
promise = app
|
|
36
|
+
.call()
|
|
37
|
+
.get<{ message?: string }>("frappe.auth.get_logged_user")
|
|
38
|
+
.then((res) => {
|
|
39
|
+
const user = res.payload?.message;
|
|
40
|
+
if (!user) throw new Error("No session user");
|
|
41
|
+
return app.call().get<{ message?: string[] }>("frappe.core.doctype.user.user.get_roles", {
|
|
42
|
+
uid: user,
|
|
43
|
+
});
|
|
44
|
+
})
|
|
45
|
+
.then((res) => {
|
|
46
|
+
const list = res.payload?.message;
|
|
47
|
+
if (!Array.isArray(list)) throw new Error("No roles returned");
|
|
48
|
+
return list;
|
|
49
|
+
});
|
|
50
|
+
rolesCache.set(baseURL, promise);
|
|
51
|
+
promise.catch(() => rolesCache.delete(baseURL));
|
|
52
|
+
}
|
|
53
|
+
promise
|
|
54
|
+
.then((list) => {
|
|
55
|
+
if (!cancelled) setRoles(list);
|
|
56
|
+
})
|
|
57
|
+
.catch(() => {
|
|
58
|
+
if (!cancelled) setRoles(undefined);
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
};
|
|
63
|
+
}, [app, fixtures, baseURL]);
|
|
64
|
+
|
|
65
|
+
return { roles };
|
|
66
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace chart block — renders a Dashboard Chart by name using the desk
|
|
3
|
+
* chart RPC for real series data, drawn with the catalog ChartCard.
|
|
4
|
+
*
|
|
5
|
+
* Mappings (frappe → catalog): Line→line, Bar→bar, Pie/Donut/Percentage→pie.
|
|
6
|
+
* Heatmap has no catalog equivalent and falls back to bar (documented gap).
|
|
7
|
+
* Only the first dataset is drawn — the catalog chart is single-series
|
|
8
|
+
* (documented gap). In fixture mode (stories) the RPC is unavailable, so
|
|
9
|
+
* the block renders an informational card instead.
|
|
10
|
+
*/
|
|
11
|
+
import { AsyncBoundary, Card, SizableText, Skeleton, YStack } from "@multiplatform.one/components";
|
|
12
|
+
import { ChartCard, type ChartConfig, type ChartType } from "@multiplatform.one/frappe-ui";
|
|
13
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
14
|
+
import { useMemo } from "react";
|
|
15
|
+
import { useTranslation } from "react-i18next";
|
|
16
|
+
import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
17
|
+
import type { WorkspaceChartRow } from "./types";
|
|
18
|
+
import { useDashboardChartData, type DashboardChartDoc } from "./useDashboardChart";
|
|
19
|
+
import { useFrappeDoc } from "./useWorkspaceDoc";
|
|
20
|
+
|
|
21
|
+
function toChartType(frappeType: string | undefined): ChartType {
|
|
22
|
+
switch (frappeType) {
|
|
23
|
+
case "Line":
|
|
24
|
+
return "line";
|
|
25
|
+
case "Pie":
|
|
26
|
+
case "Donut":
|
|
27
|
+
case "Percentage":
|
|
28
|
+
return "pie";
|
|
29
|
+
default:
|
|
30
|
+
// "Bar", "Heatmap" (no catalog heatmap yet), unknown → bar.
|
|
31
|
+
return "bar";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WorkspaceChartBlockProps {
|
|
36
|
+
row: WorkspaceChartRow;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function WorkspaceChartBlock({ row }: WorkspaceChartBlockProps) {
|
|
40
|
+
const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
|
|
41
|
+
const { knobProps } = useResolvedKnobs();
|
|
42
|
+
const title = row.label || row.chart_name;
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
doc: chartDoc,
|
|
46
|
+
isLoading: docLoading,
|
|
47
|
+
error: docError,
|
|
48
|
+
} = useFrappeDoc<DashboardChartDoc>("Dashboard Chart", row.chart_name);
|
|
49
|
+
const {
|
|
50
|
+
series,
|
|
51
|
+
isLoading: dataLoading,
|
|
52
|
+
error: dataError,
|
|
53
|
+
unsupported,
|
|
54
|
+
} = useDashboardChartData(row.chart_name);
|
|
55
|
+
|
|
56
|
+
const config = useMemo<ChartConfig | undefined>(() => {
|
|
57
|
+
if (!series) return undefined;
|
|
58
|
+
const dataset = series.datasets?.[0];
|
|
59
|
+
if (!dataset) return undefined;
|
|
60
|
+
return {
|
|
61
|
+
id: `workspace-chart-${row.name}`,
|
|
62
|
+
title,
|
|
63
|
+
type: toChartType(chartDoc?.type),
|
|
64
|
+
data: series.labels.map((label, index) => ({
|
|
65
|
+
label,
|
|
66
|
+
value: dataset.values[index] ?? 0,
|
|
67
|
+
})),
|
|
68
|
+
height: 220,
|
|
69
|
+
};
|
|
70
|
+
}, [series, chartDoc?.type, row.name, title]);
|
|
71
|
+
|
|
72
|
+
if (unsupported) {
|
|
73
|
+
return (
|
|
74
|
+
<Card flexGrow={1}>
|
|
75
|
+
<YStack {...knobProps.panelPadding} {...knobProps.gap}>
|
|
76
|
+
<SizableText size="$4" fontWeight="700">
|
|
77
|
+
{title}
|
|
78
|
+
</SizableText>
|
|
79
|
+
<SizableText size="$2" color="$color10">
|
|
80
|
+
{t(
|
|
81
|
+
"workspaceChartFixture",
|
|
82
|
+
"Chart data is only available on a live bench",
|
|
83
|
+
)}
|
|
84
|
+
</SizableText>
|
|
85
|
+
</YStack>
|
|
86
|
+
</Card>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (docLoading || dataLoading) {
|
|
91
|
+
return (
|
|
92
|
+
<Card flexGrow={1} aria-busy aria-label={t("loading", "Loading")}>
|
|
93
|
+
<YStack {...knobProps.panelPadding} {...knobProps.gap}>
|
|
94
|
+
<Skeleton width="40%" height={16} />
|
|
95
|
+
<Skeleton variant="rounded" width="100%" height={220} />
|
|
96
|
+
</YStack>
|
|
97
|
+
</Card>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const error = docError ?? dataError;
|
|
102
|
+
if (error || !config) {
|
|
103
|
+
return (
|
|
104
|
+
<AsyncBoundary
|
|
105
|
+
compact
|
|
106
|
+
error={error ?? true}
|
|
107
|
+
errorTitle={t("workspaceChartError", "Couldn't load chart {{title}}", {
|
|
108
|
+
title,
|
|
109
|
+
})}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return <ChartCard config={config} flexGrow={1} />;
|
|
115
|
+
}
|