@multiplatform.one/backoffice 6.3.0 → 6.4.1
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/shell/WorkspaceSidebar.cjs +4 -0
- package/dist/cjs/shell/WorkspaceSidebar.native.js +4 -0
- package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -1
- 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/shell/WorkspaceSidebar.mjs +4 -0
- package/dist/esm/shell/WorkspaceSidebar.mjs.map +1 -1
- package/dist/esm/shell/WorkspaceSidebar.native.js +4 -0
- package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -1
- 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/shell/WorkspaceSidebar.mjs +4 -0
- package/dist/jsx/shell/WorkspaceSidebar.mjs.map +1 -1
- package/dist/jsx/shell/WorkspaceSidebar.native.js +4 -0
- package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -1
- 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/shell/WorkspaceSidebar.tsx +3 -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/shell/WorkspaceSidebar.d.ts.map +1 -1
- 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,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
|
+
}
|
|
@@ -99,6 +99,9 @@ export function WorkspaceSidebar({ activePath, onNavigate }: WorkspaceSidebarPro
|
|
|
99
99
|
items: flattenTree(publicRoots, childrenByParent, paths, activePath, onNavigate),
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
|
+
// A lone group label is noise (owner-flagged): only caption sections when
|
|
103
|
+
// there is more than one group to distinguish.
|
|
104
|
+
if (result.length === 1) result[0] = { ...result[0], title: undefined };
|
|
102
105
|
return result;
|
|
103
106
|
}, [privateWorkspaces, publicRoots, childrenByParent, paths, activePath, onNavigate, t]);
|
|
104
107
|
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 12-column workspace grid — desk parity for the editorjs block layout.
|
|
3
|
+
*
|
|
4
|
+
* Blocks carry a `col` span (3 = shortcut, 4 = card, 12 = full row).
|
|
5
|
+
* Consecutive blocks accumulate into rows of up to 12 columns and each row
|
|
6
|
+
* lays out with flex ratios, so no pixel math is needed. The container
|
|
7
|
+
* measures itself with `onLayout` (cross-platform — no media queries, no
|
|
8
|
+
* DOM) and collapses spans on narrow widths: everything full-width on
|
|
9
|
+
* phones, two-up on mid widths, desk's ~3–4 column layout when wide.
|
|
10
|
+
*/
|
|
11
|
+
import { XStack, YStack, type YStackProps } from "@multiplatform.one/components";
|
|
12
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
13
|
+
import { useState, type ReactNode } from "react";
|
|
14
|
+
|
|
15
|
+
/** Container width below which every block is full-width. */
|
|
16
|
+
const NARROW_MAX = 640;
|
|
17
|
+
/** Container width below which small blocks go two-up. */
|
|
18
|
+
const MEDIUM_MAX = 960;
|
|
19
|
+
|
|
20
|
+
export interface WorkspaceGridItem {
|
|
21
|
+
key: string;
|
|
22
|
+
/** 12-column span requested by the block (defaults to 12). */
|
|
23
|
+
col?: number;
|
|
24
|
+
node: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function effectiveSpan(raw: number | undefined, width: number): number {
|
|
28
|
+
const span = Math.min(Math.max(Math.round(raw ?? 12), 1), 12);
|
|
29
|
+
if (width <= 0) return span; // not measured yet — desk-wide layout
|
|
30
|
+
if (width < NARROW_MAX) return 12;
|
|
31
|
+
if (width < MEDIUM_MAX) return span <= 6 ? 6 : 12;
|
|
32
|
+
return span;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WorkspaceGridProps extends YStackProps {
|
|
36
|
+
items: WorkspaceGridItem[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function WorkspaceGrid({ items, ...stackProps }: WorkspaceGridProps) {
|
|
40
|
+
const { knobProps } = useResolvedKnobs();
|
|
41
|
+
const [width, setWidth] = useState(0);
|
|
42
|
+
|
|
43
|
+
// Chunk consecutive blocks into rows of up to 12 columns (desk behavior).
|
|
44
|
+
const rows: Array<Array<{ item: WorkspaceGridItem; span: number }>> = [];
|
|
45
|
+
let current: Array<{ item: WorkspaceGridItem; span: number }> = [];
|
|
46
|
+
let used = 0;
|
|
47
|
+
for (const item of items) {
|
|
48
|
+
const span = effectiveSpan(item.col, width);
|
|
49
|
+
if (used + span > 12 && current.length > 0) {
|
|
50
|
+
rows.push(current);
|
|
51
|
+
current = [];
|
|
52
|
+
used = 0;
|
|
53
|
+
}
|
|
54
|
+
current.push({ item, span });
|
|
55
|
+
used += span;
|
|
56
|
+
}
|
|
57
|
+
if (current.length > 0) rows.push(current);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<YStack
|
|
61
|
+
{...knobProps.gap}
|
|
62
|
+
onLayout={(event) => setWidth(event.nativeEvent.layout.width)}
|
|
63
|
+
{...stackProps}
|
|
64
|
+
>
|
|
65
|
+
{rows.map((row, rowIndex) => {
|
|
66
|
+
const rowUsed = row.reduce((sum, cell) => sum + cell.span, 0);
|
|
67
|
+
return (
|
|
68
|
+
<XStack key={row[0]?.item.key ?? rowIndex} {...knobProps.gap} alignItems="stretch">
|
|
69
|
+
{row.map(({ item, span }) => (
|
|
70
|
+
<YStack key={item.key} flexGrow={span} flexShrink={1} flexBasis={0} minWidth={0}>
|
|
71
|
+
{item.node}
|
|
72
|
+
</YStack>
|
|
73
|
+
))}
|
|
74
|
+
{/* Filler keeps a partial row's cells at their 12-col width
|
|
75
|
+
(a lone col-3 shortcut stays ~25%, like desk). */}
|
|
76
|
+
{rowUsed < 12 ? <YStack flexGrow={12 - rowUsed} flexBasis={0} /> : null}
|
|
77
|
+
</XStack>
|
|
78
|
+
);
|
|
79
|
+
})}
|
|
80
|
+
</YStack>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace link card — desk parity for the `card` content block: a titled
|
|
3
|
+
* card listing the Link rows that follow the matching "Card Break" row in
|
|
4
|
+
* the `links` child table. DocType links navigate to the doctype list;
|
|
5
|
+
* Report/Page links go through the slug router best-effort (see
|
|
6
|
+
* `resolveLinkTarget`).
|
|
7
|
+
*
|
|
8
|
+
* Rendering gates: link rows targeting doctypes outside the provider's
|
|
9
|
+
* `doctypes` scope are hidden, and DocType/Report rows (list-surface
|
|
10
|
+
* navigations) are hidden entirely when the `views` capability is off.
|
|
11
|
+
*/
|
|
12
|
+
import { Card, SizableText, XStack, YStack } from "@multiplatform.one/components";
|
|
13
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
14
|
+
import { useTranslation } from "react-i18next";
|
|
15
|
+
import {
|
|
16
|
+
useBackofficeCapabilities,
|
|
17
|
+
useBackofficeNavigator,
|
|
18
|
+
useBackofficePaths,
|
|
19
|
+
useIsDoctypeAllowed,
|
|
20
|
+
} from "../config/BackofficeProvider";
|
|
21
|
+
import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
22
|
+
import { createBackofficePaths, type BackofficePaths } from "../navigation/paths";
|
|
23
|
+
import type { WorkspaceLinkGroup, WorkspaceLinkRow } from "./types";
|
|
24
|
+
|
|
25
|
+
const defaultPaths = createBackofficePaths();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a Workspace Link row to a router path (basePath-aware when the
|
|
29
|
+
* provider's `paths` are passed; defaults to the canonical "/backoffice").
|
|
30
|
+
* - DocType → paths.list(link_to)
|
|
31
|
+
* - Report → paths.list(link_to) fallback (no query-report route yet — the
|
|
32
|
+
* slug router decides; documented gap)
|
|
33
|
+
* - Page → `{base}/<link_to>` (page names are already slugs)
|
|
34
|
+
* - anything else → disabled
|
|
35
|
+
*/
|
|
36
|
+
export function resolveLinkTarget(
|
|
37
|
+
row: WorkspaceLinkRow,
|
|
38
|
+
paths: BackofficePaths = defaultPaths,
|
|
39
|
+
): {
|
|
40
|
+
path?: string;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
} {
|
|
43
|
+
const target = row.link_to ?? "";
|
|
44
|
+
if (!target) return { disabled: true };
|
|
45
|
+
const prefix = paths.base === "/" ? "" : paths.base;
|
|
46
|
+
switch (row.link_type) {
|
|
47
|
+
case "DocType":
|
|
48
|
+
return { path: paths.list(target) };
|
|
49
|
+
case "Report":
|
|
50
|
+
return { path: paths.list(target) };
|
|
51
|
+
case "Page":
|
|
52
|
+
return { path: `${prefix}/${target}` };
|
|
53
|
+
default:
|
|
54
|
+
return { disabled: true };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function LinkRow({ row }: { row: WorkspaceLinkRow }) {
|
|
59
|
+
const navigator = useBackofficeNavigator();
|
|
60
|
+
const paths = useBackofficePaths();
|
|
61
|
+
const target = resolveLinkTarget(row, paths);
|
|
62
|
+
return (
|
|
63
|
+
<XStack
|
|
64
|
+
onPress={target.disabled ? undefined : () => target.path && navigator.navigate(target.path)}
|
|
65
|
+
disabled={target.disabled}
|
|
66
|
+
opacity={target.disabled ? 0.5 : 1}
|
|
67
|
+
cursor={target.disabled ? "default" : "pointer"}
|
|
68
|
+
paddingVertical="$1.5"
|
|
69
|
+
alignItems="center"
|
|
70
|
+
role="link"
|
|
71
|
+
aria-label={row.label}
|
|
72
|
+
hoverStyle={target.disabled ? undefined : { backgroundColor: "$color3" }}
|
|
73
|
+
borderRadius="$2"
|
|
74
|
+
>
|
|
75
|
+
<SizableText
|
|
76
|
+
size="$3"
|
|
77
|
+
color={target.disabled ? "$color10" : "$color12"}
|
|
78
|
+
numberOfLines={1}
|
|
79
|
+
flexShrink={1}
|
|
80
|
+
hoverStyle={target.disabled ? undefined : { color: "$color12" }}
|
|
81
|
+
>
|
|
82
|
+
{row.label}
|
|
83
|
+
</SizableText>
|
|
84
|
+
</XStack>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface WorkspaceLinkCardProps {
|
|
89
|
+
group: WorkspaceLinkGroup;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function WorkspaceLinkCard({ group }: WorkspaceLinkCardProps) {
|
|
93
|
+
const { knobProps } = useResolvedKnobs();
|
|
94
|
+
const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
|
|
95
|
+
const { views } = useBackofficeCapabilities();
|
|
96
|
+
const isDoctypeAllowed = useIsDoctypeAllowed();
|
|
97
|
+
|
|
98
|
+
// Visibility gates: doctype scope + the views capability (DocType/Report
|
|
99
|
+
// rows navigate to list surfaces). Defaults (scope open, views on) keep
|
|
100
|
+
// every authored row, desk parity.
|
|
101
|
+
const visibleLinks = group.links.filter((row) => {
|
|
102
|
+
if (row.link_type === "DocType" || row.link_type === "Report") {
|
|
103
|
+
if (!views.enabled) return false;
|
|
104
|
+
if (row.link_type === "DocType" && row.link_to && !isDoctypeAllowed(row.link_to)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Card flexGrow={1} height="100%">
|
|
113
|
+
<YStack {...knobProps.panelPadding} {...knobProps.gap} flex={1}>
|
|
114
|
+
{/* Catalog list-group caption (Sidebar section-label treatment):
|
|
115
|
+
small-caps muted label, no underline rule — a full-width rule
|
|
116
|
+
under uneven columns read as table chrome (Axiom 16). */}
|
|
117
|
+
<SizableText size="$2" fontWeight="600" textTransform="uppercase" color="$color11">
|
|
118
|
+
{group.label}
|
|
119
|
+
</SizableText>
|
|
120
|
+
{visibleLinks.length === 0 ? (
|
|
121
|
+
<SizableText size="$2" color="$color10">
|
|
122
|
+
{t("workspaceCardEmpty", "No links in this section")}
|
|
123
|
+
</SizableText>
|
|
124
|
+
) : (
|
|
125
|
+
<YStack>
|
|
126
|
+
{visibleLinks.map((row) => (
|
|
127
|
+
<LinkRow key={row.name} row={row} />
|
|
128
|
+
))}
|
|
129
|
+
</YStack>
|
|
130
|
+
)}
|
|
131
|
+
</YStack>
|
|
132
|
+
</Card>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Browse all" disclosure for the workspace link farm (MOD-11): the grouped
|
|
3
|
+
* link cards render inside one collapsible region with a local type-to-filter
|
|
4
|
+
* input, so 20–30 links stay fully reachable and searchable (capability
|
|
5
|
+
* parity with desk) without owning the top of the landing page. Expanded by
|
|
6
|
+
* default on desktop-width viewports, collapsed on phones and native.
|
|
7
|
+
*/
|
|
8
|
+
import { EmptyState, Accordion, YStack } from "@multiplatform.one/components";
|
|
9
|
+
import { SearchInput, useViewportGtSm } from "@multiplatform.one/forms";
|
|
10
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
11
|
+
import { useMemo, useState } from "react";
|
|
12
|
+
import { useTranslation } from "react-i18next";
|
|
13
|
+
import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
14
|
+
import type { WorkspaceLinkGroup } from "./types";
|
|
15
|
+
import { WorkspaceGrid, type WorkspaceGridItem } from "./WorkspaceGrid";
|
|
16
|
+
import { WorkspaceLinkCard } from "./WorkspaceLinkCard";
|
|
17
|
+
|
|
18
|
+
export interface WorkspaceLinkDirectoryItem {
|
|
19
|
+
/** Stable key from the authored card block. */
|
|
20
|
+
key: string;
|
|
21
|
+
/** Authored 12-column span (defaults to 4, desk's card width). */
|
|
22
|
+
col?: number;
|
|
23
|
+
group: WorkspaceLinkGroup;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface WorkspaceLinkDirectoryProps {
|
|
27
|
+
items: WorkspaceLinkDirectoryItem[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DIRECTORY_VALUE = "links";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Case-insensitive filter over a link group: a match on the group label
|
|
34
|
+
* keeps the whole card; otherwise the card narrows to its matching links
|
|
35
|
+
* (and disappears when none match).
|
|
36
|
+
*/
|
|
37
|
+
export function filterLinkGroup(
|
|
38
|
+
group: WorkspaceLinkGroup,
|
|
39
|
+
query: string,
|
|
40
|
+
): WorkspaceLinkGroup | undefined {
|
|
41
|
+
const needle = query.trim().toLowerCase();
|
|
42
|
+
if (!needle) return group;
|
|
43
|
+
if (group.label.toLowerCase().includes(needle)) return group;
|
|
44
|
+
const links = group.links.filter((row) => row.label.toLowerCase().includes(needle));
|
|
45
|
+
return links.length > 0 ? { ...group, links } : undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function WorkspaceLinkDirectory({ items }: WorkspaceLinkDirectoryProps) {
|
|
49
|
+
const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
|
|
50
|
+
const { knobProps } = useResolvedKnobs();
|
|
51
|
+
const viewportGtSm = useViewportGtSm();
|
|
52
|
+
const [filter, setFilter] = useState("");
|
|
53
|
+
// Uncontrolled-once default: open on desktop-width viewports, collapsed on
|
|
54
|
+
// phones/native (SPA render — the viewport is known at first render).
|
|
55
|
+
// Later resizes don't override the user's toggle.
|
|
56
|
+
const [openValue, setOpenValue] = useState(() => (viewportGtSm ? DIRECTORY_VALUE : ""));
|
|
57
|
+
|
|
58
|
+
const totalLinks = useMemo(
|
|
59
|
+
() => items.reduce((sum, item) => sum + item.group.links.length, 0),
|
|
60
|
+
[items],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const filteredItems = useMemo<WorkspaceGridItem[]>(
|
|
64
|
+
() =>
|
|
65
|
+
items.flatMap((item) => {
|
|
66
|
+
const group = filterLinkGroup(item.group, filter);
|
|
67
|
+
if (!group) return [];
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
key: item.key,
|
|
71
|
+
col: item.col ?? 4,
|
|
72
|
+
node: <WorkspaceLinkCard group={group} />,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
}),
|
|
76
|
+
[items, filter],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Accordion
|
|
81
|
+
type="single"
|
|
82
|
+
collapsible
|
|
83
|
+
variant="card"
|
|
84
|
+
value={openValue}
|
|
85
|
+
onValueChange={setOpenValue}
|
|
86
|
+
>
|
|
87
|
+
<Accordion.Item value={DIRECTORY_VALUE}>
|
|
88
|
+
<Accordion.Trigger
|
|
89
|
+
// Count-based plural via i18next suffix resolution ("1 link" /
|
|
90
|
+
// "N links"); defaultValue_* carries the en fallback per form.
|
|
91
|
+
description={t("workspaceBrowseAllCount", {
|
|
92
|
+
count: totalLinks,
|
|
93
|
+
defaultValue_one: "{{count}} link",
|
|
94
|
+
defaultValue_other: "{{count}} links",
|
|
95
|
+
})}
|
|
96
|
+
>
|
|
97
|
+
{t("workspaceBrowseAll", "Browse all")}
|
|
98
|
+
</Accordion.Trigger>
|
|
99
|
+
<Accordion.Content>
|
|
100
|
+
<YStack {...knobProps.gap}>
|
|
101
|
+
<SearchInput
|
|
102
|
+
value={filter}
|
|
103
|
+
onChange={setFilter}
|
|
104
|
+
debounceMs={0}
|
|
105
|
+
compact
|
|
106
|
+
placeholder={t("workspaceFilterLinks", "Filter links...")}
|
|
107
|
+
aria-label={t("workspaceFilterLinks", "Filter links...")}
|
|
108
|
+
/>
|
|
109
|
+
{filteredItems.length === 0 ? (
|
|
110
|
+
<EmptyState
|
|
111
|
+
compact
|
|
112
|
+
title={t(
|
|
113
|
+
"workspaceFilterNoMatch",
|
|
114
|
+
'No links match "{{query}}"',
|
|
115
|
+
{ query: filter.trim() },
|
|
116
|
+
)}
|
|
117
|
+
/>
|
|
118
|
+
) : (
|
|
119
|
+
<WorkspaceGrid items={filteredItems} />
|
|
120
|
+
)}
|
|
121
|
+
</YStack>
|
|
122
|
+
</Accordion.Content>
|
|
123
|
+
</Accordion.Item>
|
|
124
|
+
</Accordion>
|
|
125
|
+
);
|
|
126
|
+
}
|