@nocobase/portal-template-default 1.0.9 → 1.0.10
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/.env.example +5 -0
- package/.github/workflows/publish.yml +106 -0
- package/.gitignore +33 -0
- package/AGENTS.md +19 -0
- package/README.MD +61 -180
- package/package.json +3 -5
- package/scripts/build-html.mjs +12 -0
- package/src/App.css +2 -0
- package/src/App.tsx +35 -186
- package/src/app/development.tsx +154 -0
- package/src/app/extension.ts +5 -0
- package/src/app/extensions.tsx +18 -4
- package/src/app/routes.tsx +65 -0
- package/src/components/access-control/acl-bootstrap.tsx +11 -3
- package/src/components/access-control/acl-gate.tsx +7 -6
- package/src/components/access-control/can-access.tsx +17 -0
- package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
- package/src/components/app-shell/sidebar.tsx +21 -5
- package/src/components/auth/auto-redirect-provider.tsx +2 -1
- package/src/extensions/nocobase-acl/README.md +74 -0
- package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
- package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
- package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
- package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
- package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
- package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
- package/src/extensions/nocobase-acl/extension.tsx +73 -0
- package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
- package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
- package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
- package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
- package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
- package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
- package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
- package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
- package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
- package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
- package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
- package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
- package/src/extensions/nocobase-ai/extension.tsx +160 -0
- package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
- package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
- package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
- package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
- package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
- package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
- package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
- package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
- package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
- package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
- package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
- package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
- package/src/extensions/nocobase-users-example/extension.tsx +109 -0
- package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
- package/src/extensions/nocobase-users-example/layout.tsx +16 -0
- package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
- package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
- package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
- package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
- package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
- package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
- package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
- package/src/extensions/nocobase-users-example/show.tsx +266 -0
- package/src/lib/nocobase/acl/context.ts +32 -0
- package/src/lib/nocobase/acl/evaluator.ts +186 -0
- package/src/lib/nocobase/acl/hooks.ts +40 -0
- package/src/lib/nocobase/acl/index.ts +4 -1
- package/src/lib/nocobase/acl/menu.ts +6 -6
- package/src/lib/nocobase/acl/record-permissions.ts +37 -7
- package/src/lib/nocobase/acl/store-provider.tsx +14 -0
- package/src/lib/nocobase/acl/store.ts +92 -86
- package/src/lib/nocobase/acl/types.ts +52 -32
- package/src/lib/nocobase/auth-session.ts +128 -0
- package/src/lib/nocobase/client.ts +48 -50
- package/src/locales/en-US.ts +0 -78
- package/src/locales/index.ts +2 -7
- package/src/locales/zh-CN.ts +0 -74
- package/src/providers/access-control.ts +43 -8
- package/src/providers/auth-callback.ts +1 -0
- package/src/providers/auth.ts +24 -26
- package/src/providers/constants.ts +0 -4
- package/src/providers/data.ts +14 -26
- package/src/providers/runtime-config.ts +40 -0
- package/src/vite-env.d.ts +9 -0
- package/tests/auth-session-regression.mjs +101 -0
- package/tsconfig.json +2 -1
- package/vite.config.ts +9 -1
- package/dist/assets/CoordinateSystem-C8P-Cleg.js +0 -1
- package/dist/assets/LegendVisualProvider-Bdgkckq-.js +0 -1
- package/dist/assets/LineDraw-C0m1GONT.js +0 -1
- package/dist/assets/SymbolDraw-CCospe5G.js +0 -1
- package/dist/assets/VisualMapping-Cbv1v7Cd.js +0 -1
- package/dist/assets/ai-employee-shortcut-B253WWow.js +0 -1
- package/dist/assets/calendar-B3dXXyIv.js +0 -1
- package/dist/assets/chat-inline-xpQ9iq4p.js +0 -1
- package/dist/assets/chat-side-panel-layout-BVWsp5rr.js +0 -1
- package/dist/assets/components-ChhJaNRc.js +0 -20
- package/dist/assets/configuration-gate-Dwdq_lxf.js +0 -1
- package/dist/assets/createClipPathFromCoordSys-uEftn1Mq.js +0 -1
- package/dist/assets/createSeriesDataSimply-CI7l7Ufv.js +0 -1
- package/dist/assets/customGraphicKeyframeAnimation-DE3jSfMd.js +0 -3
- package/dist/assets/echarts-preview-C-cNUtJF.js +0 -1
- package/dist/assets/echarts-runtime-CEgJbv68.js +0 -58
- package/dist/assets/echarts-runtime-advanced-Buhm2BHz.js +0 -1
- package/dist/assets/echarts-runtime-common-DLAdBAIa.js +0 -1
- package/dist/assets/echarts-runtime-components-ypnyLkYx.js +0 -1
- package/dist/assets/echarts-runtime-hierarchy-ClStquCF.js +0 -1
- package/dist/assets/floating-BarTmn-b.js +0 -13
- package/dist/assets/index-BJIcYTdc.js +0 -53
- package/dist/assets/index-C6xNKxpg.js +0 -656
- package/dist/assets/index-D4JQi1TM.js +0 -54
- package/dist/assets/index-YA7-AUO8.css +0 -1
- package/dist/assets/index-g8zWVpP9.js +0 -23
- package/dist/assets/labelHelper-C137W5gU.js +0 -1
- package/dist/assets/nocobase-ai-chat-VTMaZFQB.svg +0 -50
- package/dist/assets/page-context-Cxx11gzg.js +0 -73
- package/dist/assets/prompt-card-BsYMW_rq.js +0 -1
- package/dist/assets/sectorHelper-Fqgz3uEk.js +0 -1
- package/dist/assets/shortcut-xtghDP63.js +0 -47
- package/dist/assets/static.browser-CfQQzERe.js +0 -30
- package/dist/assets/switch-DrxImI8F.js +0 -1
- package/dist/assets/tool-cards-BGfsY8JF.js +0 -41
- package/dist/favicon.ico +0 -0
- package/dist/index.html +0 -40
- package/dist/index.raw.html +0 -46
- package/dist/logo-dark.png +0 -0
- package/dist/logo-mark-dark.png +0 -0
- package/dist/logo-mark.png +0 -0
- package/dist/logo.png +0 -0
- package/registry/nocobase-acl/README.md +0 -19
- package/registry/nocobase-acl/extension.tsx +0 -71
- package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
- package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
- package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
- package/registry/nocobase-ai/extension.tsx +0 -158
- package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
- package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
- package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
- package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
- package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
- package/registry/nocobase-auth-cas/README.md +0 -6
- package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
- package/registry/nocobase-auth-cas/demo.tsx +0 -20
- package/registry/nocobase-auth-cas/extension.tsx +0 -46
- package/registry/nocobase-auth-cas/index.ts +0 -1
- package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
- package/registry/nocobase-auth-dingtalk/README.md +0 -7
- package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
- package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
- package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
- package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
- package/registry/nocobase-auth-dingtalk/index.ts +0 -4
- package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
- package/registry/nocobase-auth-ldap/README.md +0 -6
- package/registry/nocobase-auth-ldap/demo.tsx +0 -21
- package/registry/nocobase-auth-ldap/extension.tsx +0 -46
- package/registry/nocobase-auth-ldap/index.ts +0 -1
- package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
- package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
- package/registry/nocobase-auth-oidc/README.md +0 -11
- package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
- package/registry/nocobase-auth-oidc/demo.tsx +0 -20
- package/registry/nocobase-auth-oidc/extension.tsx +0 -51
- package/registry/nocobase-auth-oidc/index.ts +0 -1
- package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
- package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
- package/registry/nocobase-auth-saml/README.md +0 -6
- package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
- package/registry/nocobase-auth-saml/demo.tsx +0 -20
- package/registry/nocobase-auth-saml/extension.tsx +0 -51
- package/registry/nocobase-auth-saml/index.ts +0 -1
- package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
- package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
- package/registry/nocobase-auth-sms/README.md +0 -7
- package/registry/nocobase-auth-sms/demo.tsx +0 -25
- package/registry/nocobase-auth-sms/extension.tsx +0 -46
- package/registry/nocobase-auth-sms/index.ts +0 -1
- package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
- package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
- package/registry/nocobase-auth-wecom/README.md +0 -6
- package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
- package/registry/nocobase-auth-wecom/demo.tsx +0 -23
- package/registry/nocobase-auth-wecom/extension.tsx +0 -49
- package/registry/nocobase-auth-wecom/index.ts +0 -1
- package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
- package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
- package/registry/nocobase-i18n/README.md +0 -44
- package/registry/nocobase-i18n/components/index.ts +0 -1
- package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
- package/registry/nocobase-i18n/demo/index.tsx +0 -209
- package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
- package/registry/nocobase-i18n/extension.tsx +0 -45
- package/registry/nocobase-i18n/index.ts +0 -7
- package/registry/nocobase-i18n/locales/en-US.ts +0 -39
- package/registry/nocobase-i18n/locales/index.ts +0 -8
- package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
- package/registry/nocobase-i18n/provider.tsx +0 -44
- package/registry/nocobase-i18n/server-resources.ts +0 -166
- package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
- package/registry.config.json +0 -302
- package/scripts/registry.mjs +0 -164
- package/src/components/access-control/resource-access-guard.tsx +0 -21
- package/src/components/access-control/use-acl-runtime.ts +0 -28
- package/src/lib/nocobase/acl/action.ts +0 -147
- package/src/pages/users/layout.tsx +0 -15
- package/src/pages/users/show.tsx +0 -278
- /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
- /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
- /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
- /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
- /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
- /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
- /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
- /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
- /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
|
@@ -74,8 +74,11 @@ export function useChatWorkContext(activeConversationId: string) {
|
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
const clearWorkContext = useCallback(() => setDrafts({}), []);
|
|
77
|
+
// Shared EMPTY_CONTEXT rather than a fresh `[]` — see getConversationAttachments: the send path
|
|
78
|
+
// compares this by identity across an await, so a new array each call would read as "the user
|
|
79
|
+
// changed the work context" and silently abort every send.
|
|
77
80
|
const getConversationWorkContext = useCallback(
|
|
78
|
-
(conversationId: string) => draftsRef.current[conversationId] ??
|
|
81
|
+
(conversationId: string) => draftsRef.current[conversationId] ?? EMPTY_CONTEXT,
|
|
79
82
|
[]
|
|
80
83
|
);
|
|
81
84
|
const workContext = drafts[activeConversationId] ?? EMPTY_CONTEXT;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Route surfaces keep URL navigation separate from visual presentation. The same
|
|
4
4
|
business content can be hosted by a routed drawer, dialog, or full page.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
In development, the Registry also installs a lazy-loaded Demo route at
|
|
7
|
+
`/dev/route-surfaces` with
|
|
7
8
|
live drawer, dialog, child-page, nested-drawer, and mixed page/drawer/dialog
|
|
8
9
|
scenarios plus a Prompt generator.
|
|
9
10
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { ArrowLeft, ArrowRight, Layers3 } from "lucide-react";
|
|
2
|
-
import { useOutlet, useNavigate } from "react-router";
|
|
2
|
+
import { useLocation, useOutlet, useNavigate } from "react-router";
|
|
3
3
|
|
|
4
4
|
import { Badge } from "@/components/ui/badge";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Card,
|
|
8
|
+
CardContent,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "@/components/ui/card";
|
|
7
13
|
import {
|
|
8
14
|
RouteDialog,
|
|
9
15
|
RouteDrawer,
|
|
@@ -13,7 +19,16 @@ import {
|
|
|
13
19
|
import { RouteSurfacePromptGenerator } from "./prompt-generator";
|
|
14
20
|
import { routeSurfaceScenarios } from "./scenarios";
|
|
15
21
|
|
|
16
|
-
const
|
|
22
|
+
const routeSurfaceRoot = "/route-surfaces";
|
|
23
|
+
|
|
24
|
+
function useRouteSurfaceDemoBase() {
|
|
25
|
+
const { pathname } = useLocation();
|
|
26
|
+
const rootIndex = pathname.indexOf(routeSurfaceRoot);
|
|
27
|
+
|
|
28
|
+
return rootIndex >= 0
|
|
29
|
+
? pathname.slice(0, rootIndex + routeSurfaceRoot.length)
|
|
30
|
+
: routeSurfaceRoot;
|
|
31
|
+
}
|
|
17
32
|
|
|
18
33
|
export function RouteSurfacesDemoHome() {
|
|
19
34
|
const navigate = useNavigate();
|
|
@@ -29,8 +44,8 @@ export function RouteSurfacesDemoHome() {
|
|
|
29
44
|
</h1>
|
|
30
45
|
<p className="max-w-3xl text-muted-foreground">
|
|
31
46
|
Keep business content independent from whether it appears as a page,
|
|
32
|
-
drawer, dialog, or nested combination. Every preview below supports
|
|
33
|
-
direct URL and browser history.
|
|
47
|
+
drawer, dialog, or nested combination. Every preview below supports
|
|
48
|
+
a direct URL and browser history.
|
|
34
49
|
</p>
|
|
35
50
|
</div>
|
|
36
51
|
|
|
@@ -72,6 +87,7 @@ export function RouteSurfacesDemoHome() {
|
|
|
72
87
|
export function DemoDrawerRoute() {
|
|
73
88
|
const navigate = useNavigate();
|
|
74
89
|
const nested = useOutlet();
|
|
90
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
75
91
|
|
|
76
92
|
return (
|
|
77
93
|
<RouteDrawer
|
|
@@ -96,6 +112,8 @@ export function DemoDrawerRoute() {
|
|
|
96
112
|
}
|
|
97
113
|
|
|
98
114
|
export function DemoSecondDrawerRoute() {
|
|
115
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
116
|
+
|
|
99
117
|
return (
|
|
100
118
|
<RouteDrawer
|
|
101
119
|
title="Renewal activity"
|
|
@@ -113,6 +131,8 @@ export function DemoSecondDrawerRoute() {
|
|
|
113
131
|
}
|
|
114
132
|
|
|
115
133
|
export function DemoDialogRoute() {
|
|
134
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
135
|
+
|
|
116
136
|
return (
|
|
117
137
|
<RouteDialog
|
|
118
138
|
title="Approve renewal"
|
|
@@ -131,6 +151,7 @@ export function DemoDialogRoute() {
|
|
|
131
151
|
|
|
132
152
|
export function DemoPageRoute() {
|
|
133
153
|
const nested = useOutlet();
|
|
154
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
134
155
|
|
|
135
156
|
return (
|
|
136
157
|
<RoutePage closeTo={demoBase}>
|
|
@@ -153,7 +174,8 @@ function DemoPageContent({ nested }: { nested: React.ReactNode }) {
|
|
|
153
174
|
Customer workspace
|
|
154
175
|
</h1>
|
|
155
176
|
<p className="text-muted-foreground">
|
|
156
|
-
This route replaces the Demo home instead of rendering inside its
|
|
177
|
+
This route replaces the Demo home instead of rendering inside its
|
|
178
|
+
Outlet.
|
|
157
179
|
</p>
|
|
158
180
|
</div>
|
|
159
181
|
<Button variant="outline" onClick={() => void close()}>
|
|
@@ -169,7 +191,8 @@ function DemoPageContent({ nested }: { nested: React.ReactNode }) {
|
|
|
169
191
|
<CardTitle>Page-owned workflow</CardTitle>
|
|
170
192
|
</div>
|
|
171
193
|
<CardDescription>
|
|
172
|
-
A page can still host routed overlays without becoming coupled to
|
|
194
|
+
A page can still host routed overlays without becoming coupled to
|
|
195
|
+
their content.
|
|
173
196
|
</CardDescription>
|
|
174
197
|
</CardHeader>
|
|
175
198
|
<CardContent>
|
|
@@ -185,6 +208,7 @@ function DemoPageContent({ nested }: { nested: React.ReactNode }) {
|
|
|
185
208
|
export function DemoPageDrawerRoute() {
|
|
186
209
|
const navigate = useNavigate();
|
|
187
210
|
const nested = useOutlet();
|
|
211
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
188
212
|
|
|
189
213
|
return (
|
|
190
214
|
<RouteDrawer
|
|
@@ -209,6 +233,8 @@ export function DemoPageDrawerRoute() {
|
|
|
209
233
|
}
|
|
210
234
|
|
|
211
235
|
export function DemoPageDrawerDialogRoute() {
|
|
236
|
+
const demoBase = useRouteSurfaceDemoBase();
|
|
237
|
+
|
|
212
238
|
return (
|
|
213
239
|
<RouteDialog
|
|
214
240
|
title="Confirm follow-up"
|
|
@@ -19,40 +19,45 @@ export const routeSurfaceScenarios: RouteSurfaceScenario[] = [
|
|
|
19
19
|
id: "drawer",
|
|
20
20
|
number: "1",
|
|
21
21
|
title: "Drawer route",
|
|
22
|
-
description:
|
|
23
|
-
|
|
22
|
+
description:
|
|
23
|
+
"Keep the current page mounted and open one URL-backed drawer.",
|
|
24
|
+
path: "drawer",
|
|
24
25
|
routeShape: "Page → Drawer",
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
id: "dialog",
|
|
28
29
|
number: "2",
|
|
29
30
|
title: "Dialog route",
|
|
30
|
-
description:
|
|
31
|
-
|
|
31
|
+
description:
|
|
32
|
+
"Open focused content in a modal while preserving the page URL history.",
|
|
33
|
+
path: "dialog",
|
|
32
34
|
routeShape: "Page → Dialog",
|
|
33
35
|
},
|
|
34
36
|
{
|
|
35
37
|
id: "page",
|
|
36
38
|
number: "3",
|
|
37
39
|
title: "Child page",
|
|
38
|
-
description:
|
|
39
|
-
|
|
40
|
+
description:
|
|
41
|
+
"Replace the current page instead of rendering the child inside an overlay.",
|
|
42
|
+
path: "page",
|
|
40
43
|
routeShape: "Page → Child page",
|
|
41
44
|
},
|
|
42
45
|
{
|
|
43
46
|
id: "nested-drawers",
|
|
44
47
|
number: "4",
|
|
45
48
|
title: "Second-level drawer",
|
|
46
|
-
description:
|
|
47
|
-
|
|
49
|
+
description:
|
|
50
|
+
"Push the lower drawer outward and keep the top drawer at a stable size.",
|
|
51
|
+
path: "drawer/second",
|
|
48
52
|
routeShape: "Page → Drawer → Drawer",
|
|
49
53
|
},
|
|
50
54
|
{
|
|
51
55
|
id: "page-drawer-dialog",
|
|
52
56
|
number: "5",
|
|
53
57
|
title: "Page, drawer, and dialog",
|
|
54
|
-
description:
|
|
55
|
-
|
|
58
|
+
description:
|
|
59
|
+
"Compose different presentation types without coupling business content to them.",
|
|
60
|
+
path: "page/drawer/dialog",
|
|
56
61
|
routeShape: "Child page → Drawer → Dialog",
|
|
57
62
|
},
|
|
58
63
|
];
|
|
@@ -66,15 +71,16 @@ export function getRouteSurfacePrompt(
|
|
|
66
71
|
"Keep the current page mounted and open the target content with RouteDrawer. Closing the drawer returns to the page route.",
|
|
67
72
|
dialog:
|
|
68
73
|
"Keep the current page mounted and open the target content with RouteDialog. The dialog route must be directly addressable and dismissible through the shared close lifecycle.",
|
|
69
|
-
page:
|
|
70
|
-
"Render the target content as a real child page with RoutePage. Register it as a page route branch rather than placing it inside a drawer outlet.",
|
|
74
|
+
page: "Render the target content as a real child page with RoutePage. Register it as a page route branch rather than placing it inside a drawer outlet.",
|
|
71
75
|
"nested-drawers":
|
|
72
76
|
"Create a parent RouteDrawer with an Outlet and pass the rendered child outlet through its nested prop. The second RouteDrawer must push the lower drawer outward, keep equal widths, and close back to the parent route.",
|
|
73
77
|
"page-drawer-dialog":
|
|
74
78
|
"Render a RoutePage first, place a RouteDrawer route inside that page, then render a RouteDialog as the drawer's nested route. Closing each layer must return to the immediately lower URL.",
|
|
75
79
|
}[scenario.id];
|
|
76
80
|
|
|
77
|
-
return `Build ${
|
|
81
|
+
return `Build ${
|
|
82
|
+
target || "the requested workflow"
|
|
83
|
+
} with the installed NocoBase route surfaces.
|
|
78
84
|
|
|
79
85
|
Scenario:
|
|
80
86
|
- ${scenario.routeShape}
|
|
@@ -89,7 +95,7 @@ Implementation requirements:
|
|
|
89
95
|
- A full child page must be a page route branch, not an Outlet rendered inside a drawer.
|
|
90
96
|
- For editable forms, use useRefineUnsavedChangesGuard, pass its beforeClose callback to the surface, render its confirmation node, and use useRouteSurfaceClose for Cancel and successful submission.
|
|
91
97
|
- Direct URL entry, refresh, browser back/forward, Escape, the close button, and backdrop dismissal must all restore the correct layer stack.
|
|
92
|
-
- Apply
|
|
98
|
+
- Apply Refine CanAccess with an AccessDenied fallback to each business resource/action independently; do not rely on a parent list permission for unrelated child pages.
|
|
93
99
|
|
|
94
100
|
Deliver the complete route definitions and React components using the application's existing design system.`;
|
|
95
101
|
}
|
|
@@ -36,84 +36,87 @@ const DemoPageDrawerDialog = lazy(() =>
|
|
|
36
36
|
|
|
37
37
|
const routeSurfacesExtension: AppExtension = {
|
|
38
38
|
id: "nocobase-route-surfaces",
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
dev: {
|
|
40
|
+
resources: [
|
|
41
|
+
{
|
|
42
|
+
name: "route-surfaces",
|
|
43
|
+
list: "route-surfaces",
|
|
44
|
+
meta: {
|
|
45
|
+
label: "Route surfaces",
|
|
46
|
+
icon: <Layers3 />,
|
|
47
|
+
description:
|
|
48
|
+
"URL-backed drawer, dialog, page, and nested route patterns.",
|
|
49
|
+
acl: { type: "authenticated" },
|
|
50
|
+
},
|
|
48
51
|
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<>
|
|
53
|
-
<Route
|
|
54
|
-
key="route-surfaces-overlays"
|
|
55
|
-
path="/route-surfaces"
|
|
56
|
-
element={
|
|
57
|
-
<LazyRouteSurfaceDemo>
|
|
58
|
-
<DemoHome />
|
|
59
|
-
</LazyRouteSurfaceDemo>
|
|
60
|
-
}
|
|
61
|
-
>
|
|
52
|
+
],
|
|
53
|
+
routes: (
|
|
54
|
+
<>
|
|
62
55
|
<Route
|
|
63
|
-
|
|
56
|
+
key="route-surfaces-overlays"
|
|
57
|
+
path="route-surfaces"
|
|
64
58
|
element={
|
|
65
59
|
<LazyRouteSurfaceDemo>
|
|
66
|
-
<
|
|
60
|
+
<DemoHome />
|
|
67
61
|
</LazyRouteSurfaceDemo>
|
|
68
62
|
}
|
|
69
63
|
>
|
|
70
64
|
<Route
|
|
71
|
-
path="
|
|
65
|
+
path="drawer"
|
|
72
66
|
element={
|
|
73
67
|
<LazyRouteSurfaceDemo>
|
|
74
|
-
<
|
|
68
|
+
<DemoDrawer />
|
|
69
|
+
</LazyRouteSurfaceDemo>
|
|
70
|
+
}
|
|
71
|
+
>
|
|
72
|
+
<Route
|
|
73
|
+
path="second"
|
|
74
|
+
element={
|
|
75
|
+
<LazyRouteSurfaceDemo>
|
|
76
|
+
<DemoSecondDrawer />
|
|
77
|
+
</LazyRouteSurfaceDemo>
|
|
78
|
+
}
|
|
79
|
+
/>
|
|
80
|
+
</Route>
|
|
81
|
+
<Route
|
|
82
|
+
path="dialog"
|
|
83
|
+
element={
|
|
84
|
+
<LazyRouteSurfaceDemo>
|
|
85
|
+
<DemoDialog />
|
|
75
86
|
</LazyRouteSurfaceDemo>
|
|
76
87
|
}
|
|
77
88
|
/>
|
|
78
89
|
</Route>
|
|
79
90
|
<Route
|
|
80
|
-
|
|
91
|
+
key="route-surfaces-page"
|
|
92
|
+
path="route-surfaces/page"
|
|
81
93
|
element={
|
|
82
94
|
<LazyRouteSurfaceDemo>
|
|
83
|
-
<
|
|
84
|
-
</LazyRouteSurfaceDemo>
|
|
85
|
-
}
|
|
86
|
-
/>
|
|
87
|
-
</Route>
|
|
88
|
-
<Route
|
|
89
|
-
key="route-surfaces-page"
|
|
90
|
-
path="/route-surfaces/page"
|
|
91
|
-
element={
|
|
92
|
-
<LazyRouteSurfaceDemo>
|
|
93
|
-
<DemoPage />
|
|
94
|
-
</LazyRouteSurfaceDemo>
|
|
95
|
-
}
|
|
96
|
-
>
|
|
97
|
-
<Route
|
|
98
|
-
path="drawer"
|
|
99
|
-
element={
|
|
100
|
-
<LazyRouteSurfaceDemo>
|
|
101
|
-
<DemoPageDrawer />
|
|
95
|
+
<DemoPage />
|
|
102
96
|
</LazyRouteSurfaceDemo>
|
|
103
97
|
}
|
|
104
98
|
>
|
|
105
99
|
<Route
|
|
106
|
-
path="
|
|
100
|
+
path="drawer"
|
|
107
101
|
element={
|
|
108
102
|
<LazyRouteSurfaceDemo>
|
|
109
|
-
<
|
|
103
|
+
<DemoPageDrawer />
|
|
110
104
|
</LazyRouteSurfaceDemo>
|
|
111
105
|
}
|
|
112
|
-
|
|
106
|
+
>
|
|
107
|
+
<Route
|
|
108
|
+
path="dialog"
|
|
109
|
+
element={
|
|
110
|
+
<LazyRouteSurfaceDemo>
|
|
111
|
+
<DemoPageDrawerDialog />
|
|
112
|
+
</LazyRouteSurfaceDemo>
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
</Route>
|
|
113
116
|
</Route>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
</>
|
|
118
|
+
),
|
|
119
|
+
},
|
|
117
120
|
};
|
|
118
121
|
|
|
119
122
|
export default routeSurfacesExtension;
|
|
@@ -4,7 +4,7 @@ import { useMemo } from "react";
|
|
|
4
4
|
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { Form } from "@/components/ui/form";
|
|
7
|
-
import { useAIForm, type AIFormField } from "
|
|
7
|
+
import { useAIForm, type AIFormField } from "./optional-ai";
|
|
8
8
|
import {
|
|
9
9
|
RouteDrawer,
|
|
10
10
|
RouteDrawerFooter,
|
|
@@ -60,7 +60,7 @@ function UserCreateForm() {
|
|
|
60
60
|
action: "create",
|
|
61
61
|
redirect: false,
|
|
62
62
|
onMutationSuccess: () => {
|
|
63
|
-
|
|
63
|
+
close({ skipBeforeClose: true });
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
66
|
defaultValues: {
|
|
@@ -90,11 +90,11 @@ function UserCreateForm() {
|
|
|
90
90
|
onSubmit={form.handleSubmit((values) => onFinish(values))}
|
|
91
91
|
className="flex min-h-0 flex-1 flex-col"
|
|
92
92
|
>
|
|
93
|
-
<div className="
|
|
93
|
+
<div className="min-h-0 flex-1 space-y-6 overflow-y-auto px-5 py-5 [&_[data-slot=input]]:h-10 [&_[data-slot=select-trigger]]:h-10 [&_[data-slot=textarea]]:min-h-56">
|
|
94
94
|
<UserFormFields form={form} includePassword translate={translate} />
|
|
95
95
|
</div>
|
|
96
96
|
<RouteDrawerFooter className="flex-row justify-end">
|
|
97
|
-
<Button type="button" variant="outline" onClick={() =>
|
|
97
|
+
<Button type="button" variant="outline" onClick={() => close()}>
|
|
98
98
|
{translate("users.form.cancel", { ns: "app" }, "Cancel")}
|
|
99
99
|
</Button>
|
|
100
100
|
<Button
|
|
@@ -5,7 +5,7 @@ import { useParams } from "react-router";
|
|
|
5
5
|
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { Form } from "@/components/ui/form";
|
|
8
|
-
import { useAIForm, type AIFormField } from "
|
|
8
|
+
import { useAIForm, type AIFormField } from "./optional-ai";
|
|
9
9
|
import {
|
|
10
10
|
RouteDrawer,
|
|
11
11
|
RouteDrawerFooter,
|
|
@@ -35,11 +35,7 @@ export const UserEdit = ({
|
|
|
35
35
|
return (
|
|
36
36
|
<>
|
|
37
37
|
<RouteDrawer
|
|
38
|
-
title={translate(
|
|
39
|
-
"users.drawer.edit.title",
|
|
40
|
-
{ ns: "app" },
|
|
41
|
-
"Edit user"
|
|
42
|
-
)}
|
|
38
|
+
title={translate("users.drawer.edit.title", { ns: "app" }, "Edit user")}
|
|
43
39
|
description={translate(
|
|
44
40
|
"users.drawer.edit.description",
|
|
45
41
|
{ ns: "app" },
|
|
@@ -69,7 +65,7 @@ function UserEditForm({ id }: { id?: string }) {
|
|
|
69
65
|
id,
|
|
70
66
|
redirect: false,
|
|
71
67
|
onMutationSuccess: () => {
|
|
72
|
-
|
|
68
|
+
close({ skipBeforeClose: true });
|
|
73
69
|
},
|
|
74
70
|
},
|
|
75
71
|
});
|
|
@@ -92,11 +88,11 @@ function UserEditForm({ id }: { id?: string }) {
|
|
|
92
88
|
onSubmit={form.handleSubmit((values) => onFinish(values))}
|
|
93
89
|
className="flex min-h-0 flex-1 flex-col"
|
|
94
90
|
>
|
|
95
|
-
<div className="
|
|
91
|
+
<div className="min-h-0 flex-1 space-y-6 overflow-y-auto px-5 py-5 [&_[data-slot=input]]:h-10 [&_[data-slot=select-trigger]]:h-10 [&_[data-slot=textarea]]:min-h-56">
|
|
96
92
|
<UserFormFields form={form} translate={translate} />
|
|
97
93
|
</div>
|
|
98
94
|
<RouteDrawerFooter className="flex-row justify-end">
|
|
99
|
-
<Button type="button" variant="outline" onClick={() =>
|
|
95
|
+
<Button type="button" variant="outline" onClick={() => close()}>
|
|
100
96
|
{translate("users.form.cancel", { ns: "app" }, "Cancel")}
|
|
101
97
|
</Button>
|
|
102
98
|
<Button
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { CanAccess } from "@/components/access-control/can-access";
|
|
2
|
+
import { UsersRound } from "lucide-react";
|
|
3
|
+
import { Route } from "react-router";
|
|
4
|
+
|
|
5
|
+
import type { AppExtension } from "@/app/extension";
|
|
6
|
+
import { AccessDenied } from "@/components/access-control/access-denied";
|
|
7
|
+
import { UserCreate } from "./create";
|
|
8
|
+
import { UserEdit } from "./edit";
|
|
9
|
+
import { UserResourceLayout } from "./layout";
|
|
10
|
+
import { RoleDetailRoute } from "./role-detail";
|
|
11
|
+
import { userRoutes } from "./routes";
|
|
12
|
+
import { UserShow } from "./show";
|
|
13
|
+
import "./locales";
|
|
14
|
+
|
|
15
|
+
const usersExampleExtension: AppExtension = {
|
|
16
|
+
id: "nocobase-users-example",
|
|
17
|
+
priority: 0,
|
|
18
|
+
resources: [
|
|
19
|
+
{
|
|
20
|
+
name: "users",
|
|
21
|
+
list: userRoutes.list,
|
|
22
|
+
create: userRoutes.create,
|
|
23
|
+
edit: userRoutes.edit,
|
|
24
|
+
show: userRoutes.show,
|
|
25
|
+
meta: {
|
|
26
|
+
label: "Users",
|
|
27
|
+
priority: 1,
|
|
28
|
+
singularLabel: "User",
|
|
29
|
+
i18nKey: "resources.users",
|
|
30
|
+
i18nSingularKey: "resources.user",
|
|
31
|
+
i18nOptions: { ns: "app" },
|
|
32
|
+
descriptionI18nKey: "resources.users.description",
|
|
33
|
+
icon: <UsersRound />,
|
|
34
|
+
description:
|
|
35
|
+
"Manage the people who can sign in and work in this NocoBase application.",
|
|
36
|
+
canCreate: true,
|
|
37
|
+
canDelete: true,
|
|
38
|
+
acl: { type: "collection" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
routes: (
|
|
43
|
+
<Route path="/users" element={<UserResourceLayout />}>
|
|
44
|
+
<Route
|
|
45
|
+
path="create"
|
|
46
|
+
element={
|
|
47
|
+
<CanAccess
|
|
48
|
+
resource="users"
|
|
49
|
+
action="create"
|
|
50
|
+
fallback={<AccessDenied />}
|
|
51
|
+
>
|
|
52
|
+
<UserCreate />
|
|
53
|
+
</CanAccess>
|
|
54
|
+
}
|
|
55
|
+
/>
|
|
56
|
+
<Route
|
|
57
|
+
path="edit/:id"
|
|
58
|
+
element={
|
|
59
|
+
<CanAccess resource="users" action="edit" fallback={<AccessDenied />}>
|
|
60
|
+
<UserEdit />
|
|
61
|
+
</CanAccess>
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
<Route
|
|
65
|
+
path="roles/:roleName"
|
|
66
|
+
element={
|
|
67
|
+
<CanAccess resource="roles" action="show" fallback={<AccessDenied />}>
|
|
68
|
+
<RoleDetailRoute returnTo="list" />
|
|
69
|
+
</CanAccess>
|
|
70
|
+
}
|
|
71
|
+
/>
|
|
72
|
+
<Route
|
|
73
|
+
path="show/:id"
|
|
74
|
+
element={
|
|
75
|
+
<CanAccess resource="users" action="show" fallback={<AccessDenied />}>
|
|
76
|
+
<UserShow />
|
|
77
|
+
</CanAccess>
|
|
78
|
+
}
|
|
79
|
+
>
|
|
80
|
+
<Route
|
|
81
|
+
path="edit"
|
|
82
|
+
element={
|
|
83
|
+
<CanAccess
|
|
84
|
+
resource="users"
|
|
85
|
+
action="edit"
|
|
86
|
+
fallback={<AccessDenied />}
|
|
87
|
+
>
|
|
88
|
+
<UserEdit returnTo="show" />
|
|
89
|
+
</CanAccess>
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
<Route
|
|
93
|
+
path="roles/:roleName"
|
|
94
|
+
element={
|
|
95
|
+
<CanAccess
|
|
96
|
+
resource="roles"
|
|
97
|
+
action="show"
|
|
98
|
+
fallback={<AccessDenied />}
|
|
99
|
+
>
|
|
100
|
+
<RoleDetailRoute returnTo="show" />
|
|
101
|
+
</CanAccess>
|
|
102
|
+
}
|
|
103
|
+
/>
|
|
104
|
+
</Route>
|
|
105
|
+
</Route>
|
|
106
|
+
),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default usersExampleExtension;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { useTranslate } from "@refinedev/core";
|
|
2
|
-
import type { UseFormReturn } from "react-hook-form";
|
|
2
|
+
import type { Path, PathValue, UseFormReturn } from "react-hook-form";
|
|
3
3
|
|
|
4
|
-
import { applyReactHookFormValues } from "@/extensions/nocobase-ai/adapters/react-hook-form";
|
|
5
4
|
import type { UserFormValues } from "./types";
|
|
6
5
|
|
|
7
6
|
type Translate = ReturnType<typeof useTranslate>;
|
|
@@ -18,14 +17,17 @@ export function applyAIUserFormValues(
|
|
|
18
17
|
form: Pick<UseFormReturn<UserFormValues>, "setValue">,
|
|
19
18
|
values: Record<string, unknown>
|
|
20
19
|
) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Object.fromEntries(
|
|
24
|
-
Object.entries(values).filter(([name]) =>
|
|
25
|
-
AI_EDITABLE_FIELDS.includes(name as (typeof AI_EDITABLE_FIELDS)[number])
|
|
26
|
-
)
|
|
27
|
-
)
|
|
20
|
+
const editableValues = Object.entries(values).filter(([name]) =>
|
|
21
|
+
AI_EDITABLE_FIELDS.includes(name as (typeof AI_EDITABLE_FIELDS)[number])
|
|
28
22
|
);
|
|
23
|
+
for (const [name, value] of editableValues) {
|
|
24
|
+
const path = name as Path<UserFormValues>;
|
|
25
|
+
form.setValue(path, value as PathValue<UserFormValues, typeof path>, {
|
|
26
|
+
shouldDirty: true,
|
|
27
|
+
shouldTouch: true,
|
|
28
|
+
shouldValidate: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export function getAIUserFormFields(translate: Translate) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CanAccess } from "@/components/access-control/can-access";
|
|
2
|
+
import { Outlet } from "react-router";
|
|
3
|
+
|
|
4
|
+
import { AccessDenied } from "@/components/access-control/access-denied";
|
|
5
|
+
import { UserList } from "./list";
|
|
6
|
+
|
|
7
|
+
export function UserResourceLayout() {
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<CanAccess resource="users" action="list" fallback={<AccessDenied />}>
|
|
11
|
+
<UserList />
|
|
12
|
+
</CanAccess>
|
|
13
|
+
<Outlet />
|
|
14
|
+
</>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -15,7 +15,7 @@ import { DeleteButton } from "@/components/resources/buttons/delete";
|
|
|
15
15
|
import { EditButton } from "@/components/resources/buttons/edit";
|
|
16
16
|
import { ShowButton } from "@/components/resources/buttons/show";
|
|
17
17
|
import { ListView } from "@/components/resources/views/list-view";
|
|
18
|
-
import { useAIPageElementHandle } from "
|
|
18
|
+
import { useAIPageElementHandle } from "./optional-ai";
|
|
19
19
|
import type { Role } from "@/lib/nocobase/acl";
|
|
20
20
|
import { RoleBadges } from "./role-badges";
|
|
21
21
|
import { resolveRoleLabel } from "./role-utils";
|