@multiplatform.one/backoffice 7.6.2 → 7.7.0
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/BackofficeAwesomebar.native.js +1 -1
- package/dist/cjs/chrome/BackofficeAwesomebar.native.js.map +1 -1
- package/dist/cjs/chrome/CommandPalette.native.js +1 -1
- package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
- package/dist/cjs/chrome/HelpMenu.native.js +1 -1
- package/dist/cjs/chrome/HelpMenu.native.js.map +1 -1
- package/dist/cjs/chrome/NotificationsButton.native.js +1 -1
- package/dist/cjs/chrome/NotificationsButton.native.js.map +1 -1
- package/dist/cjs/chrome/UserMenu.native.js +1 -1
- package/dist/cjs/chrome/UserMenu.native.js.map +1 -1
- package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/cjs/chrome/command-palette-model.native.js +1 -1
- package/dist/cjs/chrome/command-palette-model.native.js.map +1 -1
- package/dist/cjs/chrome/help-about-dialog.native.js +1 -1
- package/dist/cjs/chrome/help-about-dialog.native.js.map +1 -1
- package/dist/cjs/chrome/help-shortcuts-dialog.native.js +1 -1
- package/dist/cjs/chrome/help-shortcuts-dialog.native.js.map +1 -1
- package/dist/cjs/chrome/notifications-data.native.js.map +1 -1
- package/dist/cjs/chrome/notifications-panel.native.js +1 -1
- package/dist/cjs/chrome/notifications-panel.native.js.map +1 -1
- package/dist/cjs/chrome/notifications-utils.native.js.map +1 -1
- package/dist/cjs/chrome/shortcuts.native.js.map +1 -1
- package/dist/cjs/chrome/user-session.native.js.map +1 -1
- package/dist/cjs/config/BackofficeProvider.native.js +1 -1
- package/dist/cjs/config/BackofficeProvider.native.js.map +1 -1
- package/dist/cjs/config/capabilities.native.js.map +1 -1
- package/dist/cjs/config/events.native.js.map +1 -1
- package/dist/cjs/config/slots.native.js.map +1 -1
- package/dist/cjs/desk/Desk.native.js.map +1 -1
- package/dist/cjs/desk/DeskSurface.native.js.map +1 -1
- package/dist/cjs/desk/resolveDeskPath.native.js.map +1 -1
- package/dist/cjs/desk/scope.native.js.map +1 -1
- package/dist/cjs/desk/target.native.js.map +1 -1
- package/dist/cjs/form/DocFormScreen.native.js +1 -1
- package/dist/cjs/form/DocFormScreen.native.js.map +1 -1
- package/dist/cjs/form/FormConnections.native.js +1 -1
- package/dist/cjs/form/FormConnections.native.js.map +1 -1
- package/dist/cjs/form/FormDialogs.native.js +1 -1
- package/dist/cjs/form/FormDialogs.native.js.map +1 -1
- package/dist/cjs/form/FormErrorBoundary.native.js +1 -1
- package/dist/cjs/form/FormErrorBoundary.native.js.map +1 -1
- package/dist/cjs/form/FormHeaderCluster.native.js +1 -1
- package/dist/cjs/form/FormHeaderCluster.native.js.map +1 -1
- package/dist/cjs/form/FormSidebarPanel.native.js +1 -1
- package/dist/cjs/form/FormSidebarPanel.native.js.map +1 -1
- package/dist/cjs/form/FormTimeline.native.js +1 -1
- package/dist/cjs/form/FormTimeline.native.js.map +1 -1
- package/dist/cjs/form/FormToolbar.native.js +1 -1
- package/dist/cjs/form/FormToolbar.native.js.map +1 -1
- package/dist/cjs/form/connections-data.native.js.map +1 -1
- package/dist/cjs/form/createPrefill.cjs +3 -11
- package/dist/cjs/form/createPrefill.native.js +3 -11
- package/dist/cjs/form/createPrefill.native.js.map +1 -1
- package/dist/cjs/form/dependsOn.native.js.map +1 -1
- package/dist/cjs/form/dirtyTracking.cjs +4 -29
- package/dist/cjs/form/dirtyTracking.native.js +4 -48
- package/dist/cjs/form/dirtyTracking.native.js.map +1 -1
- package/dist/cjs/form/docinfo.native.js.map +1 -1
- package/dist/cjs/form/fetchFrom.cjs +4 -41
- package/dist/cjs/form/fetchFrom.native.js +4 -81
- package/dist/cjs/form/fetchFrom.native.js.map +1 -1
- package/dist/cjs/form/fieldPermissions.cjs +4 -29
- package/dist/cjs/form/fieldPermissions.native.js +4 -51
- package/dist/cjs/form/fieldPermissions.native.js.map +1 -1
- package/dist/cjs/form/formActions.cjs +2 -171
- package/dist/cjs/form/formActions.native.js +2 -178
- package/dist/cjs/form/formActions.native.js.map +1 -1
- package/dist/cjs/form/formLayout.cjs +5 -219
- package/dist/cjs/form/formLayout.native.js +5 -359
- package/dist/cjs/form/formLayout.native.js.map +1 -1
- package/dist/cjs/form/platformHooks.native.js.map +1 -1
- package/dist/cjs/form/sidebar-attachments.native.js +1 -1
- package/dist/cjs/form/sidebar-attachments.native.js.map +1 -1
- package/dist/cjs/form/sidebar-dialogs.native.js +1 -1
- package/dist/cjs/form/sidebar-dialogs.native.js.map +1 -1
- package/dist/cjs/form/sidebar-people.native.js +1 -1
- package/dist/cjs/form/sidebar-people.native.js.map +1 -1
- package/dist/cjs/form/sidebar-rpc.native.js.map +1 -1
- package/dist/cjs/form/sidebar-utils.native.js.map +1 -1
- package/dist/cjs/form/timeline-entries.native.js.map +1 -1
- package/dist/cjs/form/timeline-utils.native.js.map +1 -1
- package/dist/cjs/form/useFormEngine.cjs +2 -361
- package/dist/cjs/form/useFormEngine.native.js +2 -447
- package/dist/cjs/form/useFormEngine.native.js.map +1 -1
- package/dist/cjs/i18n/en.native.js.map +1 -1
- package/dist/cjs/i18n/index.native.js.map +1 -1
- package/dist/cjs/index.native.js +1 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/list/AddTagsDialog.native.js +1 -1
- package/dist/cjs/list/AddTagsDialog.native.js.map +1 -1
- package/dist/cjs/list/AssignToDialog.native.js +1 -1
- package/dist/cjs/list/AssignToDialog.native.js.map +1 -1
- package/dist/cjs/list/DoctypeListScreen.native.js +1 -1
- package/dist/cjs/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/cjs/list/ListBulkActionsBar.native.js +1 -1
- package/dist/cjs/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/cjs/list/ListFilterBar.native.js +1 -1
- package/dist/cjs/list/ListFilterBar.native.js.map +1 -1
- package/dist/cjs/list/ListSidebar.native.js +1 -1
- package/dist/cjs/list/ListSidebar.native.js.map +1 -1
- package/dist/cjs/list/bulk-edit-dialog.native.js +1 -1
- package/dist/cjs/list/bulk-edit-dialog.native.js.map +1 -1
- package/dist/cjs/list/bulk-export.native.js.map +1 -1
- package/dist/cjs/list/bulk-submit-cancel.native.js.map +1 -1
- package/dist/cjs/list/bulkActions.native.js.map +1 -1
- package/dist/cjs/list/filter-chips.native.js +1 -1
- package/dist/cjs/list/filter-chips.native.js.map +1 -1
- package/dist/cjs/list/filter-editor.native.js +1 -1
- package/dist/cjs/list/filter-editor.native.js.map +1 -1
- package/dist/cjs/list/filter-fields.native.js.map +1 -1
- package/dist/cjs/list/filter-saved.native.js +1 -1
- package/dist/cjs/list/filter-saved.native.js.map +1 -1
- package/dist/cjs/list/filter-sort.native.js +1 -1
- package/dist/cjs/list/filter-sort.native.js.map +1 -1
- package/dist/cjs/list/filter-standard.native.js.map +1 -1
- package/dist/cjs/list/filterConversion.native.js.map +1 -1
- package/dist/cjs/list/list-tools-sheet.native.js +1 -1
- package/dist/cjs/list/list-tools-sheet.native.js.map +1 -1
- package/dist/cjs/list/listColumns.native.js.map +1 -1
- package/dist/cjs/list/listFields.native.js.map +1 -1
- package/dist/cjs/list/listFormat.native.js.map +1 -1
- package/dist/cjs/list/listUrlState.native.js.map +1 -1
- package/dist/cjs/list/sidebar-section.native.js +1 -1
- package/dist/cjs/list/sidebar-section.native.js.map +1 -1
- package/dist/cjs/list/sidebar-stats.native.js.map +1 -1
- package/dist/cjs/list/statusColors.native.js.map +1 -1
- package/dist/cjs/navigation/navigator.native.js.map +1 -1
- package/dist/cjs/navigation/paths.native.js.map +1 -1
- package/dist/cjs/oneAdapter/index.native.js.map +1 -1
- package/dist/cjs/quickEntry/QuickEntryDialog.native.js +1 -1
- package/dist/cjs/quickEntry/QuickEntryDialog.native.js.map +1 -1
- package/dist/cjs/quickEntry/quickEntryPlan.native.js.map +1 -1
- package/dist/cjs/report/DoctypeReportView.native.js +1 -1
- package/dist/cjs/report/DoctypeReportView.native.js.map +1 -1
- package/dist/cjs/report/QueryReportScreen.native.js +1 -1
- package/dist/cjs/report/QueryReportScreen.native.js.map +1 -1
- package/dist/cjs/report/queryReportColumns.native.js.map +1 -1
- package/dist/cjs/report/queryReportFilterBar.native.js +1 -1
- package/dist/cjs/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/cjs/report/queryReportUrlState.native.js.map +1 -1
- package/dist/cjs/report/report-grid.native.js +1 -1
- package/dist/cjs/report/report-grid.native.js.map +1 -1
- package/dist/cjs/report/report-toolbar.native.js +1 -1
- package/dist/cjs/report/report-toolbar.native.js.map +1 -1
- package/dist/cjs/report/reportShapes.native.js.map +1 -1
- package/dist/cjs/report/reportViewModel.native.js.map +1 -1
- package/dist/cjs/report/savedReports.native.js.map +1 -1
- package/dist/cjs/report/useQueryReport.native.js.map +1 -1
- package/dist/cjs/shared/deskLinks.native.js.map +1 -1
- package/dist/cjs/shared/listNavContext.native.js.map +1 -1
- package/dist/cjs/shared/types.native.js.map +1 -1
- package/dist/cjs/shared/useDocinfo.native.js.map +1 -1
- package/dist/cjs/shared/useDoctypeMeta.native.js.map +1 -1
- package/dist/cjs/shared/useFrappeApp.native.js.map +1 -1
- package/dist/cjs/shared/useRecentVisit.native.js.map +1 -1
- package/dist/cjs/shared/useSessionUserId.native.js.map +1 -1
- package/dist/cjs/shared/useSlugTarget.native.js.map +1 -1
- package/dist/cjs/shared/useUserRoles.native.js.map +1 -1
- package/dist/cjs/shell/BackofficeShell.native.js +1 -1
- package/dist/cjs/shell/BackofficeShell.native.js.map +1 -1
- package/dist/cjs/shell/HomeRedirect.native.js +1 -1
- package/dist/cjs/shell/HomeRedirect.native.js.map +1 -1
- package/dist/cjs/shell/NotFoundScreen.native.js +1 -1
- package/dist/cjs/shell/NotFoundScreen.native.js.map +1 -1
- package/dist/cjs/shell/SlugScreen.native.js +1 -1
- package/dist/cjs/shell/SlugScreen.native.js.map +1 -1
- package/dist/cjs/shell/WorkspaceSidebar.native.js +1 -1
- package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -1
- package/dist/cjs/shell/useWorkspaces.native.js.map +1 -1
- package/dist/cjs/shell/workspaceIcons.native.js.map +1 -1
- package/dist/cjs/views/CalendarView.native.js +1 -1
- package/dist/cjs/views/CalendarView.native.js.map +1 -1
- package/dist/cjs/views/DashboardView.native.js +1 -1
- package/dist/cjs/views/DashboardView.native.js.map +1 -1
- package/dist/cjs/views/DoctypeViewScreen.native.js.map +1 -1
- package/dist/cjs/views/ImageView.native.js +1 -1
- package/dist/cjs/views/ImageView.native.js.map +1 -1
- package/dist/cjs/views/KanbanView.native.js +1 -1
- package/dist/cjs/views/KanbanView.native.js.map +1 -1
- package/dist/cjs/views/ReportView.native.js +1 -1
- package/dist/cjs/views/ReportView.native.js.map +1 -1
- package/dist/cjs/views/TreeView.native.js +1 -1
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- package/dist/cjs/views/ViewHeader.native.js +1 -1
- package/dist/cjs/views/ViewHeader.native.js.map +1 -1
- package/dist/cjs/views/ViewStates.native.js +1 -1
- package/dist/cjs/views/ViewStates.native.js.map +1 -1
- package/dist/cjs/views/ViewSwitcherMenu.native.js +1 -1
- package/dist/cjs/views/ViewSwitcherMenu.native.js.map +1 -1
- package/dist/cjs/views/calendar-field-selector.native.js +1 -1
- package/dist/cjs/views/calendar-field-selector.native.js.map +1 -1
- package/dist/cjs/views/connection.native.js.map +1 -1
- package/dist/cjs/views/dashboard-data.native.js.map +1 -1
- package/dist/cjs/views/deskViewConfigs.native.js.map +1 -1
- package/dist/cjs/views/kanban-board-controls.native.js +1 -1
- package/dist/cjs/views/kanban-board-controls.native.js.map +1 -1
- package/dist/cjs/views/kanban-field-picker.native.js +1 -1
- package/dist/cjs/views/kanban-field-picker.native.js.map +1 -1
- package/dist/cjs/views/kanbanBoardOps.native.js.map +1 -1
- package/dist/cjs/views/report-controls.native.js +1 -1
- package/dist/cjs/views/report-controls.native.js.map +1 -1
- package/dist/cjs/views/viewAvailability.native.js.map +1 -1
- package/dist/cjs/views/viewMeta.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceChartBlock.native.js +1 -1
- package/dist/cjs/workspace/WorkspaceChartBlock.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceGrid.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceLinkCard.native.js +1 -1
- package/dist/cjs/workspace/WorkspaceLinkCard.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js +1 -1
- package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceNumberCard.native.js +1 -1
- package/dist/cjs/workspace/WorkspaceNumberCard.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceQuickList.cjs +1 -1
- package/dist/cjs/workspace/WorkspaceQuickList.native.js +4 -2
- package/dist/cjs/workspace/WorkspaceQuickList.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceRecents.cjs +1 -3
- package/dist/cjs/workspace/WorkspaceRecents.native.js +4 -4
- package/dist/cjs/workspace/WorkspaceRecents.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceScreen.native.js +1 -1
- package/dist/cjs/workspace/WorkspaceScreen.native.js.map +1 -1
- package/dist/cjs/workspace/WorkspaceShortcut.cjs +1 -3
- package/dist/cjs/workspace/WorkspaceShortcut.native.js +5 -4
- package/dist/cjs/workspace/WorkspaceShortcut.native.js.map +1 -1
- package/dist/cjs/workspace/parseContent.native.js.map +1 -1
- package/dist/cjs/workspace/types.native.js.map +1 -1
- package/dist/cjs/workspace/useDashboardChart.native.js.map +1 -1
- package/dist/cjs/workspace/useWorkspaceDoc.native.js.map +1 -1
- package/dist/esm/desk/DeskSurface.mjs.map +1 -1
- package/dist/esm/desk/DeskSurface.native.js.map +1 -1
- package/dist/esm/form/createPrefill.mjs +1 -9
- package/dist/esm/form/createPrefill.mjs.map +1 -1
- package/dist/esm/form/createPrefill.native.js +1 -9
- package/dist/esm/form/createPrefill.native.js.map +1 -1
- package/dist/esm/form/dirtyTracking.mjs +1 -26
- package/dist/esm/form/dirtyTracking.mjs.map +1 -1
- package/dist/esm/form/dirtyTracking.native.js +1 -45
- package/dist/esm/form/dirtyTracking.native.js.map +1 -1
- package/dist/esm/form/fetchFrom.mjs +1 -38
- package/dist/esm/form/fetchFrom.mjs.map +1 -1
- package/dist/esm/form/fetchFrom.native.js +1 -78
- package/dist/esm/form/fetchFrom.native.js.map +1 -1
- package/dist/esm/form/fieldPermissions.mjs +1 -26
- package/dist/esm/form/fieldPermissions.mjs.map +1 -1
- package/dist/esm/form/fieldPermissions.native.js +1 -48
- package/dist/esm/form/fieldPermissions.native.js.map +1 -1
- package/dist/esm/form/formActions.mjs +1 -145
- package/dist/esm/form/formActions.mjs.map +1 -1
- package/dist/esm/form/formActions.native.js +1 -152
- package/dist/esm/form/formActions.native.js.map +1 -1
- package/dist/esm/form/formLayout.mjs +1 -215
- package/dist/esm/form/formLayout.mjs.map +1 -1
- package/dist/esm/form/formLayout.native.js +1 -355
- package/dist/esm/form/formLayout.native.js.map +1 -1
- package/dist/esm/form/useFormEngine.mjs +1 -360
- package/dist/esm/form/useFormEngine.mjs.map +1 -1
- package/dist/esm/form/useFormEngine.native.js +1 -446
- package/dist/esm/form/useFormEngine.native.js.map +1 -1
- package/dist/esm/shared/useDoctypeMeta.mjs.map +1 -1
- package/dist/esm/shared/useDoctypeMeta.native.js.map +1 -1
- package/dist/esm/workspace/WorkspaceQuickList.mjs +2 -2
- package/dist/esm/workspace/WorkspaceQuickList.mjs.map +1 -1
- package/dist/esm/workspace/WorkspaceQuickList.native.js +4 -2
- package/dist/esm/workspace/WorkspaceQuickList.native.js.map +1 -1
- package/dist/esm/workspace/WorkspaceRecents.mjs +1 -3
- package/dist/esm/workspace/WorkspaceRecents.mjs.map +1 -1
- package/dist/esm/workspace/WorkspaceRecents.native.js +3 -3
- package/dist/esm/workspace/WorkspaceRecents.native.js.map +1 -1
- package/dist/esm/workspace/WorkspaceShortcut.mjs +1 -3
- package/dist/esm/workspace/WorkspaceShortcut.mjs.map +1 -1
- package/dist/esm/workspace/WorkspaceShortcut.native.js +4 -3
- package/dist/esm/workspace/WorkspaceShortcut.native.js.map +1 -1
- package/dist/jsx/desk/DeskSurface.mjs.map +1 -1
- package/dist/jsx/desk/DeskSurface.native.js.map +1 -1
- package/dist/jsx/form/createPrefill.mjs +1 -9
- package/dist/jsx/form/createPrefill.mjs.map +1 -1
- package/dist/jsx/form/createPrefill.native.js +3 -11
- package/dist/jsx/form/createPrefill.native.js.map +1 -1
- package/dist/jsx/form/dirtyTracking.mjs +1 -26
- package/dist/jsx/form/dirtyTracking.mjs.map +1 -1
- package/dist/jsx/form/dirtyTracking.native.js +4 -48
- package/dist/jsx/form/dirtyTracking.native.js.map +1 -1
- package/dist/jsx/form/fetchFrom.mjs +1 -38
- package/dist/jsx/form/fetchFrom.mjs.map +1 -1
- package/dist/jsx/form/fetchFrom.native.js +4 -81
- package/dist/jsx/form/fetchFrom.native.js.map +1 -1
- package/dist/jsx/form/fieldPermissions.mjs +1 -26
- package/dist/jsx/form/fieldPermissions.mjs.map +1 -1
- package/dist/jsx/form/fieldPermissions.native.js +4 -51
- package/dist/jsx/form/fieldPermissions.native.js.map +1 -1
- package/dist/jsx/form/formActions.mjs +1 -145
- package/dist/jsx/form/formActions.mjs.map +1 -1
- package/dist/jsx/form/formActions.native.js +2 -178
- package/dist/jsx/form/formActions.native.js.map +1 -1
- package/dist/jsx/form/formLayout.mjs +1 -215
- package/dist/jsx/form/formLayout.mjs.map +1 -1
- package/dist/jsx/form/formLayout.native.js +5 -359
- package/dist/jsx/form/formLayout.native.js.map +1 -1
- package/dist/jsx/form/useFormEngine.mjs +1 -360
- package/dist/jsx/form/useFormEngine.mjs.map +1 -1
- package/dist/jsx/form/useFormEngine.native.js +2 -447
- package/dist/jsx/form/useFormEngine.native.js.map +1 -1
- package/dist/jsx/shared/useDoctypeMeta.mjs.map +1 -1
- package/dist/jsx/shared/useDoctypeMeta.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceQuickList.mjs +2 -2
- package/dist/jsx/workspace/WorkspaceQuickList.mjs.map +1 -1
- package/dist/jsx/workspace/WorkspaceQuickList.native.js +3 -1
- package/dist/jsx/workspace/WorkspaceQuickList.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceRecents.mjs +1 -3
- package/dist/jsx/workspace/WorkspaceRecents.mjs.map +1 -1
- package/dist/jsx/workspace/WorkspaceRecents.native.js +3 -3
- package/dist/jsx/workspace/WorkspaceRecents.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceShortcut.mjs +1 -3
- package/dist/jsx/workspace/WorkspaceShortcut.mjs.map +1 -1
- package/dist/jsx/workspace/WorkspaceShortcut.native.js +4 -3
- package/dist/jsx/workspace/WorkspaceShortcut.native.js.map +1 -1
- package/package.json +12 -13
- package/src/desk/DeskSurface.tsx +8 -4
- package/src/form/createPrefill.ts +5 -25
- package/src/form/dirtyTracking.ts +4 -61
- package/src/form/fetchFrom.ts +5 -108
- package/src/form/fieldPermissions.ts +7 -65
- package/src/form/formActions.ts +10 -281
- package/src/form/formEngineShim.spec.ts +122 -0
- package/src/form/formLayout.ts +15 -387
- package/src/form/useFormEngine.ts +5 -493
- package/src/shared/useDoctypeMeta.ts +12 -100
- package/src/workspace/WorkspaceQuickList.tsx +10 -2
- package/src/workspace/WorkspaceRecents.tsx +3 -1
- package/src/workspace/WorkspaceShortcut.tsx +4 -1
- package/types/desk/DeskSurface.d.ts.map +1 -1
- package/types/form/createPrefill.d.ts +5 -16
- package/types/form/createPrefill.d.ts.map +1 -1
- package/types/form/dirtyTracking.d.ts +4 -33
- package/types/form/dirtyTracking.d.ts.map +1 -1
- package/types/form/fetchFrom.d.ts +5 -40
- package/types/form/fetchFrom.d.ts.map +1 -1
- package/types/form/fieldPermissions.d.ts +7 -35
- package/types/form/fieldPermissions.d.ts.map +1 -1
- package/types/form/formActions.d.ts +6 -91
- package/types/form/formActions.d.ts.map +1 -1
- package/types/form/formLayout.d.ts +12 -89
- package/types/form/formLayout.d.ts.map +1 -1
- package/types/form/useFormEngine.d.ts +5 -58
- package/types/form/useFormEngine.d.ts.map +1 -1
- package/types/shared/useDoctypeMeta.d.ts +2 -100
- package/types/shared/useDoctypeMeta.d.ts.map +1 -1
- package/types/workspace/WorkspaceQuickList.d.ts.map +1 -1
- package/types/workspace/WorkspaceRecents.d.ts.map +1 -1
- package/types/workspace/WorkspaceShortcut.d.ts.map +1 -1
- package/src/form/dirtyTracking.spec.ts +0 -91
- package/src/form/fetchFrom.spec.ts +0 -92
- package/src/form/fieldPermissions.spec.ts +0 -61
- package/src/form/formLayout.spec.ts +0 -373
|
@@ -1,496 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* -
|
|
5
|
-
* (useFrappeCollection + useLiveQuery) so realtime updates flow in; saves
|
|
6
|
-
* write only dirty fields through the collection's optimistic update path
|
|
7
|
-
* (live update_doc enforces If-Match OCC — a version conflict surfaces as
|
|
8
|
-
* a save error and flips `conflict` so the UI can offer a reload).
|
|
9
|
-
* - Create mode: seeds meta defaults (+ optional Duplicate/Amend prefill)
|
|
10
|
-
* and inserts via direct REST so the server-issued name is returned for
|
|
11
|
-
* router.replace(formPath(...)).
|
|
12
|
-
* - Dirty tracking against the last server baseline; external updates land
|
|
13
|
-
* on clean fields in place, dirty fields are preserved.
|
|
14
|
-
* - Submit / Cancel lifecycle for is_submittable doctypes via
|
|
15
|
-
* frappe.client.submit / frappe.client.cancel (probed) — realtime then
|
|
16
|
-
* refreshes the live doc; Amend is a create-mode copy with amended_from.
|
|
2
|
+
* Desk form state engine — moved to
|
|
3
|
+
* `@multiplatform.one/frappe-ui/src/formEngine/useFormEngine` (MPO-15).
|
|
4
|
+
* Re-exported here so existing imports keep resolving.
|
|
17
5
|
*/
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import type { DoctypeMeta } from "../shared/useDoctypeMeta";
|
|
22
|
-
import type { FrappeConnection } from "./formActions";
|
|
23
|
-
import {
|
|
24
|
-
cancelDoc,
|
|
25
|
-
createHttpClient,
|
|
26
|
-
getLoggedUser,
|
|
27
|
-
insertDoc,
|
|
28
|
-
submitDoc,
|
|
29
|
-
validateLink,
|
|
30
|
-
} from "./formActions";
|
|
31
|
-
import { buildCreateDefaults, isPromptNamed } from "./formLayout";
|
|
32
|
-
import { takeCreatePrefill } from "./createPrefill";
|
|
33
|
-
import { evaluateDependsOn } from "./dependsOn";
|
|
34
|
-
import { computeDirtyFields, isEmptyValue, isSameValue } from "./dirtyTracking";
|
|
35
|
-
import { applyFetchedValues, buildFetchMap } from "./fetchFrom";
|
|
36
|
-
|
|
37
|
-
type DocRecord = Record<string, unknown> & { name: string; doctype: string };
|
|
38
|
-
|
|
39
|
-
export interface FormEngine {
|
|
40
|
-
/** Current (locally edited) doc values. */
|
|
41
|
-
doc: Record<string, unknown>;
|
|
42
|
-
/** Last authoritative server snapshot (undefined in create mode). */
|
|
43
|
-
baseline: Record<string, unknown> | undefined;
|
|
44
|
-
isLoading: boolean;
|
|
45
|
-
/** Edit-mode doc missing after load settles (bad name / no permission). */
|
|
46
|
-
notFound: boolean;
|
|
47
|
-
/** The open doc was deleted on the server while open. */
|
|
48
|
-
wasDeleted: boolean;
|
|
49
|
-
isCreate: boolean;
|
|
50
|
-
isDirty: boolean;
|
|
51
|
-
dirtyFields: string[];
|
|
52
|
-
isSaving: boolean;
|
|
53
|
-
/** Per-field + `general` error messages. */
|
|
54
|
-
errors: Record<string, string>;
|
|
55
|
-
/** True after a save failed with a version conflict (OCC 409). */
|
|
56
|
-
conflict: boolean;
|
|
57
|
-
/** Logged-in user (for comments / likes / assignments). */
|
|
58
|
-
currentUser: string;
|
|
59
|
-
docstatus: number;
|
|
60
|
-
setValue: (field: string, value: unknown) => void;
|
|
61
|
-
/**
|
|
62
|
-
* Save. Create mode resolves with the server-issued doc name;
|
|
63
|
-
* edit mode resolves with the current name. Undefined = validation failed.
|
|
64
|
-
*/
|
|
65
|
-
save: () => Promise<string | undefined>;
|
|
66
|
-
/** Submit lifecycle (is_submittable): docstatus 0 → 1. */
|
|
67
|
-
submit: () => Promise<boolean>;
|
|
68
|
-
/** Cancel lifecycle: docstatus 1 → 2. */
|
|
69
|
-
cancel: () => Promise<boolean>;
|
|
70
|
-
/** Delete the doc through the live collection. */
|
|
71
|
-
remove: () => Promise<boolean>;
|
|
72
|
-
/** Discard local edits / clear conflict, back to the server baseline. */
|
|
73
|
-
reload: () => void;
|
|
74
|
-
/** Client-side required validation (fires on save too). */
|
|
75
|
-
validate: () => Record<string, string>;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const LAYOUT_TYPES = new Set(["Section Break", "Column Break", "Tab Break", "Fold"]);
|
|
79
|
-
|
|
80
|
-
function isConflictError(err: unknown): boolean {
|
|
81
|
-
const e = err as { status?: number; exc_type?: string; message?: string } | undefined;
|
|
82
|
-
if (!e) return false;
|
|
83
|
-
if (e.status === 409) return true;
|
|
84
|
-
if (e.exc_type === "TimestampMismatchError") return true;
|
|
85
|
-
return /TimestampMismatch|has been modified/i.test(e.message ?? "");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function extractServerError(err: unknown, fallback: string): string {
|
|
89
|
-
const e = err as { _server_messages?: string; message?: string; exc_type?: string } | undefined;
|
|
90
|
-
if (e?._server_messages) {
|
|
91
|
-
try {
|
|
92
|
-
const messages = JSON.parse(e._server_messages) as string[];
|
|
93
|
-
const parsed = messages
|
|
94
|
-
.map((msg) => {
|
|
95
|
-
try {
|
|
96
|
-
return (JSON.parse(msg) as { message?: string }).message ?? "";
|
|
97
|
-
} catch {
|
|
98
|
-
return msg;
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
.filter(Boolean);
|
|
102
|
-
if (parsed.length > 0) return parsed.join("\n");
|
|
103
|
-
} catch {
|
|
104
|
-
// fall through
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return e?.message || fallback;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function useFormEngine(
|
|
111
|
-
connection: FrappeConnection | undefined,
|
|
112
|
-
doctype: string,
|
|
113
|
-
/** Document name, or undefined for create mode. */
|
|
114
|
-
name: string | undefined,
|
|
115
|
-
meta: DoctypeMeta | undefined,
|
|
116
|
-
translate: (key: string, options?: Record<string, unknown>) => string,
|
|
117
|
-
): FormEngine {
|
|
118
|
-
const isCreate = !name;
|
|
119
|
-
const [localDoc, setLocalDoc] = useState<Record<string, unknown>>({});
|
|
120
|
-
const [baseline, setBaseline] = useState<Record<string, unknown> | undefined>(undefined);
|
|
121
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
122
|
-
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
123
|
-
const [conflict, setConflict] = useState(false);
|
|
124
|
-
const [wasDeleted, setWasDeleted] = useState(false);
|
|
125
|
-
const [currentUser, setCurrentUser] = useState("");
|
|
126
|
-
const everLoadedRef = useRef(false);
|
|
127
|
-
const seededRef = useRef(false);
|
|
128
|
-
const localDocRef = useRef(localDoc);
|
|
129
|
-
localDocRef.current = localDoc;
|
|
130
|
-
const baselineRef = useRef(baseline);
|
|
131
|
-
baselineRef.current = baseline;
|
|
132
|
-
|
|
133
|
-
// ---------------------------------------------------------------------
|
|
134
|
-
// Live doc subscription (edit mode)
|
|
135
|
-
// ---------------------------------------------------------------------
|
|
136
|
-
const docCollection = useFrappeCollection<DocRecord>(name ? connection : undefined, doctype, {
|
|
137
|
-
filters: name ? { name } : undefined,
|
|
138
|
-
});
|
|
139
|
-
const { data: rawDocData, isLoading: isDocLoading } = useLiveQuery(
|
|
140
|
-
(q) => {
|
|
141
|
-
if (!docCollection || !name) return null;
|
|
142
|
-
return q.from({ _doc: docCollection }).where(({ _doc }) => eq(_doc.name, name));
|
|
143
|
-
},
|
|
144
|
-
[docCollection, name],
|
|
145
|
-
);
|
|
146
|
-
const liveDoc = useMemo(() => {
|
|
147
|
-
const arr = Array.isArray(rawDocData) ? rawDocData : rawDocData ? [rawDocData] : [];
|
|
148
|
-
return (arr[0] ?? null) as DocRecord | null;
|
|
149
|
-
}, [rawDocData]);
|
|
150
|
-
|
|
151
|
-
// ---------------------------------------------------------------------
|
|
152
|
-
// Session user
|
|
153
|
-
// ---------------------------------------------------------------------
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
if (!connection) return;
|
|
156
|
-
let cancelled = false;
|
|
157
|
-
getLoggedUser(createHttpClient(connection))
|
|
158
|
-
.then((user) => {
|
|
159
|
-
if (!cancelled) setCurrentUser(user);
|
|
160
|
-
})
|
|
161
|
-
.catch(() => {
|
|
162
|
-
// Anonymous fallback — comment composer degrades gracefully.
|
|
163
|
-
});
|
|
164
|
-
return () => {
|
|
165
|
-
cancelled = true;
|
|
166
|
-
};
|
|
167
|
-
}, [connection?.baseURL]);
|
|
168
|
-
|
|
169
|
-
// ---------------------------------------------------------------------
|
|
170
|
-
// Reset per-document
|
|
171
|
-
// ---------------------------------------------------------------------
|
|
172
|
-
useEffect(() => {
|
|
173
|
-
everLoadedRef.current = false;
|
|
174
|
-
seededRef.current = false;
|
|
175
|
-
setWasDeleted(false);
|
|
176
|
-
setConflict(false);
|
|
177
|
-
setErrors({});
|
|
178
|
-
setBaseline(undefined);
|
|
179
|
-
setLocalDoc({});
|
|
180
|
-
}, [doctype, name]);
|
|
181
|
-
|
|
182
|
-
// ---------------------------------------------------------------------
|
|
183
|
-
// Create-mode seeding (meta defaults + Duplicate/Amend prefill)
|
|
184
|
-
// ---------------------------------------------------------------------
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
if (!isCreate || !meta || seededRef.current) return;
|
|
187
|
-
seededRef.current = true;
|
|
188
|
-
const defaults = buildCreateDefaults(meta.fields, currentUser || undefined);
|
|
189
|
-
const prefill = takeCreatePrefill(doctype);
|
|
190
|
-
setLocalDoc({ ...defaults, ...prefill, doctype });
|
|
191
|
-
// currentUser intentionally not a dep: seeding happens once per mount;
|
|
192
|
-
// a late-resolving user only matters for brand-new blank forms where
|
|
193
|
-
// the meta usually arrives later anyway.
|
|
194
|
-
}, [isCreate, meta, doctype]);
|
|
195
|
-
|
|
196
|
-
// ---------------------------------------------------------------------
|
|
197
|
-
// Field-level in-place sync (edit mode): server updates land on clean
|
|
198
|
-
// fields; dirty fields keep the user's edits.
|
|
199
|
-
// ---------------------------------------------------------------------
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
if (!name) return;
|
|
202
|
-
if (liveDoc) {
|
|
203
|
-
everLoadedRef.current = true;
|
|
204
|
-
setWasDeleted(false);
|
|
205
|
-
const incoming = liveDoc as Record<string, unknown>;
|
|
206
|
-
const prevBaseline = baselineRef.current;
|
|
207
|
-
if (!prevBaseline) {
|
|
208
|
-
setBaseline(incoming);
|
|
209
|
-
setLocalDoc(incoming);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const local = localDocRef.current;
|
|
213
|
-
const merged: Record<string, unknown> = { ...incoming };
|
|
214
|
-
for (const key of Object.keys(local)) {
|
|
215
|
-
// Same predicate as dirty tracking: a normalization-identical local
|
|
216
|
-
// value is not an edit, so the incoming server value wins.
|
|
217
|
-
if (!isSameValue(local[key], prevBaseline[key])) merged[key] = local[key];
|
|
218
|
-
}
|
|
219
|
-
setBaseline(incoming);
|
|
220
|
-
setLocalDoc(merged);
|
|
221
|
-
} else if (everLoadedRef.current && !isDocLoading) {
|
|
222
|
-
setWasDeleted(true);
|
|
223
|
-
}
|
|
224
|
-
}, [liveDoc, isDocLoading, name]);
|
|
225
|
-
|
|
226
|
-
// ---------------------------------------------------------------------
|
|
227
|
-
// Dirty tracking
|
|
228
|
-
// ---------------------------------------------------------------------
|
|
229
|
-
const dirtyFields = useMemo(() => computeDirtyFields(localDoc, baseline), [localDoc, baseline]);
|
|
230
|
-
const isDirty = dirtyFields.length > 0;
|
|
231
|
-
|
|
232
|
-
// ---------------------------------------------------------------------
|
|
233
|
-
// fetch_from preview: when a Link with dependent fetch_from targets
|
|
234
|
-
// changes, pull the source columns via frappe.client.validate_link and
|
|
235
|
-
// patch the targets (desk validate_link_and_fetch). Server stays
|
|
236
|
-
// authoritative on save.
|
|
237
|
-
// ---------------------------------------------------------------------
|
|
238
|
-
const fetchMap = useMemo(() => (meta ? buildFetchMap(meta.fields) : undefined), [meta]);
|
|
239
|
-
const fetchStateRef = useRef<{
|
|
240
|
-
meta: DoctypeMeta | undefined;
|
|
241
|
-
connection: FrappeConnection | undefined;
|
|
242
|
-
}>({ meta: undefined, connection: undefined });
|
|
243
|
-
fetchStateRef.current = { meta, connection };
|
|
244
|
-
const fetchMapRef = useRef(fetchMap);
|
|
245
|
-
fetchMapRef.current = fetchMap;
|
|
246
|
-
|
|
247
|
-
const runFetchFrom = useCallback((field: string, value: unknown) => {
|
|
248
|
-
const rules = fetchMapRef.current?.get(field);
|
|
249
|
-
if (!rules || rules.length === 0) return;
|
|
250
|
-
const { meta: currentMeta, connection: currentConnection } = fetchStateRef.current;
|
|
251
|
-
const linkField = currentMeta?.fields.find((f) => f.fieldname === field);
|
|
252
|
-
const targetDoctype = typeof linkField?.options === "string" ? linkField.options : "";
|
|
253
|
-
const applyPatch = (fetched: Record<string, unknown>) => {
|
|
254
|
-
setLocalDoc((prev) => {
|
|
255
|
-
// The link changed again while the fetch was in flight — drop it.
|
|
256
|
-
if (prev[field] !== value) return prev;
|
|
257
|
-
const patch = applyFetchedValues(rules, fetched, prev);
|
|
258
|
-
return Object.keys(patch).length > 0 ? { ...prev, ...patch } : prev;
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
if (value === undefined || value === null || value === "") {
|
|
262
|
-
// Cleared link → desk blanks the dependent targets.
|
|
263
|
-
applyPatch({});
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (!currentConnection || !targetDoctype || currentConnection.fixtures) return;
|
|
267
|
-
const sources = Array.from(new Set(rules.map((rule) => rule.source)));
|
|
268
|
-
validateLink(createHttpClient(currentConnection), targetDoctype, String(value), sources)
|
|
269
|
-
.then(applyPatch)
|
|
270
|
-
.catch(() => {
|
|
271
|
-
// Invalid link / no read perm on source — leave targets as-is;
|
|
272
|
-
// the server re-validates on save.
|
|
273
|
-
});
|
|
274
|
-
}, []);
|
|
275
|
-
|
|
276
|
-
const setValue = useCallback(
|
|
277
|
-
(field: string, value: unknown) => {
|
|
278
|
-
setLocalDoc((prev) => ({ ...prev, [field]: value }));
|
|
279
|
-
setErrors((prev) => {
|
|
280
|
-
if (!(field in prev)) return prev;
|
|
281
|
-
const next = { ...prev };
|
|
282
|
-
delete next[field];
|
|
283
|
-
return next;
|
|
284
|
-
});
|
|
285
|
-
runFetchFrom(field, value);
|
|
286
|
-
},
|
|
287
|
-
[runFetchFrom],
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// ---------------------------------------------------------------------
|
|
291
|
-
// Validation
|
|
292
|
-
// ---------------------------------------------------------------------
|
|
293
|
-
const validate = useCallback((): Record<string, string> => {
|
|
294
|
-
if (!meta) return {};
|
|
295
|
-
// Same evaluation context desk gives depends_on: `__islocal` marks an
|
|
296
|
-
// unsaved doc, `__session_user` backs `frappe.session.user` references.
|
|
297
|
-
const doc: Record<string, unknown> = {
|
|
298
|
-
...localDocRef.current,
|
|
299
|
-
__islocal: isCreate ? 1 : 0,
|
|
300
|
-
...(currentUser ? { __session_user: currentUser } : {}),
|
|
301
|
-
};
|
|
302
|
-
const validationErrors: Record<string, string> = {};
|
|
303
|
-
// Prompt-named doctypes need a user-entered name before insert
|
|
304
|
-
// (probed: REST insert without one fails with "Please set the document name").
|
|
305
|
-
if (isCreate && isPromptNamed(meta.autoname, meta.naming_rule) && isEmptyValue(doc.name)) {
|
|
306
|
-
validationErrors.name = translate("nameRequired", {
|
|
307
|
-
defaultValue: "Name is required",
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
for (const field of meta.fields) {
|
|
311
|
-
if (field.hidden || LAYOUT_TYPES.has(field.fieldtype)) continue;
|
|
312
|
-
// Desk parity: a field hidden by depends_on is not enforced client-side.
|
|
313
|
-
if (!evaluateDependsOn(field.depends_on, doc, true)) continue;
|
|
314
|
-
const required =
|
|
315
|
-
!!field.reqd ||
|
|
316
|
-
(!!field.mandatory_depends_on && evaluateDependsOn(field.mandatory_depends_on, doc, false));
|
|
317
|
-
if (!required) continue;
|
|
318
|
-
if (isEmptyValue(doc[field.fieldname])) {
|
|
319
|
-
validationErrors[field.fieldname] = translate("fieldRequired", {
|
|
320
|
-
defaultValue: "{{label}} is required",
|
|
321
|
-
label: field.label || field.fieldname,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return validationErrors;
|
|
326
|
-
}, [meta, isCreate, currentUser, translate]);
|
|
327
|
-
|
|
328
|
-
// ---------------------------------------------------------------------
|
|
329
|
-
// Save
|
|
330
|
-
// ---------------------------------------------------------------------
|
|
331
|
-
const save = useCallback(async (): Promise<string | undefined> => {
|
|
332
|
-
if (wasDeleted || !connection) return undefined;
|
|
333
|
-
const requiredErrors = validate();
|
|
334
|
-
if (Object.keys(requiredErrors).length > 0) {
|
|
335
|
-
setErrors(requiredErrors);
|
|
336
|
-
return undefined;
|
|
337
|
-
}
|
|
338
|
-
setIsSaving(true);
|
|
339
|
-
setErrors({});
|
|
340
|
-
try {
|
|
341
|
-
if (name) {
|
|
342
|
-
if (!docCollection) return undefined;
|
|
343
|
-
const local = localDocRef.current;
|
|
344
|
-
const base = baselineRef.current ?? {};
|
|
345
|
-
// Same predicate as dirty tracking: normalization-identical values
|
|
346
|
-
// must not be written back to the server as phantom edits.
|
|
347
|
-
const changed = Object.keys(local).filter(
|
|
348
|
-
(key) => key !== "doctype" && key !== "name" && !isSameValue(local[key], base[key]),
|
|
349
|
-
);
|
|
350
|
-
if (changed.length > 0) {
|
|
351
|
-
const tx = docCollection.update(name, (draft) => {
|
|
352
|
-
for (const key of changed) {
|
|
353
|
-
(draft as Record<string, unknown>)[key] = local[key];
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
await tx.isPersisted.promise;
|
|
357
|
-
}
|
|
358
|
-
setConflict(false);
|
|
359
|
-
return name;
|
|
360
|
-
}
|
|
361
|
-
// Create mode: direct REST insert to obtain the server-issued name.
|
|
362
|
-
// A user-entered `name` is kept for prompt-named doctypes; empty names
|
|
363
|
-
// are stripped so hash/series autonaming applies.
|
|
364
|
-
const { doctype: _dt, name: newName, ...values } = localDocRef.current;
|
|
365
|
-
if (typeof newName === "string" && newName.trim()) {
|
|
366
|
-
values.name = newName.trim();
|
|
367
|
-
}
|
|
368
|
-
const created = await insertDoc(createHttpClient(connection), doctype, values);
|
|
369
|
-
return String(created.name ?? "");
|
|
370
|
-
} catch (err) {
|
|
371
|
-
if (isConflictError(err)) {
|
|
372
|
-
setConflict(true);
|
|
373
|
-
setErrors({
|
|
374
|
-
general: translate("conflictError", {
|
|
375
|
-
defaultValue:
|
|
376
|
-
"This document was modified on the server after you opened it. Reload to get the latest version — your unsaved edits will be discarded.",
|
|
377
|
-
}),
|
|
378
|
-
});
|
|
379
|
-
} else {
|
|
380
|
-
setErrors({
|
|
381
|
-
general: extractServerError(
|
|
382
|
-
err,
|
|
383
|
-
translate("saveFailed", { defaultValue: "Failed to save" }),
|
|
384
|
-
),
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
return undefined;
|
|
388
|
-
} finally {
|
|
389
|
-
setIsSaving(false);
|
|
390
|
-
}
|
|
391
|
-
}, [wasDeleted, connection, validate, name, docCollection, doctype, translate]);
|
|
392
|
-
|
|
393
|
-
// ---------------------------------------------------------------------
|
|
394
|
-
// Submit / Cancel lifecycle
|
|
395
|
-
// ---------------------------------------------------------------------
|
|
396
|
-
const submit = useCallback(async (): Promise<boolean> => {
|
|
397
|
-
if (!connection || !name) return false;
|
|
398
|
-
setIsSaving(true);
|
|
399
|
-
setErrors({});
|
|
400
|
-
try {
|
|
401
|
-
// The full current server doc (incl. `modified`) is required for OCC.
|
|
402
|
-
const base = baselineRef.current;
|
|
403
|
-
if (!base) return false;
|
|
404
|
-
await submitDoc(createHttpClient(connection), { ...base, doctype });
|
|
405
|
-
return true;
|
|
406
|
-
} catch (err) {
|
|
407
|
-
if (isConflictError(err)) setConflict(true);
|
|
408
|
-
setErrors({
|
|
409
|
-
general: extractServerError(
|
|
410
|
-
err,
|
|
411
|
-
translate("submitFailed", { defaultValue: "Failed to submit" }),
|
|
412
|
-
),
|
|
413
|
-
});
|
|
414
|
-
return false;
|
|
415
|
-
} finally {
|
|
416
|
-
setIsSaving(false);
|
|
417
|
-
}
|
|
418
|
-
}, [connection, name, doctype, translate]);
|
|
419
|
-
|
|
420
|
-
const cancel = useCallback(async (): Promise<boolean> => {
|
|
421
|
-
if (!connection || !name) return false;
|
|
422
|
-
setIsSaving(true);
|
|
423
|
-
setErrors({});
|
|
424
|
-
try {
|
|
425
|
-
await cancelDoc(createHttpClient(connection), doctype, name);
|
|
426
|
-
return true;
|
|
427
|
-
} catch (err) {
|
|
428
|
-
setErrors({
|
|
429
|
-
general: extractServerError(
|
|
430
|
-
err,
|
|
431
|
-
translate("cancelFailed", { defaultValue: "Failed to cancel" }),
|
|
432
|
-
),
|
|
433
|
-
});
|
|
434
|
-
return false;
|
|
435
|
-
} finally {
|
|
436
|
-
setIsSaving(false);
|
|
437
|
-
}
|
|
438
|
-
}, [connection, name, doctype, translate]);
|
|
439
|
-
|
|
440
|
-
const remove = useCallback(async (): Promise<boolean> => {
|
|
441
|
-
if (!docCollection || !name || wasDeleted) return false;
|
|
442
|
-
setIsSaving(true);
|
|
443
|
-
setErrors({});
|
|
444
|
-
try {
|
|
445
|
-
const tx = docCollection.delete(name);
|
|
446
|
-
await tx.isPersisted.promise;
|
|
447
|
-
return true;
|
|
448
|
-
} catch (err) {
|
|
449
|
-
setErrors({
|
|
450
|
-
general: extractServerError(
|
|
451
|
-
err,
|
|
452
|
-
translate("deleteFailed", { defaultValue: "Failed to delete" }),
|
|
453
|
-
),
|
|
454
|
-
});
|
|
455
|
-
return false;
|
|
456
|
-
} finally {
|
|
457
|
-
setIsSaving(false);
|
|
458
|
-
}
|
|
459
|
-
}, [docCollection, name, wasDeleted, translate]);
|
|
460
|
-
|
|
461
|
-
const reload = useCallback(() => {
|
|
462
|
-
setConflict(false);
|
|
463
|
-
setErrors({});
|
|
464
|
-
if (baselineRef.current) {
|
|
465
|
-
setLocalDoc(baselineRef.current);
|
|
466
|
-
} else if (!name) {
|
|
467
|
-
seededRef.current = false;
|
|
468
|
-
setLocalDoc({ doctype });
|
|
469
|
-
}
|
|
470
|
-
}, [doctype, name]);
|
|
471
|
-
|
|
472
|
-
const docstatus = Number((baseline as { docstatus?: unknown } | undefined)?.docstatus ?? 0);
|
|
473
|
-
|
|
474
|
-
return {
|
|
475
|
-
doc: localDoc,
|
|
476
|
-
baseline,
|
|
477
|
-
isLoading: !!name && isDocLoading && !everLoadedRef.current,
|
|
478
|
-
notFound: !!name && !isDocLoading && !liveDoc && !everLoadedRef.current,
|
|
479
|
-
wasDeleted,
|
|
480
|
-
isCreate,
|
|
481
|
-
isDirty,
|
|
482
|
-
dirtyFields,
|
|
483
|
-
isSaving,
|
|
484
|
-
errors,
|
|
485
|
-
conflict,
|
|
486
|
-
currentUser,
|
|
487
|
-
docstatus,
|
|
488
|
-
setValue,
|
|
489
|
-
save,
|
|
490
|
-
submit,
|
|
491
|
-
cancel,
|
|
492
|
-
remove,
|
|
493
|
-
reload,
|
|
494
|
-
validate,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
7
|
+
export { useFormEngine } from "@multiplatform.one/frappe-ui";
|
|
8
|
+
export type { FormEngine } from "@multiplatform.one/frappe-ui";
|
|
@@ -6,109 +6,21 @@
|
|
|
6
6
|
// module-wide cache either way.
|
|
7
7
|
import type { FixtureProvider } from "@multiplatform.one/frappe";
|
|
8
8
|
import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
|
|
9
|
+
import type { DocFieldMeta, DoctypeMeta } from "@multiplatform.one/frappe-ui";
|
|
9
10
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
10
11
|
import { useFrappeApp } from "./useFrappeApp";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
/** Desk treeview_settings asset (`{doctype}_tree.js`) — gates Tree. */
|
|
84
|
-
__tree_js?: string;
|
|
85
|
-
/** Desk calendar_settings asset (`{doctype}_calendar.js`) — gates Calendar. */
|
|
86
|
-
__calendar_js?: string;
|
|
87
|
-
is_virtual?: 0 | 1;
|
|
88
|
-
editable_grid?: 0 | 1;
|
|
89
|
-
track_changes?: 0 | 1;
|
|
90
|
-
quick_entry?: 0 | 1;
|
|
91
|
-
autoname?: string;
|
|
92
|
-
naming_rule?: string;
|
|
93
|
-
allow_rename?: 0 | 1;
|
|
94
|
-
allow_copy?: 0 | 1;
|
|
95
|
-
read_only?: 0 | 1;
|
|
96
|
-
in_create?: 0 | 1;
|
|
97
|
-
hide_toolbar?: 0 | 1;
|
|
98
|
-
max_attachments?: number;
|
|
99
|
-
title_field?: string;
|
|
100
|
-
image_field?: string;
|
|
101
|
-
search_fields?: string;
|
|
102
|
-
sort_field?: string;
|
|
103
|
-
sort_order?: string;
|
|
104
|
-
icon?: string;
|
|
105
|
-
document_type?: string;
|
|
106
|
-
fields: DocFieldMeta[];
|
|
107
|
-
permissions?: DocPermissionRule[];
|
|
108
|
-
links?: DocLinkMeta[];
|
|
109
|
-
states?: Array<{ title?: string; color?: string } & Record<string, unknown>>;
|
|
110
|
-
[key: string]: unknown;
|
|
111
|
-
}
|
|
13
|
+
// The meta SHAPES live one tier down, in
|
|
14
|
+
// `@multiplatform.one/frappe-ui/src/formEngine/doctypeMeta` — the desk form
|
|
15
|
+
// engine consumes them and the engine moved there (MPO-15). This module keeps
|
|
16
|
+
// the LOADER (it needs the backoffice app context) and re-exports the shapes
|
|
17
|
+
// so every existing `@multiplatform.one/backoffice` import still resolves.
|
|
18
|
+
export type {
|
|
19
|
+
DocFieldMeta,
|
|
20
|
+
DocLinkMeta,
|
|
21
|
+
DocPermissionRule,
|
|
22
|
+
DoctypeMeta,
|
|
23
|
+
} from "@multiplatform.one/frappe-ui";
|
|
112
24
|
|
|
113
25
|
export interface DoctypeMetaBundle {
|
|
114
26
|
/** Parent doctype meta (docs[0]) */
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Card, Separator, Text, XStack, YStack } from "@multiplatform.one/components";
|
|
9
9
|
import { FrappeList } from "@multiplatform.one/frappe-ui";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
ensureFocusVisibleRing,
|
|
12
|
+
FOCUS_RING_HALO_OFFSET,
|
|
13
|
+
useResolvedKnobs,
|
|
14
|
+
} from "@multiplatform.one/theme";
|
|
11
15
|
import { useMemo } from "react";
|
|
12
16
|
import { useTranslation } from "react-i18next";
|
|
13
17
|
import { useBackofficeNavigator, useBackofficePaths } from "../config/BackofficeProvider";
|
|
@@ -62,7 +66,11 @@ export function WorkspaceQuickList({ row }: WorkspaceQuickListProps) {
|
|
|
62
66
|
tabIndex={0}
|
|
63
67
|
outlineWidth={0}
|
|
64
68
|
focusStyle={{ outlineWidth: 0 }}
|
|
65
|
-
|
|
69
|
+
// LC-71 §4 halo: inline text link ("View All" sits on the header
|
|
70
|
+
// row beside the section title, not on its own ground).
|
|
71
|
+
focusVisibleStyle={ensureFocusVisibleRing({
|
|
72
|
+
outlineOffset: FOCUS_RING_HALO_OFFSET,
|
|
73
|
+
})}
|
|
66
74
|
hoverStyle={{ color: "$color12" }}
|
|
67
75
|
onPress={() => navigator.navigate(paths.list(row.document_type))}
|
|
68
76
|
>
|
|
@@ -86,7 +86,9 @@ export function WorkspaceRecents() {
|
|
|
86
86
|
tabIndex={0}
|
|
87
87
|
outlineWidth={0}
|
|
88
88
|
focusStyle={{ outlineWidth: 0 }}
|
|
89
|
-
|
|
89
|
+
// Default ring (offset 0): a recents card is a standalone
|
|
90
|
+
// rectangle on the page ground, so no LC-71 §4 carve-out (MPO-92).
|
|
91
|
+
focusVisibleStyle={ensureFocusVisibleRing()}
|
|
90
92
|
>
|
|
91
93
|
<XStack paddingHorizontal="$3" minHeight={44} alignItems="center" {...knobProps.gap}>
|
|
92
94
|
<ClockCounterClockwiseIcon size={iconSize} color={iconColor} aria-hidden />
|
|
@@ -210,7 +210,10 @@ export function WorkspaceShortcut({ row, onPress }: WorkspaceShortcutProps) {
|
|
|
210
210
|
tabIndex={target.disabled && !onPress ? undefined : 0}
|
|
211
211
|
outlineWidth={0}
|
|
212
212
|
focusStyle={{ outlineWidth: 0 }}
|
|
213
|
-
|
|
213
|
+
// Default ring (offset 0): a shortcut tile is a standalone rectangle on
|
|
214
|
+
// the page ground, not a thumb or an inline link, so it takes no LC-71 §4
|
|
215
|
+
// carve-out (MPO-92).
|
|
216
|
+
focusVisibleStyle={ensureFocusVisibleRing()}
|
|
214
217
|
>
|
|
215
218
|
{/* Compact chip anatomy: icon + label + count, ≥44px press target
|
|
216
219
|
(DG-LAY-04), self-sized so the band wraps instead of stretching. */}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskSurface.d.ts","sourceRoot":"","sources":["../../src/desk/DeskSurface.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAIpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,4DAA4D;AAC5D,MAAM,MAAM,mBAAmB,GAC3B,aAAa,CAAC,gBAAgB,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAEpD,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAoCzF,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,gEAAgE;IAChE,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACnC;AAED,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,GACN,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"DeskSurface.d.ts","sourceRoot":"","sources":["../../src/desk/DeskSurface.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAIpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,4DAA4D;AAC5D,MAAM,MAAM,mBAAmB,GAC3B,aAAa,CAAC,gBAAgB,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAEpD,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAoCzF,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,gEAAgE;IAChE,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACnC;AAED,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,GACN,EAAE,oBAAoB,2CAwCtB;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,4CAMjC"}
|