@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.
Files changed (338) hide show
  1. package/.env.example +5 -0
  2. package/.github/workflows/publish.yml +106 -0
  3. package/.gitignore +33 -0
  4. package/AGENTS.md +19 -0
  5. package/README.MD +61 -180
  6. package/dist/assets/{LineDraw-C0m1GONT.js → LineDraw-iG-ADes-.js} +1 -1
  7. package/dist/assets/{SymbolDraw-CCospe5G.js → SymbolDraw-DXoSw1tM.js} +1 -1
  8. package/dist/assets/VisualMapping-C_ee4Eht.js +1 -0
  9. package/dist/assets/auth-method-demo-DaOCuvsq.js +1 -0
  10. package/dist/assets/auto-login-provider-5Uxza9Ms.js +2 -0
  11. package/dist/assets/auto-login-provider-DyaPGK2D.js +1 -0
  12. package/dist/assets/auto-redirect-provider-CfwRvYOI.js +1 -0
  13. package/dist/assets/auto-redirect-provider-Cr3jmYts.js +1 -0
  14. package/dist/assets/auto-redirect-provider-QkJQRBbG.js +1 -0
  15. package/dist/assets/calendar-DjDRfD5t.js +1 -0
  16. package/dist/assets/card-DuQBOS6A.js +1 -0
  17. package/dist/assets/cas-sign-in-button-CyqG1QME.js +1 -0
  18. package/dist/assets/components-D-ptYjX_.js +20 -0
  19. package/dist/assets/configuration-gate-C08mkkiX.js +1 -0
  20. package/dist/assets/{createClipPathFromCoordSys-uEftn1Mq.js → createClipPathFromCoordSys-wj34-b1B.js} +1 -1
  21. package/dist/assets/createSeriesDataSimply-D0A6SAVQ.js +1 -0
  22. package/dist/assets/customGraphicKeyframeAnimation-DWrhrHL_.js +3 -0
  23. package/dist/assets/demo-BYWHeKUe.js +1 -0
  24. package/dist/assets/demo-CPWoNtx8.js +1 -0
  25. package/dist/assets/demo-CyvJk5zV.js +1 -0
  26. package/dist/assets/demo-D4OiKplc.js +1 -0
  27. package/dist/assets/demo-DBueJg0m.js +1 -0
  28. package/dist/assets/demo-DkIhyiRK.js +1 -0
  29. package/dist/assets/demo-tpTjO0sC.js +1 -0
  30. package/dist/assets/dingtalk-sign-in-button-DSWsSruv.js +1 -0
  31. package/dist/assets/echarts-preview-DxHNfq1C.js +1 -0
  32. package/dist/assets/{echarts-runtime-CEgJbv68.js → echarts-runtime-D8kXn6ME.js} +3 -3
  33. package/dist/assets/{echarts-runtime-advanced-Buhm2BHz.js → echarts-runtime-advanced-QY8v95qK.js} +1 -1
  34. package/dist/assets/{echarts-runtime-common-DLAdBAIa.js → echarts-runtime-common-D-yqAK-r.js} +1 -1
  35. package/dist/assets/{echarts-runtime-components-ypnyLkYx.js → echarts-runtime-components-P4-i8jPU.js} +1 -1
  36. package/dist/assets/{echarts-runtime-hierarchy-ClStquCF.js → echarts-runtime-hierarchy-ByQZ-DbP.js} +1 -1
  37. package/dist/assets/floating-BFVEyPEp.js +15 -0
  38. package/dist/assets/index-BgCbGDMc.js +43 -0
  39. package/dist/assets/index-Btk35Nx-.js +23 -0
  40. package/dist/assets/index-CwNxd66J.js +1 -0
  41. package/dist/assets/index-MB-jfQld.js +686 -0
  42. package/dist/assets/index-NH3aqqQz.css +1 -0
  43. package/dist/assets/index-gFtCXH3o.js +58 -0
  44. package/dist/assets/index-um5hdKdZ.js +23 -0
  45. package/dist/assets/{labelHelper-C137W5gU.js → labelHelper-DQqRy3a_.js} +1 -1
  46. package/dist/assets/ldap-sign-in-form--6Gt3yGq.js +1 -0
  47. package/dist/assets/oidc-sign-in-button-CQdRcR_p.js +1 -0
  48. package/dist/assets/{page-context-Cxx11gzg.js → page-context-CnQ4GYM7.js} +10 -10
  49. package/dist/assets/prompt-card-B2RS4nt2.js +1 -0
  50. package/dist/assets/prompt-output-DZHRM2ft.js +1 -0
  51. package/dist/assets/saml-sign-in-button-BHL5a0JV.js +1 -0
  52. package/dist/assets/{sectorHelper-Fqgz3uEk.js → sectorHelper-DN_Vv6OL.js} +1 -1
  53. package/dist/assets/shortcut-C5x5LQWU.js +47 -0
  54. package/dist/assets/sms-sign-in-form-DTX9GV2I.js +1 -0
  55. package/dist/assets/{static.browser-CfQQzERe.js → static.browser-jI1psK0d.js} +8 -8
  56. package/dist/assets/{switch-DrxImI8F.js → switch-CRL8PmRI.js} +1 -1
  57. package/dist/assets/{tool-cards-BGfsY8JF.js → tool-cards-DFttLAfo.js} +6 -7
  58. package/dist/assets/use-authenticator-sign-in-CRGjzXju.js +1 -0
  59. package/dist/assets/use-dingtalk-sign-in-8WYzJ2jf.js +1 -0
  60. package/dist/assets/use-wecom-sign-in-C9j8wfCD.js +1 -0
  61. package/dist/assets/wecom-sign-in-button-mIS1zGSU.js +1 -0
  62. package/dist/index.html +2 -2
  63. package/dist/index.raw.html +5 -2
  64. package/package.json +3 -5
  65. package/scripts/build-html.mjs +12 -0
  66. package/src/App.css +2 -0
  67. package/src/App.tsx +35 -186
  68. package/src/app/development.tsx +154 -0
  69. package/src/app/extension.ts +5 -0
  70. package/src/app/extensions.tsx +18 -4
  71. package/src/app/routes.tsx +65 -0
  72. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  73. package/src/components/access-control/acl-gate.tsx +7 -6
  74. package/src/components/access-control/can-access.tsx +17 -0
  75. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  76. package/src/components/app-shell/sidebar.tsx +21 -5
  77. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  78. package/src/extensions/nocobase-acl/README.md +74 -0
  79. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  80. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  81. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  82. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  83. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  84. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  85. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  86. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  87. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  88. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  89. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  90. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  91. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  92. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  93. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  94. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  95. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  96. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  97. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  98. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  99. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  100. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  101. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  102. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  103. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  104. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  105. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  106. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  107. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  108. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  109. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  110. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  111. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  112. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  113. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  114. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  115. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  116. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  117. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  118. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  119. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  120. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  121. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  122. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  123. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  124. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  125. package/src/lib/nocobase/acl/context.ts +32 -0
  126. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  127. package/src/lib/nocobase/acl/hooks.ts +40 -0
  128. package/src/lib/nocobase/acl/index.ts +4 -1
  129. package/src/lib/nocobase/acl/menu.ts +6 -6
  130. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  131. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  132. package/src/lib/nocobase/acl/store.ts +92 -86
  133. package/src/lib/nocobase/acl/types.ts +52 -32
  134. package/src/lib/nocobase/auth-session.ts +128 -0
  135. package/src/lib/nocobase/client.ts +48 -50
  136. package/src/locales/en-US.ts +0 -78
  137. package/src/locales/index.ts +2 -7
  138. package/src/locales/zh-CN.ts +0 -74
  139. package/src/providers/access-control.ts +43 -8
  140. package/src/providers/auth-callback.ts +1 -0
  141. package/src/providers/auth.ts +24 -26
  142. package/src/providers/constants.ts +0 -4
  143. package/src/providers/data.ts +14 -26
  144. package/src/providers/runtime-config.ts +40 -0
  145. package/src/vite-env.d.ts +9 -0
  146. package/tests/auth-session-regression.mjs +101 -0
  147. package/tsconfig.json +2 -1
  148. package/vite.config.ts +9 -1
  149. package/dist/assets/VisualMapping-Cbv1v7Cd.js +0 -1
  150. package/dist/assets/ai-employee-shortcut-B253WWow.js +0 -1
  151. package/dist/assets/calendar-B3dXXyIv.js +0 -1
  152. package/dist/assets/chat-inline-xpQ9iq4p.js +0 -1
  153. package/dist/assets/chat-side-panel-layout-BVWsp5rr.js +0 -1
  154. package/dist/assets/components-ChhJaNRc.js +0 -20
  155. package/dist/assets/configuration-gate-Dwdq_lxf.js +0 -1
  156. package/dist/assets/createSeriesDataSimply-CI7l7Ufv.js +0 -1
  157. package/dist/assets/customGraphicKeyframeAnimation-DE3jSfMd.js +0 -3
  158. package/dist/assets/echarts-preview-C-cNUtJF.js +0 -1
  159. package/dist/assets/floating-BarTmn-b.js +0 -13
  160. package/dist/assets/index-BJIcYTdc.js +0 -53
  161. package/dist/assets/index-C6xNKxpg.js +0 -656
  162. package/dist/assets/index-D4JQi1TM.js +0 -54
  163. package/dist/assets/index-YA7-AUO8.css +0 -1
  164. package/dist/assets/index-g8zWVpP9.js +0 -23
  165. package/dist/assets/prompt-card-BsYMW_rq.js +0 -1
  166. package/dist/assets/shortcut-xtghDP63.js +0 -47
  167. package/registry/nocobase-acl/README.md +0 -19
  168. package/registry/nocobase-acl/extension.tsx +0 -71
  169. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  170. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  171. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  172. package/registry/nocobase-ai/extension.tsx +0 -158
  173. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  174. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  175. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  176. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  177. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  178. package/registry/nocobase-auth-cas/README.md +0 -6
  179. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  180. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  181. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  182. package/registry/nocobase-auth-cas/index.ts +0 -1
  183. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  184. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  185. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  186. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  187. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  188. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  189. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  190. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  191. package/registry/nocobase-auth-ldap/README.md +0 -6
  192. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  193. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  194. package/registry/nocobase-auth-ldap/index.ts +0 -1
  195. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  196. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  197. package/registry/nocobase-auth-oidc/README.md +0 -11
  198. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  199. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  200. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  201. package/registry/nocobase-auth-oidc/index.ts +0 -1
  202. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  203. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  204. package/registry/nocobase-auth-saml/README.md +0 -6
  205. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  206. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  207. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  208. package/registry/nocobase-auth-saml/index.ts +0 -1
  209. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  210. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  211. package/registry/nocobase-auth-sms/README.md +0 -7
  212. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  213. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  214. package/registry/nocobase-auth-sms/index.ts +0 -1
  215. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  216. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  217. package/registry/nocobase-auth-wecom/README.md +0 -6
  218. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  219. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  220. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  221. package/registry/nocobase-auth-wecom/index.ts +0 -1
  222. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  223. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  224. package/registry/nocobase-i18n/README.md +0 -44
  225. package/registry/nocobase-i18n/components/index.ts +0 -1
  226. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  227. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  228. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  229. package/registry/nocobase-i18n/extension.tsx +0 -45
  230. package/registry/nocobase-i18n/index.ts +0 -7
  231. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  232. package/registry/nocobase-i18n/locales/index.ts +0 -8
  233. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  234. package/registry/nocobase-i18n/provider.tsx +0 -44
  235. package/registry/nocobase-i18n/server-resources.ts +0 -166
  236. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  237. package/registry.config.json +0 -302
  238. package/scripts/registry.mjs +0 -164
  239. package/src/components/access-control/resource-access-guard.tsx +0 -21
  240. package/src/components/access-control/use-acl-runtime.ts +0 -28
  241. package/src/lib/nocobase/acl/action.ts +0 -147
  242. package/src/pages/users/layout.tsx +0 -15
  243. package/src/pages/users/show.tsx +0 -278
  244. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  247. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  248. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  252. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  253. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  255. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  257. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  258. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  259. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  260. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  261. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  262. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  263. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  264. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  265. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  266. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  267. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  268. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  269. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  270. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  271. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  272. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  273. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  274. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  275. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  276. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  277. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  278. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  279. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  280. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  281. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  282. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  283. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  284. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  285. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  286. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  287. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  288. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  289. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  290. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  291. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  292. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  293. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  294. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  295. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  296. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  297. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  298. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  299. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  300. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  301. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  302. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  303. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  304. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  305. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  306. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  307. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  308. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  309. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  310. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  311. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  312. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  313. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  314. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  315. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  316. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  317. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  318. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  319. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  320. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  321. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  322. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  323. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  324. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  325. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  326. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  327. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  328. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  329. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  330. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  331. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  332. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  333. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  334. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  335. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  336. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  337. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  338. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -0,0 +1,77 @@
