@nocobase/portal-template-default 1.0.9 → 1.0.11
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/dist/assets/{LineDraw-C0m1GONT.js → LineDraw-iG-ADes-.js} +1 -1
- package/dist/assets/{SymbolDraw-CCospe5G.js → SymbolDraw-DXoSw1tM.js} +1 -1
- package/dist/assets/VisualMapping-C_ee4Eht.js +1 -0
- package/dist/assets/auth-method-demo-DaOCuvsq.js +1 -0
- package/dist/assets/auto-login-provider-5Uxza9Ms.js +2 -0
- package/dist/assets/auto-login-provider-DyaPGK2D.js +1 -0
- package/dist/assets/auto-redirect-provider-CfwRvYOI.js +1 -0
- package/dist/assets/auto-redirect-provider-Cr3jmYts.js +1 -0
- package/dist/assets/auto-redirect-provider-QkJQRBbG.js +1 -0
- package/dist/assets/calendar-DjDRfD5t.js +1 -0
- package/dist/assets/card-DuQBOS6A.js +1 -0
- package/dist/assets/cas-sign-in-button-CyqG1QME.js +1 -0
- package/dist/assets/components-D-ptYjX_.js +20 -0
- package/dist/assets/configuration-gate-C08mkkiX.js +1 -0
- package/dist/assets/{createClipPathFromCoordSys-uEftn1Mq.js → createClipPathFromCoordSys-wj34-b1B.js} +1 -1
- package/dist/assets/createSeriesDataSimply-D0A6SAVQ.js +1 -0
- package/dist/assets/customGraphicKeyframeAnimation-DWrhrHL_.js +3 -0
- package/dist/assets/demo-BYWHeKUe.js +1 -0
- package/dist/assets/demo-CPWoNtx8.js +1 -0
- package/dist/assets/demo-CyvJk5zV.js +1 -0
- package/dist/assets/demo-D4OiKplc.js +1 -0
- package/dist/assets/demo-DBueJg0m.js +1 -0
- package/dist/assets/demo-DkIhyiRK.js +1 -0
- package/dist/assets/demo-tpTjO0sC.js +1 -0
- package/dist/assets/dingtalk-sign-in-button-DSWsSruv.js +1 -0
- package/dist/assets/echarts-preview-DxHNfq1C.js +1 -0
- package/dist/assets/{echarts-runtime-CEgJbv68.js → echarts-runtime-D8kXn6ME.js} +3 -3
- package/dist/assets/{echarts-runtime-advanced-Buhm2BHz.js → echarts-runtime-advanced-QY8v95qK.js} +1 -1
- package/dist/assets/{echarts-runtime-common-DLAdBAIa.js → echarts-runtime-common-D-yqAK-r.js} +1 -1
- package/dist/assets/{echarts-runtime-components-ypnyLkYx.js → echarts-runtime-components-P4-i8jPU.js} +1 -1
- package/dist/assets/{echarts-runtime-hierarchy-ClStquCF.js → echarts-runtime-hierarchy-ByQZ-DbP.js} +1 -1
- package/dist/assets/floating-BFVEyPEp.js +15 -0
- package/dist/assets/index-BgCbGDMc.js +43 -0
- package/dist/assets/index-Btk35Nx-.js +23 -0
- package/dist/assets/index-CwNxd66J.js +1 -0
- package/dist/assets/index-MB-jfQld.js +686 -0
- package/dist/assets/index-NH3aqqQz.css +1 -0
- package/dist/assets/index-gFtCXH3o.js +58 -0
- package/dist/assets/index-um5hdKdZ.js +23 -0
- package/dist/assets/{labelHelper-C137W5gU.js → labelHelper-DQqRy3a_.js} +1 -1
- package/dist/assets/ldap-sign-in-form--6Gt3yGq.js +1 -0
- package/dist/assets/oidc-sign-in-button-CQdRcR_p.js +1 -0
- package/dist/assets/{page-context-Cxx11gzg.js → page-context-CnQ4GYM7.js} +10 -10
- package/dist/assets/prompt-card-B2RS4nt2.js +1 -0
- package/dist/assets/prompt-output-DZHRM2ft.js +1 -0
- package/dist/assets/saml-sign-in-button-BHL5a0JV.js +1 -0
- package/dist/assets/{sectorHelper-Fqgz3uEk.js → sectorHelper-DN_Vv6OL.js} +1 -1
- package/dist/assets/shortcut-C5x5LQWU.js +47 -0
- package/dist/assets/sms-sign-in-form-DTX9GV2I.js +1 -0
- package/dist/assets/{static.browser-CfQQzERe.js → static.browser-jI1psK0d.js} +8 -8
- package/dist/assets/{switch-DrxImI8F.js → switch-CRL8PmRI.js} +1 -1
- package/dist/assets/{tool-cards-BGfsY8JF.js → tool-cards-DFttLAfo.js} +6 -7
- package/dist/assets/use-authenticator-sign-in-CRGjzXju.js +1 -0
- package/dist/assets/use-dingtalk-sign-in-8WYzJ2jf.js +1 -0
- package/dist/assets/use-wecom-sign-in-C9j8wfCD.js +1 -0
- package/dist/assets/wecom-sign-in-button-mIS1zGSU.js +1 -0
- package/dist/index.html +2 -2
- package/dist/index.raw.html +5 -2
- 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/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/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/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/prompt-card-BsYMW_rq.js +0 -1
- package/dist/assets/shortcut-xtghDP63.js +0 -47
- 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
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
import { Badge } from "@/components/ui/badge";
|
|
14
14
|
import { Button } from "@/components/ui/button";
|
|
15
|
+
import { CanAccess } from "@/components/access-control/can-access";
|
|
15
16
|
import {
|
|
16
17
|
Card,
|
|
17
18
|
CardContent,
|
|
@@ -20,6 +21,9 @@ import {
|
|
|
20
21
|
CardTitle,
|
|
21
22
|
} from "@/components/ui/card";
|
|
22
23
|
import { Input } from "@/components/ui/input";
|
|
24
|
+
import type { RoleConstraint } from "@/lib/nocobase/acl";
|
|
25
|
+
import { AclField, AclPage, AclRegion } from "../components/acl-boundary";
|
|
26
|
+
import { AclPreviewProvider } from "./acl-preview-provider";
|
|
23
27
|
import { AclBoundaryApi } from "./boundary-api";
|
|
24
28
|
import { AclScenarioSection } from "./scenario-section";
|
|
25
29
|
|
|
@@ -50,6 +54,55 @@ const demoUsers = [
|
|
|
50
54
|
},
|
|
51
55
|
];
|
|
52
56
|
|
|
57
|
+
const usersRouteRoles = {
|
|
58
|
+
anyOf: ["admin"],
|
|
59
|
+
} satisfies RoleConstraint;
|
|
60
|
+
|
|
61
|
+
const allowedRegionPermissions = {
|
|
62
|
+
"users:list": {},
|
|
63
|
+
"departments:list": {},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const restrictedRegionPermissions = {
|
|
67
|
+
"users:list": {},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const allowedActionPermissions = {
|
|
71
|
+
"users:get": {},
|
|
72
|
+
"users:create": {},
|
|
73
|
+
"users:update": {},
|
|
74
|
+
"users:destroy": {},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const restrictedActionPermissions = {
|
|
78
|
+
"users:get": {},
|
|
79
|
+
"users:update": {},
|
|
80
|
+
"users:destroy": {},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const restrictedRecordPermissions = demoUsers.flatMap((user) => [
|
|
84
|
+
{
|
|
85
|
+
resource: "users",
|
|
86
|
+
action: "update",
|
|
87
|
+
id: user.id,
|
|
88
|
+
allowed: false,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
resource: "users",
|
|
92
|
+
action: "destroy",
|
|
93
|
+
id: user.id,
|
|
94
|
+
allowed: false,
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const editableFieldPermissions = {
|
|
99
|
+
"users:update": { fields: ["nickname", "email", "phone"] },
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const restrictedFieldPermissions = {
|
|
103
|
+
"users:update": { fields: ["nickname"] },
|
|
104
|
+
};
|
|
105
|
+
|
|
53
106
|
export function AclPatternsPage() {
|
|
54
107
|
return (
|
|
55
108
|
<div className="space-y-10 pb-12">
|
|
@@ -65,18 +118,19 @@ export function AclPatternsPage() {
|
|
|
65
118
|
|
|
66
119
|
<AclScenarioSection
|
|
67
120
|
eyebrow="Navigation permission"
|
|
68
|
-
title="
|
|
69
|
-
description="A
|
|
121
|
+
title="Role-restricted routes disappear from navigation"
|
|
122
|
+
description="A route outside the current effective role is removed from the sidebar and its direct URL is guarded. Other accessible navigation remains unchanged."
|
|
70
123
|
prompt={{
|
|
71
124
|
title: "Resource navigation",
|
|
72
125
|
description:
|
|
73
126
|
"Generate application resources, sidebar behavior, and guarded routes backed by NocoBase collections.",
|
|
74
127
|
defaultScene: "User and role administration",
|
|
75
128
|
defaultTarget:
|
|
76
|
-
"Allow
|
|
77
|
-
requirements: `-
|
|
129
|
+
"Allow administrators to open Users, but remove it from navigation and block its direct URL for other roles.",
|
|
130
|
+
requirements: `- Put role constraints on the route resource through meta.acl.roles using anyOf, allOf, or noneOf.
|
|
131
|
+
- Use meta.acl.type = "authenticated" when the route itself is role-controlled and its inner regions perform collection checks.
|
|
78
132
|
- Let the Starter filter inaccessible sidebar items and choose the first accessible default route.
|
|
79
|
-
- Wrap list, create, show, and edit routes with
|
|
133
|
+
- Wrap list, create, show, and edit routes with the Starter CanAccess and provide an AccessDenied fallback.
|
|
80
134
|
- Mark non-collection utility pages as authenticated, snippet, route, or acl: false instead of treating them as collections.
|
|
81
135
|
- If a parent route is denied but an accessible child remains, navigation must continue to the child route.`,
|
|
82
136
|
}}
|
|
@@ -84,8 +138,16 @@ export function AclPatternsPage() {
|
|
|
84
138
|
<OutcomeComparison
|
|
85
139
|
allowedTitle="Users permission granted"
|
|
86
140
|
deniedTitle="Users permission denied"
|
|
87
|
-
allowed={
|
|
88
|
-
|
|
141
|
+
allowed={
|
|
142
|
+
<AclPreviewProvider roles={["admin"]}>
|
|
143
|
+
<NavigationPreview />
|
|
144
|
+
</AclPreviewProvider>
|
|
145
|
+
}
|
|
146
|
+
denied={
|
|
147
|
+
<AclPreviewProvider roles={["member"]}>
|
|
148
|
+
<NavigationPreview />
|
|
149
|
+
</AclPreviewProvider>
|
|
150
|
+
}
|
|
89
151
|
/>
|
|
90
152
|
</AclScenarioSection>
|
|
91
153
|
|
|
@@ -99,9 +161,10 @@ export function AclPatternsPage() {
|
|
|
99
161
|
"Generate a complete page whose availability depends on NocoBase permissions.",
|
|
100
162
|
defaultScene: "User directory and account administration page",
|
|
101
163
|
defaultTarget:
|
|
102
|
-
"
|
|
164
|
+
"Allow administrators and user managers to render the complete page.",
|
|
103
165
|
requirements: `- Wrap the complete business page with AclPage.
|
|
104
|
-
- Use
|
|
166
|
+
- Use the AclPage roles prop with anyOf, allOf, or noneOf for page-level role access.
|
|
167
|
+
- Keep anyOf and allOf for resource permissions when the page also needs collection access.
|
|
105
168
|
- Provide a useful page-level fallback instead of rendering an empty screen.
|
|
106
169
|
- Keep each inner data region independently protected when the page combines multiple collections.`,
|
|
107
170
|
}}
|
|
@@ -109,34 +172,56 @@ export function AclPatternsPage() {
|
|
|
109
172
|
<OutcomeComparison
|
|
110
173
|
allowedTitle="Page allowed"
|
|
111
174
|
deniedTitle="Page denied"
|
|
112
|
-
allowed={
|
|
113
|
-
|
|
175
|
+
allowed={
|
|
176
|
+
<AclPreviewProvider roles={["user-manager"]}>
|
|
177
|
+
<PagePermissionPreview />
|
|
178
|
+
</AclPreviewProvider>
|
|
179
|
+
}
|
|
180
|
+
denied={
|
|
181
|
+
<AclPreviewProvider roles={["member"]}>
|
|
182
|
+
<PagePermissionPreview />
|
|
183
|
+
</AclPreviewProvider>
|
|
184
|
+
}
|
|
114
185
|
/>
|
|
115
186
|
</AclScenarioSection>
|
|
116
187
|
|
|
117
188
|
<AclScenarioSection
|
|
118
189
|
eyebrow="Region permission"
|
|
119
190
|
title="Only the protected region changes"
|
|
120
|
-
description="On a page with multiple
|
|
191
|
+
description="On a page with multiple resources, denying Departments hides only that panel and must not affect the Users table."
|
|
121
192
|
prompt={{
|
|
122
193
|
title: "Region permission",
|
|
123
194
|
description:
|
|
124
|
-
"Generate a multi-
|
|
125
|
-
defaultScene: "
|
|
195
|
+
"Generate a multi-resource page where each region follows its own collection ACL.",
|
|
196
|
+
defaultScene: "Organization management dashboard",
|
|
126
197
|
defaultTarget:
|
|
127
|
-
"Keep Users visible while
|
|
198
|
+
"Keep Users visible while hiding the denied Departments panel.",
|
|
128
199
|
requirements: `- Keep the outer page visible when its page-level permission is satisfied.
|
|
129
200
|
- Wrap every collection-backed panel in its own AclRegion.
|
|
130
201
|
- Enable each data query only when the corresponding list permission is allowed.
|
|
131
|
-
- Use fallback="hidden"
|
|
202
|
+
- Use fallback="hidden" so denied regions do not reveal unavailable resources or placeholders.
|
|
132
203
|
- Never use permission for one collection to hide unrelated tables or controls.`,
|
|
133
204
|
}}
|
|
134
205
|
>
|
|
135
206
|
<OutcomeComparison
|
|
136
207
|
allowedTitle="Both regions allowed"
|
|
137
|
-
deniedTitle="
|
|
138
|
-
allowed={
|
|
139
|
-
|
|
208
|
+
deniedTitle="Departments resource hidden"
|
|
209
|
+
allowed={
|
|
210
|
+
<AclPreviewProvider
|
|
211
|
+
roles={["member"]}
|
|
212
|
+
permissions={allowedRegionPermissions}
|
|
213
|
+
>
|
|
214
|
+
<RegionPreview />
|
|
215
|
+
</AclPreviewProvider>
|
|
216
|
+
}
|
|
217
|
+
denied={
|
|
218
|
+
<AclPreviewProvider
|
|
219
|
+
roles={["member"]}
|
|
220
|
+
permissions={restrictedRegionPermissions}
|
|
221
|
+
>
|
|
222
|
+
<RegionPreview />
|
|
223
|
+
</AclPreviewProvider>
|
|
224
|
+
}
|
|
140
225
|
/>
|
|
141
226
|
</AclScenarioSection>
|
|
142
227
|
|
|
@@ -161,8 +246,23 @@ export function AclPatternsPage() {
|
|
|
161
246
|
<OutcomeComparison
|
|
162
247
|
allowedTitle="Create and row actions allowed"
|
|
163
248
|
deniedTitle="Create, Update, and Delete denied"
|
|
164
|
-
allowed={
|
|
165
|
-
|
|
249
|
+
allowed={
|
|
250
|
+
<AclPreviewProvider
|
|
251
|
+
roles={["admin"]}
|
|
252
|
+
permissions={allowedActionPermissions}
|
|
253
|
+
>
|
|
254
|
+
<ActionsPreview />
|
|
255
|
+
</AclPreviewProvider>
|
|
256
|
+
}
|
|
257
|
+
denied={
|
|
258
|
+
<AclPreviewProvider
|
|
259
|
+
roles={["viewer"]}
|
|
260
|
+
permissions={restrictedActionPermissions}
|
|
261
|
+
recordPermissions={restrictedRecordPermissions}
|
|
262
|
+
>
|
|
263
|
+
<ActionsPreview />
|
|
264
|
+
</AclPreviewProvider>
|
|
265
|
+
}
|
|
166
266
|
/>
|
|
167
267
|
</AclScenarioSection>
|
|
168
268
|
|
|
@@ -187,8 +287,22 @@ export function AclPatternsPage() {
|
|
|
187
287
|
<OutcomeComparison
|
|
188
288
|
allowedTitle="All fields editable"
|
|
189
289
|
deniedTitle="Email read-only and phone hidden"
|
|
190
|
-
allowed={
|
|
191
|
-
|
|
290
|
+
allowed={
|
|
291
|
+
<AclPreviewProvider
|
|
292
|
+
roles={["admin"]}
|
|
293
|
+
permissions={editableFieldPermissions}
|
|
294
|
+
>
|
|
295
|
+
<ProfileFormPreview />
|
|
296
|
+
</AclPreviewProvider>
|
|
297
|
+
}
|
|
298
|
+
denied={
|
|
299
|
+
<AclPreviewProvider
|
|
300
|
+
roles={["member"]}
|
|
301
|
+
permissions={restrictedFieldPermissions}
|
|
302
|
+
>
|
|
303
|
+
<ProfileFormPreview />
|
|
304
|
+
</AclPreviewProvider>
|
|
305
|
+
}
|
|
192
306
|
/>
|
|
193
307
|
</AclScenarioSection>
|
|
194
308
|
|
|
@@ -228,7 +342,7 @@ function OutcomeComparison({
|
|
|
228
342
|
);
|
|
229
343
|
}
|
|
230
344
|
|
|
231
|
-
function NavigationPreview(
|
|
345
|
+
function NavigationPreview() {
|
|
232
346
|
return (
|
|
233
347
|
<div className="grid min-h-48 grid-cols-[150px_minmax(0,1fr)] overflow-hidden rounded-xl border bg-background">
|
|
234
348
|
<aside className="border-r bg-muted/35 p-3">
|
|
@@ -236,13 +350,24 @@ function NavigationPreview({ showUsers }: { showUsers: boolean }) {
|
|
|
236
350
|
Workspace
|
|
237
351
|
</div>
|
|
238
352
|
<NavItem icon={<LayoutPanelTop />} label="Dashboard" />
|
|
239
|
-
{
|
|
353
|
+
<CanAccess roles={usersRouteRoles}>
|
|
240
354
|
<NavItem icon={<UsersRound />} label="Users" active />
|
|
241
|
-
|
|
355
|
+
</CanAccess>
|
|
242
356
|
<NavItem icon={<UserRoundCog />} label="Roles" />
|
|
243
357
|
</aside>
|
|
244
358
|
<div className="flex items-center justify-center p-5 text-center">
|
|
245
|
-
|
|
359
|
+
<CanAccess
|
|
360
|
+
roles={usersRouteRoles}
|
|
361
|
+
fallback={
|
|
362
|
+
<div>
|
|
363
|
+
<LayoutPanelTop className="mx-auto size-7 text-muted-foreground" />
|
|
364
|
+
<div className="mt-2 font-medium">Dashboard</div>
|
|
365
|
+
<div className="mt-1 max-w-48 text-xs text-muted-foreground">
|
|
366
|
+
Users is absent from navigation and its direct route is guarded.
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
}
|
|
370
|
+
>
|
|
246
371
|
<div>
|
|
247
372
|
<UsersRound className="mx-auto size-7 text-primary" />
|
|
248
373
|
<div className="mt-2 font-medium">Users</div>
|
|
@@ -250,15 +375,7 @@ function NavigationPreview({ showUsers }: { showUsers: boolean }) {
|
|
|
250
375
|
Route is available
|
|
251
376
|
</div>
|
|
252
377
|
</div>
|
|
253
|
-
|
|
254
|
-
<div>
|
|
255
|
-
<LayoutPanelTop className="mx-auto size-7 text-muted-foreground" />
|
|
256
|
-
<div className="mt-2 font-medium">Dashboard</div>
|
|
257
|
-
<div className="mt-1 max-w-48 text-xs text-muted-foreground">
|
|
258
|
-
Users is absent from navigation and its direct route is guarded.
|
|
259
|
-
</div>
|
|
260
|
-
</div>
|
|
261
|
-
)}
|
|
378
|
+
</CanAccess>
|
|
262
379
|
</div>
|
|
263
380
|
</div>
|
|
264
381
|
);
|
|
@@ -285,6 +402,17 @@ function NavItem({
|
|
|
285
402
|
);
|
|
286
403
|
}
|
|
287
404
|
|
|
405
|
+
function PagePermissionPreview() {
|
|
406
|
+
return (
|
|
407
|
+
<AclPage
|
|
408
|
+
roles={{ anyOf: ["admin", "user-manager"] }}
|
|
409
|
+
fallback={<PageDeniedPreview />}
|
|
410
|
+
>
|
|
411
|
+
<UserDirectoryPreview />
|
|
412
|
+
</AclPage>
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
288
416
|
function UserDirectoryPreview() {
|
|
289
417
|
return (
|
|
290
418
|
<Card>
|
|
@@ -317,39 +445,35 @@ function PageDeniedPreview() {
|
|
|
317
445
|
);
|
|
318
446
|
}
|
|
319
447
|
|
|
320
|
-
function RegionPreview(
|
|
448
|
+
function RegionPreview() {
|
|
321
449
|
return (
|
|
322
|
-
<div
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
? "md:grid-cols-[minmax(0,1.3fr)_minmax(180px,0.7fr)]"
|
|
326
|
-
: "grid-cols-1"
|
|
327
|
-
}`}
|
|
328
|
-
>
|
|
329
|
-
<Card size="sm">
|
|
330
|
-
<CardHeader>
|
|
331
|
-
<CardTitle>Users</CardTitle>
|
|
332
|
-
</CardHeader>
|
|
333
|
-
<CardContent>
|
|
334
|
-
<CompactUsersTable limit={2} />
|
|
335
|
-
</CardContent>
|
|
336
|
-
</Card>
|
|
337
|
-
{showRoles ? (
|
|
338
|
-
<Card size="sm">
|
|
450
|
+
<div className="flex flex-col gap-3 rounded-xl border bg-background p-3 md:flex-row">
|
|
451
|
+
<AclRegion resource="users" action="list">
|
|
452
|
+
<Card size="sm" className="min-w-0 flex-1">
|
|
339
453
|
<CardHeader>
|
|
340
|
-
<CardTitle>
|
|
454
|
+
<CardTitle>Users</CardTitle>
|
|
455
|
+
</CardHeader>
|
|
456
|
+
<CardContent>
|
|
457
|
+
<CompactUsersTable limit={2} />
|
|
458
|
+
</CardContent>
|
|
459
|
+
</Card>
|
|
460
|
+
</AclRegion>
|
|
461
|
+
<AclRegion resource="departments" action="list">
|
|
462
|
+
<Card size="sm" className="md:w-56">
|
|
463
|
+
<CardHeader>
|
|
464
|
+
<CardTitle>Departments</CardTitle>
|
|
341
465
|
</CardHeader>
|
|
342
466
|
<CardContent className="space-y-2">
|
|
343
|
-
<
|
|
344
|
-
<
|
|
467
|
+
<ResourceRow title="Engineering" name="engineering" />
|
|
468
|
+
<ResourceRow title="Operations" name="operations" />
|
|
345
469
|
</CardContent>
|
|
346
470
|
</Card>
|
|
347
|
-
|
|
471
|
+
</AclRegion>
|
|
348
472
|
</div>
|
|
349
473
|
);
|
|
350
474
|
}
|
|
351
475
|
|
|
352
|
-
function ActionsPreview(
|
|
476
|
+
function ActionsPreview() {
|
|
353
477
|
return (
|
|
354
478
|
<Card>
|
|
355
479
|
<CardHeader className="flex-row items-center justify-between gap-3">
|
|
@@ -357,11 +481,11 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
|
|
|
357
481
|
<CardTitle>Users</CardTitle>
|
|
358
482
|
<CardDescription>Record actions</CardDescription>
|
|
359
483
|
</div>
|
|
360
|
-
|
|
484
|
+
<CanAccess resource="users" action="create">
|
|
361
485
|
<Button size="sm">
|
|
362
486
|
<Plus /> New user
|
|
363
487
|
</Button>
|
|
364
|
-
|
|
488
|
+
</CanAccess>
|
|
365
489
|
</CardHeader>
|
|
366
490
|
<CardContent className="overflow-x-auto">
|
|
367
491
|
<table className="w-full min-w-[420px] text-sm">
|
|
@@ -379,31 +503,33 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
|
|
|
379
503
|
<td className="py-3 text-muted-foreground">{user.role}</td>
|
|
380
504
|
<td className="py-3">
|
|
381
505
|
<div className="flex justify-end gap-1">
|
|
382
|
-
<
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
506
|
+
<CanAccess resource="users" action="show" id={user.id}>
|
|
507
|
+
<Button
|
|
508
|
+
variant="ghost"
|
|
509
|
+
size="icon-sm"
|
|
510
|
+
aria-label="View user"
|
|
511
|
+
>
|
|
512
|
+
<Eye />
|
|
513
|
+
</Button>
|
|
514
|
+
</CanAccess>
|
|
515
|
+
<CanAccess resource="users" action="edit" id={user.id}>
|
|
516
|
+
<Button
|
|
517
|
+
variant="ghost"
|
|
518
|
+
size="icon-sm"
|
|
519
|
+
aria-label="Edit user"
|
|
520
|
+
>
|
|
521
|
+
<Pencil />
|
|
522
|
+
</Button>
|
|
523
|
+
</CanAccess>
|
|
524
|
+
<CanAccess resource="users" action="delete" id={user.id}>
|
|
525
|
+
<Button
|
|
526
|
+
variant="ghost"
|
|
527
|
+
size="icon-sm"
|
|
528
|
+
aria-label="Delete user"
|
|
529
|
+
>
|
|
530
|
+
<Trash2 />
|
|
531
|
+
</Button>
|
|
532
|
+
</CanAccess>
|
|
407
533
|
</div>
|
|
408
534
|
</td>
|
|
409
535
|
</tr>
|
|
@@ -415,8 +541,9 @@ function ActionsPreview({ allowed }: { allowed: boolean }) {
|
|
|
415
541
|
);
|
|
416
542
|
}
|
|
417
543
|
|
|
418
|
-
function ProfileFormPreview(
|
|
544
|
+
function ProfileFormPreview() {
|
|
419
545
|
const user = demoUsers[1];
|
|
546
|
+
|
|
420
547
|
return (
|
|
421
548
|
<Card>
|
|
422
549
|
<CardHeader>
|
|
@@ -424,27 +551,38 @@ function ProfileFormPreview({ restricted }: { restricted: boolean }) {
|
|
|
424
551
|
<CardDescription>{user.name}</CardDescription>
|
|
425
552
|
</CardHeader>
|
|
426
553
|
<CardContent className="space-y-4">
|
|
427
|
-
<
|
|
428
|
-
<
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
554
|
+
<AclField resource="users" action="edit" field="nickname">
|
|
555
|
+
<DemoField label="Nickname">
|
|
556
|
+
<Input defaultValue={user.name} />
|
|
557
|
+
</DemoField>
|
|
558
|
+
</AclField>
|
|
559
|
+
<AclField
|
|
560
|
+
resource="users"
|
|
561
|
+
action="edit"
|
|
562
|
+
field="email"
|
|
563
|
+
fallback={
|
|
564
|
+
<DemoField label="Email">
|
|
565
|
+
<div className="rounded-md border bg-muted/45 px-3 py-2 text-sm text-muted-foreground">
|
|
566
|
+
{user.email}
|
|
567
|
+
<Badge className="ml-2" variant="outline">
|
|
568
|
+
Read only
|
|
569
|
+
</Badge>
|
|
570
|
+
</div>
|
|
571
|
+
</DemoField>
|
|
572
|
+
}
|
|
573
|
+
>
|
|
574
|
+
<DemoField label="Email">
|
|
439
575
|
<Input defaultValue={user.email} />
|
|
440
|
-
|
|
441
|
-
</
|
|
442
|
-
|
|
576
|
+
</DemoField>
|
|
577
|
+
</AclField>
|
|
578
|
+
<AclField resource="users" action="edit" field="phone">
|
|
443
579
|
<DemoField label="Phone">
|
|
444
580
|
<Input defaultValue={user.phone} />
|
|
445
581
|
</DemoField>
|
|
446
|
-
|
|
447
|
-
<
|
|
582
|
+
</AclField>
|
|
583
|
+
<CanAccess resource="users" action="edit">
|
|
584
|
+
<Button size="sm">Save changes</Button>
|
|
585
|
+
</CanAccess>
|
|
448
586
|
</CardContent>
|
|
449
587
|
</Card>
|
|
450
588
|
);
|
|
@@ -493,7 +631,7 @@ function CompactUsersTable({ limit = 3 }: { limit?: number }) {
|
|
|
493
631
|
);
|
|
494
632
|
}
|
|
495
633
|
|
|
496
|
-
function
|
|
634
|
+
function ResourceRow({ title, name }: { title: string; name: string }) {
|
|
497
635
|
return (
|
|
498
636
|
<div className="rounded-lg border px-3 py-2">
|
|
499
637
|
<div className="text-sm font-medium">{title}</div>
|
|
@@ -43,7 +43,9 @@ ${config.requirements}
|
|
|
43
43
|
|
|
44
44
|
Implementation contract
|
|
45
45
|
- Use the Starter's built-in NocoBase accessControlProvider and ACL store.
|
|
46
|
-
- NocoBase roles:check is the source of truth. Do not create a second permission store
|
|
46
|
+
- NocoBase roles:check is the source of truth. Do not create a second permission store. Use configured role names only when the requirement explicitly includes a role constraint.
|
|
47
|
+
- Import the Starter CanAccess from @/components/access-control/can-access. Use it with AclPage, AclRegion, and AclField for permission presentation. Do not copy their checks into local booleans or role comparisons.
|
|
48
|
+
- Use useGetRoles only when UI needs to display or pass through the current effective ACL roles; it is not a replacement permission evaluator.
|
|
47
49
|
- Keep backend ACL enforcement in place; frontend checks only control presentation and navigation.
|
|
48
50
|
- Import reusable components from the installed local entry point at @/extensions/nocobase-acl when page composition is needed.
|
|
49
51
|
- Resource actions use the application names list, show, create, edit, and delete. Let the Starter map them to NocoBase list/get/create/update/destroy.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AppExtension } from "@/app/extensions";
|
|
2
|
+
import { Blocks, PanelsTopLeft, ShieldCheck } from "lucide-react";
|
|
3
|
+
import { lazy } from "react";
|
|
4
|
+
import { Outlet, Route } from "react-router";
|
|
5
|
+
import { LazyAclRoute } from "./demo/lazy-route";
|
|
6
|
+
|
|
7
|
+
const AclComponentsPage = lazy(() =>
|
|
8
|
+
import("./demo/components").then((module) => ({
|
|
9
|
+
default: module.AclComponentsPage,
|
|
10
|
+
}))
|
|
11
|
+
);
|
|
12
|
+
const AclPatternsPage = lazy(() =>
|
|
13
|
+
import("./demo").then((module) => ({ default: module.AclPatternsPage }))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const nocobaseAclExtension: AppExtension = {
|
|
17
|
+
id: "nocobase-acl",
|
|
18
|
+
dev: {
|
|
19
|
+
resources: [
|
|
20
|
+
{
|
|
21
|
+
name: "acl-integration",
|
|
22
|
+
meta: {
|
|
23
|
+
label: "Access control",
|
|
24
|
+
icon: <ShieldCheck />,
|
|
25
|
+
description: "NocoBase ACL integration for admin applications.",
|
|
26
|
+
acl: { type: "authenticated" },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "acl-components",
|
|
31
|
+
list: "acl",
|
|
32
|
+
meta: {
|
|
33
|
+
parent: "acl-integration",
|
|
34
|
+
label: "Role switcher",
|
|
35
|
+
icon: <Blocks />,
|
|
36
|
+
acl: { type: "authenticated" },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "acl-patterns",
|
|
41
|
+
list: "acl/patterns",
|
|
42
|
+
meta: {
|
|
43
|
+
parent: "acl-integration",
|
|
44
|
+
label: "Permission patterns",
|
|
45
|
+
icon: <PanelsTopLeft />,
|
|
46
|
+
acl: { type: "authenticated" },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
routes: (
|
|
51
|
+
<Route key="nocobase-acl" path="acl" element={<Outlet />}>
|
|
52
|
+
<Route
|
|
53
|
+
index
|
|
54
|
+
element={
|
|
55
|
+
<LazyAclRoute>
|
|
56
|
+
<AclComponentsPage />
|
|
57
|
+
</LazyAclRoute>
|
|
58
|
+
}
|
|
59
|
+
/>
|
|
60
|
+
<Route
|
|
61
|
+
path="patterns"
|
|
62
|
+
element={
|
|
63
|
+
<LazyAclRoute>
|
|
64
|
+
<AclPatternsPage />
|
|
65
|
+
</LazyAclRoute>
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
68
|
+
</Route>
|
|
69
|
+
),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default nocobaseAclExtension;
|
|
@@ -61,10 +61,26 @@ Frontend Tools are page-local browser actions and remain separate from Form fill
|
|
|
61
61
|
|
|
62
62
|
Pass application-specific message renderers through `NocoBaseAIRootProvider.toolRenderers`. Built-in renderers remain available automatically. A custom renderer receives the Tool part, disabled state, edit/approve/reject actions, and a composer-focus callback.
|
|
63
63
|
|
|
64
|
-
##
|
|
64
|
+
## Chat surfaces
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
- `pnpm registry:install` installs the Registry source only when the target directory is missing.
|
|
68
|
-
- `pnpm registry:preview` intentionally replaces the local installed preview with the authoritative source.
|
|
66
|
+
Use one `AIChatWindow` inside `ChatSurface` when a global conversation can switch between a side panel and an expanded dialog. Change only the `variant` prop so the message list, composer, scroll position, and renderer state remain mounted:
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
```tsx
|
|
69
|
+
<ChatSurface
|
|
70
|
+
open={open}
|
|
71
|
+
variant={expanded ? "dialog" : "side-panel"}
|
|
72
|
+
onOpenChange={setOpen}
|
|
73
|
+
>
|
|
74
|
+
<AIChatWindow />
|
|
75
|
+
</ChatSurface>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`ChatDialog` and `ChatSidePanel` remain available as convenience wrappers for fixed, non-switching placements.
|
|
79
|
+
|
|
80
|
+
## Registry development
|
|
81
|
+
|
|
82
|
+
- In the Registry source repository, edit `registry/nocobase-ai` directly. The normal `pnpm dev` and `pnpm build` commands load canonical Registry source without a preview copy.
|
|
83
|
+
- Keep relative imports inside this Registry root. Use the host application's `@/` alias for application services and shared components.
|
|
84
|
+
- During publication, the release pipeline materializes default Registry items into the npm template's `src/extensions` directory.
|
|
85
|
+
|
|
86
|
+
Once installed in an application, `src/extensions/nocobase-ai` is application-owned source and can be edited and committed normally.
|
|
@@ -71,6 +71,7 @@ export function ChatComposer({
|
|
|
71
71
|
employees,
|
|
72
72
|
currentEmployee,
|
|
73
73
|
currentModel,
|
|
74
|
+
canSend,
|
|
74
75
|
selectModel,
|
|
75
76
|
selectEmployee,
|
|
76
77
|
composerFocusRequest,
|
|
@@ -193,7 +194,7 @@ export function ChatComposer({
|
|
|
193
194
|
className="max-h-44 min-h-16 px-3 pt-3 text-sm"
|
|
194
195
|
onChange={(event) => setDraft(event.target.value)}
|
|
195
196
|
onPaste={(event) => {
|
|
196
|
-
if (!enableAttachments) return;
|
|
197
|
+
if (!enableAttachments || !canSend) return;
|
|
197
198
|
const files = Array.from(event.clipboardData.items)
|
|
198
199
|
.filter((item) => item.kind === "file")
|
|
199
200
|
.map((item) => item.getAsFile())
|
|
@@ -360,6 +361,7 @@ export function ChatComposer({
|
|
|
360
361
|
className="rounded-lg"
|
|
361
362
|
aria-label="Send message"
|
|
362
363
|
disabled={
|
|
364
|
+
!canSend ||
|
|
363
365
|
uploadingAttachments ||
|
|
364
366
|
(!draft.trim() &&
|
|
365
367
|
!attachments.some(
|