@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
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useCallback, useMemo, useState } from "react";
|
|
2
|
-
import { useSearchParams } from "react-router";
|
|
3
|
-
|
|
4
|
-
import type { Authenticator } from "@/components/auth/types";
|
|
5
|
-
import { nocobaseClient } from "@/lib/nocobase/client";
|
|
6
|
-
|
|
7
|
-
type WecomAuthUrlResponse = {
|
|
8
|
-
url?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export function isWecomBrowser() {
|
|
12
|
-
return (
|
|
13
|
-
typeof navigator !== "undefined" &&
|
|
14
|
-
navigator.userAgent.toLowerCase().includes("wxwork")
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useWecomSignIn(authenticator: Authenticator) {
|
|
19
|
-
const [searchParams] = useSearchParams();
|
|
20
|
-
const [isPending, setIsPending] = useState(false);
|
|
21
|
-
const [error, setError] = useState<Error>();
|
|
22
|
-
const callbackError = useMemo(() => {
|
|
23
|
-
if (searchParams.get("authenticator") !== authenticator.name) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
const message = searchParams.get("errorMsg");
|
|
27
|
-
return message ? new Error(message) : undefined;
|
|
28
|
-
}, [authenticator.name, searchParams]);
|
|
29
|
-
|
|
30
|
-
const signIn = useCallback(async () => {
|
|
31
|
-
setError(undefined);
|
|
32
|
-
setIsPending(true);
|
|
33
|
-
try {
|
|
34
|
-
const redirect =
|
|
35
|
-
searchParams.get("to") || searchParams.get("redirect") || "/";
|
|
36
|
-
const result = await nocobaseClient.action<WecomAuthUrlResponse>(
|
|
37
|
-
"wecom",
|
|
38
|
-
"getAuthUrl",
|
|
39
|
-
{
|
|
40
|
-
method: "POST",
|
|
41
|
-
authenticator: authenticator.name,
|
|
42
|
-
includeRole: false,
|
|
43
|
-
withAclMeta: false,
|
|
44
|
-
body: { redirect, isWeComBrowser: isWecomBrowser() },
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
if (!result?.url) {
|
|
48
|
-
throw new Error("NocoBase did not return a WeCom authorization URL.");
|
|
49
|
-
}
|
|
50
|
-
window.location.replace(nocobaseClient.resolveUrl(result.url));
|
|
51
|
-
} catch (cause) {
|
|
52
|
-
setError(
|
|
53
|
-
cause instanceof Error
|
|
54
|
-
? cause
|
|
55
|
-
: new Error("Unable to start WeCom sign-in.")
|
|
56
|
-
);
|
|
57
|
-
setIsPending(false);
|
|
58
|
-
}
|
|
59
|
-
}, [authenticator.name, searchParams]);
|
|
60
|
-
|
|
61
|
-
return { signIn, isPending, error: error ?? callbackError };
|
|
62
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { Building2 } from "lucide-react";
|
|
2
|
-
|
|
3
|
-
import type { AuthenticatorComponentProps } from "@/components/auth/types";
|
|
4
|
-
import {
|
|
5
|
-
Alert,
|
|
6
|
-
AlertDescription,
|
|
7
|
-
AlertTitle,
|
|
8
|
-
} from "@/components/ui/alert";
|
|
9
|
-
import { Button } from "@/components/ui/button";
|
|
10
|
-
import {
|
|
11
|
-
Tooltip,
|
|
12
|
-
TooltipContent,
|
|
13
|
-
TooltipTrigger,
|
|
14
|
-
} from "@/components/ui/tooltip";
|
|
15
|
-
import { resolveTranslatableText } from "@/lib/i18n";
|
|
16
|
-
|
|
17
|
-
import { useWecomSignIn } from "./use-wecom-sign-in";
|
|
18
|
-
|
|
19
|
-
function plainText(value: unknown) {
|
|
20
|
-
return typeof value === "string"
|
|
21
|
-
? value.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim()
|
|
22
|
-
: "";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default function WecomSignInButton({
|
|
26
|
-
authenticator,
|
|
27
|
-
onSignIn,
|
|
28
|
-
}: AuthenticatorComponentProps & { onSignIn?: () => void }) {
|
|
29
|
-
const { signIn, isPending, error } = useWecomSignIn(authenticator);
|
|
30
|
-
const tooltip = plainText(authenticator.options?.btnTooltip);
|
|
31
|
-
const button = (
|
|
32
|
-
<Button
|
|
33
|
-
type="button"
|
|
34
|
-
variant="outline"
|
|
35
|
-
className="w-full"
|
|
36
|
-
disabled={!onSignIn && isPending}
|
|
37
|
-
onClick={onSignIn ?? signIn}
|
|
38
|
-
>
|
|
39
|
-
<Building2 />
|
|
40
|
-
{!onSignIn && isPending
|
|
41
|
-
? "Redirecting…"
|
|
42
|
-
: resolveTranslatableText(
|
|
43
|
-
authenticator.title ||
|
|
44
|
-
authenticator.authTypeTitle ||
|
|
45
|
-
"Sign in via WeCom"
|
|
46
|
-
)}
|
|
47
|
-
</Button>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<div className="space-y-3">
|
|
52
|
-
{error && (
|
|
53
|
-
<Alert variant="destructive">
|
|
54
|
-
<AlertTitle>WeCom sign-in failed</AlertTitle>
|
|
55
|
-
<AlertDescription>{error.message}</AlertDescription>
|
|
56
|
-
</Alert>
|
|
57
|
-
)}
|
|
58
|
-
{tooltip ? (
|
|
59
|
-
<Tooltip>
|
|
60
|
-
<TooltipTrigger render={button} />
|
|
61
|
-
<TooltipContent side="right" className="max-w-xs">
|
|
62
|
-
{tooltip}
|
|
63
|
-
</TooltipContent>
|
|
64
|
-
</Tooltip>
|
|
65
|
-
) : (
|
|
66
|
-
button
|
|
67
|
-
)}
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# NocoBase i18n
|
|
2
|
-
|
|
3
|
-
Optional NocoBase server-language integration and language controls for the
|
|
4
|
-
NocoBase Admin Starter.
|
|
5
|
-
|
|
6
|
-
The Starter owns the Refine i18n provider, local i18next runtime, application
|
|
7
|
-
resources, locale selection, and the shared `systemSettings:get` bootstrap.
|
|
8
|
-
This Registry reuses those foundations and adds only the NocoBase-specific
|
|
9
|
-
remote capabilities:
|
|
10
|
-
|
|
11
|
-
- load registered dynamic namespaces from `app:getLang`;
|
|
12
|
-
- persist a signed-in user's selected language through `users:updateLang`;
|
|
13
|
-
- expose reusable page and user-menu language switchers;
|
|
14
|
-
- provide an integrated Demo and Prompt generator.
|
|
15
|
-
|
|
16
|
-
When this Registry is not installed, the Starter still uses the system default
|
|
17
|
-
language and all local translations normally. It does not request
|
|
18
|
-
`app:getLang`. When installed, the Registry reads the already-cached system
|
|
19
|
-
settings and requests only registered server namespaces. `lm-collections` is
|
|
20
|
-
registered by default for collection and field metadata.
|
|
21
|
-
|
|
22
|
-
Application-owned React translations belong in `src/locales`, outside the
|
|
23
|
-
installed Registry directory:
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { registerLocaleResources } from "@/providers/i18n";
|
|
27
|
-
|
|
28
|
-
registerLocaleResources("my-feature", {
|
|
29
|
-
"en-US": { title: "Orders" },
|
|
30
|
-
"zh-CN": { title: "订单" },
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Other installed components can opt into another server-generated namespace:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import { registerServerResourceNamespace } from "@/extensions/nocobase-i18n";
|
|
38
|
-
|
|
39
|
-
registerServerResourceNamespace("my-dynamic-namespace");
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Namespaces registered after startup are loaded incrementally. Existing exact
|
|
43
|
-
NocoBase expressions such as `{{t("Orders")}}` remain supported by the
|
|
44
|
-
Starter's translation compatibility helper.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./language-switcher";
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { useGetLocale, useSetLocale, useTranslate } from "@refinedev/core";
|
|
2
|
-
import { Languages, Loader2 } from "lucide-react";
|
|
3
|
-
import { useState, type ReactNode } from "react";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
DropdownMenuRadioGroup,
|
|
7
|
-
DropdownMenuRadioItem,
|
|
8
|
-
DropdownMenuSeparator,
|
|
9
|
-
DropdownMenuSub,
|
|
10
|
-
DropdownMenuSubContent,
|
|
11
|
-
DropdownMenuSubTrigger,
|
|
12
|
-
} from "@/components/ui/dropdown-menu";
|
|
13
|
-
import {
|
|
14
|
-
Select,
|
|
15
|
-
SelectContent,
|
|
16
|
-
SelectItem,
|
|
17
|
-
SelectTrigger,
|
|
18
|
-
SelectValue,
|
|
19
|
-
} from "@/components/ui/select";
|
|
20
|
-
import { cn } from "@/lib/utils";
|
|
21
|
-
import { useEnabledLocales } from "@/providers/i18n";
|
|
22
|
-
|
|
23
|
-
export type LanguageSwitcherProps = {
|
|
24
|
-
className?: string;
|
|
25
|
-
label?: ReactNode | false;
|
|
26
|
-
triggerClassName?: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export function LanguageSwitcher({
|
|
30
|
-
className,
|
|
31
|
-
label,
|
|
32
|
-
triggerClassName,
|
|
33
|
-
}: LanguageSwitcherProps) {
|
|
34
|
-
const translate = useTranslate();
|
|
35
|
-
const getLocale = useGetLocale();
|
|
36
|
-
const setLocale = useSetLocale();
|
|
37
|
-
const locales = useEnabledLocales();
|
|
38
|
-
const currentLocale = getLocale();
|
|
39
|
-
const [switching, setSwitching] = useState(false);
|
|
40
|
-
const [error, setError] = useState<string>();
|
|
41
|
-
const resolvedLabel =
|
|
42
|
-
typeof label === "undefined"
|
|
43
|
-
? translate("language.label", { ns: "nocobase-i18n" }, "Language")
|
|
44
|
-
: label;
|
|
45
|
-
|
|
46
|
-
if (locales.length < 2) return null;
|
|
47
|
-
|
|
48
|
-
const currentDefinition =
|
|
49
|
-
locales.find(({ locale }) => locale === currentLocale) ?? locales[0];
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<div className={cn("space-y-2", className)}>
|
|
53
|
-
{resolvedLabel === false ? null : (
|
|
54
|
-
<p className="text-xs font-medium text-muted-foreground">
|
|
55
|
-
{resolvedLabel}
|
|
56
|
-
</p>
|
|
57
|
-
)}
|
|
58
|
-
<Select
|
|
59
|
-
value={currentLocale}
|
|
60
|
-
disabled={switching}
|
|
61
|
-
onValueChange={(value) => {
|
|
62
|
-
if (!value || value === currentLocale) return;
|
|
63
|
-
setSwitching(true);
|
|
64
|
-
setError(undefined);
|
|
65
|
-
Promise.resolve(setLocale(value)).catch((reason) => {
|
|
66
|
-
setError(
|
|
67
|
-
reason instanceof Error
|
|
68
|
-
? reason.message
|
|
69
|
-
: translate(
|
|
70
|
-
"language.switchError",
|
|
71
|
-
{ ns: "nocobase-i18n" },
|
|
72
|
-
"Unable to switch language."
|
|
73
|
-
)
|
|
74
|
-
);
|
|
75
|
-
setSwitching(false);
|
|
76
|
-
});
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
<SelectTrigger
|
|
80
|
-
className={cn("w-full min-w-52", triggerClassName)}
|
|
81
|
-
aria-label={String(resolvedLabel || "Language")}
|
|
82
|
-
>
|
|
83
|
-
{switching ? <Loader2 className="animate-spin" /> : <Languages />}
|
|
84
|
-
<SelectValue>{currentDefinition?.label ?? currentLocale}</SelectValue>
|
|
85
|
-
</SelectTrigger>
|
|
86
|
-
<SelectContent>
|
|
87
|
-
{locales.map((definition) => (
|
|
88
|
-
<SelectItem key={definition.locale} value={definition.locale}>
|
|
89
|
-
{definition.label}
|
|
90
|
-
</SelectItem>
|
|
91
|
-
))}
|
|
92
|
-
</SelectContent>
|
|
93
|
-
</Select>
|
|
94
|
-
{error ? <p className="text-xs text-destructive">{error}</p> : null}
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function LanguageUserMenuItems() {
|
|
100
|
-
const translate = useTranslate();
|
|
101
|
-
const getLocale = useGetLocale();
|
|
102
|
-
const setLocale = useSetLocale();
|
|
103
|
-
const locales = useEnabledLocales();
|
|
104
|
-
const currentLocale = getLocale();
|
|
105
|
-
|
|
106
|
-
if (locales.length < 2) return null;
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<>
|
|
110
|
-
<DropdownMenuSeparator />
|
|
111
|
-
<DropdownMenuSub>
|
|
112
|
-
<DropdownMenuSubTrigger className="min-h-9 gap-2 px-2 text-muted-foreground focus:text-foreground">
|
|
113
|
-
<Languages />
|
|
114
|
-
<span>
|
|
115
|
-
{translate("language.label", { ns: "nocobase-i18n" }, "Language")}
|
|
116
|
-
</span>
|
|
117
|
-
</DropdownMenuSubTrigger>
|
|
118
|
-
<DropdownMenuSubContent className="min-w-44">
|
|
119
|
-
<DropdownMenuRadioGroup
|
|
120
|
-
value={currentLocale}
|
|
121
|
-
onValueChange={(value) => {
|
|
122
|
-
if (value && value !== currentLocale) void setLocale(value);
|
|
123
|
-
}}
|
|
124
|
-
>
|
|
125
|
-
{locales.map((definition) => (
|
|
126
|
-
<DropdownMenuRadioItem
|
|
127
|
-
key={definition.locale}
|
|
128
|
-
value={definition.locale}
|
|
129
|
-
>
|
|
130
|
-
{definition.label}
|
|
131
|
-
</DropdownMenuRadioItem>
|
|
132
|
-
))}
|
|
133
|
-
</DropdownMenuRadioGroup>
|
|
134
|
-
</DropdownMenuSubContent>
|
|
135
|
-
</DropdownMenuSub>
|
|
136
|
-
</>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { useGetLocale, useTranslate } from "@refinedev/core";
|
|
2
|
-
import { Database, Languages, PanelsTopLeft } from "lucide-react";
|
|
3
|
-
|
|
4
|
-
import { Badge } from "@/components/ui/badge";
|
|
5
|
-
import {
|
|
6
|
-
Card,
|
|
7
|
-
CardContent,
|
|
8
|
-
CardDescription,
|
|
9
|
-
CardHeader,
|
|
10
|
-
CardTitle,
|
|
11
|
-
} from "@/components/ui/card";
|
|
12
|
-
import { nocobaseClient } from "@/lib/nocobase/client";
|
|
13
|
-
import {
|
|
14
|
-
LanguageSwitcher,
|
|
15
|
-
useEnabledLocales,
|
|
16
|
-
} from "@/extensions/nocobase-i18n";
|
|
17
|
-
import { I18nPromptGenerator } from "./prompt-generator";
|
|
18
|
-
|
|
19
|
-
export function I18nDemoPage() {
|
|
20
|
-
const t = useTranslate();
|
|
21
|
-
const getLocale = useGetLocale();
|
|
22
|
-
const locale = getLocale();
|
|
23
|
-
const locales = useEnabledLocales();
|
|
24
|
-
const canSwitchLanguage = locales.length > 1;
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className="space-y-10 pb-12">
|
|
28
|
-
<header className="space-y-2">
|
|
29
|
-
<div className="flex items-center gap-2">
|
|
30
|
-
<Badge variant="secondary">i18n</Badge>
|
|
31
|
-
<Badge variant="outline">{locale}</Badge>
|
|
32
|
-
</div>
|
|
33
|
-
<h1 className="text-2xl font-semibold tracking-[-0.025em] sm:text-3xl">
|
|
34
|
-
{t("demo.title", { ns: "nocobase-i18n" }, "Internationalization")}
|
|
35
|
-
</h1>
|
|
36
|
-
<p className="max-w-3xl text-sm leading-6 text-muted-foreground">
|
|
37
|
-
{t(
|
|
38
|
-
"demo.description",
|
|
39
|
-
{ ns: "nocobase-i18n" },
|
|
40
|
-
"Optional NocoBase server translations and language controls built on the Starter's internationalization runtime."
|
|
41
|
-
)}
|
|
42
|
-
</p>
|
|
43
|
-
</header>
|
|
44
|
-
|
|
45
|
-
<section className="grid gap-5 xl:grid-cols-3">
|
|
46
|
-
<Card>
|
|
47
|
-
<CardHeader>
|
|
48
|
-
<CardTitle className="flex items-center gap-2">
|
|
49
|
-
<Languages className="size-4" />
|
|
50
|
-
{t(
|
|
51
|
-
"demo.switcher.title",
|
|
52
|
-
{ ns: "nocobase-i18n" },
|
|
53
|
-
"Language switcher"
|
|
54
|
-
)}
|
|
55
|
-
</CardTitle>
|
|
56
|
-
<CardDescription>
|
|
57
|
-
{t(
|
|
58
|
-
"demo.switcher.description",
|
|
59
|
-
{ ns: "nocobase-i18n" },
|
|
60
|
-
"The same reusable component is also integrated into the signed-in user menu."
|
|
61
|
-
)}
|
|
62
|
-
</CardDescription>
|
|
63
|
-
</CardHeader>
|
|
64
|
-
<CardContent>
|
|
65
|
-
{canSwitchLanguage ? (
|
|
66
|
-
<LanguageSwitcher className="max-w-sm" />
|
|
67
|
-
) : (
|
|
68
|
-
<div className="flex items-start gap-3 rounded-lg border bg-muted/30 p-4">
|
|
69
|
-
<Languages className="mt-0.5 size-5 shrink-0 text-muted-foreground" />
|
|
70
|
-
<div>
|
|
71
|
-
<p className="text-sm font-medium">
|
|
72
|
-
{t(
|
|
73
|
-
"demo.switcher.unavailable.title",
|
|
74
|
-
{ ns: "nocobase-i18n" },
|
|
75
|
-
"Only one language is enabled"
|
|
76
|
-
)}
|
|
77
|
-
</p>
|
|
78
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
79
|
-
{t(
|
|
80
|
-
"demo.switcher.unavailable.description",
|
|
81
|
-
{ ns: "nocobase-i18n" },
|
|
82
|
-
"Enable another language in NocoBase system settings to display the switcher."
|
|
83
|
-
)}
|
|
84
|
-
</p>
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
)}
|
|
88
|
-
</CardContent>
|
|
89
|
-
</Card>
|
|
90
|
-
|
|
91
|
-
<CapabilityCard
|
|
92
|
-
icon={<PanelsTopLeft className="size-4" />}
|
|
93
|
-
title={t(
|
|
94
|
-
"demo.frontend.title",
|
|
95
|
-
{ ns: "nocobase-i18n" },
|
|
96
|
-
"Application UI translations"
|
|
97
|
-
)}
|
|
98
|
-
description={t(
|
|
99
|
-
"demo.frontend.description",
|
|
100
|
-
{ ns: "nocobase-i18n" },
|
|
101
|
-
"The Starter provides the runtime while application and Registry components keep their messages in local resource files."
|
|
102
|
-
)}
|
|
103
|
-
>
|
|
104
|
-
<div className="divide-y rounded-lg border">
|
|
105
|
-
<DemoRow
|
|
106
|
-
label={t(
|
|
107
|
-
"language.current",
|
|
108
|
-
{ ns: "nocobase-i18n" },
|
|
109
|
-
"Current language"
|
|
110
|
-
)}
|
|
111
|
-
value={locale}
|
|
112
|
-
/>
|
|
113
|
-
<DemoRow
|
|
114
|
-
label={t(
|
|
115
|
-
"demo.frontend.actions",
|
|
116
|
-
{ ns: "nocobase-i18n" },
|
|
117
|
-
"Common actions"
|
|
118
|
-
)}
|
|
119
|
-
value={`${t("buttons.apply", "Apply")} · ${t(
|
|
120
|
-
"buttons.clear",
|
|
121
|
-
"Clear"
|
|
122
|
-
)}`}
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
</CapabilityCard>
|
|
126
|
-
|
|
127
|
-
<CapabilityCard
|
|
128
|
-
icon={<Database className="size-4" />}
|
|
129
|
-
title={t(
|
|
130
|
-
"demo.server.title",
|
|
131
|
-
{ ns: "nocobase-i18n" },
|
|
132
|
-
"NocoBase metadata translations"
|
|
133
|
-
)}
|
|
134
|
-
description={t(
|
|
135
|
-
"demo.server.description",
|
|
136
|
-
{ ns: "nocobase-i18n" },
|
|
137
|
-
"Collection and field labels are loaded from dynamic server namespaces such as lm-collections."
|
|
138
|
-
)}
|
|
139
|
-
>
|
|
140
|
-
<div className="divide-y rounded-lg border">
|
|
141
|
-
<DemoRow
|
|
142
|
-
label="Users"
|
|
143
|
-
value={t("Users", { ns: "lm-collections" }, "Users")}
|
|
144
|
-
/>
|
|
145
|
-
<DemoRow
|
|
146
|
-
label="Nickname"
|
|
147
|
-
value={t("Nickname", { ns: "lm-collections" }, "Nickname")}
|
|
148
|
-
/>
|
|
149
|
-
<DemoRow
|
|
150
|
-
label="X-Locale"
|
|
151
|
-
value={nocobaseClient.getLocale() ?? locale}
|
|
152
|
-
code
|
|
153
|
-
/>
|
|
154
|
-
</div>
|
|
155
|
-
</CapabilityCard>
|
|
156
|
-
</section>
|
|
157
|
-
|
|
158
|
-
<section className="border-t pt-8">
|
|
159
|
-
<I18nPromptGenerator />
|
|
160
|
-
</section>
|
|
161
|
-
</div>
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function DemoRow({
|
|
166
|
-
code,
|
|
167
|
-
label,
|
|
168
|
-
value,
|
|
169
|
-
}: {
|
|
170
|
-
code?: boolean;
|
|
171
|
-
label: string;
|
|
172
|
-
value: string;
|
|
173
|
-
}) {
|
|
174
|
-
return (
|
|
175
|
-
<div className="flex items-center justify-between gap-4 px-3 py-2.5">
|
|
176
|
-
<span className="text-xs text-muted-foreground">{label}</span>
|
|
177
|
-
{code ? (
|
|
178
|
-
<code className="rounded bg-muted px-2 py-1 text-xs">{value}</code>
|
|
179
|
-
) : (
|
|
180
|
-
<span className="text-sm font-medium">{value}</span>
|
|
181
|
-
)}
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function CapabilityCard({
|
|
187
|
-
children,
|
|
188
|
-
description,
|
|
189
|
-
icon,
|
|
190
|
-
title,
|
|
191
|
-
}: {
|
|
192
|
-
children: React.ReactNode;
|
|
193
|
-
description: string;
|
|
194
|
-
icon: React.ReactNode;
|
|
195
|
-
title: string;
|
|
196
|
-
}) {
|
|
197
|
-
return (
|
|
198
|
-
<Card>
|
|
199
|
-
<CardHeader>
|
|
200
|
-
<CardTitle className="flex items-center gap-2">
|
|
201
|
-
{icon}
|
|
202
|
-
{title}
|
|
203
|
-
</CardTitle>
|
|
204
|
-
<CardDescription>{description}</CardDescription>
|
|
205
|
-
</CardHeader>
|
|
206
|
-
<CardContent className="text-sm">{children}</CardContent>
|
|
207
|
-
</Card>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { useTranslate } from "@refinedev/core";
|
|
2
|
-
import { ChevronDown, Sparkles } from "lucide-react";
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
|
|
5
|
-
import { PromptOutput } from "@/components/demo/prompt-output";
|
|
6
|
-
import {
|
|
7
|
-
Card,
|
|
8
|
-
CardContent,
|
|
9
|
-
CardDescription,
|
|
10
|
-
CardHeader,
|
|
11
|
-
CardTitle,
|
|
12
|
-
} from "@/components/ui/card";
|
|
13
|
-
import { Input } from "@/components/ui/input";
|
|
14
|
-
import { Label } from "@/components/ui/label";
|
|
15
|
-
import { Textarea } from "@/components/ui/textarea";
|
|
16
|
-
|
|
17
|
-
export function I18nPromptGenerator() {
|
|
18
|
-
const t = useTranslate();
|
|
19
|
-
const [scene, setScene] = useState("A customer support workspace");
|
|
20
|
-
const [languages, setLanguages] = useState("English and Simplified Chinese");
|
|
21
|
-
|
|
22
|
-
const prompt = useMemo(
|
|
23
|
-
() => `Build a complete multilingual scene in the NocoBase Admin Starter.
|
|
24
|
-
|
|
25
|
-
Application scene
|
|
26
|
-
- Product or page: ${scene}
|
|
27
|
-
- Supported languages: ${languages}
|
|
28
|
-
- Translate the complete visible user flow, including empty, loading, validation, and error states.
|
|
29
|
-
|
|
30
|
-
Implementation contract
|
|
31
|
-
- Use the Starter's @/providers/i18n runtime and the application's translation and locale hooks.
|
|
32
|
-
- Put application-owned React messages in src/locales and register the app namespace through registerTranslationResources from @/lib/i18n.
|
|
33
|
-
- Keep each Registry component's messages in its own namespace and register them with registerLocaleResources().
|
|
34
|
-
- Do not place application messages inside src/extensions/nocobase-i18n/locales because installed Registry files may be refreshed independently.
|
|
35
|
-
- Use the reusable LanguageSwitcher when the page needs an explicit language control; the signed-in user menu already includes one.
|
|
36
|
-
- Reuse the Starter's shared system settings instead of requesting systemSettings:get again.
|
|
37
|
-
- Keep Starter and Registry UI resources local. Use app:getLang only for registered dynamic server namespaces such as lm-collections when the NocoBase i18n Registry is installed.
|
|
38
|
-
- Let the shared NocoBase client send the selected locale through X-Locale.
|
|
39
|
-
- Preserve compatibility when server metadata contains exact {{t("...")}} expressions.
|
|
40
|
-
- Provide complete React components, locale resource files, route/resource integration, and realistic visible content.`,
|
|
41
|
-
[languages, scene]
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<details open className="group rounded-xl border bg-muted/15">
|
|
46
|
-
<summary className="flex cursor-pointer list-none items-center gap-2 px-4 py-3 text-sm font-medium marker:hidden">
|
|
47
|
-
<Sparkles className="size-4 text-primary" />
|
|
48
|
-
{t("demo.prompt.title", { ns: "nocobase-i18n" }, "Prompt generator")}
|
|
49
|
-
<ChevronDown className="ml-auto size-4 text-muted-foreground transition-transform group-open:rotate-180" />
|
|
50
|
-
</summary>
|
|
51
|
-
<div className="grid items-start gap-5 border-t p-4 xl:grid-cols-[360px_minmax(0,1fr)]">
|
|
52
|
-
<Card className="gap-0 py-0">
|
|
53
|
-
<CardHeader className="border-b py-4">
|
|
54
|
-
<CardTitle className="text-base">
|
|
55
|
-
{t(
|
|
56
|
-
"demo.prompt.settings",
|
|
57
|
-
{ ns: "nocobase-i18n" },
|
|
58
|
-
"Scene settings"
|
|
59
|
-
)}
|
|
60
|
-
</CardTitle>
|
|
61
|
-
<CardDescription>
|
|
62
|
-
{t(
|
|
63
|
-
"demo.prompt.description",
|
|
64
|
-
{ ns: "nocobase-i18n" },
|
|
65
|
-
"Describe a complete multilingual application scene and generate an implementation prompt."
|
|
66
|
-
)}
|
|
67
|
-
</CardDescription>
|
|
68
|
-
</CardHeader>
|
|
69
|
-
<CardContent className="space-y-5 py-5">
|
|
70
|
-
<div className="space-y-2">
|
|
71
|
-
<Label htmlFor="i18n-prompt-scene">
|
|
72
|
-
{t(
|
|
73
|
-
"demo.prompt.scene",
|
|
74
|
-
{ ns: "nocobase-i18n" },
|
|
75
|
-
"Application scene"
|
|
76
|
-
)}
|
|
77
|
-
</Label>
|
|
78
|
-
<Input
|
|
79
|
-
id="i18n-prompt-scene"
|
|
80
|
-
value={scene}
|
|
81
|
-
onChange={(event) => setScene(event.target.value)}
|
|
82
|
-
/>
|
|
83
|
-
</div>
|
|
84
|
-
<div className="space-y-2">
|
|
85
|
-
<Label htmlFor="i18n-prompt-languages">
|
|
86
|
-
{t(
|
|
87
|
-
"demo.prompt.languages",
|
|
88
|
-
{ ns: "nocobase-i18n" },
|
|
89
|
-
"Languages"
|
|
90
|
-
)}
|
|
91
|
-
</Label>
|
|
92
|
-
<Textarea
|
|
93
|
-
id="i18n-prompt-languages"
|
|
94
|
-
value={languages}
|
|
95
|
-
onChange={(event) => setLanguages(event.target.value)}
|
|
96
|
-
className="min-h-24"
|
|
97
|
-
/>
|
|
98
|
-
</div>
|
|
99
|
-
</CardContent>
|
|
100
|
-
</Card>
|
|
101
|
-
<PromptOutput
|
|
102
|
-
title={t(
|
|
103
|
-
"demo.prompt.output",
|
|
104
|
-
{ ns: "nocobase-i18n" },
|
|
105
|
-
"Generated prompt"
|
|
106
|
-
)}
|
|
107
|
-
description={t(
|
|
108
|
-
"demo.prompt.outputDescription",
|
|
109
|
-
{ ns: "nocobase-i18n" },
|
|
110
|
-
"Updates as you change the scene and language requirements."
|
|
111
|
-
)}
|
|
112
|
-
prompt={prompt}
|
|
113
|
-
copyLabel={t(
|
|
114
|
-
"demo.prompt.copy",
|
|
115
|
-
{ ns: "nocobase-i18n" },
|
|
116
|
-
"Copy prompt"
|
|
117
|
-
)}
|
|
118
|
-
copiedLabel={t(
|
|
119
|
-
"demo.prompt.copied",
|
|
120
|
-
{ ns: "nocobase-i18n" },
|
|
121
|
-
"Copied"
|
|
122
|
-
)}
|
|
123
|
-
copyErrorLabel={t(
|
|
124
|
-
"demo.prompt.copyError",
|
|
125
|
-
{ ns: "nocobase-i18n" },
|
|
126
|
-
"Clipboard access failed. Select the prompt and copy it manually."
|
|
127
|
-
)}
|
|
128
|
-
/>
|
|
129
|
-
</div>
|
|
130
|
-
</details>
|
|
131
|
-
);
|
|
132
|
-
}
|