@nocobase/portal-template-default 1.0.1 → 1.0.3
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/README.MD +22 -3
- package/package.json +4 -2
- package/registry/nocobase-acl/components/role-options.ts +7 -0
- package/registry/nocobase-acl/components/role-switcher.tsx +9 -4
- package/registry/nocobase-acl/demo/boundary-api.tsx +87 -16
- package/registry/nocobase-acl/demo/components.tsx +32 -11
- package/registry/nocobase-acl/demo/index.tsx +45 -14
- package/registry/nocobase-acl/demo/lazy-route.tsx +2 -7
- package/registry/nocobase-acl/demo/prompt-generator.tsx +52 -33
- package/registry/nocobase-acl/demo/role-switcher-prompt-generator.tsx +28 -9
- package/registry/nocobase-acl/extension.tsx +1 -1
- package/registry/nocobase-acl/tests/acl-regression.mjs +7 -6
- package/registry/nocobase-ai/components/chat/chat-messages.tsx +3 -4
- package/registry/nocobase-ai/components/chat/conversation-list.tsx +2 -4
- package/registry/nocobase-ai/components/tools/business-report-dialog.tsx +16 -19
- package/registry/nocobase-ai/components/tools/chart-renderer.tsx +3 -14
- package/registry/nocobase-ai/components/tools/echarts-preview.tsx +2 -5
- package/registry/nocobase-ai/demo/configuration-gate.tsx +9 -12
- package/registry/nocobase-ai/extension.tsx +2 -7
- package/registry/nocobase-i18n/README.md +53 -0
- package/registry/nocobase-i18n/components/index.ts +1 -0
- package/registry/nocobase-i18n/components/language-switcher.tsx +138 -0
- package/registry/nocobase-i18n/demo/index.tsx +209 -0
- package/registry/nocobase-i18n/demo/prompt-generator.tsx +155 -0
- package/registry/nocobase-i18n/extension.tsx +46 -0
- package/registry/nocobase-i18n/i18n-provider.ts +9 -0
- package/registry/nocobase-i18n/index.ts +6 -0
- package/registry/nocobase-i18n/locale-store.ts +134 -0
- package/registry/nocobase-i18n/locales/en-US.ts +89 -0
- package/registry/nocobase-i18n/locales/zh-CN.ts +85 -0
- package/registry/nocobase-i18n/provider.tsx +32 -0
- package/registry/nocobase-i18n/runtime.ts +118 -0
- package/registry/nocobase-i18n/server-resources.ts +184 -0
- package/registry/nocobase-i18n/tests/i18n-regression.mjs +42 -0
- package/registry.config.json +34 -14
- package/scripts/registry.mjs +5 -0
- package/src/App.tsx +27 -86
- package/src/app/extension.ts +3 -1
- package/src/app/extensions.tsx +21 -0
- package/src/components/access-control/acl-gate.tsx +4 -10
- package/src/components/app-shell/breadcrumb.tsx +39 -3
- package/src/components/app-shell/document-title-handler.tsx +11 -20
- package/src/components/app-shell/header.tsx +18 -3
- package/src/components/app-shell/loading-overlay.tsx +2 -4
- package/src/components/app-shell/loading-state.tsx +15 -0
- package/src/components/app-shell/sidebar.tsx +75 -47
- package/src/components/data-table/data-table-pagination.tsx +27 -7
- package/src/components/data-table/data-table-styles.ts +57 -0
- package/src/components/data-table/data-table.tsx +14 -59
- package/src/components/resources/resource-label.ts +101 -0
- package/src/components/resources/views/create-view.tsx +17 -14
- package/src/components/resources/views/edit-view.tsx +17 -15
- package/src/components/resources/views/list-view.tsx +20 -10
- package/src/components/resources/views/show-view.tsx +20 -18
- package/src/extensions/nocobase-acl/README.md +19 -0
- package/src/extensions/nocobase-acl/components/acl-boundary.tsx +106 -0
- package/src/extensions/nocobase-acl/components/index.ts +3 -0
- package/src/extensions/nocobase-acl/components/role-options.ts +36 -0
- package/src/extensions/nocobase-acl/components/role-switcher.tsx +135 -0
- package/src/extensions/nocobase-acl/demo/boundary-api.tsx +186 -0
- package/src/extensions/nocobase-acl/demo/components.tsx +231 -0
- package/src/extensions/nocobase-acl/demo/index.tsx +505 -0
- package/src/extensions/nocobase-acl/demo/lazy-route.tsx +10 -0
- package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +134 -0
- package/src/extensions/nocobase-acl/demo/role-switcher-prompt-generator.tsx +198 -0
- package/src/extensions/nocobase-acl/demo/scenario-section.tsx +37 -0
- package/src/extensions/nocobase-acl/extension.tsx +71 -0
- package/src/extensions/nocobase-acl/index.ts +1 -0
- package/src/extensions/nocobase-ai/README.md +70 -0
- package/src/extensions/nocobase-ai/adapters/react-hook-form.ts +20 -0
- package/src/extensions/nocobase-ai/assets/nocobase-ai-chat.svg +50 -0
- package/src/extensions/nocobase-ai/components/ai-root-provider.tsx +32 -0
- package/src/extensions/nocobase-ai/components/chat/ai-employee-avatar.tsx +31 -0
- package/src/extensions/nocobase-ai/components/chat/chat-attachment.tsx +92 -0
- package/src/extensions/nocobase-ai/components/chat/chat-compact.tsx +90 -0
- package/src/extensions/nocobase-ai/components/chat/chat-composer.tsx +429 -0
- package/src/extensions/nocobase-ai/components/chat/chat-empty-state.tsx +51 -0
- package/src/extensions/nocobase-ai/components/chat/chat-header.tsx +88 -0
- package/src/extensions/nocobase-ai/components/chat/chat-history-dialog.tsx +77 -0
- package/src/extensions/nocobase-ai/components/chat/chat-message.tsx +252 -0
- package/src/extensions/nocobase-ai/components/chat/chat-messages.tsx +173 -0
- package/src/extensions/nocobase-ai/components/chat/chat-window.tsx +118 -0
- package/src/extensions/nocobase-ai/components/chat/conversation-list.tsx +343 -0
- package/src/extensions/nocobase-ai/components/chat/markdown-message.tsx +165 -0
- package/src/extensions/nocobase-ai/components/chat/model-select-options.tsx +30 -0
- package/src/extensions/nocobase-ai/components/chat/reasoning-panel.tsx +29 -0
- package/src/extensions/nocobase-ai/components/chat/sub-agent-conversation.tsx +142 -0
- package/src/extensions/nocobase-ai/components/chat/tool-call-card.tsx +316 -0
- package/src/extensions/nocobase-ai/components/chat/user-prompt-editor.tsx +99 -0
- package/src/extensions/nocobase-ai/components/chat/work-context-chip.tsx +114 -0
- package/src/extensions/nocobase-ai/components/index.ts +59 -0
- package/src/extensions/nocobase-ai/components/page-elements/ai-form.tsx +44 -0
- package/src/extensions/nocobase-ai/components/page-elements/page-element-provider.tsx +469 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +31 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-inline.tsx +18 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-page.tsx +18 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +54 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +74 -0
- package/src/extensions/nocobase-ai/components/surfaces/chat-surface-actions.tsx +54 -0
- package/src/extensions/nocobase-ai/components/tools/builtin-tool-renderers.tsx +31 -0
- package/src/extensions/nocobase-ai/components/tools/business-report-dialog.tsx +364 -0
- package/src/extensions/nocobase-ai/components/tools/business-report-renderer.tsx +122 -0
- package/src/extensions/nocobase-ai/components/tools/business-report-utils.tsx +282 -0
- package/src/extensions/nocobase-ai/components/tools/chart-renderer.tsx +99 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-preview.tsx +59 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-advanced.ts +18 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-common.ts +20 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-components.ts +34 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +24 -0
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime.ts +138 -0
- package/src/extensions/nocobase-ai/components/tools/sub-agent-renderer.tsx +67 -0
- package/src/extensions/nocobase-ai/components/tools/suggestions-renderer.tsx +59 -0
- package/src/extensions/nocobase-ai/components/tools/tool-renderer-provider.tsx +55 -0
- package/src/extensions/nocobase-ai/components/tools/tool-renderer-utils.ts +18 -0
- package/src/extensions/nocobase-ai/components/tools/workflow-renderer.tsx +108 -0
- package/src/extensions/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +73 -0
- package/src/extensions/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +149 -0
- package/src/extensions/nocobase-ai/demo/configuration-gate.tsx +49 -0
- package/src/extensions/nocobase-ai/demo/container-showcase.tsx +210 -0
- package/src/extensions/nocobase-ai/demo/floating.tsx +134 -0
- package/src/extensions/nocobase-ai/demo/index.tsx +346 -0
- package/src/extensions/nocobase-ai/demo/interaction-showcase.tsx +151 -0
- package/src/extensions/nocobase-ai/demo/page-context-prompt-generator.tsx +355 -0
- package/src/extensions/nocobase-ai/demo/page-context.tsx +884 -0
- package/src/extensions/nocobase-ai/demo/page-element-showcase.tsx +197 -0
- package/src/extensions/nocobase-ai/demo/prompt-card.tsx +41 -0
- package/src/extensions/nocobase-ai/demo/prompt-generator.tsx +452 -0
- package/src/extensions/nocobase-ai/demo/shortcut.tsx +1207 -0
- package/src/extensions/nocobase-ai/demo/tool-cards.tsx +530 -0
- package/src/extensions/nocobase-ai/extension.tsx +158 -0
- package/src/extensions/nocobase-ai/global-ai-chat.tsx +213 -0
- package/src/extensions/nocobase-ai/index.ts +3 -0
- package/src/extensions/nocobase-ai/providers/ai-provider.tsx +323 -0
- package/src/extensions/nocobase-ai/providers/avatars.ts +817 -0
- package/src/extensions/nocobase-ai/providers/chat-context.tsx +109 -0
- package/src/extensions/nocobase-ai/providers/chat-controller.ts +84 -0
- package/src/extensions/nocobase-ai/providers/chat-message-utils.ts +198 -0
- package/src/extensions/nocobase-ai/providers/chat-provider.tsx +871 -0
- package/src/extensions/nocobase-ai/providers/chat-reducer.ts +147 -0
- package/src/extensions/nocobase-ai/providers/chat-task-utils.ts +105 -0
- package/src/extensions/nocobase-ai/providers/chat-transport.ts +184 -0
- package/src/extensions/nocobase-ai/providers/form-registry.tsx +271 -0
- package/src/extensions/nocobase-ai/providers/frontend-tool-registry.tsx +231 -0
- package/src/extensions/nocobase-ai/providers/index.ts +61 -0
- package/src/extensions/nocobase-ai/providers/model.ts +32 -0
- package/src/extensions/nocobase-ai/providers/page-context.tsx +136 -0
- package/src/extensions/nocobase-ai/providers/stream-coalescer.ts +60 -0
- package/src/extensions/nocobase-ai/providers/stream-event-utils.ts +82 -0
- package/src/extensions/nocobase-ai/providers/stream-parser.ts +61 -0
- package/src/extensions/nocobase-ai/providers/sub-agent-stream.ts +316 -0
- package/src/extensions/nocobase-ai/providers/types.ts +183 -0
- package/src/extensions/nocobase-ai/providers/ui-message-stream.ts +463 -0
- package/src/extensions/nocobase-ai/providers/use-automatic-tool-approval.ts +94 -0
- package/src/extensions/nocobase-ai/providers/use-chat-attachments.ts +206 -0
- package/src/extensions/nocobase-ai/providers/use-chat-message-actions.ts +554 -0
- package/src/extensions/nocobase-ai/providers/use-chat-runtime.ts +210 -0
- package/src/extensions/nocobase-ai/providers/use-chat-work-context.ts +93 -0
- package/src/extensions/nocobase-ai/providers/use-conversation-catalog.ts +108 -0
- package/src/extensions/nocobase-ai/providers/use-conversation-history.ts +178 -0
- package/src/extensions/nocobase-ai/services/index.ts +7 -0
- package/src/extensions/nocobase-ai/services/nocobase-ai-service.ts +454 -0
- package/src/extensions/nocobase-ai/services/types.ts +82 -0
- package/src/index.tsx +2 -3
- package/src/lib/i18n.ts +161 -0
- package/src/lib/nocobase/client.ts +19 -2
- package/src/locales/en-US.ts +82 -0
- package/src/locales/index.ts +13 -0
- package/src/locales/zh-CN.ts +79 -0
- package/src/pages/users/create.tsx +98 -0
- package/src/pages/users/edit.tsx +92 -0
- package/src/pages/users/form-context.ts +57 -0
- package/src/pages/users/form-fields.tsx +211 -0
- package/src/pages/users/list.tsx +205 -0
- package/src/pages/users/show.tsx +187 -0
- package/src/pages/users/types.ts +20 -0
- package/src/providers/constants.ts +1 -0
- package/src/pages/blog-posts/create.tsx +0 -177
- package/src/pages/blog-posts/edit.tsx +0 -197
- package/src/pages/blog-posts/list.tsx +0 -131
- package/src/pages/blog-posts/show.tsx +0 -81
- package/src/pages/categories/create.tsx +0 -73
- package/src/pages/categories/edit.tsx +0 -73
- package/src/pages/categories/index.ts +0 -4
- package/src/pages/categories/list.tsx +0 -86
- package/src/pages/categories/show.tsx +0 -37
- /package/src/pages/{blog-posts → users}/index.ts +0 -0
package/src/App.tsx
CHANGED
|
@@ -5,18 +5,7 @@ import routerProvider, {
|
|
|
5
5
|
CatchAllNavigate,
|
|
6
6
|
UnsavedChangesNotifier,
|
|
7
7
|
} from "@refinedev/react-router";
|
|
8
|
-
import {
|
|
9
|
-
BlogPostList,
|
|
10
|
-
BlogPostCreate,
|
|
11
|
-
BlogPostEdit,
|
|
12
|
-
BlogPostShow,
|
|
13
|
-
} from "./pages/blog-posts";
|
|
14
|
-
import {
|
|
15
|
-
CategoryList,
|
|
16
|
-
CategoryCreate,
|
|
17
|
-
CategoryEdit,
|
|
18
|
-
CategoryShow,
|
|
19
|
-
} from "./pages/categories";
|
|
8
|
+
import { UserCreate, UserEdit, UserList, UserShow } from "./pages/users";
|
|
20
9
|
import { dataProvider } from "./providers/data";
|
|
21
10
|
import { Login } from "./pages/login";
|
|
22
11
|
import { Register } from "./pages/register";
|
|
@@ -31,6 +20,7 @@ import { TooltipProvider } from "./components/ui/tooltip";
|
|
|
31
20
|
import { BrandLogo } from "./components/app-shell/brand";
|
|
32
21
|
import {
|
|
33
22
|
AppExtensionProviders,
|
|
23
|
+
extensionI18nProvider,
|
|
34
24
|
extensionResources,
|
|
35
25
|
extensionRouteElements,
|
|
36
26
|
} from "./app/extensions";
|
|
@@ -40,38 +30,25 @@ import { accessControlProvider } from "./providers/access-control";
|
|
|
40
30
|
import { AclBootstrap } from "./components/access-control/acl-bootstrap";
|
|
41
31
|
import { ResourceAccessGuard } from "./components/access-control/resource-access-guard";
|
|
42
32
|
import { NavigateToAccessibleResource } from "./components/access-control/navigate-to-accessible-resource";
|
|
43
|
-
import {
|
|
33
|
+
import { UsersRound } from "lucide-react";
|
|
44
34
|
|
|
45
35
|
const coreResources: ResourceProps[] = [
|
|
46
36
|
{
|
|
47
|
-
name: "
|
|
48
|
-
list: "/
|
|
49
|
-
create: "/
|
|
50
|
-
edit: "/
|
|
51
|
-
show: "/
|
|
37
|
+
name: "users",
|
|
38
|
+
list: "/users",
|
|
39
|
+
create: "/users/create",
|
|
40
|
+
edit: "/users/edit/:id",
|
|
41
|
+
show: "/users/show/:id",
|
|
52
42
|
meta: {
|
|
53
|
-
label: "
|
|
54
|
-
|
|
43
|
+
label: "Users",
|
|
44
|
+
singularLabel: "User",
|
|
45
|
+
i18nKey: "resources.users",
|
|
46
|
+
i18nSingularKey: "resources.user",
|
|
47
|
+
i18nOptions: { ns: "app" },
|
|
48
|
+
descriptionI18nKey: "resources.users.description",
|
|
49
|
+
icon: <UsersRound />,
|
|
55
50
|
description:
|
|
56
|
-
"
|
|
57
|
-
canCreate: true,
|
|
58
|
-
canDelete: true,
|
|
59
|
-
acl: {
|
|
60
|
-
type: "collection",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: "categories",
|
|
66
|
-
list: "/categories",
|
|
67
|
-
create: "/categories/create",
|
|
68
|
-
edit: "/categories/edit/:id",
|
|
69
|
-
show: "/categories/show/:id",
|
|
70
|
-
meta: {
|
|
71
|
-
label: "Categories",
|
|
72
|
-
icon: <Tags />,
|
|
73
|
-
description:
|
|
74
|
-
"Organize reusable structures while NocoBase keeps the underlying data consistent.",
|
|
51
|
+
"Manage the people who can sign in and work in this NocoBase application.",
|
|
75
52
|
canCreate: true,
|
|
76
53
|
canDelete: true,
|
|
77
54
|
acl: {
|
|
@@ -94,6 +71,7 @@ function App() {
|
|
|
94
71
|
routerProvider={routerProvider}
|
|
95
72
|
authProvider={authProvider}
|
|
96
73
|
accessControlProvider={accessControlProvider}
|
|
74
|
+
i18nProvider={extensionI18nProvider}
|
|
97
75
|
resources={[...coreResources, ...extensionResources]}
|
|
98
76
|
options={{
|
|
99
77
|
syncWithLocation: true,
|
|
@@ -122,74 +100,37 @@ function App() {
|
|
|
122
100
|
</Authenticated>
|
|
123
101
|
}
|
|
124
102
|
>
|
|
125
|
-
<Route
|
|
126
|
-
|
|
127
|
-
element={<NavigateToAccessibleResource />}
|
|
128
|
-
/>
|
|
129
|
-
<Route path="/blog-posts">
|
|
130
|
-
<Route
|
|
131
|
-
index
|
|
132
|
-
element={
|
|
133
|
-
<ResourceAccessGuard resource="blog_posts" action="list">
|
|
134
|
-
<BlogPostList />
|
|
135
|
-
</ResourceAccessGuard>
|
|
136
|
-
}
|
|
137
|
-
/>
|
|
138
|
-
<Route
|
|
139
|
-
path="create"
|
|
140
|
-
element={
|
|
141
|
-
<ResourceAccessGuard resource="blog_posts" action="create">
|
|
142
|
-
<BlogPostCreate />
|
|
143
|
-
</ResourceAccessGuard>
|
|
144
|
-
}
|
|
145
|
-
/>
|
|
146
|
-
<Route
|
|
147
|
-
path="edit/:id"
|
|
148
|
-
element={
|
|
149
|
-
<ResourceAccessGuard resource="blog_posts" action="edit">
|
|
150
|
-
<BlogPostEdit />
|
|
151
|
-
</ResourceAccessGuard>
|
|
152
|
-
}
|
|
153
|
-
/>
|
|
154
|
-
<Route
|
|
155
|
-
path="show/:id"
|
|
156
|
-
element={
|
|
157
|
-
<ResourceAccessGuard resource="blog_posts" action="show">
|
|
158
|
-
<BlogPostShow />
|
|
159
|
-
</ResourceAccessGuard>
|
|
160
|
-
}
|
|
161
|
-
/>
|
|
162
|
-
</Route>
|
|
163
|
-
<Route path="/categories">
|
|
103
|
+
<Route index element={<NavigateToAccessibleResource />} />
|
|
104
|
+
<Route path="/users">
|
|
164
105
|
<Route
|
|
165
106
|
index
|
|
166
107
|
element={
|
|
167
|
-
<ResourceAccessGuard resource="
|
|
168
|
-
<
|
|
108
|
+
<ResourceAccessGuard resource="users" action="list">
|
|
109
|
+
<UserList />
|
|
169
110
|
</ResourceAccessGuard>
|
|
170
111
|
}
|
|
171
112
|
/>
|
|
172
113
|
<Route
|
|
173
114
|
path="create"
|
|
174
115
|
element={
|
|
175
|
-
<ResourceAccessGuard resource="
|
|
176
|
-
<
|
|
116
|
+
<ResourceAccessGuard resource="users" action="create">
|
|
117
|
+
<UserCreate />
|
|
177
118
|
</ResourceAccessGuard>
|
|
178
119
|
}
|
|
179
120
|
/>
|
|
180
121
|
<Route
|
|
181
122
|
path="edit/:id"
|
|
182
123
|
element={
|
|
183
|
-
<ResourceAccessGuard resource="
|
|
184
|
-
<
|
|
124
|
+
<ResourceAccessGuard resource="users" action="edit">
|
|
125
|
+
<UserEdit />
|
|
185
126
|
</ResourceAccessGuard>
|
|
186
127
|
}
|
|
187
128
|
/>
|
|
188
129
|
<Route
|
|
189
130
|
path="show/:id"
|
|
190
131
|
element={
|
|
191
|
-
<ResourceAccessGuard resource="
|
|
192
|
-
<
|
|
132
|
+
<ResourceAccessGuard resource="users" action="show">
|
|
133
|
+
<UserShow />
|
|
193
134
|
</ResourceAccessGuard>
|
|
194
135
|
}
|
|
195
136
|
/>
|
package/src/app/extension.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResourceProps } from "@refinedev/core";
|
|
1
|
+
import type { I18nProvider, ResourceProps } from "@refinedev/core";
|
|
2
2
|
import type { ComponentType, PropsWithChildren, ReactElement } from "react";
|
|
3
3
|
|
|
4
4
|
export type AppExtension = {
|
|
@@ -6,4 +6,6 @@ export type AppExtension = {
|
|
|
6
6
|
resources?: ResourceProps[];
|
|
7
7
|
routes?: ReactElement;
|
|
8
8
|
Provider?: ComponentType<PropsWithChildren>;
|
|
9
|
+
UserMenuItems?: ComponentType;
|
|
10
|
+
i18nProvider?: I18nProvider;
|
|
9
11
|
};
|
package/src/app/extensions.tsx
CHANGED
|
@@ -18,6 +18,27 @@ export const extensionRouteElements = appExtensions
|
|
|
18
18
|
.map((extension) => extension.routes)
|
|
19
19
|
.filter((routes): routes is NonNullable<typeof routes> => Boolean(routes));
|
|
20
20
|
|
|
21
|
+
const i18nExtensions = appExtensions.filter(
|
|
22
|
+
(extension) => extension.i18nProvider
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (i18nExtensions.length > 1) {
|
|
26
|
+
console.warn(
|
|
27
|
+
`Multiple app extensions provide i18nProvider (${i18nExtensions
|
|
28
|
+
.map((extension) => extension.id)
|
|
29
|
+
.join(", ")}). Using ${i18nExtensions[0].id}.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const extensionI18nProvider = i18nExtensions[0]?.i18nProvider;
|
|
34
|
+
|
|
35
|
+
export const extensionUserMenuItems = appExtensions
|
|
36
|
+
.filter((extension) => extension.UserMenuItems)
|
|
37
|
+
.map((extension) => ({
|
|
38
|
+
id: extension.id,
|
|
39
|
+
Component: extension.UserMenuItems!,
|
|
40
|
+
}));
|
|
41
|
+
|
|
21
42
|
export function AppExtensionProviders({ children }: PropsWithChildren) {
|
|
22
43
|
return appExtensions.reduceRight<ReactNode>((content, extension) => {
|
|
23
44
|
const Provider = extension.Provider;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RotateCcw } from "lucide-react";
|
|
2
2
|
import type { PropsWithChildren } from "react";
|
|
3
3
|
|
|
4
4
|
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { LoadingState } from "@/components/app-shell/loading-state";
|
|
5
6
|
import { loadAcl, useAclSnapshot } from "@/lib/nocobase/acl";
|
|
6
7
|
|
|
7
8
|
export function AclGate({ children }: PropsWithChildren) {
|
|
@@ -21,9 +22,7 @@ export function AclGate({ children }: PropsWithChildren) {
|
|
|
21
22
|
<Button
|
|
22
23
|
className="mt-5"
|
|
23
24
|
variant="outline"
|
|
24
|
-
onClick={() =>
|
|
25
|
-
void loadAcl({ force: true }).catch(() => undefined)
|
|
26
|
-
}
|
|
25
|
+
onClick={() => void loadAcl({ force: true }).catch(() => undefined)}
|
|
27
26
|
>
|
|
28
27
|
<RotateCcw />
|
|
29
28
|
Retry
|
|
@@ -33,10 +32,5 @@ export function AclGate({ children }: PropsWithChildren) {
|
|
|
33
32
|
);
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
return
|
|
37
|
-
<div className="flex min-h-screen items-center justify-center bg-background">
|
|
38
|
-
<Loader2 className="size-7 animate-spin text-muted-foreground" />
|
|
39
|
-
<span className="sr-only">Loading permissions…</span>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
35
|
+
return <LoadingState className="min-h-screen bg-background" />;
|
|
42
36
|
}
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
useBreadcrumb,
|
|
8
8
|
useLink,
|
|
9
9
|
useResourceParams,
|
|
10
|
+
useTranslate,
|
|
11
|
+
useUserFriendlyName,
|
|
10
12
|
} from "@refinedev/core";
|
|
11
13
|
import {
|
|
12
14
|
BreadcrumbSeparator as ShadcnBreadcrumbSeparator,
|
|
@@ -15,11 +17,15 @@ import {
|
|
|
15
17
|
BreadcrumbPage as ShadcnBreadcrumbPage,
|
|
16
18
|
Breadcrumb as ShadcnBreadcrumb,
|
|
17
19
|
} from "@/components/ui/breadcrumb";
|
|
20
|
+
import { resolveTranslatableText } from "@/lib/i18n";
|
|
21
|
+
import { getResourceLabel } from "@/components/resources/resource-label";
|
|
18
22
|
|
|
19
23
|
export function Breadcrumb() {
|
|
20
24
|
const Link = useLink();
|
|
21
25
|
const { breadcrumbs } = useBreadcrumb();
|
|
22
26
|
const { resources } = useResourceParams();
|
|
27
|
+
const translate = useTranslate();
|
|
28
|
+
const getUserFriendlyName = useUserFriendlyName();
|
|
23
29
|
const rootRouteResource = matchResourceFromRoute("/", resources);
|
|
24
30
|
|
|
25
31
|
const breadCrumbItems = useMemo(() => {
|
|
@@ -42,15 +48,45 @@ export function Breadcrumb() {
|
|
|
42
48
|
});
|
|
43
49
|
|
|
44
50
|
for (const { label, href } of breadcrumbs) {
|
|
51
|
+
const matchingResource = resources.find((resource) => {
|
|
52
|
+
const metaLabel = resource.meta?.label;
|
|
53
|
+
return (
|
|
54
|
+
label === metaLabel ||
|
|
55
|
+
label === resource.name ||
|
|
56
|
+
(href &&
|
|
57
|
+
[resource.list, resource.create, resource.edit, resource.show].some(
|
|
58
|
+
(route) => typeof route === "string" && route === href
|
|
59
|
+
))
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
const displayLabel = matchingResource
|
|
63
|
+
? getResourceLabel(
|
|
64
|
+
matchingResource,
|
|
65
|
+
"plural",
|
|
66
|
+
translate,
|
|
67
|
+
getUserFriendlyName
|
|
68
|
+
)
|
|
69
|
+
: resolveTranslatableText(label);
|
|
45
70
|
list.push({
|
|
46
|
-
key: `breadcrumb-item-${
|
|
71
|
+
key: `breadcrumb-item-${displayLabel}`,
|
|
47
72
|
href: href ?? "",
|
|
48
|
-
Component: href ?
|
|
73
|
+
Component: href ? (
|
|
74
|
+
<Link to={href}>{displayLabel}</Link>
|
|
75
|
+
) : (
|
|
76
|
+
<span>{displayLabel}</span>
|
|
77
|
+
),
|
|
49
78
|
});
|
|
50
79
|
}
|
|
51
80
|
|
|
52
81
|
return list;
|
|
53
|
-
}, [
|
|
82
|
+
}, [
|
|
83
|
+
breadcrumbs,
|
|
84
|
+
getUserFriendlyName,
|
|
85
|
+
Link,
|
|
86
|
+
resources,
|
|
87
|
+
rootRouteResource,
|
|
88
|
+
translate,
|
|
89
|
+
]);
|
|
54
90
|
|
|
55
91
|
return (
|
|
56
92
|
<ShadcnBreadcrumb>
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import type { Action, IResourceItem } from "@refinedev/core";
|
|
2
|
-
import {
|
|
3
|
-
useParsed,
|
|
4
|
-
useTranslate,
|
|
5
|
-
useUserFriendlyName,
|
|
6
|
-
} from "@refinedev/core";
|
|
2
|
+
import { useParsed, useTranslate } from "@refinedev/core";
|
|
7
3
|
import { useLayoutEffect } from "react";
|
|
8
4
|
import { useLocation } from "react-router";
|
|
5
|
+
import { useResourceLabel } from "@/components/resources/resource-label";
|
|
9
6
|
|
|
10
7
|
export type ActionPrefixContext = {
|
|
11
8
|
id?: string;
|
|
12
9
|
};
|
|
13
10
|
|
|
14
|
-
export type ActionPrefix =
|
|
15
|
-
| string
|
|
16
|
-
| ((context: ActionPrefixContext) => string);
|
|
11
|
+
export type ActionPrefix = string | ((context: ActionPrefixContext) => string);
|
|
17
12
|
|
|
18
13
|
export type DocumentTitleContext = {
|
|
19
14
|
action?: Action;
|
|
@@ -85,19 +80,14 @@ export function DocumentTitleHandler({
|
|
|
85
80
|
const location = useLocation();
|
|
86
81
|
const { action, id, params, pathname, resource } = useParsed();
|
|
87
82
|
const translate = useTranslate();
|
|
88
|
-
const
|
|
83
|
+
const identifier = resource?.identifier ?? resource?.name;
|
|
84
|
+
const resourceNameFallback = useResourceLabel(
|
|
85
|
+
resource,
|
|
86
|
+
action === "list" ? "plural" : "singular",
|
|
87
|
+
identifier
|
|
88
|
+
);
|
|
89
89
|
|
|
90
90
|
useLayoutEffect(() => {
|
|
91
|
-
const identifier = resource?.identifier ?? resource?.name;
|
|
92
|
-
const resourceNameFallback =
|
|
93
|
-
resource?.meta?.label ??
|
|
94
|
-
(identifier
|
|
95
|
-
? getUserFriendlyName(
|
|
96
|
-
identifier,
|
|
97
|
-
action === "list" ? "plural" : "singular"
|
|
98
|
-
)
|
|
99
|
-
: undefined);
|
|
100
|
-
|
|
101
91
|
const resourceName = resource?.name
|
|
102
92
|
? safeTranslate(
|
|
103
93
|
translate,
|
|
@@ -155,12 +145,13 @@ export function DocumentTitleHandler({
|
|
|
155
145
|
appName,
|
|
156
146
|
defaultTitle,
|
|
157
147
|
formatTitle,
|
|
158
|
-
getUserFriendlyName,
|
|
159
148
|
id,
|
|
149
|
+
identifier,
|
|
160
150
|
location,
|
|
161
151
|
params,
|
|
162
152
|
pathname,
|
|
163
153
|
resource,
|
|
154
|
+
resourceNameFallback,
|
|
164
155
|
separator,
|
|
165
156
|
translate,
|
|
166
157
|
]);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useActiveAuthProvider,
|
|
3
|
+
useLogout,
|
|
4
|
+
useTranslate,
|
|
5
|
+
} from "@refinedev/core";
|
|
2
6
|
import {
|
|
3
7
|
DropdownMenu,
|
|
4
8
|
DropdownMenuItem,
|
|
@@ -13,6 +17,7 @@ import { useSidebar, SidebarTrigger } from "@/components/ui/sidebar";
|
|
|
13
17
|
import { LogOutIcon } from "lucide-react";
|
|
14
18
|
import { cn } from "@/lib/utils";
|
|
15
19
|
import { Brand } from "@/components/app-shell/brand";
|
|
20
|
+
import { extensionUserMenuItems } from "@/app/extensions";
|
|
16
21
|
|
|
17
22
|
export const Header = () => {
|
|
18
23
|
const { isMobile } = useSidebar();
|
|
@@ -21,6 +26,8 @@ export const Header = () => {
|
|
|
21
26
|
};
|
|
22
27
|
|
|
23
28
|
function DesktopHeader() {
|
|
29
|
+
const translate = useTranslate();
|
|
30
|
+
|
|
24
31
|
return (
|
|
25
32
|
<header
|
|
26
33
|
className={cn(
|
|
@@ -44,7 +51,7 @@ function DesktopHeader() {
|
|
|
44
51
|
<SidebarTrigger className="size-9 rounded-xl text-muted-foreground hover:text-foreground" />
|
|
45
52
|
<div className="hidden h-5 w-px bg-border sm:block" />
|
|
46
53
|
<span className="hidden text-sm font-medium text-muted-foreground sm:block">
|
|
47
|
-
AI application workspace
|
|
54
|
+
{translate("shell.workspace", "AI application workspace")}
|
|
48
55
|
</span>
|
|
49
56
|
</div>
|
|
50
57
|
<div className="flex items-center gap-2">
|
|
@@ -94,6 +101,7 @@ function MobileHeader() {
|
|
|
94
101
|
|
|
95
102
|
const UserDropdown = () => {
|
|
96
103
|
const { mutate: logout, isPending: isLoggingOut } = useLogout();
|
|
104
|
+
const translate = useTranslate();
|
|
97
105
|
|
|
98
106
|
const authProvider = useActiveAuthProvider();
|
|
99
107
|
|
|
@@ -110,6 +118,9 @@ const UserDropdown = () => {
|
|
|
110
118
|
<div className="px-2 py-2">
|
|
111
119
|
<UserInfo />
|
|
112
120
|
</div>
|
|
121
|
+
{extensionUserMenuItems.map(({ id, Component }) => (
|
|
122
|
+
<Component key={id} />
|
|
123
|
+
))}
|
|
113
124
|
<DropdownMenuSeparator />
|
|
114
125
|
<DropdownMenuItem
|
|
115
126
|
className="mt-1 min-h-9 cursor-pointer gap-2 px-2 text-muted-foreground focus:text-foreground"
|
|
@@ -118,7 +129,11 @@ const UserDropdown = () => {
|
|
|
118
129
|
}}
|
|
119
130
|
>
|
|
120
131
|
<LogOutIcon />
|
|
121
|
-
<span>
|
|
132
|
+
<span>
|
|
133
|
+
{isLoggingOut
|
|
134
|
+
? translate("auth.signingOut", "Signing out...")
|
|
135
|
+
: translate("auth.signOut", "Sign out")}
|
|
136
|
+
</span>
|
|
122
137
|
</DropdownMenuItem>
|
|
123
138
|
</DropdownMenuContent>
|
|
124
139
|
</DropdownMenu>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Loader2 } from "lucide-react";
|
|
5
4
|
import { cn } from "@/lib/utils";
|
|
5
|
+
import { LoadingState } from "@/components/app-shell/loading-state";
|
|
6
6
|
|
|
7
7
|
interface LoadingOverlayProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
8
|
loading?: boolean;
|
|
@@ -25,9 +25,7 @@ export const LoadingOverlay = React.forwardRef<
|
|
|
25
25
|
className
|
|
26
26
|
)}
|
|
27
27
|
>
|
|
28
|
-
<
|
|
29
|
-
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
|
30
|
-
</div>
|
|
28
|
+
<LoadingState />
|
|
31
29
|
</div>
|
|
32
30
|
</div>
|
|
33
31
|
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Loader2 } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export function LoadingState({ className }: { className?: string }) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
role="status"
|
|
9
|
+
className={cn("flex items-center justify-center", className)}
|
|
10
|
+
>
|
|
11
|
+
<Loader2 className="size-7 animate-spin text-primary" />
|
|
12
|
+
<span className="sr-only">Loading</span>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|