@nocobase/portal-template-default 1.0.2 → 1.0.4

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 (62) hide show
  1. package/README.MD +10 -5
  2. package/package.json +1 -1
  3. package/registry/nocobase-ai/components/chat/chat-message.tsx +8 -6
  4. package/registry/nocobase-ai/components/chat/chat-messages.tsx +8 -3
  5. package/registry/nocobase-ai/components/chat/markdown-message.tsx +26 -0
  6. package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +3 -3
  7. package/registry/nocobase-ai/demo/configuration-gate.tsx +83 -35
  8. package/registry/nocobase-i18n/README.md +23 -32
  9. package/registry/nocobase-i18n/components/language-switcher.tsx +1 -1
  10. package/registry/nocobase-i18n/demo/index.tsx +2 -2
  11. package/registry/nocobase-i18n/demo/prompt-generator.tsx +3 -2
  12. package/registry/nocobase-i18n/extension.tsx +3 -4
  13. package/registry/nocobase-i18n/index.ts +4 -3
  14. package/registry/nocobase-i18n/locales/en-US.ts +2 -52
  15. package/registry/nocobase-i18n/locales/index.ts +8 -0
  16. package/registry/nocobase-i18n/locales/zh-CN.ts +2 -52
  17. package/registry/nocobase-i18n/provider.tsx +18 -6
  18. package/registry/nocobase-i18n/server-resources.ts +13 -31
  19. package/registry/nocobase-i18n/tests/i18n-regression.mjs +14 -5
  20. package/registry/nocobase-route-surfaces/README.md +21 -0
  21. package/registry/nocobase-route-surfaces/demo/index.tsx +266 -0
  22. package/registry/nocobase-route-surfaces/demo/lazy-route.tsx +11 -0
  23. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +90 -0
  24. package/registry/nocobase-route-surfaces/demo/scenarios.ts +95 -0
  25. package/registry/nocobase-route-surfaces/extension.tsx +119 -0
  26. package/registry/nocobase-route-surfaces/index.ts +7 -0
  27. package/registry/nocobase-route-surfaces/route-dialog.tsx +145 -0
  28. package/registry/nocobase-route-surfaces/route-drawer.tsx +186 -0
  29. package/registry/nocobase-route-surfaces/route-page.tsx +29 -0
  30. package/registry/nocobase-route-surfaces/route-surface-context.ts +13 -0
  31. package/registry/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +106 -0
  32. package/registry/nocobase-route-surfaces/use-route-surface-close.ts +15 -0
  33. package/registry/nocobase-route-surfaces/use-route-surface-state.ts +65 -0
  34. package/registry.config.json +30 -3
  35. package/src/App.tsx +65 -35
  36. package/src/app/extension.ts +1 -2
  37. package/src/app/extensions.tsx +0 -14
  38. package/src/components/app-shell/breadcrumb.tsx +67 -2
  39. package/src/components/app-shell/sidebar.tsx +2 -2
  40. package/src/components/resources/views/list-view.tsx +3 -2
  41. package/src/components/ui/sheet.tsx +22 -21
  42. package/src/lib/nocobase/client.ts +6 -1
  43. package/src/locales/en-US.ts +81 -0
  44. package/src/locales/zh-CN.ts +77 -0
  45. package/src/pages/users/create.tsx +76 -53
  46. package/src/pages/users/edit.tsx +87 -56
  47. package/src/pages/users/index.ts +3 -0
  48. package/src/pages/users/layout.tsx +15 -0
  49. package/src/pages/users/list.tsx +19 -21
  50. package/src/pages/users/role-badges.tsx +43 -0
  51. package/src/pages/users/role-detail.tsx +272 -0
  52. package/src/pages/users/role-utils.ts +6 -0
  53. package/src/pages/users/routes.ts +24 -0
  54. package/src/pages/users/show.tsx +195 -104
  55. package/src/pages/users/types.ts +13 -0
  56. package/src/providers/i18n/index.ts +3 -0
  57. package/{registry/nocobase-i18n → src/providers/i18n}/runtime.ts +63 -16
  58. package/src/providers/system-settings/context.ts +26 -0
  59. package/src/providers/system-settings/index.ts +2 -0
  60. package/src/providers/system-settings/provider.tsx +94 -0
  61. /package/{registry/nocobase-i18n → src/providers/i18n}/i18n-provider.ts +0 -0
  62. /package/{registry/nocobase-i18n → src/providers/i18n}/locale-store.ts +0 -0
