@multiplatform.one/backoffice 7.22.0 → 7.23.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/list/DoctypeListScreen.cjs +4 -42
- package/dist/cjs/list/DoctypeListScreen.native.js +5 -43
- package/dist/cjs/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/cjs/list/filter-chips.cjs +3 -3
- package/dist/cjs/list/filter-chips.native.js +3 -3
- package/dist/cjs/list/filter-chips.native.js.map +1 -1
- package/dist/cjs/list/filter-editor.cjs +10 -11
- package/dist/cjs/list/filter-editor.native.js +10 -11
- package/dist/cjs/list/filter-editor.native.js.map +1 -1
- package/dist/cjs/list/filter-fields.cjs +15 -267
- package/dist/cjs/list/filter-fields.native.js +15 -337
- package/dist/cjs/list/filter-fields.native.js.map +1 -1
- package/dist/cjs/list/filter-standard.cjs +5 -6
- package/dist/cjs/list/filter-standard.native.js +5 -6
- package/dist/cjs/list/filter-standard.native.js.map +1 -1
- package/dist/cjs/list/list-tools-sheet.cjs +2 -2
- package/dist/cjs/list/list-tools-sheet.native.js +2 -2
- package/dist/cjs/list/list-tools-sheet.native.js.map +1 -1
- package/dist/cjs/list/listColumns.cjs +2 -371
- package/dist/cjs/list/listColumns.native.js +2 -418
- package/dist/cjs/list/listColumns.native.js.map +1 -1
- package/dist/cjs/list/listFields.cjs +4 -24
- package/dist/cjs/list/listFields.native.js +4 -50
- package/dist/cjs/list/listFields.native.js.map +1 -1
- package/dist/cjs/list/listFormat.cjs +7 -42
- package/dist/cjs/list/listFormat.native.js +7 -45
- package/dist/cjs/list/listFormat.native.js.map +1 -1
- package/dist/cjs/list/statusColors.cjs +2 -76
- package/dist/cjs/list/statusColors.native.js +2 -80
- package/dist/cjs/list/statusColors.native.js.map +1 -1
- package/dist/cjs/report/queryReportColumns.cjs +2 -2
- package/dist/cjs/report/queryReportColumns.native.js +2 -2
- package/dist/cjs/report/queryReportColumns.native.js.map +1 -1
- package/dist/cjs/report/queryReportFilterBar.cjs +5 -6
- package/dist/cjs/report/queryReportFilterBar.native.js +5 -6
- package/dist/cjs/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/cjs/report/report-grid.cjs +2 -2
- package/dist/cjs/report/report-grid.native.js +2 -2
- package/dist/cjs/report/report-grid.native.js.map +1 -1
- package/dist/cjs/report/report-toolbar.cjs +2 -2
- package/dist/cjs/report/report-toolbar.native.js +2 -2
- package/dist/cjs/report/report-toolbar.native.js.map +1 -1
- package/dist/cjs/report/reportViewModel.cjs +3 -3
- package/dist/cjs/report/reportViewModel.native.js +3 -3
- package/dist/cjs/report/reportViewModel.native.js.map +1 -1
- package/dist/esm/list/DoctypeListScreen.mjs +3 -41
- package/dist/esm/list/DoctypeListScreen.mjs.map +1 -1
- package/dist/esm/list/DoctypeListScreen.native.js +4 -42
- package/dist/esm/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/esm/list/filter-chips.mjs +1 -1
- package/dist/esm/list/filter-chips.native.js +1 -1
- package/dist/esm/list/filter-chips.native.js.map +1 -1
- package/dist/esm/list/filter-editor.mjs +1 -2
- package/dist/esm/list/filter-editor.mjs.map +1 -1
- package/dist/esm/list/filter-editor.native.js +1 -2
- package/dist/esm/list/filter-editor.native.js.map +1 -1
- package/dist/esm/list/filter-fields.mjs +1 -253
- package/dist/esm/list/filter-fields.mjs.map +1 -1
- package/dist/esm/list/filter-fields.native.js +1 -323
- package/dist/esm/list/filter-fields.native.js.map +1 -1
- package/dist/esm/list/filter-standard.mjs +1 -2
- package/dist/esm/list/filter-standard.mjs.map +1 -1
- package/dist/esm/list/filter-standard.native.js +1 -2
- package/dist/esm/list/filter-standard.native.js.map +1 -1
- package/dist/esm/list/list-tools-sheet.mjs +1 -1
- package/dist/esm/list/list-tools-sheet.native.js +1 -1
- package/dist/esm/list/list-tools-sheet.native.js.map +1 -1
- package/dist/esm/list/listColumns.mjs +1 -370
- package/dist/esm/list/listColumns.mjs.map +1 -1
- package/dist/esm/list/listColumns.native.js +1 -417
- package/dist/esm/list/listColumns.native.js.map +1 -1
- package/dist/esm/list/listFields.mjs +1 -21
- package/dist/esm/list/listFields.mjs.map +1 -1
- package/dist/esm/list/listFields.native.js +1 -47
- package/dist/esm/list/listFields.native.js.map +1 -1
- package/dist/esm/list/listFormat.mjs +1 -36
- package/dist/esm/list/listFormat.mjs.map +1 -1
- package/dist/esm/list/listFormat.native.js +1 -39
- package/dist/esm/list/listFormat.native.js.map +1 -1
- package/dist/esm/list/statusColors.mjs +1 -75
- package/dist/esm/list/statusColors.mjs.map +1 -1
- package/dist/esm/list/statusColors.native.js +1 -79
- package/dist/esm/list/statusColors.native.js.map +1 -1
- package/dist/esm/report/queryReportColumns.mjs +1 -1
- package/dist/esm/report/queryReportColumns.native.js +1 -1
- package/dist/esm/report/queryReportColumns.native.js.map +1 -1
- package/dist/esm/report/queryReportFilterBar.mjs +1 -2
- package/dist/esm/report/queryReportFilterBar.mjs.map +1 -1
- package/dist/esm/report/queryReportFilterBar.native.js +1 -2
- package/dist/esm/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/esm/report/report-grid.mjs +1 -1
- package/dist/esm/report/report-grid.native.js +1 -1
- package/dist/esm/report/report-grid.native.js.map +1 -1
- package/dist/esm/report/report-toolbar.mjs +1 -1
- package/dist/esm/report/report-toolbar.native.js +1 -1
- package/dist/esm/report/report-toolbar.native.js.map +1 -1
- package/dist/esm/report/reportViewModel.mjs +1 -1
- package/dist/esm/report/reportViewModel.mjs.map +1 -1
- package/dist/esm/report/reportViewModel.native.js +1 -1
- package/dist/esm/report/reportViewModel.native.js.map +1 -1
- package/dist/jsx/chrome/BackofficeAwesomebar.native.js +1 -1
- package/dist/jsx/chrome/BackofficeAwesomebar.native.js.map +1 -1
- package/dist/jsx/chrome/CommandPalette.native.js +1 -1
- package/dist/jsx/chrome/CommandPalette.native.js.map +1 -1
- package/dist/jsx/chrome/HelpMenu.native.js +1 -1
- package/dist/jsx/chrome/HelpMenu.native.js.map +1 -1
- package/dist/jsx/chrome/NotificationsButton.native.js +1 -1
- package/dist/jsx/chrome/NotificationsButton.native.js.map +1 -1
- package/dist/jsx/chrome/UserMenu.native.js +1 -1
- package/dist/jsx/chrome/UserMenu.native.js.map +1 -1
- package/dist/jsx/chrome/awesomebar-data.native.js.map +1 -1
- package/dist/jsx/chrome/command-palette-model.native.js +1 -1
- package/dist/jsx/chrome/command-palette-model.native.js.map +1 -1
- package/dist/jsx/chrome/help-about-dialog.native.js +1 -1
- package/dist/jsx/chrome/help-about-dialog.native.js.map +1 -1
- package/dist/jsx/chrome/help-shortcuts-dialog.native.js +1 -1
- package/dist/jsx/chrome/help-shortcuts-dialog.native.js.map +1 -1
- package/dist/jsx/chrome/notifications-data.native.js.map +1 -1
- package/dist/jsx/chrome/notifications-panel.native.js +1 -1
- package/dist/jsx/chrome/notifications-panel.native.js.map +1 -1
- package/dist/jsx/chrome/notifications-utils.native.js.map +1 -1
- package/dist/jsx/chrome/shortcuts.native.js.map +1 -1
- package/dist/jsx/chrome/user-session.native.js.map +1 -1
- package/dist/jsx/config/BackofficeProvider.native.js +1 -1
- package/dist/jsx/config/BackofficeProvider.native.js.map +1 -1
- package/dist/jsx/config/capabilities.native.js.map +1 -1
- package/dist/jsx/config/events.native.js.map +1 -1
- package/dist/jsx/config/slots.native.js.map +1 -1
- package/dist/jsx/desk/Desk.native.js.map +1 -1
- package/dist/jsx/desk/DeskSurface.native.js.map +1 -1
- package/dist/jsx/desk/resolveDeskPath.native.js.map +1 -1
- package/dist/jsx/desk/scope.native.js.map +1 -1
- package/dist/jsx/desk/target.native.js.map +1 -1
- package/dist/jsx/form/DocFormScreen.native.js +1 -1
- package/dist/jsx/form/DocFormScreen.native.js.map +1 -1
- package/dist/jsx/form/FormConnections.native.js +1 -1
- package/dist/jsx/form/FormConnections.native.js.map +1 -1
- package/dist/jsx/form/FormDialogs.native.js +1 -1
- package/dist/jsx/form/FormDialogs.native.js.map +1 -1
- package/dist/jsx/form/FormErrorBoundary.native.js +1 -1
- package/dist/jsx/form/FormErrorBoundary.native.js.map +1 -1
- package/dist/jsx/form/FormHeaderCluster.native.js +1 -1
- package/dist/jsx/form/FormHeaderCluster.native.js.map +1 -1
- package/dist/jsx/form/FormSidebarPanel.native.js +1 -1
- package/dist/jsx/form/FormSidebarPanel.native.js.map +1 -1
- package/dist/jsx/form/FormTimeline.native.js +1 -1
- package/dist/jsx/form/FormTimeline.native.js.map +1 -1
- package/dist/jsx/form/FormToolbar.native.js +1 -1
- package/dist/jsx/form/FormToolbar.native.js.map +1 -1
- package/dist/jsx/form/connections-data.native.js.map +1 -1
- package/dist/jsx/form/createPrefill.native.js.map +1 -1
- package/dist/jsx/form/dependsOn.native.js.map +1 -1
- package/dist/jsx/form/dirtyTracking.native.js.map +1 -1
- package/dist/jsx/form/docinfo.native.js.map +1 -1
- package/dist/jsx/form/fetchFrom.native.js.map +1 -1
- package/dist/jsx/form/fieldPermissions.native.js.map +1 -1
- package/dist/jsx/form/formActions.native.js.map +1 -1
- package/dist/jsx/form/formLayout.native.js.map +1 -1
- package/dist/jsx/form/platformHooks.native.js.map +1 -1
- package/dist/jsx/form/sidebar-attachments.native.js +1 -1
- package/dist/jsx/form/sidebar-attachments.native.js.map +1 -1
- package/dist/jsx/form/sidebar-dialogs.native.js +1 -1
- package/dist/jsx/form/sidebar-dialogs.native.js.map +1 -1
- package/dist/jsx/form/sidebar-people.native.js +1 -1
- package/dist/jsx/form/sidebar-people.native.js.map +1 -1
- package/dist/jsx/form/sidebar-rpc.native.js.map +1 -1
- package/dist/jsx/form/sidebar-utils.native.js.map +1 -1
- package/dist/jsx/form/timeline-entries.native.js.map +1 -1
- package/dist/jsx/form/timeline-utils.native.js.map +1 -1
- package/dist/jsx/form/useFormEngine.native.js.map +1 -1
- package/dist/jsx/i18n/en.native.js.map +1 -1
- package/dist/jsx/i18n/index.native.js.map +1 -1
- package/dist/jsx/index.native.js +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/list/AddTagsDialog.native.js +1 -1
- package/dist/jsx/list/AddTagsDialog.native.js.map +1 -1
- package/dist/jsx/list/AssignToDialog.native.js +1 -1
- package/dist/jsx/list/AssignToDialog.native.js.map +1 -1
- package/dist/jsx/list/DoctypeListScreen.mjs +3 -41
- package/dist/jsx/list/DoctypeListScreen.mjs.map +1 -1
- package/dist/jsx/list/DoctypeListScreen.native.js +6 -44
- package/dist/jsx/list/DoctypeListScreen.native.js.map +1 -1
- package/dist/jsx/list/ListBulkActionsBar.native.js +1 -1
- package/dist/jsx/list/ListBulkActionsBar.native.js.map +1 -1
- package/dist/jsx/list/ListFilterBar.native.js +1 -1
- package/dist/jsx/list/ListFilterBar.native.js.map +1 -1
- package/dist/jsx/list/ListSidebar.native.js +1 -1
- package/dist/jsx/list/ListSidebar.native.js.map +1 -1
- package/dist/jsx/list/bulk-edit-dialog.native.js +1 -1
- package/dist/jsx/list/bulk-edit-dialog.native.js.map +1 -1
- package/dist/jsx/list/bulk-export.native.js.map +1 -1
- package/dist/jsx/list/bulk-submit-cancel.native.js.map +1 -1
- package/dist/jsx/list/bulkActions.native.js.map +1 -1
- package/dist/jsx/list/filter-chips.mjs +1 -1
- package/dist/jsx/list/filter-chips.native.js +4 -4
- package/dist/jsx/list/filter-chips.native.js.map +1 -1
- package/dist/jsx/list/filter-editor.mjs +1 -2
- package/dist/jsx/list/filter-editor.mjs.map +1 -1
- package/dist/jsx/list/filter-editor.native.js +11 -12
- package/dist/jsx/list/filter-editor.native.js.map +1 -1
- package/dist/jsx/list/filter-fields.mjs +1 -253
- package/dist/jsx/list/filter-fields.mjs.map +1 -1
- package/dist/jsx/list/filter-fields.native.js +15 -337
- package/dist/jsx/list/filter-fields.native.js.map +1 -1
- package/dist/jsx/list/filter-saved.native.js +1 -1
- package/dist/jsx/list/filter-saved.native.js.map +1 -1
- package/dist/jsx/list/filter-sort.native.js +1 -1
- package/dist/jsx/list/filter-sort.native.js.map +1 -1
- package/dist/jsx/list/filter-standard.mjs +1 -2
- package/dist/jsx/list/filter-standard.mjs.map +1 -1
- package/dist/jsx/list/filter-standard.native.js +5 -6
- package/dist/jsx/list/filter-standard.native.js.map +1 -1
- package/dist/jsx/list/filterConversion.native.js.map +1 -1
- package/dist/jsx/list/list-tools-sheet.mjs +1 -1
- package/dist/jsx/list/list-tools-sheet.native.js +3 -3
- package/dist/jsx/list/list-tools-sheet.native.js.map +1 -1
- package/dist/jsx/list/listColumns.mjs +1 -370
- package/dist/jsx/list/listColumns.mjs.map +1 -1
- package/dist/jsx/list/listColumns.native.js +2 -418
- package/dist/jsx/list/listColumns.native.js.map +1 -1
- package/dist/jsx/list/listFields.mjs +1 -21
- package/dist/jsx/list/listFields.mjs.map +1 -1
- package/dist/jsx/list/listFields.native.js +4 -50
- package/dist/jsx/list/listFields.native.js.map +1 -1
- package/dist/jsx/list/listFormat.mjs +1 -36
- package/dist/jsx/list/listFormat.mjs.map +1 -1
- package/dist/jsx/list/listFormat.native.js +7 -45
- package/dist/jsx/list/listFormat.native.js.map +1 -1
- package/dist/jsx/list/listUrlState.native.js.map +1 -1
- package/dist/jsx/list/sidebar-section.native.js +1 -1
- package/dist/jsx/list/sidebar-section.native.js.map +1 -1
- package/dist/jsx/list/sidebar-stats.native.js.map +1 -1
- package/dist/jsx/list/statusColors.mjs +1 -75
- package/dist/jsx/list/statusColors.mjs.map +1 -1
- package/dist/jsx/list/statusColors.native.js +2 -80
- package/dist/jsx/list/statusColors.native.js.map +1 -1
- package/dist/jsx/navigation/navigator.native.js.map +1 -1
- package/dist/jsx/navigation/paths.native.js.map +1 -1
- package/dist/jsx/oneAdapter/index.native.js.map +1 -1
- package/dist/jsx/quickEntry/QuickEntryDialog.native.js +1 -1
- package/dist/jsx/quickEntry/QuickEntryDialog.native.js.map +1 -1
- package/dist/jsx/quickEntry/quickEntryPlan.native.js.map +1 -1
- package/dist/jsx/report/DoctypeReportView.native.js +1 -1
- package/dist/jsx/report/DoctypeReportView.native.js.map +1 -1
- package/dist/jsx/report/QueryReportScreen.native.js +1 -1
- package/dist/jsx/report/QueryReportScreen.native.js.map +1 -1
- package/dist/jsx/report/queryReportColumns.mjs +1 -1
- package/dist/jsx/report/queryReportColumns.native.js +2 -2
- package/dist/jsx/report/queryReportColumns.native.js.map +1 -1
- package/dist/jsx/report/queryReportFilterBar.mjs +1 -2
- package/dist/jsx/report/queryReportFilterBar.mjs.map +1 -1
- package/dist/jsx/report/queryReportFilterBar.native.js +6 -7
- package/dist/jsx/report/queryReportFilterBar.native.js.map +1 -1
- package/dist/jsx/report/queryReportUrlState.native.js.map +1 -1
- package/dist/jsx/report/report-grid.mjs +1 -1
- package/dist/jsx/report/report-grid.native.js +3 -3
- package/dist/jsx/report/report-grid.native.js.map +1 -1
- package/dist/jsx/report/report-toolbar.mjs +1 -1
- package/dist/jsx/report/report-toolbar.native.js +3 -3
- package/dist/jsx/report/report-toolbar.native.js.map +1 -1
- package/dist/jsx/report/reportShapes.native.js.map +1 -1
- package/dist/jsx/report/reportViewModel.mjs +1 -1
- package/dist/jsx/report/reportViewModel.mjs.map +1 -1
- package/dist/jsx/report/reportViewModel.native.js +3 -3
- package/dist/jsx/report/reportViewModel.native.js.map +1 -1
- package/dist/jsx/report/savedReports.native.js.map +1 -1
- package/dist/jsx/report/useQueryReport.native.js.map +1 -1
- package/dist/jsx/shared/deskLinks.native.js.map +1 -1
- package/dist/jsx/shared/listNavContext.native.js.map +1 -1
- package/dist/jsx/shared/types.native.js.map +1 -1
- package/dist/jsx/shared/useDocinfo.native.js.map +1 -1
- package/dist/jsx/shared/useDoctypeMeta.native.js.map +1 -1
- package/dist/jsx/shared/useFrappeApp.native.js.map +1 -1
- package/dist/jsx/shared/useRecentVisit.native.js.map +1 -1
- package/dist/jsx/shared/useSessionUserId.native.js.map +1 -1
- package/dist/jsx/shared/useSlugTarget.native.js.map +1 -1
- package/dist/jsx/shared/useUserRoles.native.js.map +1 -1
- package/dist/jsx/shell/BackofficeShell.native.js +1 -1
- package/dist/jsx/shell/BackofficeShell.native.js.map +1 -1
- package/dist/jsx/shell/HomeRedirect.native.js +1 -1
- package/dist/jsx/shell/HomeRedirect.native.js.map +1 -1
- package/dist/jsx/shell/NotFoundScreen.native.js +1 -1
- package/dist/jsx/shell/NotFoundScreen.native.js.map +1 -1
- package/dist/jsx/shell/SlugScreen.native.js +1 -1
- package/dist/jsx/shell/SlugScreen.native.js.map +1 -1
- package/dist/jsx/shell/WorkspaceSidebar.native.js +1 -1
- package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -1
- package/dist/jsx/shell/useWorkspaces.native.js.map +1 -1
- package/dist/jsx/shell/workspaceIcons.native.js.map +1 -1
- package/dist/jsx/views/CalendarView.native.js +1 -1
- package/dist/jsx/views/CalendarView.native.js.map +1 -1
- package/dist/jsx/views/DashboardView.native.js +1 -1
- package/dist/jsx/views/DashboardView.native.js.map +1 -1
- package/dist/jsx/views/DoctypeViewScreen.native.js.map +1 -1
- package/dist/jsx/views/ImageView.native.js +1 -1
- package/dist/jsx/views/ImageView.native.js.map +1 -1
- package/dist/jsx/views/KanbanView.native.js +1 -1
- package/dist/jsx/views/KanbanView.native.js.map +1 -1
- package/dist/jsx/views/ReportView.native.js +1 -1
- package/dist/jsx/views/ReportView.native.js.map +1 -1
- package/dist/jsx/views/TreeView.native.js +1 -1
- package/dist/jsx/views/TreeView.native.js.map +1 -1
- package/dist/jsx/views/ViewHeader.native.js +1 -1
- package/dist/jsx/views/ViewHeader.native.js.map +1 -1
- package/dist/jsx/views/ViewStates.native.js +1 -1
- package/dist/jsx/views/ViewStates.native.js.map +1 -1
- package/dist/jsx/views/ViewSwitcherMenu.native.js +1 -1
- package/dist/jsx/views/ViewSwitcherMenu.native.js.map +1 -1
- package/dist/jsx/views/calendar-field-selector.native.js +1 -1
- package/dist/jsx/views/calendar-field-selector.native.js.map +1 -1
- package/dist/jsx/views/connection.native.js.map +1 -1
- package/dist/jsx/views/dashboard-data.native.js.map +1 -1
- package/dist/jsx/views/deskViewConfigs.native.js.map +1 -1
- package/dist/jsx/views/kanban-board-controls.native.js +1 -1
- package/dist/jsx/views/kanban-board-controls.native.js.map +1 -1
- package/dist/jsx/views/kanban-field-picker.native.js +1 -1
- package/dist/jsx/views/kanban-field-picker.native.js.map +1 -1
- package/dist/jsx/views/kanbanBoardOps.native.js.map +1 -1
- package/dist/jsx/views/report-controls.native.js +1 -1
- package/dist/jsx/views/report-controls.native.js.map +1 -1
- package/dist/jsx/views/viewAvailability.native.js.map +1 -1
- package/dist/jsx/views/viewMeta.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceChartBlock.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceChartBlock.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceGrid.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceLinkCard.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceLinkCard.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceNumberCard.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceNumberCard.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceQuickList.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceQuickList.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceRecents.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceRecents.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceScreen.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceScreen.native.js.map +1 -1
- package/dist/jsx/workspace/WorkspaceShortcut.native.js +1 -1
- package/dist/jsx/workspace/WorkspaceShortcut.native.js.map +1 -1
- package/dist/jsx/workspace/parseContent.native.js.map +1 -1
- package/dist/jsx/workspace/types.native.js.map +1 -1
- package/dist/jsx/workspace/useDashboardChart.native.js.map +1 -1
- package/dist/jsx/workspace/useWorkspaceDoc.native.js.map +1 -1
- package/package.json +12 -12
- package/src/backofficeScreens.designLaw.spec.ts +1 -3
- package/src/list/DoctypeListScreen.auth.spec.tsx +84 -0
- package/src/list/DoctypeListScreen.tsx +18 -51
- package/src/list/filter-chips.tsx +1 -1
- package/src/list/filter-editor.tsx +2 -2
- package/src/list/filter-fields.tsx +29 -356
- package/src/list/filter-standard.tsx +2 -2
- package/src/list/list-tools-sheet.tsx +1 -1
- package/src/list/listColumns.tsx +11 -488
- package/src/list/listFields.ts +9 -66
- package/src/list/listFormat.ts +12 -77
- package/src/list/listShim.spec.ts +101 -0
- package/src/list/statusColors.ts +7 -92
- package/src/report/queryReportColumns.tsx +1 -1
- package/src/report/queryReportFilterBar.tsx +7 -2
- package/src/report/report-grid.tsx +1 -1
- package/src/report/report-toolbar.tsx +1 -1
- package/src/report/reportViewModel.ts +5 -1
- package/types/list/DoctypeListScreen.d.ts +1 -1
- package/types/list/DoctypeListScreen.d.ts.map +1 -1
- package/types/list/filter-editor.d.ts.map +1 -1
- package/types/list/filter-fields.d.ts +10 -78
- package/types/list/filter-fields.d.ts.map +1 -1
- package/types/list/filter-standard.d.ts.map +1 -1
- package/types/list/listColumns.d.ts +10 -40
- package/types/list/listColumns.d.ts.map +1 -1
- package/types/list/listFields.d.ts +5 -13
- package/types/list/listFields.d.ts.map +1 -1
- package/types/list/listFormat.d.ts +5 -27
- package/types/list/listFormat.d.ts.map +1 -1
- package/types/list/statusColors.d.ts +6 -11
- package/types/list/statusColors.d.ts.map +1 -1
- package/types/report/queryReportFilterBar.d.ts.map +1 -1
- package/types/report/reportViewModel.d.ts +1 -1
- package/types/report/reportViewModel.d.ts.map +1 -1
- package/src/list/filter-fields.spec.ts +0 -29
- package/src/list/listFields.spec.ts +0 -148
- package/src/list/listFormat.spec.ts +0 -151
- package/src/list/statusColors.spec.ts +0 -100
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// A Keycloak-signed-in reader hands the provider its token as a getter
|
|
2
|
+
// (MPO-399). Every request the list makes must carry that bearer: the bench
|
|
3
|
+
// answers a tokenless call as Guest (403), which is how the header count
|
|
4
|
+
// read "0 results" beside a full page of rows.
|
|
5
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
6
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { BackofficeProvider } from "../config/BackofficeProvider";
|
|
9
|
+
import { createMemoryNavigator } from "../navigation/navigator";
|
|
10
|
+
import { DoctypeListScreen } from "./DoctypeListScreen";
|
|
11
|
+
|
|
12
|
+
const doctype = "Pokemon";
|
|
13
|
+
const bearer = "Bearer kc-access-token";
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
name: doctype,
|
|
17
|
+
doctype: "DocType",
|
|
18
|
+
title_field: "pokemon_name",
|
|
19
|
+
fields: [
|
|
20
|
+
{ fieldname: "pokemon_name", fieldtype: "Data", label: "Pokemon Name", in_list_view: 1 },
|
|
21
|
+
],
|
|
22
|
+
permissions: [{ role: "Pokemon Reader", read: 1 }],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let run = 0;
|
|
26
|
+
let calls: { path: string; authorization: string | null }[] = [];
|
|
27
|
+
|
|
28
|
+
function json(body: unknown, status = 200) {
|
|
29
|
+
return new Response(JSON.stringify(body), {
|
|
30
|
+
status,
|
|
31
|
+
headers: { "content-type": "application/json" },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
run += 1;
|
|
37
|
+
calls = [];
|
|
38
|
+
vi.stubGlobal("fetch", async (input: string | URL | Request, init?: RequestInit) => {
|
|
39
|
+
const url = new URL(String(input instanceof Request ? input.url : input));
|
|
40
|
+
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
41
|
+
const authorization = headers.get("Authorization");
|
|
42
|
+
calls.push({ path: `${url.pathname}${url.search}`, authorization });
|
|
43
|
+
if (authorization !== bearer) {
|
|
44
|
+
return json({ exc_type: "PermissionError" }, 403);
|
|
45
|
+
}
|
|
46
|
+
const method = url.pathname.replace(/^\/api\/method\//, "");
|
|
47
|
+
if (method === "frappe.auth.get_logged_user") return json({ message: "ash@pokemon.test" });
|
|
48
|
+
if (method === "frappe.core.doctype.user.user.get_roles") {
|
|
49
|
+
return json({ message: ["Pokemon Reader"] });
|
|
50
|
+
}
|
|
51
|
+
if (method === "frappe.desk.form.load.getdoctype") return json({ docs: [meta] });
|
|
52
|
+
if (method === "frappe.client.get_count") return json({ message: 21 });
|
|
53
|
+
if (url.pathname === `/api/resource/${doctype}`) {
|
|
54
|
+
return json({ data: [{ name: "bulbasaur", doctype, pokemon_name: "Bulbasaur" }] });
|
|
55
|
+
}
|
|
56
|
+
return json({ data: [], message: [] });
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
vi.unstubAllGlobals();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("DoctypeListScreen auth", () => {
|
|
66
|
+
it("sends the provider's token getter on every call, the header count included", async () => {
|
|
67
|
+
renderWithProviders(
|
|
68
|
+
<BackofficeProvider
|
|
69
|
+
frappe={{
|
|
70
|
+
baseURL: `https://list-auth-${run}.spec.test`,
|
|
71
|
+
auth: { useToken: true, type: "Bearer", token: async () => "kc-access-token" },
|
|
72
|
+
}}
|
|
73
|
+
navigation={createMemoryNavigator("/backoffice")}
|
|
74
|
+
>
|
|
75
|
+
<DoctypeListScreen doctype={doctype} />
|
|
76
|
+
</BackofficeProvider>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
await waitFor(() => expect(screen.getByText("21 results")).toBeTruthy(), { timeout: 5000 });
|
|
80
|
+
const counts = calls.filter((call) => call.path.includes("frappe.client.get_count"));
|
|
81
|
+
expect(counts.length).toBeGreaterThan(0);
|
|
82
|
+
expect(calls.filter((call) => call.authorization !== bearer)).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -32,10 +32,14 @@ import {
|
|
|
32
32
|
} from "@phosphor-icons/react";
|
|
33
33
|
import {
|
|
34
34
|
FrappeTable,
|
|
35
|
+
buildListColumns,
|
|
35
36
|
docFieldsToFilterFields,
|
|
37
|
+
listFieldsToFetch,
|
|
36
38
|
sessionDocRights,
|
|
37
39
|
useFrappeAggregate,
|
|
38
40
|
useFrappeConfig,
|
|
41
|
+
type ListDoc,
|
|
42
|
+
type ListRow,
|
|
39
43
|
} from "@multiplatform.one/frappe-ui";
|
|
40
44
|
import type {
|
|
41
45
|
BulkAction,
|
|
@@ -86,8 +90,6 @@ import {
|
|
|
86
90
|
import { toggleFacetFilter } from "./sidebar-stats";
|
|
87
91
|
import { ListFilterBar } from "./ListFilterBar";
|
|
88
92
|
import { ListSidebar } from "./ListSidebar";
|
|
89
|
-
import { type ListDoc, type ListRow, buildListColumns } from "./listColumns";
|
|
90
|
-
import { listFieldsToFetch } from "./listFields";
|
|
91
93
|
import {
|
|
92
94
|
parseFiltersParam,
|
|
93
95
|
parseSortParam,
|
|
@@ -359,38 +361,11 @@ export function DoctypeListScreen({
|
|
|
359
361
|
effectiveSortRef.current = { field: sortField, order: sortOrder };
|
|
360
362
|
const currentFilters = effectiveFilters ?? withFixed(baseFilters);
|
|
361
363
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
socketPort: config.socketPort,
|
|
368
|
-
siteName: config.siteName,
|
|
369
|
-
socketBaseURL: config.socketBaseURL,
|
|
370
|
-
auth: config.auth
|
|
371
|
-
? {
|
|
372
|
-
useToken: config.auth.useToken,
|
|
373
|
-
token: typeof config.auth.token === "string" ? config.auth.token : undefined,
|
|
374
|
-
type: config.auth.type,
|
|
375
|
-
}
|
|
376
|
-
: undefined,
|
|
377
|
-
// Forward the provider's engine options: the SyncModule is
|
|
378
|
-
// shared per baseURL and the FIRST consumer's config wins — a
|
|
379
|
-
// connection built without syncOptions here would silently
|
|
380
|
-
// strip persistKey/cdcInterval for every other screen when the
|
|
381
|
-
// list's aggregate watcher happens to construct the engine
|
|
382
|
-
// first (observed on GNOME, where this screen is the first
|
|
383
|
-
// engine touch).
|
|
384
|
-
syncOptions: config.syncOptions,
|
|
385
|
-
socketOptions: config.socketOptions,
|
|
386
|
-
fixtures: config.fixtures,
|
|
387
|
-
}
|
|
388
|
-
: undefined,
|
|
389
|
-
[config],
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
// Live result count — realtime refetch on doctype events.
|
|
393
|
-
const count = useFrappeAggregate(connection, {
|
|
364
|
+
// Live result count — realtime refetch on doctype events. It takes the
|
|
365
|
+
// provider config itself, never a copy: the SyncModule is shared per socket
|
|
366
|
+
// target and the first consumer's config wins, so a field a copy drops
|
|
367
|
+
// (syncOptions, a token getter) is gone for every screen.
|
|
368
|
+
const count = useFrappeAggregate(config, {
|
|
394
369
|
doctype,
|
|
395
370
|
filters: currentFilters.length > 0 ? currentFilters : undefined,
|
|
396
371
|
});
|
|
@@ -453,23 +428,15 @@ export function DoctypeListScreen({
|
|
|
453
428
|
|
|
454
429
|
const fields = useMemo(() => listFieldsToFetch(meta, permMatrix), [meta, permMatrix]);
|
|
455
430
|
|
|
456
|
-
// Filterable-field declarations (meta fields
|
|
457
|
-
// columns). With the table's
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
id: "_assign",
|
|
466
|
-
label: t("list.assignedTo", "Assigned To"),
|
|
467
|
-
type: "text",
|
|
468
|
-
},
|
|
469
|
-
{ id: "_user_tags", label: t("list.tags", "Tags"), type: "text" },
|
|
470
|
-
{ id: "_liked_by", label: t("list.likedBy", "Liked By"), type: "text" },
|
|
471
|
-
];
|
|
472
|
-
}, [meta, t]);
|
|
431
|
+
// Filterable-field declarations from the one vocabulary (meta fields, the
|
|
432
|
+
// standard doc fields and the desk `_` facet columns). With the table's
|
|
433
|
+
// filter chrome suppressed these render no toolbar surface — they resolve
|
|
434
|
+
// filter TYPES for the injected tuples and keep FrappeTable from
|
|
435
|
+
// refetching meta to derive its own list.
|
|
436
|
+
const filterableFields = useMemo<FilterableField[] | undefined>(
|
|
437
|
+
() => (meta?.fields ? docFieldsToFilterFields(meta.fields) : undefined),
|
|
438
|
+
[meta],
|
|
439
|
+
);
|
|
473
440
|
|
|
474
441
|
const initialTableFilters = useMemo<TableFilter[]>(
|
|
475
442
|
() => frappeFiltersToTableFilters(withFixed(baseFilters)),
|
|
@@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";
|
|
|
10
10
|
import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
11
11
|
import type { FrappeFilters } from "../shared/types";
|
|
12
12
|
import type { DoctypeMeta } from "../shared/useDoctypeMeta";
|
|
13
|
-
import { filterTupleLabel, filterableFieldsFromMeta } from "
|
|
13
|
+
import { filterTupleLabel, filterableFieldsFromMeta } from "@multiplatform.one/frappe-ui";
|
|
14
14
|
|
|
15
15
|
export interface FilterChipsProps {
|
|
16
16
|
meta?: DoctypeMeta;
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
// House Button: the raw tamagui Button (components re-export) never gets the
|
|
17
17
|
// house 44px press floor (LC-54) — filter chrome takes the forms Button.
|
|
18
18
|
import { Button, Input, Select } from "@multiplatform.one/forms";
|
|
19
|
-
import { Link as LinkField } from "@multiplatform.one/frappe-ui";
|
|
20
19
|
import { FunnelIcon, PlusIcon, XIcon } from "@phosphor-icons/react";
|
|
21
20
|
import { useMemo, useRef, useState } from "react";
|
|
22
21
|
import { useTranslation } from "react-i18next";
|
|
@@ -28,13 +27,14 @@ import {
|
|
|
28
27
|
defaultOperatorForKind,
|
|
29
28
|
draftToTuple,
|
|
30
29
|
filterableFieldsFromMeta,
|
|
30
|
+
Link as LinkField,
|
|
31
31
|
operatorsForKind,
|
|
32
32
|
selectOptionsFromField,
|
|
33
33
|
tupleToDraft,
|
|
34
34
|
type DeskOperator,
|
|
35
35
|
type FilterFieldDescriptor,
|
|
36
36
|
type FilterRowDraft,
|
|
37
|
-
} from "
|
|
37
|
+
} from "@multiplatform.one/frappe-ui";
|
|
38
38
|
|
|
39
39
|
export interface FilterEditorButtonProps {
|
|
40
40
|
meta?: DoctypeMeta;
|
|
@@ -1,359 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* (
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* frappe
|
|
9
|
-
* bridged separately (filterConversion.ts) for the DataTable surface;
|
|
10
|
-
* desk's own operator list is the parity target here.
|
|
2
|
+
* The filterable-field vocabulary — moved to
|
|
3
|
+
* `@multiplatform.one/frappe-ui/src/filterFields` (MPO-15), where it is the
|
|
4
|
+
* one vocabulary both list stacks read: this desk chrome and FrappeTable's
|
|
5
|
+
* toolbar (`docFieldsToFilterFields`). Re-exported here so the barrel and
|
|
6
|
+
* the `@multiplatform.one/backoffice/src/list/filter-fields` deep import
|
|
7
|
+
* keep resolving (the GNOME shim deep-imports this path). New code imports
|
|
8
|
+
* from `@multiplatform.one/frappe-ui`.
|
|
11
9
|
*/
|
|
12
|
-
import type { FrappeFilter, FrappeFilters } from "../shared/types";
|
|
13
|
-
import type { DocFieldMeta, DoctypeMeta } from "../shared/useDoctypeMeta";
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"Image",
|
|
38
|
-
"Attach",
|
|
39
|
-
"Attach Image",
|
|
40
|
-
"Signature",
|
|
41
|
-
"Geolocation",
|
|
42
|
-
"Password",
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const NUMBER_FIELDTYPES = new Set(["Int", "Float", "Currency", "Percent", "Duration", "Rating"]);
|
|
46
|
-
const DATE_FIELDTYPES = new Set(["Date", "Datetime", "Time"]);
|
|
47
|
-
|
|
48
|
-
/** Editor kind for a frappe fieldtype (Dynamic Link has no static target → text). */
|
|
49
|
-
export function fieldKind(fieldtype: string): FilterFieldKind {
|
|
50
|
-
if (fieldtype === "Select") return "select";
|
|
51
|
-
if (fieldtype === "Link") return "link";
|
|
52
|
-
if (fieldtype === "Check") return "check";
|
|
53
|
-
if (DATE_FIELDTYPES.has(fieldtype)) return "date";
|
|
54
|
-
if (NUMBER_FIELDTYPES.has(fieldtype)) return "number";
|
|
55
|
-
return "text";
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function toDescriptor(field: DocFieldMeta): FilterFieldDescriptor {
|
|
59
|
-
return {
|
|
60
|
-
fieldname: field.fieldname,
|
|
61
|
-
label: field.label || field.fieldname,
|
|
62
|
-
fieldtype: field.fieldtype,
|
|
63
|
-
kind: fieldKind(field.fieldtype),
|
|
64
|
-
options: field.options,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Standard doc fields present on every doctype but absent from meta.fields. */
|
|
69
|
-
export function standardDocFilterFields(): FilterFieldDescriptor[] {
|
|
70
|
-
return [
|
|
71
|
-
{ fieldname: "name", label: "ID", fieldtype: "Data", kind: "text" },
|
|
72
|
-
{ fieldname: "owner", label: "Created By", fieldtype: "Link", kind: "link", options: "User" },
|
|
73
|
-
{ fieldname: "creation", label: "Created On", fieldtype: "Datetime", kind: "date" },
|
|
74
|
-
{ fieldname: "modified", label: "Last Updated On", fieldtype: "Datetime", kind: "date" },
|
|
75
|
-
{
|
|
76
|
-
fieldname: "modified_by",
|
|
77
|
-
label: "Last Updated By",
|
|
78
|
-
fieldtype: "Link",
|
|
79
|
-
kind: "link",
|
|
80
|
-
options: "User",
|
|
81
|
-
},
|
|
82
|
-
// Desk `_` facet columns — the sidebar facets and heart column write
|
|
83
|
-
// tuples on these, so chips and the editor must label them (desk offers
|
|
84
|
-
// them in its filter field picker too). Never surface the raw
|
|
85
|
-
// underscore fieldname (no-raw-fieldname rule candidate).
|
|
86
|
-
{ fieldname: "_assign", label: "Assigned To", fieldtype: "Data", kind: "text" },
|
|
87
|
-
{ fieldname: "_user_tags", label: "Tags", fieldtype: "Data", kind: "text" },
|
|
88
|
-
{ fieldname: "_liked_by", label: "Liked By", fieldtype: "Data", kind: "text" },
|
|
89
|
-
{ fieldname: "_comments", label: "Comments", fieldtype: "Data", kind: "text" },
|
|
90
|
-
];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* All fields offered by the filter editor's field picker — data fields from
|
|
95
|
-
* meta (hidden ones included, desk parity) plus the standard doc fields.
|
|
96
|
-
*/
|
|
97
|
-
export function filterableFieldsFromMeta(meta?: DoctypeMeta): FilterFieldDescriptor[] {
|
|
98
|
-
const out: FilterFieldDescriptor[] = [];
|
|
99
|
-
const seen = new Set<string>();
|
|
100
|
-
for (const field of meta?.fields ?? []) {
|
|
101
|
-
if (!field.fieldname || seen.has(field.fieldname)) continue;
|
|
102
|
-
const fieldtype = String(field.fieldtype);
|
|
103
|
-
if (NON_FILTERABLE_FIELDTYPES.has(fieldtype) || fieldtype.startsWith("Table")) continue;
|
|
104
|
-
seen.add(field.fieldname);
|
|
105
|
-
out.push(toDescriptor(field));
|
|
106
|
-
}
|
|
107
|
-
for (const standard of standardDocFilterFields()) {
|
|
108
|
-
if (!seen.has(standard.fieldname)) out.push(standard);
|
|
109
|
-
}
|
|
110
|
-
return out;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Fields flagged `in_standard_filter`, in meta order (the quick-filter row).
|
|
114
|
-
* Desk always offers ID plus the title field as live header inputs even when
|
|
115
|
-
* those flags are off — typing a fragment of ID/subject is the fastest
|
|
116
|
-
* narrowing habit list users have (audit E1). */
|
|
117
|
-
export function standardFiltersFromMeta(meta?: DoctypeMeta): FilterFieldDescriptor[] {
|
|
118
|
-
const fromMeta = (meta?.fields ?? [])
|
|
119
|
-
.filter((field) => field.in_standard_filter === 1 && field.hidden !== 1 && field.fieldname)
|
|
120
|
-
.map(toDescriptor);
|
|
121
|
-
const seen = new Set(fromMeta.map((field) => field.fieldname));
|
|
122
|
-
const extras: FilterFieldDescriptor[] = [];
|
|
123
|
-
if (!seen.has("name")) {
|
|
124
|
-
extras.push({ fieldname: "name", label: "ID", fieldtype: "Data", kind: "text" });
|
|
125
|
-
seen.add("name");
|
|
126
|
-
}
|
|
127
|
-
const titleField = meta?.title_field;
|
|
128
|
-
if (titleField && !seen.has(titleField)) {
|
|
129
|
-
const titleMeta = meta?.fields?.find((field) => field.fieldname === titleField);
|
|
130
|
-
extras.push({
|
|
131
|
-
fieldname: titleField,
|
|
132
|
-
label: titleMeta?.label || "Title",
|
|
133
|
-
fieldtype: titleMeta?.fieldtype || "Data",
|
|
134
|
-
kind: fieldKind(titleMeta?.fieldtype || "Data"),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
return extras.length > 0 ? [...extras, ...fromMeta] : fromMeta;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** Select fieldtype options string → dropdown options (blank lines dropped). */
|
|
141
|
-
export function selectOptionsFromField(options?: string): Array<{ label: string; value: string }> {
|
|
142
|
-
return (options ?? "")
|
|
143
|
-
.split("\n")
|
|
144
|
-
.map((line) => line.trim())
|
|
145
|
-
.filter(Boolean)
|
|
146
|
-
.map((line) => ({ label: line, value: line }));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Human label for a fieldname — descriptor label when known, else a
|
|
151
|
-
* title-cased form. The raw identifier (underscores) never reaches chips or
|
|
152
|
-
* editor rows (no-raw-fieldname rule candidate, parity audit pair 04/16).
|
|
153
|
-
*/
|
|
154
|
-
export function fieldLabelFor(fields: FilterFieldDescriptor[], fieldname: string): string {
|
|
155
|
-
const known = fields.find((field) => field.fieldname === fieldname)?.label;
|
|
156
|
-
if (known) return known;
|
|
157
|
-
const pretty = fieldname
|
|
158
|
-
.replace(/^_+/, "")
|
|
159
|
-
.split("_")
|
|
160
|
-
.filter(Boolean)
|
|
161
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
162
|
-
.join(" ");
|
|
163
|
-
return pretty || fieldname;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ── Desk operator vocabulary ──────────────────────────────────
|
|
167
|
-
|
|
168
|
-
export type DeskOperator =
|
|
169
|
-
| "="
|
|
170
|
-
| "!="
|
|
171
|
-
| "like"
|
|
172
|
-
| "not like"
|
|
173
|
-
| "in"
|
|
174
|
-
| "not in"
|
|
175
|
-
| ">"
|
|
176
|
-
| "<"
|
|
177
|
-
| ">="
|
|
178
|
-
| "<="
|
|
179
|
-
| "between"
|
|
180
|
-
| "is set"
|
|
181
|
-
| "is not set";
|
|
182
|
-
|
|
183
|
-
/** English operator labels — translate at render time via t(). */
|
|
184
|
-
export const DESK_OPERATOR_LABELS: Record<DeskOperator, string> = {
|
|
185
|
-
"=": "Equals",
|
|
186
|
-
"!=": "Not Equals",
|
|
187
|
-
like: "Like",
|
|
188
|
-
"not like": "Not Like",
|
|
189
|
-
in: "In",
|
|
190
|
-
"not in": "Not In",
|
|
191
|
-
">": "Greater Than",
|
|
192
|
-
"<": "Less Than",
|
|
193
|
-
">=": "Greater Than or Equal",
|
|
194
|
-
"<=": "Less Than or Equal",
|
|
195
|
-
between: "Between",
|
|
196
|
-
"is set": "Is Set",
|
|
197
|
-
"is not set": "Is Not Set",
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const BASE_OPERATORS: DeskOperator[] = [
|
|
201
|
-
"=",
|
|
202
|
-
"!=",
|
|
203
|
-
"like",
|
|
204
|
-
"not like",
|
|
205
|
-
"in",
|
|
206
|
-
"not in",
|
|
207
|
-
"is set",
|
|
208
|
-
"is not set",
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
/** Operators offered for an editor kind (desk's list, comparisons gated). */
|
|
212
|
-
export function operatorsForKind(kind: FilterFieldKind): DeskOperator[] {
|
|
213
|
-
switch (kind) {
|
|
214
|
-
case "check":
|
|
215
|
-
return ["=", "!="];
|
|
216
|
-
case "number":
|
|
217
|
-
case "date":
|
|
218
|
-
return ["=", "!=", ">", "<", ">=", "<=", "between", "in", "not in", "is set", "is not set"];
|
|
219
|
-
default:
|
|
220
|
-
return BASE_OPERATORS;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** Initial operator when a field is picked (desk: text searches, others match). */
|
|
225
|
-
export function defaultOperatorForKind(kind: FilterFieldKind): DeskOperator {
|
|
226
|
-
return kind === "text" ? "like" : "=";
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// ── Editor row model + tuple conversion ───────────────────────
|
|
230
|
-
|
|
231
|
-
export interface FilterRowDraft {
|
|
232
|
-
fieldname: string;
|
|
233
|
-
operator: DeskOperator;
|
|
234
|
-
/** Scalar editor value (single input / select / link / comma list). */
|
|
235
|
-
value: string;
|
|
236
|
-
/** Multi values: in / not in multi-select, or [from, to] for between. */
|
|
237
|
-
values: string[];
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Draft → frappe tuple; null while the draft is incomplete.
|
|
242
|
-
* `like` wraps `%value%` unless the user typed their own wildcard;
|
|
243
|
-
* `is set` / `is not set` become ["field", "is", "set" | "not set"].
|
|
244
|
-
*/
|
|
245
|
-
export function draftToTuple(draft: FilterRowDraft, kind: FilterFieldKind): FrappeFilter | null {
|
|
246
|
-
const { fieldname, operator } = draft;
|
|
247
|
-
if (!fieldname) return null;
|
|
248
|
-
switch (operator) {
|
|
249
|
-
case "is set":
|
|
250
|
-
return [fieldname, "is", "set"];
|
|
251
|
-
case "is not set":
|
|
252
|
-
return [fieldname, "is", "not set"];
|
|
253
|
-
case "in":
|
|
254
|
-
case "not in": {
|
|
255
|
-
const values =
|
|
256
|
-
kind === "select"
|
|
257
|
-
? draft.values.map((value) => value.trim()).filter(Boolean)
|
|
258
|
-
: draft.value
|
|
259
|
-
.split(",")
|
|
260
|
-
.map((value) => value.trim())
|
|
261
|
-
.filter(Boolean);
|
|
262
|
-
return values.length ? [fieldname, operator, values] : null;
|
|
263
|
-
}
|
|
264
|
-
case "between": {
|
|
265
|
-
const from = (draft.values[0] ?? "").trim();
|
|
266
|
-
const to = (draft.values[1] ?? "").trim();
|
|
267
|
-
return from && to ? [fieldname, "between", [from, to]] : null;
|
|
268
|
-
}
|
|
269
|
-
case "like":
|
|
270
|
-
case "not like": {
|
|
271
|
-
const text = draft.value.trim();
|
|
272
|
-
if (!text) return null;
|
|
273
|
-
return [fieldname, operator, text.includes("%") ? text : `%${text}%`];
|
|
274
|
-
}
|
|
275
|
-
default: {
|
|
276
|
-
const text = draft.value.trim();
|
|
277
|
-
return text ? [fieldname, operator, text] : null;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const SCALAR_OPERATORS: readonly string[] = ["=", "!=", "like", "not like", ">", "<", ">=", "<="];
|
|
283
|
-
|
|
284
|
-
/** Frappe tuple → editable draft (inverse of draftToTuple). */
|
|
285
|
-
export function tupleToDraft(tuple: FrappeFilter): FilterRowDraft {
|
|
286
|
-
const [fieldname, operator, raw] = tuple;
|
|
287
|
-
if (operator === "is") {
|
|
288
|
-
return {
|
|
289
|
-
fieldname,
|
|
290
|
-
operator: raw === "set" ? "is set" : "is not set",
|
|
291
|
-
value: "",
|
|
292
|
-
values: [],
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
if (operator === "in" || operator === "not in") {
|
|
296
|
-
const values = Array.isArray(raw)
|
|
297
|
-
? raw.map(String)
|
|
298
|
-
: String(raw ?? "")
|
|
299
|
-
.split(",")
|
|
300
|
-
.map((value) => value.trim())
|
|
301
|
-
.filter(Boolean);
|
|
302
|
-
return { fieldname, operator, value: values.join(", "), values };
|
|
303
|
-
}
|
|
304
|
-
if (operator === "between") {
|
|
305
|
-
const values = Array.isArray(raw) ? raw.map(String) : [];
|
|
306
|
-
return {
|
|
307
|
-
fieldname,
|
|
308
|
-
operator: "between",
|
|
309
|
-
value: "",
|
|
310
|
-
values: [values[0] ?? "", values[1] ?? ""],
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
const known = SCALAR_OPERATORS.includes(operator) ? (operator as DeskOperator) : "=";
|
|
314
|
-
return {
|
|
315
|
-
fieldname,
|
|
316
|
-
operator: known,
|
|
317
|
-
value: raw === null || raw === undefined ? "" : String(raw),
|
|
318
|
-
values: [],
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// ── Filter list helpers ───────────────────────────────────────
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Quick-filter semantics: a standard filter control owns every tuple for its
|
|
326
|
-
* fieldname — replace them with `tuple`, or clear when `tuple` is undefined.
|
|
327
|
-
*/
|
|
328
|
-
export function setFieldFilter(
|
|
329
|
-
filters: FrappeFilters,
|
|
330
|
-
fieldname: string,
|
|
331
|
-
tuple?: FrappeFilter,
|
|
332
|
-
): FrappeFilters {
|
|
333
|
-
const rest = filters.filter(([name]) => name !== fieldname);
|
|
334
|
-
return tuple ? [...rest, tuple] : rest;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/** The active quick-filter display value for a fieldname ("" when none). */
|
|
338
|
-
export function quickFilterValue(filters: FrappeFilters, fieldname: string): string {
|
|
339
|
-
const tuple = filters.find(([name]) => name === fieldname);
|
|
340
|
-
if (!tuple) return "";
|
|
341
|
-
const [, operator, raw] = tuple;
|
|
342
|
-
if (operator === "like") {
|
|
343
|
-
const text = String(raw ?? "");
|
|
344
|
-
return text.startsWith("%") && text.endsWith("%") && text.length >= 2
|
|
345
|
-
? text.slice(1, -1)
|
|
346
|
-
: text;
|
|
347
|
-
}
|
|
348
|
-
if (operator === "=") return raw === null || raw === undefined ? "" : String(raw);
|
|
349
|
-
return "";
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/** Chip label: "Status = Open", "Due Date between a, b", "Owner is set". */
|
|
353
|
-
export function filterTupleLabel(fields: FilterFieldDescriptor[], tuple: FrappeFilter): string {
|
|
354
|
-
const [fieldname, operator, raw] = tuple;
|
|
355
|
-
const label = fieldLabelFor(fields, fieldname);
|
|
356
|
-
if (operator === "is") return `${label} ${raw === "set" ? "is set" : "is not set"}`;
|
|
357
|
-
const value = Array.isArray(raw) ? raw.map(String).join(", ") : String(raw ?? "");
|
|
358
|
-
return `${label} ${operator} ${value}`;
|
|
359
|
-
}
|
|
11
|
+
export {
|
|
12
|
+
defaultOperatorForKind,
|
|
13
|
+
DESK_OPERATOR_LABELS,
|
|
14
|
+
draftToTuple,
|
|
15
|
+
fieldKind,
|
|
16
|
+
fieldLabelFor,
|
|
17
|
+
filterableFieldsFromMeta,
|
|
18
|
+
filterTupleLabel,
|
|
19
|
+
operatorsForKind,
|
|
20
|
+
quickFilterValue,
|
|
21
|
+
selectOptionsFromField,
|
|
22
|
+
setFieldFilter,
|
|
23
|
+
standardDocFilterFields,
|
|
24
|
+
standardFiltersFromMeta,
|
|
25
|
+
tupleToDraft,
|
|
26
|
+
} from "@multiplatform.one/frappe-ui";
|
|
27
|
+
export type {
|
|
28
|
+
DeskOperator,
|
|
29
|
+
FilterFieldDescriptor,
|
|
30
|
+
FilterFieldKind,
|
|
31
|
+
FilterRowDraft,
|
|
32
|
+
} from "@multiplatform.one/frappe-ui";
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { XStack, YStack } from "@multiplatform.one/components";
|
|
11
11
|
import { Checkbox, Input, Select } from "@multiplatform.one/forms";
|
|
12
|
-
import { Link as LinkField } from "@multiplatform.one/frappe-ui";
|
|
13
12
|
import { useEffect, useRef, useState } from "react";
|
|
14
13
|
import type { FrappeFilter, FrappeFilters } from "../shared/types";
|
|
15
14
|
import type { DoctypeMeta } from "../shared/useDoctypeMeta";
|
|
16
15
|
import {
|
|
16
|
+
Link as LinkField,
|
|
17
17
|
quickFilterValue,
|
|
18
18
|
selectOptionsFromField,
|
|
19
19
|
setFieldFilter,
|
|
20
20
|
standardFiltersFromMeta,
|
|
21
21
|
type FilterFieldDescriptor,
|
|
22
|
-
} from "
|
|
22
|
+
} from "@multiplatform.one/frappe-ui";
|
|
23
23
|
|
|
24
24
|
export interface StandardFiltersProps {
|
|
25
25
|
meta?: DoctypeMeta;
|
|
@@ -19,7 +19,7 @@ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
|
|
|
19
19
|
import type { FrappeFilters } from "../shared/types";
|
|
20
20
|
import type { DoctypeMeta } from "../shared/useDoctypeMeta";
|
|
21
21
|
import { FilterEditorPanel } from "./filter-editor";
|
|
22
|
-
import { standardFiltersFromMeta } from "
|
|
22
|
+
import { standardFiltersFromMeta } from "@multiplatform.one/frappe-ui";
|
|
23
23
|
import { SavedFiltersControl } from "./filter-saved";
|
|
24
24
|
import { SortControl } from "./filter-sort";
|
|
25
25
|
import { StandardFilters } from "./filter-standard";
|