1
+ export const usersExample = {
2
+ "resources.users": "Users",
3
+ "resources.user": "User",
4
+ "resources.users.description":
5
+ "Manage the people who can sign in and work in this NocoBase application.",
6
+ "users.fields.id": "ID",
7
+ "users.fields.nickname": "Nickname",
8
+ "users.fields.username": "Username",
9
+ "users.fields.email": "Email",
10
+ "users.fields.phone": "Phone",
11
+ "users.fields.password": "Password",
12
+ "users.fields.roles": "Roles",
13
+ "users.fields.createdAt": "Created at",
14
+ "users.fields.updatedAt": "Updated at",
15
+ "users.fields.actions": "Actions",
16
+ "users.filters.roles.placeholder": "Select roles...",
17
+ "users.filters.roles.noResults": "No roles found.",
18
+ "users.actions.view": "View user",
19
+ "users.actions.edit": "Edit user",
20
+ "users.actions.delete": "Delete user",
21
+ "users.drawer.create.title": "Create user",
22
+ "users.drawer.create.description":
23
+ "Add a user who can sign in to this NocoBase application.",
24
+ "users.drawer.edit.title": "Edit user",
25
+ "users.drawer.edit.description":
26
+ "Update this user's identity and contact information.",
27
+ "users.drawer.show.description":
28
+ "Review this user's identity, contact information, and roles.",
29
+ "users.form.create.submit": "Create user",
30
+ "users.form.create.submitting": "Creating...",
31
+ "users.form.edit.submit": "Save changes",
32
+ "users.form.edit.submitting": "Saving...",
33
+ "users.form.cancel": "Cancel",
34
+ "users.form.nickname.placeholder": "Enter a display name",
35
+ "users.form.username.placeholder": "Enter a unique username",
36
+ "users.form.email.placeholder": "Enter an email address",
37
+ "users.form.phone.placeholder": "Enter a phone number",
38
+ "users.form.password.placeholder": "Set an initial password",
39
+ "users.form.password.aiNotice":
40
+ "Passwords are never included in AI page context or Form filler.",
41
+ "users.validation.username": "Username is required",
42
+ "users.validation.email": "Email is required",
43
+ "users.validation.emailFormat": "Enter a valid email address",
44
+ "users.validation.phone": "Phone is required",
45
+ "users.validation.password": "Password is required",
46
+ "users.detail.identity": "Identity",
47
+ "users.detail.contact": "Contact",
48
+ "users.detail.access": "Access",
49
+ "users.detail.timestamps": "Timestamps",
50
+ "users.detail.noRoles": "No assigned roles",
51
+ "users.detail.unnamed": "Unnamed user",
52
+ "users.detail.loadError.title": "Unable to load user",
53
+ "users.detail.loadError.description":
54
+ "The user may no longer exist, or you may not have permission to view it.",
55
+ "users.ai.table": "Users table",
56
+ "users.ai.detail": "User details",
57
+ "users.ai.createForm": "Create user form",
58
+ "users.ai.editForm": "Edit user form",
59
+ "common.yes": "Yes",
60
+ "common.no": "No",
61
+ "roles.drawer.description": "Review the role associated with this user.",
62
+ "roles.fields.name": "Name",
63
+ "roles.fields.title": "Title",
64
+ "roles.fields.description": "Description",
65
+ "roles.fields.default": "Default role",
66
+ "roles.fields.hidden": "Hidden",
67
+ "roles.fields.allowConfigure": "Can configure",
68
+ "roles.fields.createdAt": "Created at",
69
+ "roles.fields.updatedAt": "Updated at",
70
+ "roles.detail.identity": "Role identity",
71
+ "roles.detail.settings": "Settings",
72
+ "roles.detail.actions": "Allowed actions",
73
+ "roles.detail.timestamps": "Timestamps",
74
+ "roles.detail.unavailable":
75
+ "Additional role details are unavailable. Showing the relation data included with the user.",
76
+ "roles.ai.detail": "Role details",
77
+ } as const;
@@ -0,0 +1,8 @@
1
+ import { registerTranslationResources } from "@/lib/i18n";
2
+ import { usersExample as enUS } from "./en-US";
3
+ import { usersExample as zhCN } from "./zh-CN";
4
+
5
+ registerTranslationResources("app", {
6
+ "en-US": enUS,
7
+ "zh-CN": zhCN,
8
+ });
@@ -0,0 +1,73 @@
1
+ export const usersExample = {
2
+ "resources.users": "用户",
3
+ "resources.user": "用户",
4
+ "resources.users.description": "管理可以登录并使用此 NocoBase 应用的用户。",
5
+ "users.fields.id": "ID",
6
+ "users.fields.nickname": "昵称",
7
+ "users.fields.username": "用户名",
8
+ "users.fields.email": "邮箱",
9
+ "users.fields.phone": "手机号",
10
+ "users.fields.password": "密码",
11
+ "users.fields.roles": "角色",
12
+ "users.fields.createdAt": "创建时间",
13
+ "users.fields.updatedAt": "更新时间",
14
+ "users.fields.actions": "操作",
15
+ "users.filters.roles.placeholder": "选择角色…",
16
+ "users.filters.roles.noResults": "未找到角色。",
17
+ "users.actions.view": "查看用户",
18
+ "users.actions.edit": "编辑用户",
19
+ "users.actions.delete": "删除用户",
20
+ "users.drawer.create.title": "新建用户",
21
+ "users.drawer.create.description": "添加可以登录此 NocoBase 应用的用户。",
22
+ "users.drawer.edit.title": "编辑用户",
23
+ "users.drawer.edit.description": "更新用户的身份和联系方式。",
24
+ "users.drawer.show.description": "查看用户的身份、联系方式和关联角色。",
25
+ "users.form.create.submit": "创建用户",
26
+ "users.form.create.submitting": "正在创建...",
27
+ "users.form.edit.submit": "保存修改",
28
+ "users.form.edit.submitting": "正在保存...",
29
+ "users.form.cancel": "取消",
30
+ "users.form.nickname.placeholder": "输入显示名称",
31
+ "users.form.username.placeholder": "输入唯一用户名",
32
+ "users.form.email.placeholder": "输入邮箱地址",
33
+ "users.form.phone.placeholder": "输入手机号",
34
+ "users.form.password.placeholder": "设置初始密码",
35
+ "users.form.password.aiNotice":
36
+ "密码不会包含在 AI 页面上下文中,Form filler 也无法填写密码。",
37
+ "users.validation.username": "请输入用户名",
38
+ "users.validation.email": "请输入邮箱",
39
+ "users.validation.emailFormat": "请输入有效的邮箱地址",
40
+ "users.validation.phone": "请输入手机号",
41
+ "users.validation.password": "请输入密码",
42
+ "users.detail.identity": "身份信息",
43
+ "users.detail.contact": "联系方式",
44
+ "users.detail.access": "访问权限",
45
+ "users.detail.timestamps": "时间信息",
46
+ "users.detail.noRoles": "尚未分配角色",
47
+ "users.detail.unnamed": "未命名用户",
48
+ "users.detail.loadError.title": "无法加载用户",
49
+ "users.detail.loadError.description":
50
+ "该用户可能已不存在,或者你没有权限查看此用户。",
51
+ "users.ai.table": "用户表格",
52
+ "users.ai.detail": "用户详情",
53
+ "users.ai.createForm": "新建用户表单",
54
+ "users.ai.editForm": "编辑用户表单",
55
+ "common.yes": "是",
56
+ "common.no": "否",
57
+ "roles.drawer.description": "查看与当前用户关联的角色信息。",
58
+ "roles.fields.name": "名称",
59
+ "roles.fields.title": "标题",
60
+ "roles.fields.description": "描述",
61
+ "roles.fields.default": "默认角色",
62
+ "roles.fields.hidden": "隐藏",
63
+ "roles.fields.allowConfigure": "允许配置",
64
+ "roles.fields.createdAt": "创建时间",
65
+ "roles.fields.updatedAt": "更新时间",
66
+ "roles.detail.identity": "角色标识",
67
+ "roles.detail.settings": "角色设置",
68
+ "roles.detail.actions": "允许的操作",
69
+ "roles.detail.timestamps": "时间信息",
70
+ "roles.detail.unavailable":
71
+ "无法加载更多角色详情,当前展示用户关联数据中已有的信息。",
72
+ "roles.ai.detail": "角色详情",
73
+ } as const;
@@ -0,0 +1,67 @@
1
+ import { useCallback, type RefCallback } from "react";
2
+
3
+ export type AIFormField = {
4
+ name: string;
5
+ title?: string;
6
+ type?: string;
7
+ description?: string;
8
+ readonly?: boolean;
9
+ required?: boolean;
10
+ enum?: unknown;
11
+ [key: string]: unknown;
12
+ };
13
+
14
+ type AIFormDescriptor = {
15
+ id: string;
16
+ title: string;
17
+ fields: AIFormField[];
18
+ getValues: () => unknown | Promise<unknown>;
19
+ setValues: (values: Record<string, unknown>) => void | Promise<void>;
20
+ };
21
+
22
+ type AIPageElementDescriptor = {
23
+ id: string;
24
+ title: string;
25
+ kind: string;
26
+ getContext: () => unknown | Promise<unknown>;
27
+ };
28
+
29
+ type AIPageElementHandle = {
30
+ ref: RefCallback<HTMLElement>;
31
+ context: unknown;
32
+ };
33
+
34
+ type OptionalAIModule = {
35
+ useAIForm?: (descriptor: AIFormDescriptor) => RefCallback<HTMLElement>;
36
+ useAIPageElementHandle?: (
37
+ descriptor: AIPageElementDescriptor
38
+ ) => AIPageElementHandle;
39
+ };
40
+
41
+ const aiModules = import.meta.glob<OptionalAIModule>(
42
+ "../nocobase-ai/index.ts",
43
+ { eager: true }
44
+ );
45
+ const aiModule = Object.values(aiModules)[0];
46
+
47
+ function useFallbackAIForm() {
48
+ return useCallback<RefCallback<HTMLElement>>(() => undefined, []);
49
+ }
50
+
51
+ function useFallbackAIPageElementHandle(
52
+ descriptor: AIPageElementDescriptor
53
+ ): AIPageElementHandle {
54
+ const ref = useCallback<RefCallback<HTMLElement>>(() => undefined, []);
55
+ return {
56
+ ref,
57
+ context: {
58
+ id: descriptor.id,
59
+ title: descriptor.title,
60
+ kind: descriptor.kind,
61
+ },
62
+ };
63
+ }
64
+
65
+ export const useAIForm = aiModule?.useAIForm ?? useFallbackAIForm;
66
+ export const useAIPageElementHandle =
67
+ aiModule?.useAIPageElementHandle ?? useFallbackAIPageElementHandle;
@@ -5,7 +5,7 @@ import { LoadingState } from "@/components/app-shell/loading-state";
5
5
  import { Alert, AlertDescription } from "@/components/ui/alert";