@@ -3,6 +3,7 @@ import { createInstance, type TOptions } from "i18next";
3
3
  import {
4
4
  getTranslationResources,
5
5
  registerTranslationResources,
6
+ setTranslationResolver,
6
7
  subscribeTranslationResources,
7
8
  type TranslationOptions,
8
9
  } from "@/lib/i18n";
@@ -14,15 +15,23 @@ import {
14
15
  getLocaleLabel,
15
16
  registerLocale,
16
17
  resolveSupportedLocale,
18
+ setEnabledLocales,
17
19
  } from "./locale-store";
18
- import enUS from "./locales/en-US";
19
- import zhCN from "./locales/zh-CN";
20
20
 
21
21
  export type LocaleResources = Record<
22
22
  string,
23
23
  Record<string, string | number | boolean>
24
24
  >;
25
25
 
26
+ export type LocaleSystemSettings = {
27
+ appLang?: string | null;
28
+ enabledLanguages?: string[] | null;
29
+ };
30
+
31
+ type LocalePersistence = (locale: string) => void | Promise<void>;
32
+
33
+ let localePersistence: LocalePersistence | undefined;
34
+
26
35
  export const i18n = createInstance();
27
36
 
28
37
  void i18n.init({
@@ -36,10 +45,6 @@ void i18n.init({
36
45
  interpolation: {
37
46
  escapeValue: false,
38
47
  },
39
- resources: {
40
- "en-US": enUS,
41
- "zh-CN": zhCN,
42
- },
43
48
  });
44
49
 
45
50
  function addLocaleResources(namespace: string, resources: LocaleResources) {
@@ -56,7 +61,6 @@ function addLocaleResources(namespace: string, resources: LocaleResources) {
56
61
  getTranslationResources().forEach(([namespace, resources]) =>
57
62
  addLocaleResources(namespace, resources)
58
63
  );
59
-
60
64
  subscribeTranslationResources(addLocaleResources);
61
65
 
62
66
  export function getCurrentLocale() {
@@ -94,18 +98,60 @@ export function applyDocumentLocale(locale = getCurrentLocale()) {
94
98
  document.documentElement.dir = direction;
95
99
  }
96
100
 
101
+ function resolveSystemLocale(settings?: LocaleSystemSettings) {
102
+ const enabledLanguages = Array.isArray(settings?.enabledLanguages)
103
+ ? settings.enabledLanguages.filter(Boolean)
104
+ : [];
105
+ const storedLocale = nocobaseClient.getStoredLocale();
106
+
107
+ if (
108
+ storedLocale &&
109
+ (!enabledLanguages.length || enabledLanguages.includes(storedLocale))
110
+ ) {
111
+ return resolveSupportedLocale(storedLocale);
112
+ }
113
+
114
+ const defaultLocale =
115
+ settings?.appLang || enabledLanguages[0] || DEFAULT_LOCALE;
116
+ return resolveSupportedLocale(defaultLocale);
117
+ }
118
+
119
+ export async function applySystemLocale(settings?: LocaleSystemSettings) {
120
+ const enabledLanguages = Array.isArray(settings?.enabledLanguages)
121
+ ? settings.enabledLanguages.filter(Boolean)
122
+ : [];
123
+ if (enabledLanguages.length) setEnabledLocales(enabledLanguages);
124
+
125
+ const storedLocale = nocobaseClient.getStoredLocale();
126
+ if (
127
+ storedLocale &&
128
+ enabledLanguages.length &&
129
+ !enabledLanguages.includes(storedLocale)
130
+ ) {
131
+ nocobaseClient.setLocale(null);
132
+ }
133
+
134
+ const locale = resolveSystemLocale(settings);
135
+ nocobaseClient.setRuntimeLocale(locale);
136
+ await i18n.changeLanguage(locale);
137
+ applyDocumentLocale(locale);
138
+ return locale;
139
+ }
140
+
141
+ export function setLocalePersistence(persistence?: LocalePersistence) {
142
+ localePersistence = persistence;
143
+ return () => {
144
+ if (localePersistence === persistence) localePersistence = undefined;
145
+ };
146
+ }
147
+
97
148
  export async function changeLocale(locale: string) {
98
149
  const nextLocale = resolveSupportedLocale(locale);
99
150
 
100
- if (nocobaseClient.getToken()) {
101
- try {
102
- await nocobaseClient.action("users", "updateLang", {
103
- method: "POST",
104
- body: { appLang: nextLocale },
105
- });
106
- } catch (error) {
107
- console.warn("Unable to persist the NocoBase language preference", error);
108
- }
151
+ try {
152
+ await localePersistence?.(nextLocale);
153
+ } catch (error) {
154
+ console.warn("Unable to persist the language preference", error);
109
155
  }
110
156
 
111
157
  nocobaseClient.setLocale(nextLocale);
@@ -115,4 +161,5 @@ export async function changeLocale(locale: string) {
115
161
  if (typeof window !== "undefined") window.location.reload();
116
162
  }
117
163
 
164
+ setTranslationResolver(translate);
118
165
  applyDocumentLocale();
@@ -0,0 +1,26 @@
1
+ import { createContext, useContext } from "react";
2
+
3
+ export type SystemSettings = Record<string, unknown> & {
4
+ appLang?: string | null;
5
+ enabledLanguages?: string[] | null;
6
+ };
7
+
8
+ export type SystemSettingsContextValue = {
9
+ settings?: SystemSettings;
10
+ error?: Error;
11
+ loading: boolean;
12
+ refresh: () => Promise<SystemSettings | undefined>;
13
+ };
14
+
15
+ export const SystemSettingsContext =
16
+ createContext<SystemSettingsContextValue | null>(null);
17
+
18
+ export function useSystemSettings() {
19
+ const value = useContext(SystemSettingsContext);
20
+ if (!value) {
21
+ throw new Error(
22
+ "useSystemSettings must be used inside SystemSettingsProvider"
23
+ );
24
+ }
25
+ return value;
26
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./context";
2
+ export * from "./provider";
@@ -0,0 +1,94 @@
1
+ import {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useState,
6
+ type PropsWithChildren,
7
+ } from "react";
8
+
9
+ import { LoadingState } from "@/components/app-shell/loading-state";
10
+ import { nocobaseClient } from "@/lib/nocobase/client";
11
+ import { applySystemLocale } from "../i18n";
12
+ import {
13
+ SystemSettingsContext,
14
+ type SystemSettings,
15
+ type SystemSettingsContextValue,
16
+ } from "./context";
17
+
18
+ let cachedSettings: SystemSettings | undefined;
19
+ let settingsRequest: Promise<SystemSettings> | undefined;
20
+
21
+ function requestSystemSettings(force = false) {
22
+ if (!force && cachedSettings) return Promise.resolve(cachedSettings);
23
+ if (!force && settingsRequest) return settingsRequest;
24
+
25
+ const request = nocobaseClient
26
+ .action<SystemSettings>("systemSettings", "get", {
27
+ method: "GET",
28
+ includeRole: false,
29
+ withAclMeta: false,
30
+ })
31
+ .then((settings) => {
32
+ cachedSettings = settings;
33
+ return settings;
34
+ });
35
+
36
+ settingsRequest = request;
37
+ const clearRequest = () => {
38
+ if (settingsRequest === request) settingsRequest = undefined;
39
+ };
40
+ void request.then(clearRequest, clearRequest);
41
+ return request;
42
+ }
43
+
44
+ export function SystemSettingsProvider({ children }: PropsWithChildren) {
45
+ const [settings, setSettings] = useState(cachedSettings);
46
+ const [error, setError] = useState<Error>();
47
+ const [ready, setReady] = useState(false);
48
+ const [loading, setLoading] = useState(true);
49
+
50
+ const load = useCallback(async (force = false) => {
51
+ setLoading(true);
52
+ try {
53
+ const nextSettings = await requestSystemSettings(force);
54
+ await applySystemLocale(nextSettings);
55
+ setSettings(nextSettings);
56
+ setError(undefined);
57
+ return nextSettings;
58
+ } catch (reason) {
59
+ const nextError =
60
+ reason instanceof Error
61
+ ? reason
62
+ : new Error("Unable to load system settings");
63
+ console.warn("Unable to load NocoBase system settings", reason);
64
+ setError(nextError);
65
+ await applySystemLocale();
66
+ return undefined;
67
+ } finally {
68
+ setLoading(false);
69
+ setReady(true);
70
+ }
71
+ }, []);
72
+
73
+ useEffect(() => {
74
+ void load();
75
+ }, [load]);
76
+
77
+ const value = useMemo<SystemSettingsContextValue>(
78
+ () => ({
79
+ settings,
80
+ error,
81
+ loading,
82
+ refresh: () => load(true),
83
+ }),
84
+ [error, load, loading, settings]
85
+ );
86
+
87
+ if (!ready) return <LoadingState className="min-h-svh" />;
88
+
89
+ return (
90
+ <SystemSettingsContext.Provider value={value}>
91
+ {children}
92
+ </SystemSettingsContext.Provider>
93
+ );
94
+ }