6
6
  import { Badge } from "@/components/ui/badge";
7
7
  import { Separator } from "@/components/ui/separator";
8
- import { useAIPageElementHandle } from "@/extensions/nocobase-ai";
8
+ import { useAIPageElementHandle } from "./optional-ai";
9
9
  import { RouteDrawer } from "@/extensions/nocobase-route-surfaces";
10
10
  import type { Role } from "@/lib/nocobase/acl";
11
11
  import { resolveTranslatableText } from "@/lib/i18n";
@@ -13,11 +13,7 @@ import { resolveRoleLabel } from "./role-utils";
13
13
  import { getUserShowPath, userRoutes } from "./routes";
14
14
  import type { RoleRecord } from "./types";
15
15
 
16
- export function RoleDetailRoute({
17
- returnTo,
18
- }: {
19
- returnTo: "list" | "show";
20
- }) {
16
+ export function RoleDetailRoute({ returnTo }: { returnTo: "list" | "show" }) {
21
17
  const { id, roleName } = useParams<{
22
18
  id?: string;
23
19
  roleName: string;
@@ -26,16 +22,9 @@ export function RoleDetailRoute({
26
22
  if (!roleName) return null;
27
23
 
28
24
  const closeTarget =
29
- returnTo === "show" && id
30
- ? getUserShowPath(id)
31
- : userRoutes.list;
25
+ returnTo === "show" && id ? getUserShowPath(id) : userRoutes.list;
32
26
 
33
- return (
34
- <RoleDetailDrawer
35
- role={{ name: roleName }}
36
- closeTo={closeTarget}
37
- />
38
- );
27
+ return <RoleDetailDrawer role={{ name: roleName }} closeTo={closeTarget} />;
39
28
  }
40
29
 
41
30
  export function RoleDetailDrawer({
@@ -17,8 +17,5 @@ export const getUserShowPath = (id: string | number) =>
17
17
  export const getRolePath = (roleName: string) =>
18
18
  `/users/roles/${encodeURIComponent(roleName)}`;
19
19
 
20
- export const getUserRolePath = (
21
- id: string | number,
22
- roleName: string
23
- ) =>
20
+ export const getUserRolePath = (id: string | number, roleName: string) =>
24
21
  `/users/show/${encodeURIComponent(id)}/roles/${encodeURIComponent(roleName)}`;
@@ -0,0 +1,266 @@
1
+ import { useGetLocale, useShow, useTranslate } from "@refinedev/core";
2
+ import { useNavigate, useOutlet, useParams } from "react-router";
3
+ import { Pencil, RotateCw } from "lucide-react";
4
+
5
+ import { LoadingState } from "@/components/app-shell/loading-state";
6
+ import { EditButton } from "@/components/resources/buttons/edit";
7
+ import { RefreshButton } from "@/components/resources/buttons/refresh";
8
+ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
9
+ import { Separator } from "@/components/ui/separator";
10
+ import { Skeleton } from "@/components/ui/skeleton";
11
+ import { RouteDrawer } from "@/extensions/nocobase-route-surfaces";
12
+ import { useAIPageElementHandle } from "./optional-ai";
13
+ import { RoleBadges } from "./role-badges";
14
+ import { resolveRoleLabel } from "./role-utils";
15
+ import { getUserRolePath, userRoutes } from "./routes";
16
+ import type { UserRecord } from "./types";
17
+
18
+ export const UserShow = () => {
19
+ const translate = useTranslate();
20
+ const getLocale = useGetLocale();
21
+ const locale = getLocale();
22
+ const navigate = useNavigate();
23
+ const { id } = useParams<{ id: string }>();
24
+ const nestedDrawer = useOutlet();
25
+ const { result: record, query } = useShow<UserRecord>({
26
+ resource: "users",
27
+ id,
28
+ meta: {
29
+ appends: ["roles"],
30
+ },
31
+ });
32
+
33
+ const displayName =
34
+ record?.nickname ||
35
+ record?.username ||
36
+ record?.email ||
37
+ translate("users.detail.unnamed", { ns: "app" }, "Unnamed user");
38
+ const roles = record?.roles ?? [];
39
+ const detailContext = useAIPageElementHandle({
40
+ id: `users-detail-${id ?? "current"}`,
41
+ title: `${translate(
42
+ "users.ai.detail",
43
+ { ns: "app" },
44
+ "User details"
45
+ )}: ${displayName}`,
46
+ kind: "detail",
47
+ getContext: () => ({
48
+ resource: "users",
49
+ record: {
50
+ id: record?.id,
51
+ nickname: record?.nickname,
52
+ username: record?.username,
53
+ email: record?.email,
54
+ phone: record?.phone,
55
+ roles: roles.map((role) => ({
56
+ name: role.name,
57
+ title: resolveRoleLabel(role),
58
+ })),
59
+ createdAt: record?.createdAt,
60
+ updatedAt: record?.updatedAt,
61
+ },
62
+ }),
63
+ });
64
+
65
+ const formatDate = (value?: string) =>
66
+ value
67
+ ? new Intl.DateTimeFormat(locale, {
68
+ dateStyle: "medium",
69
+ timeStyle: "short",
70
+ }).format(new Date(value))
71
+ : "-";
72
+
73
+ return (
74
+ <RouteDrawer
75
+ title={
76
+ query.isLoading && !record ? (
77
+ <Skeleton className="h-6 w-40" />
78
+ ) : (
79
+ displayName
80
+ )
81
+ }
82
+ description={translate(
83
+ "users.drawer.show.description",
84
+ { ns: "app" },
85
+ "Review this user's identity, contact information, and roles."
86
+ )}
87
+ closeLabel={translate("buttons.close", "Close")}
88
+ closeTo={userRoutes.list}
89
+ nested={nestedDrawer}
90
+ actions={
91
+ record ? (
92
+ <>
93
+ <RefreshButton
94
+ resource="users"
95
+ recordItemId={record.id}
96
+ variant="outline"
97
+ size="icon-sm"
98
+ aria-label={translate("buttons.refresh", "Refresh")}
99
+ title={translate("buttons.refresh", "Refresh")}
100
+ >
101
+ <RotateCw />
102
+ </RefreshButton>
103
+ <EditButton
104
+ resource="users"
105
+ recordItemId={record.id}
106
+ variant="outline"
107
+ size="icon-sm"
108
+ aria-label={translate(
109
+ "users.actions.edit",
110
+ { ns: "app" },
111
+ "Edit user"
112
+ )}
113
+ title={translate(
114
+ "users.actions.edit",
115
+ { ns: "app" },
116
+ "Edit user"
117
+ )}
118
+ onClick={() => navigate("edit")}
119
+ >
120
+ <Pencil />
121
+ </EditButton>
122
+ </>
123
+ ) : null
124
+ }
125
+ >
126
+ <div
127
+ ref={detailContext.ref}
128
+ className="min-h-0 flex-1 overflow-y-auto px-5 py-5"
129
+ >
130
+ {query.isLoading ? (
131
+ <LoadingState className="min-h-64" />
132
+ ) : query.isError ? (
133
+ <Alert variant="destructive">
134
+ <AlertTitle>
135
+ {translate(
136
+ "users.detail.loadError.title",
137
+ { ns: "app" },
138
+ "Unable to load user"
139
+ )}
140
+ </AlertTitle>
141
+ <AlertDescription>
142
+ {translate(
143
+ "users.detail.loadError.description",
144
+ { ns: "app" },
145
+ "The user may no longer exist, or you may not have permission to view it."
146
+ )}
147
+ </AlertDescription>
148
+ </Alert>
149
+ ) : (
150
+ <div className="space-y-5">
151
+ <DetailSection
152
+ title={translate(
153
+ "users.detail.identity",
154
+ { ns: "app" },
155
+ "Identity"
156
+ )}
157
+ items={[
158
+ [
159
+ translate("users.fields.id", { ns: "app" }, "ID"),
160
+ record?.id ?? "-",
161
+ ],
162
+ [
163
+ translate("users.fields.nickname", { ns: "app" }, "Nickname"),
164
+ record?.nickname || "-",
165
+ ],
166
+ [
167
+ translate("users.fields.username", { ns: "app" }, "Username"),
168
+ record?.username || "-",
169
+ ],
170
+ ]}
171
+ />
172
+
173
+ <Separator />
174
+
175
+ <DetailSection
176
+ title={translate(
177
+ "users.detail.contact",
178
+ { ns: "app" },
179
+ "Contact"
180
+ )}
181
+ items={[
182
+ [
183
+ translate("users.fields.email", { ns: "app" }, "Email"),
184
+ record?.email || "-",
185
+ ],
186
+ [
187
+ translate("users.fields.phone", { ns: "app" }, "Phone"),
188
+ record?.phone || "-",
189
+ ],
190
+ ]}
191
+ />
192
+
193
+ <Separator />
194
+
195
+ <section className="space-y-3">
196
+ <h3 className="text-sm font-medium">
197
+ {translate("users.detail.access", { ns: "app" }, "Access")}
198
+ </h3>
199
+ <RoleBadges
200
+ roles={roles}
201
+ onSelect={(role) =>
202
+ id ? navigate(getUserRolePath(id, role.name)) : undefined
203
+ }
204
+ empty={translate(
205
+ "users.detail.noRoles",
206
+ { ns: "app" },
207
+ "No assigned roles"
208
+ )}
209
+ />
210
+ </section>
211
+
212
+ <Separator />
213
+
214
+ <DetailSection
215
+ title={translate(
216
+ "users.detail.timestamps",
217
+ { ns: "app" },
218
+ "Timestamps"
219
+ )}
220
+ items={[
221
+ [
222
+ translate(
223
+ "users.fields.createdAt",
224
+ { ns: "app" },
225
+ "Created at"
226
+ ),
227
+ formatDate(record?.createdAt),
228
+ ],
229
+ [
230
+ translate(
231
+ "users.fields.updatedAt",
232
+ { ns: "app" },
233
+ "Updated at"
234
+ ),
235
+ formatDate(record?.updatedAt),
236
+ ],
237
+ ]}
238
+ />
239
+ </div>
240
+ )}
241
+ </div>
242
+ </RouteDrawer>
243
+ );
244
+ };
245
+
246
+ function DetailSection({
247
+ title,
248
+ items,
249
+ }: {
250
+ title: string;
251
+ items: Array<[label: string, value: string | number]>;
252
+ }) {
253
+ return (
254
+ <section className="space-y-3">
255
+ <h3 className="text-sm font-medium">{title}</h3>
256
+ <dl className="grid gap-4 sm:grid-cols-2">
257
+ {items.map(([label, value]) => (
258
+ <div key={label} className="space-y-1">
259
+ <dt className="text-xs text-muted-foreground">{label}</dt>
260
+ <dd className="text-sm font-medium break-words">{value}</dd>
261
+ </div>
262
+ ))}
263
+ </dl>
264
+ </section>
265
+ );
266
+ }
@@ -0,0 +1,32 @@
1
+ import { createContext, useContext, useSyncExternalStore } from "react";
2
+
3
+ import type { RecordPermissionResolver } from "./evaluator";
4
+ import type { AclState } from "./types";
5
+
6
+ export type AclStore = {
7
+ getState: () => AclState;
8
+ subscribe: (listener: () => void) => () => void;
9
+ load: () => Promise<AclState>;
10
+ retry: () => Promise<AclState>;
11
+ clear: () => void;
12
+ recordPermissions: {
13
+ getState: () => object;
14
+ subscribe: (listener: () => void) => () => void;
15
+ getPermission: RecordPermissionResolver;
16
+ };
17
+ };
18
+
19
+ export const AclStoreContext = createContext<AclStore | undefined>(undefined);
20
+
21
+ export const useAclStore = () => {
22
+ const store = useContext(AclStoreContext);
23
+ if (!store) {
24
+ throw new Error("useAclStore must be used within an AclStoreProvider");
25
+ }
26
+ return store;
27
+ };
28
+
29
+ export const useAclState = () => {
30
+ const store = useAclStore();
31
+ return useSyncExternalStore(store.subscribe, store.getState, store.getState);
32
+ };