@nocobase/portal-template-default 1.0.0 → 1.0.2

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 (194) hide show
  1. package/README.MD +90 -1
  2. package/components.json +3 -1
  3. package/package.json +21 -5
  4. package/registry/nocobase-acl/README.md +19 -0
  5. package/registry/nocobase-acl/components/acl-boundary.tsx +106 -0
  6. package/registry/nocobase-acl/components/index.ts +3 -0
  7. package/registry/nocobase-acl/components/role-options.ts +36 -0
  8. package/registry/nocobase-acl/components/role-switcher.tsx +135 -0
  9. package/registry/nocobase-acl/demo/boundary-api.tsx +186 -0
  10. package/registry/nocobase-acl/demo/components.tsx +231 -0
  11. package/registry/nocobase-acl/demo/index.tsx +505 -0
  12. package/registry/nocobase-acl/demo/lazy-route.tsx +10 -0
  13. package/registry/nocobase-acl/demo/prompt-generator.tsx +134 -0
  14. package/registry/nocobase-acl/demo/role-switcher-prompt-generator.tsx +198 -0
  15. package/registry/nocobase-acl/demo/scenario-section.tsx +37 -0
  16. package/registry/nocobase-acl/extension.tsx +71 -0
  17. package/registry/nocobase-acl/index.ts +1 -0
  18. package/registry/nocobase-acl/tests/acl-regression.mjs +314 -0
  19. package/registry/nocobase-ai/README.md +70 -0
  20. package/registry/nocobase-ai/adapters/react-hook-form.ts +20 -0
  21. package/registry/nocobase-ai/assets/nocobase-ai-chat.svg +50 -0
  22. package/registry/nocobase-ai/components/ai-root-provider.tsx +32 -0
  23. package/registry/nocobase-ai/components/chat/ai-employee-avatar.tsx +31 -0
  24. package/registry/nocobase-ai/components/chat/chat-attachment.tsx +92 -0
  25. package/registry/nocobase-ai/components/chat/chat-compact.tsx +90 -0
  26. package/registry/nocobase-ai/components/chat/chat-composer.tsx +429 -0
  27. package/registry/nocobase-ai/components/chat/chat-empty-state.tsx +51 -0
  28. package/registry/nocobase-ai/components/chat/chat-header.tsx +88 -0
  29. package/registry/nocobase-ai/components/chat/chat-history-dialog.tsx +77 -0
  30. package/registry/nocobase-ai/components/chat/chat-message.tsx +252 -0
  31. package/registry/nocobase-ai/components/chat/chat-messages.tsx +173 -0
  32. package/registry/nocobase-ai/components/chat/chat-window.tsx +118 -0
  33. package/registry/nocobase-ai/components/chat/conversation-list.tsx +343 -0
  34. package/registry/nocobase-ai/components/chat/markdown-message.tsx +165 -0
  35. package/registry/nocobase-ai/components/chat/model-select-options.tsx +30 -0
  36. package/registry/nocobase-ai/components/chat/reasoning-panel.tsx +29 -0
  37. package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +142 -0
  38. package/registry/nocobase-ai/components/chat/tool-call-card.tsx +316 -0
  39. package/registry/nocobase-ai/components/chat/user-prompt-editor.tsx +99 -0
  40. package/registry/nocobase-ai/components/chat/work-context-chip.tsx +114 -0
  41. package/registry/nocobase-ai/components/index.ts +59 -0
  42. package/registry/nocobase-ai/components/page-elements/ai-form.tsx +44 -0
  43. package/registry/nocobase-ai/components/page-elements/page-element-provider.tsx +469 -0
  44. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +31 -0
  45. package/registry/nocobase-ai/components/surfaces/chat-inline.tsx +18 -0
  46. package/registry/nocobase-ai/components/surfaces/chat-page.tsx +18 -0
  47. package/registry/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +54 -0
  48. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +74 -0
  49. package/registry/nocobase-ai/components/surfaces/chat-surface-actions.tsx +54 -0
  50. package/registry/nocobase-ai/components/tools/builtin-tool-renderers.tsx +31 -0
  51. package/registry/nocobase-ai/components/tools/business-report-dialog.tsx +364 -0
  52. package/registry/nocobase-ai/components/tools/business-report-renderer.tsx +122 -0
  53. package/registry/nocobase-ai/components/tools/business-report-utils.tsx +282 -0
  54. package/registry/nocobase-ai/components/tools/chart-renderer.tsx +99 -0
  55. package/registry/nocobase-ai/components/tools/echarts-preview.tsx +59 -0
  56. package/registry/nocobase-ai/components/tools/echarts-runtime-advanced.ts +18 -0
  57. package/registry/nocobase-ai/components/tools/echarts-runtime-common.ts +20 -0
  58. package/registry/nocobase-ai/components/tools/echarts-runtime-components.ts +34 -0
  59. package/registry/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +24 -0
  60. package/registry/nocobase-ai/components/tools/echarts-runtime.ts +138 -0
  61. package/registry/nocobase-ai/components/tools/sub-agent-renderer.tsx +67 -0
  62. package/registry/nocobase-ai/components/tools/suggestions-renderer.tsx +59 -0
  63. package/registry/nocobase-ai/components/tools/tool-renderer-provider.tsx +55 -0
  64. package/registry/nocobase-ai/components/tools/tool-renderer-utils.ts +18 -0
  65. package/registry/nocobase-ai/components/tools/workflow-renderer.tsx +108 -0
  66. package/registry/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +73 -0
  67. package/registry/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +149 -0
  68. package/registry/nocobase-ai/demo/configuration-gate.tsx +49 -0
  69. package/registry/nocobase-ai/demo/container-showcase.tsx +210 -0
  70. package/registry/nocobase-ai/demo/floating.tsx +134 -0
  71. package/registry/nocobase-ai/demo/index.tsx +346 -0
  72. package/registry/nocobase-ai/demo/interaction-showcase.tsx +151 -0
  73. package/registry/nocobase-ai/demo/page-context-prompt-generator.tsx +355 -0
  74. package/registry/nocobase-ai/demo/page-context.tsx +884 -0
  75. package/registry/nocobase-ai/demo/page-element-showcase.tsx +197 -0
  76. package/registry/nocobase-ai/demo/prompt-card.tsx +41 -0
  77. package/registry/nocobase-ai/demo/prompt-generator.tsx +452 -0
  78. package/registry/nocobase-ai/demo/shortcut.tsx +1207 -0
  79. package/registry/nocobase-ai/demo/tool-cards.tsx +530 -0
  80. package/registry/nocobase-ai/extension.tsx +158 -0
  81. package/registry/nocobase-ai/global-ai-chat.tsx +213 -0
  82. package/registry/nocobase-ai/index.ts +3 -0
  83. package/registry/nocobase-ai/providers/ai-provider.tsx +323 -0
  84. package/registry/nocobase-ai/providers/avatars.ts +817 -0
  85. package/registry/nocobase-ai/providers/chat-context.tsx +109 -0
  86. package/registry/nocobase-ai/providers/chat-controller.ts +84 -0
  87. package/registry/nocobase-ai/providers/chat-message-utils.ts +198 -0
  88. package/registry/nocobase-ai/providers/chat-provider.tsx +871 -0
  89. package/registry/nocobase-ai/providers/chat-reducer.ts +147 -0
  90. package/registry/nocobase-ai/providers/chat-task-utils.ts +105 -0
  91. package/registry/nocobase-ai/providers/chat-transport.ts +184 -0
  92. package/registry/nocobase-ai/providers/form-registry.tsx +271 -0
  93. package/registry/nocobase-ai/providers/frontend-tool-registry.tsx +231 -0
  94. package/registry/nocobase-ai/providers/index.ts +61 -0
  95. package/registry/nocobase-ai/providers/model.ts +32 -0
  96. package/registry/nocobase-ai/providers/page-context.tsx +136 -0
  97. package/registry/nocobase-ai/providers/stream-coalescer.ts +60 -0
  98. package/registry/nocobase-ai/providers/stream-event-utils.ts +82 -0
  99. package/registry/nocobase-ai/providers/stream-parser.ts +61 -0
  100. package/registry/nocobase-ai/providers/sub-agent-stream.ts +316 -0
  101. package/registry/nocobase-ai/providers/types.ts +183 -0
  102. package/registry/nocobase-ai/providers/ui-message-stream.ts +463 -0
  103. package/registry/nocobase-ai/providers/use-automatic-tool-approval.ts +94 -0
  104. package/registry/nocobase-ai/providers/use-chat-attachments.ts +206 -0
  105. package/registry/nocobase-ai/providers/use-chat-message-actions.ts +554 -0
  106. package/registry/nocobase-ai/providers/use-chat-runtime.ts +210 -0
  107. package/registry/nocobase-ai/providers/use-chat-work-context.ts +93 -0
  108. package/registry/nocobase-ai/providers/use-conversation-catalog.ts +108 -0
  109. package/registry/nocobase-ai/providers/use-conversation-history.ts +178 -0
  110. package/registry/nocobase-ai/services/index.ts +7 -0
  111. package/registry/nocobase-ai/services/nocobase-ai-service.ts +454 -0
  112. package/registry/nocobase-ai/services/types.ts +82 -0
  113. package/registry/nocobase-ai/tests/business-report-regression.mjs +70 -0
  114. package/registry/nocobase-ai/tests/form-filler-regression.mjs +129 -0
  115. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +128 -0
  116. package/registry/nocobase-ai/tests/page-context-regression.mjs +48 -0
  117. package/registry/nocobase-ai/tests/stream-regression.mjs +207 -0
  118. package/registry/nocobase-i18n/README.md +53 -0
  119. package/registry/nocobase-i18n/components/index.ts +1 -0
  120. package/registry/nocobase-i18n/components/language-switcher.tsx +138 -0
  121. package/registry/nocobase-i18n/demo/index.tsx +209 -0
  122. package/registry/nocobase-i18n/demo/prompt-generator.tsx +155 -0
  123. package/registry/nocobase-i18n/extension.tsx +46 -0
  124. package/registry/nocobase-i18n/i18n-provider.ts +9 -0
  125. package/registry/nocobase-i18n/index.ts +6 -0
  126. package/registry/nocobase-i18n/locale-store.ts +134 -0
  127. package/registry/nocobase-i18n/locales/en-US.ts +89 -0
  128. package/registry/nocobase-i18n/locales/zh-CN.ts +85 -0
  129. package/registry/nocobase-i18n/provider.tsx +32 -0
  130. package/registry/nocobase-i18n/runtime.ts +118 -0
  131. package/registry/nocobase-i18n/server-resources.ts +184 -0
  132. package/registry/nocobase-i18n/tests/i18n-regression.mjs +42 -0
  133. package/registry.config.json +163 -0
  134. package/scripts/registry.mjs +164 -0
  135. package/src/App.css +95 -0
  136. package/src/App.tsx +87 -69
  137. package/src/app/extension.ts +11 -0
  138. package/src/app/extensions.tsx +49 -0
  139. package/src/components/access-control/access-denied.tsx +30 -0
  140. package/src/components/access-control/acl-bootstrap.tsx +9 -0
  141. package/src/components/access-control/acl-gate.tsx +36 -0
  142. package/src/components/access-control/navigate-to-accessible-resource.tsx +17 -0
  143. package/src/components/access-control/resource-access-guard.tsx +21 -0
  144. package/src/components/access-control/use-acl-runtime.ts +28 -0
  145. package/src/components/app-shell/breadcrumb.tsx +39 -3
  146. package/src/components/app-shell/document-title-handler.tsx +11 -20
  147. package/src/components/app-shell/header.tsx +22 -4
  148. package/src/components/app-shell/loading-overlay.tsx +2 -4
  149. package/src/components/app-shell/loading-state.tsx +15 -0
  150. package/src/components/app-shell/sidebar.tsx +103 -45
  151. package/src/components/data-table/data-table-pagination.tsx +27 -7
  152. package/src/components/data-table/data-table-styles.ts +57 -0
  153. package/src/components/data-table/data-table.tsx +14 -59
  154. package/src/components/resources/resource-label.ts +101 -0
  155. package/src/components/resources/views/create-view.tsx +17 -14
  156. package/src/components/resources/views/edit-view.tsx +17 -15
  157. package/src/components/resources/views/list-view.tsx +20 -10
  158. package/src/components/resources/views/show-view.tsx +20 -18
  159. package/src/index.tsx +2 -3
  160. package/src/lib/i18n.ts +161 -0
  161. package/src/lib/nocobase/acl/action.ts +147 -0
  162. package/src/lib/nocobase/acl/data-source.ts +24 -0
  163. package/src/lib/nocobase/acl/index.ts +6 -0
  164. package/src/lib/nocobase/acl/menu.ts +41 -0
  165. package/src/lib/nocobase/acl/record-permissions.ts +111 -0
  166. package/src/lib/nocobase/acl/store.ts +182 -0
  167. package/src/lib/nocobase/acl/types.ts +96 -0
  168. package/src/lib/nocobase/client.ts +279 -0
  169. package/src/lib/nocobase/error.ts +32 -0
  170. package/src/locales/en-US.ts +82 -0
  171. package/src/locales/index.ts +13 -0
  172. package/src/locales/zh-CN.ts +79 -0
  173. package/src/pages/users/create.tsx +98 -0
  174. package/src/pages/users/edit.tsx +92 -0
  175. package/src/pages/users/form-context.ts +57 -0
  176. package/src/pages/users/form-fields.tsx +211 -0
  177. package/src/pages/users/list.tsx +205 -0
  178. package/src/pages/users/show.tsx +187 -0
  179. package/src/pages/users/types.ts +20 -0
  180. package/src/providers/access-control.ts +34 -0
  181. package/src/providers/auth.ts +74 -60
  182. package/src/providers/constants.ts +3 -37
  183. package/src/providers/data.ts +242 -85
  184. package/vite.config.ts +17 -0
  185. package/src/pages/blog-posts/create.tsx +0 -177
  186. package/src/pages/blog-posts/edit.tsx +0 -197
  187. package/src/pages/blog-posts/list.tsx +0 -131
  188. package/src/pages/blog-posts/show.tsx +0 -81
  189. package/src/pages/categories/create.tsx +0 -73
  190. package/src/pages/categories/edit.tsx +0 -73
  191. package/src/pages/categories/index.ts +0 -4
  192. package/src/pages/categories/list.tsx +0 -86
  193. package/src/pages/categories/show.tsx +0 -37
  194. /package/src/pages/{blog-posts → users}/index.ts +0 -0
@@ -22,7 +22,13 @@ import type {
22
22
  UpdateResponse,
23
23
  } from "@refinedev/core";
24
24
 
25
- import { API_URL, getNocoBaseHeaders, NOCOBASE_TOKEN_KEY } from "./constants";
25
+ import { nocobaseClient } from "@/lib/nocobase/client";
26
+ import {
27
+ notifyRecordPermissionsChanged,
28
+ resolveAclDataSourceKey,
29
+ type ResourceAcl,
30
+ updateRecordPermissions,
31
+ } from "@/lib/nocobase/acl";
26
32
 
27
33
  type NocoBaseListResponse<T> = {
28
34
  rows?: T[];
@@ -30,23 +36,30 @@ type NocoBaseListResponse<T> = {
30
36
  data?: T[] | NocoBaseListResponse<T>;
31
37
  meta?: {
32
38
  count?: number;
39
+ allowedActions?: Record<string, Array<string | number>>;
40
+ };
41
+ allowedActions?: Record<string, Array<string | number>>;
42
+ };
43
+
44
+ type NocoBaseGetResponse<T> = {
45
+ data?: T;
46
+ meta?: {
47
+ allowedActions?: Record<string, Array<string | number>>;
33
48
  };
34
49
  };
35
50
 
36
51
  type NocoBaseMeta = MetaQuery & {
37
52
  appends?: string[];
38
53
  token?: string;
54
+ dataSourceKey?: string;
55
+ idField?: string;
56
+ acl?: ResourceAcl;
39
57
  };
40
58
 
41
59
  type NocoBaseFilter = Record<string, unknown>;
42
60
 
43
- const getToken = (meta?: NocoBaseMeta) =>
44
- meta?.token ??
45
- localStorage.getItem(NOCOBASE_TOKEN_KEY) ??
46
- import.meta.env.NOCOBASE_API_TOKEN;
47
-
48
61
  const toNocoBaseFilter = (
49
- filters: GetListParams["filters"] = [],
62
+ filters: GetListParams["filters"] = []
50
63
  ): NocoBaseFilter | undefined => {
51
64
  const filterItems: NocoBaseFilter[] = filters.flatMap((filter) => {
52
65
  if ("field" in filter) {
@@ -81,31 +94,6 @@ const toNocoBaseFilter = (
81
94
  return filterItems.length === 1 ? filterItems[0] : { $and: filterItems };
82
95
  };
83
96
 
84
- const buildUrl = (
85
- resource: string,
86
- action: string,
87
- query: Record<string, string | number | undefined> = {},
88
- meta?: NocoBaseMeta,
89
- ) => {
90
- const endpoint = `${API_URL.replace(/\/$/, "")}/${resource}:${action}`;
91
- const url = endpoint.startsWith("http://") || endpoint.startsWith("https://")
92
- ? new URL(endpoint)
93
- : new URL(endpoint, window.location.origin);
94
-
95
- for (const [key, value] of Object.entries(query)) {
96
- if (value !== undefined) url.searchParams.set(key, String(value));
97
- }
98
-
99
- meta?.appends?.forEach((append) => url.searchParams.append("appends[]", append));
100
- if (Array.isArray(meta?.fields)) {
101
- meta.fields
102
- .filter((field): field is string => typeof field === "string")
103
- .forEach((field) => url.searchParams.append("fields[]", field));
104
- }
105
-
106
- return url;
107
- };
108
-
109
97
  const request = async <T>(
110
98
  resource: string,
111
99
  action: string,
@@ -115,30 +103,70 @@ const request = async <T>(
115
103
  body?: unknown;
116
104
  meta?: NocoBaseMeta;
117
105
  unwrapData?: boolean;
118
- } = {},
106
+ } = {}
119
107
  ): Promise<T> => {
120
- const token = getToken(options.meta);
121
- const response = await fetch(buildUrl(resource, action, options.query, options.meta), {
122
- method: options.method ?? "GET",
123
- headers: getNocoBaseHeaders({
124
- token,
125
- includeContentType: Boolean(options.body),
126
- }),
127
- body: options.body ? JSON.stringify(options.body) : undefined,
108
+ const dataSourceKey = resolveAclDataSourceKey(options.meta);
109
+ const fields = Array.isArray(options.meta?.fields)
110
+ ? options.meta.fields.filter(
111
+ (field): field is string => typeof field === "string"
112
+ )
113
+ : undefined;
114
+ return nocobaseClient.action<T>(resource, action, {
115
+ method: options.method,
116
+ query: {
117
+ ...options.query,
118
+ ...(options.meta?.appends?.length
119
+ ? { "appends[]": options.meta.appends }
120
+ : {}),
121
+ ...(fields?.length ? { "fields[]": fields } : {}),
122
+ },
123
+ body: options.body,
124
+ token: options.meta?.token,
125
+ headers: dataSourceKey
126
+ ? { "X-Data-Source": dataSourceKey }
127
+ : undefined,
128
+ unwrap: options.unwrapData === false ? "none" : "data",
128
129
  });
130
+ };
129
131
 
130
- const payload = await response.json().catch(() => undefined);
131
- if (!response.ok) {
132
- throw new Error(
133
- payload?.errors?.[0]?.message ?? payload?.message ?? `NocoBase request failed (${response.status})`,
134
- );
135
- }
132
+ const getAllowedActions = (response: NocoBaseListResponse<unknown>) =>
133
+ response.meta?.allowedActions ??
134
+ response.allowedActions ??
135
+ (!Array.isArray(response.data) && response.data
136
+ ? response.data.meta?.allowedActions ?? response.data.allowedActions
137
+ : undefined);
136
138
 
137
- // Most NocoBase resource actions return their record in `data`. List endpoints
138
- // can also return `{ data: [...], meta: { count } }`, which must stay intact.
139
- return (options.unwrapData === false ? payload : payload?.data ?? payload) as T;
139
+ const cacheAllowedActions = <TData extends BaseRecord>({
140
+ resource,
141
+ records,
142
+ response,
143
+ meta,
144
+ }: {
145
+ resource: string;
146
+ records: TData[];
147
+ response: NocoBaseListResponse<TData>;
148
+ meta?: NocoBaseMeta;
149
+ }) => {
150
+ const idField = meta?.idField ?? "id";
151
+ const dataSourceKey = resolveAclDataSourceKey(meta);
152
+ const recordIds = records
153
+ .map((record) => record[idField])
154
+ .filter(
155
+ (id): id is string | number =>
156
+ typeof id === "string" || typeof id === "number"
157
+ );
158
+ return updateRecordPermissions({
159
+ dataSourceKey,
160
+ resource,
161
+ recordIds,
162
+ allowedActions: getAllowedActions(response),
163
+ });
140
164
  };
141
165
 
166
+ const getResponseData = <TData extends BaseRecord>(
167
+ response: NocoBaseGetResponse<TData>
168
+ ) => response.data ?? (response as TData);
169
+
142
170
  export const dataProvider: DataProvider = {
143
171
  async getList<TData extends BaseRecord = BaseRecord>({
144
172
  resource,
@@ -150,51 +178,144 @@ export const dataProvider: DataProvider = {
150
178
  const page = pagination?.currentPage ?? 1;
151
179
  const pageSize = pagination?.pageSize ?? 10;
152
180
  const filter = toNocoBaseFilter(filters);
153
- const sort = sorters?.map((sorter) => `${sorter.order === "desc" ? "-" : ""}${sorter.field}`);
154
- const response = await request<NocoBaseListResponse<TData>>(resource, "list", {
155
- query: {
156
- page,
157
- pageSize,
158
- ...(filter ? { filter: JSON.stringify(filter) } : {}),
159
- ...(sort?.length ? { sort: sort.join(",") } : {}),
160
- },
161
- meta,
162
- unwrapData: false,
163
- });
181
+ const sort = sorters?.map(
182
+ (sorter) => `${sorter.order === "desc" ? "-" : ""}${sorter.field}`
183
+ );
184
+ const response = await request<NocoBaseListResponse<TData>>(
185
+ resource,
186
+ "list",
187
+ {
188
+ query: {
189
+ page,
190
+ pageSize,
191
+ ...(filter ? { filter: JSON.stringify(filter) } : {}),
192
+ ...(sort?.length ? { sort: sort.join(",") } : {}),
193
+ },
194
+ meta,
195
+ unwrapData: false,
196
+ }
197
+ );
164
198
 
165
199
  const list = Array.isArray(response.data)
166
200
  ? { rows: response.data, count: response.meta?.count }
167
201
  : response.data ?? response;
202
+ const records = list.rows ?? [];
203
+ if (cacheAllowedActions({ resource, records, response, meta })) {
204
+ notifyRecordPermissionsChanged();
205
+ }
168
206
 
169
207
  return {
170
- data: list.rows ?? [],
171
- total: list.count ?? list.rows?.length ?? 0,
208
+ data: records,
209
+ total: list.count ?? records.length,
210
+ meta: response.meta,
172
211
  };
173
212
  },
174
213
 
175
- async getOne<TData extends BaseRecord = BaseRecord>({ resource, id, meta }: GetOneParams): Promise<GetOneResponse<TData>> {
176
- return { data: await request<TData>(resource, "get", { query: { filterByTk: id }, meta }) };
214
+ async getOne<TData extends BaseRecord = BaseRecord>({
215
+ resource,
216
+ id,
217
+ meta,
218
+ }: GetOneParams): Promise<GetOneResponse<TData>> {
219
+ const response = await request<NocoBaseGetResponse<TData>>(
220
+ resource,
221
+ "get",
222
+ {
223
+ query: { filterByTk: id },
224
+ meta,
225
+ unwrapData: false,
226
+ }
227
+ );
228
+ const data = getResponseData(response);
229
+ if (
230
+ cacheAllowedActions({
231
+ resource,
232
+ records: [data],
233
+ response: response as NocoBaseListResponse<TData>,
234
+ meta,
235
+ })
236
+ ) {
237
+ notifyRecordPermissionsChanged();
238
+ }
239
+ return {
240
+ data,
241
+ };
177
242
  },
178
243
 
179
- async getMany<TData extends BaseRecord = BaseRecord>({ resource, ids, meta }: GetManyParams): Promise<GetManyResponse<TData>> {
180
- const data = await Promise.all(
181
- ids.map((id) => request<TData>(resource, "get", { query: { filterByTk: id }, meta })),
244
+ async getMany<TData extends BaseRecord = BaseRecord>({
245
+ resource,
246
+ ids,
247
+ meta,
248
+ }: GetManyParams): Promise<GetManyResponse<TData>> {
249
+ const responses = await Promise.all(
250
+ ids.map((id) =>
251
+ request<NocoBaseGetResponse<TData>>(resource, "get", {
252
+ query: { filterByTk: id },
253
+ meta,
254
+ unwrapData: false,
255
+ })
256
+ )
182
257
  );
258
+ const data = responses.map(getResponseData);
259
+ let permissionsChanged = false;
260
+ responses.forEach((response, index) => {
261
+ permissionsChanged =
262
+ cacheAllowedActions({
263
+ resource,
264
+ records: [data[index]],
265
+ response: response as NocoBaseListResponse<TData>,
266
+ meta,
267
+ }) || permissionsChanged;
268
+ });
269
+ if (permissionsChanged) notifyRecordPermissionsChanged();
183
270
  return { data };
184
271
  },
185
272
 
186
- async create<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, variables, meta }: CreateParams<TVariables>): Promise<CreateResponse<TData>> {
187
- return { data: await request<TData>(resource, "create", { method: "POST", body: variables, meta }) };
273
+ async create<
274
+ TData extends BaseRecord = BaseRecord,
275
+ TVariables = Record<string, unknown>
276
+ >({
277
+ resource,
278
+ variables,
279
+ meta,
280
+ }: CreateParams<TVariables>): Promise<CreateResponse<TData>> {
281
+ return {
282
+ data: await request<TData>(resource, "create", {
283
+ method: "POST",
284
+ body: variables,
285
+ meta,
286
+ }),
287
+ };
188
288
  },
189
289
 
190
- async createMany<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, variables, meta }: CreateManyParams<TVariables>): Promise<CreateManyResponse<TData>> {
290
+ async createMany<
291
+ TData extends BaseRecord = BaseRecord,
292
+ TVariables = Record<string, unknown>
293
+ >({
294
+ resource,
295
+ variables,
296
+ meta,
297
+ }: CreateManyParams<TVariables>): Promise<CreateManyResponse<TData>> {
191
298
  const data = await Promise.all(
192
- variables.map((values) => request<TData>(resource, "create", { method: "POST", body: values, meta })),
299
+ variables.map((values) =>
300
+ request<TData>(resource, "create", {
301
+ method: "POST",
302
+ body: values,
303
+ meta,
304
+ })
305
+ )
193
306
  );
194
307
  return { data };
195
308
  },
196
309
 
197
- async update<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, id, variables, meta }: UpdateParams<TVariables>): Promise<UpdateResponse<TData>> {
310
+ async update<
311
+ TData extends BaseRecord = BaseRecord,
312
+ TVariables = Record<string, unknown>
313
+ >({
314
+ resource,
315
+ id,
316
+ variables,
317
+ meta,
318
+ }: UpdateParams<TVariables>): Promise<UpdateResponse<TData>> {
198
319
  return {
199
320
  data: await request<TData>(resource, "update", {
200
321
  method: "POST",
@@ -205,28 +326,64 @@ export const dataProvider: DataProvider = {
205
326
  };
206
327
  },
207
328
 
208
- async updateMany<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, ids, variables, meta }: UpdateManyParams<TVariables>): Promise<UpdateManyResponse<TData>> {
329
+ async updateMany<
330
+ TData extends BaseRecord = BaseRecord,
331
+ TVariables = Record<string, unknown>
332
+ >({
333
+ resource,
334
+ ids,
335
+ variables,
336
+ meta,
337
+ }: UpdateManyParams<TVariables>): Promise<UpdateManyResponse<TData>> {
209
338
  const data = await Promise.all(
210
- ids.map((id) => request<TData>(resource, "update", {
211
- method: "POST",
212
- query: { filterByTk: id },
213
- body: variables,
214
- meta,
215
- })),
339
+ ids.map((id) =>
340
+ request<TData>(resource, "update", {
341
+ method: "POST",
342
+ query: { filterByTk: id },
343
+ body: variables,
344
+ meta,
345
+ })
346
+ )
216
347
  );
217
348
  return { data };
218
349
  },
219
350
 
220
- async deleteOne<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, id, meta }: DeleteOneParams<TVariables>): Promise<DeleteOneResponse<TData>> {
221
- return { data: await request<TData>(resource, "destroy", { method: "POST", query: { filterByTk: id }, meta }) };
351
+ async deleteOne<
352
+ TData extends BaseRecord = BaseRecord,
353
+ TVariables = Record<string, unknown>
354
+ >({
355
+ resource,
356
+ id,
357
+ meta,
358
+ }: DeleteOneParams<TVariables>): Promise<DeleteOneResponse<TData>> {
359
+ return {
360
+ data: await request<TData>(resource, "destroy", {
361
+ method: "POST",
362
+ query: { filterByTk: id },
363
+ meta,
364
+ }),
365
+ };
222
366
  },
223
367
 
224
- async deleteMany<TData extends BaseRecord = BaseRecord, TVariables = {}>({ resource, ids, meta }: DeleteManyParams<TVariables>): Promise<DeleteManyResponse<TData>> {
368
+ async deleteMany<
369
+ TData extends BaseRecord = BaseRecord,
370
+ TVariables = Record<string, unknown>
371
+ >({
372
+ resource,
373
+ ids,
374
+ meta,
375
+ }: DeleteManyParams<TVariables>): Promise<DeleteManyResponse<TData>> {
225
376
  const data = await Promise.all(
226
- ids.map((id) => request<TData>(resource, "destroy", { method: "POST", query: { filterByTk: id }, meta })),
377
+ ids.map((id) =>
378
+ request<TData>(resource, "destroy", {
379
+ method: "POST",
380
+ query: { filterByTk: id },
381
+ meta,
382
+ })
383
+ )
227
384
  );
228
385
  return { data };
229
386
  },
230
387
 
231
- getApiUrl: () => API_URL,
388
+ getApiUrl: () => nocobaseClient.getApiUrl(),
232
389
  };
package/vite.config.ts CHANGED
@@ -60,6 +60,23 @@ export default defineConfig(({ mode }) => {
60
60
  target: proxyTarget,
61
61
  changeOrigin: true,
62
62
  secure: false,
63
+ configure(proxy) {
64
+ proxy.on("proxyReq", (proxyRequest, request) => {
65
+ if (!request.url?.includes("aiConversations:")) return;
66
+ proxyRequest.setHeader("accept-encoding", "identity");
67
+ proxyRequest.setHeader("cache-control", "no-cache");
68
+ });
69
+ proxy.on("proxyRes", (proxyResponse) => {
70
+ const contentType = String(
71
+ proxyResponse.headers["content-type"] ?? ""
72
+ );
73
+ if (!contentType.includes("text/event-stream")) return;
74
+ delete proxyResponse.headers["content-length"];
75
+ proxyResponse.headers["cache-control"] =
76
+ "no-cache, no-transform";
77
+ proxyResponse.headers["x-accel-buffering"] = "no";
78
+ });
79
+ },
63
80
  headers: proxyOrigin
64
81
  ? {
65
82
  origin: proxyOrigin,
@@ -1,177 +0,0 @@
1
- import { useForm } from "@refinedev/react-hook-form";
2
- import { useSelect } from "@refinedev/core";
3
- import { useNavigate } from "react-router";
4
- import { Textarea } from "@/components/ui/textarea";
5
-
6
- import { CreateView } from "@/components/resources/views/create-view";
7
- import { Button } from "@/components/ui/button";
8
- import { Input } from "@/components/ui/input";
9
- import {
10
- Select,
11
- SelectContent,
12
- SelectItem,
13
- SelectTrigger,
14
- SelectValue,
15
- } from "@/components/ui/select";
16
- import {
17
- Form,
18
- FormControl,
19
- FormField,
20
- FormItem,
21
- FormLabel,
22
- FormMessage,
23
- } from "@/components/ui/form";
24
-
25
- export const BlogPostCreate = () => {
26
- const navigate = useNavigate();
27
-
28
- const {
29
- refineCore: { onFinish },
30
- ...form
31
- } = useForm({
32
- refineCoreProps: {},
33
- });
34
-
35
- const { options: categoryOptions } = useSelect({
36
- resource: "categories",
37
- optionLabel: "title",
38
- optionValue: "id",
39
- pagination: {
40
- currentPage: 1,
41
- pageSize: 100,
42
- },
43
- });
44
-
45
- function onSubmit(values: Record<string, string>) {
46
- onFinish(values);
47
- }
48
-
49
- return (
50
- <CreateView>
51
- <Form {...form}>
52
- <form onSubmit={form.handleSubmit(onSubmit)} className="resource-form">
53
- <FormField
54
- control={form.control}
55
- name="title"
56
- rules={{ required: "Title is required" }}
57
- render={({ field }) => (
58
- <FormItem>
59
- <FormLabel>Title</FormLabel>
60
- <FormControl
61
- render={<Input
62
- {...field}
63
- value={field.value || ""}
64
- placeholder="Enter title"
65
- />}
66
- />
67
- <FormMessage />
68
- </FormItem>
69
- )}
70
- />
71
-
72
- <FormField
73
- control={form.control}
74
- name="content"
75
- rules={{ required: "Content is required" }}
76
- render={({ field }) => (
77
- <FormItem>
78
- <FormLabel>Content</FormLabel>
79
- <FormControl
80
- render={<Textarea
81
- {...field}
82
- value={field.value || ""}
83
- placeholder="Enter content"
84
- rows={10}
85
- />}
86
- />
87
- <FormMessage />
88
- </FormItem>
89
- )}
90
- />
91
-
92
- <FormField
93
- control={form.control}
94
- name="categoryId"
95
- rules={{ required: "Category is required" }}
96
- render={({ field }) => (
97
- <FormItem>
98
- <FormLabel>Category</FormLabel>
99
- <Select
100
- items={categoryOptions?.map((option) => ({
101
- label: option.label,
102
- value: option.value.toString(),
103
- }))}
104
- onValueChange={field.onChange}
105
- value={field.value?.toString() || ""}
106
- >
107
- <FormControl
108
- render={<SelectTrigger>
109
- <SelectValue placeholder="Select a category" />
110
- </SelectTrigger>}
111
- />
112
- <SelectContent>
113
- {categoryOptions?.map((option) => (
114
- <SelectItem key={option.value} value={option.value.toString()}>
115
- {option.label}
116
- </SelectItem>
117
- ))}
118
- </SelectContent>
119
- </Select>
120
- <FormMessage />
121
- </FormItem>
122
- )}
123
- />
124
-
125
- <FormField
126
- control={form.control}
127
- name="status"
128
- rules={{ required: "Status is required" }}
129
- render={({ field }) => (
130
- <FormItem>
131
- <FormLabel>Status</FormLabel>
132
- <Select
133
- items={{
134
- draft: "Draft",
135
- published: "Published",
136
- rejected: "Rejected",
137
- }}
138
- onValueChange={field.onChange}
139
- defaultValue={"draft"}
140
- >
141
- <FormControl
142
- render={<SelectTrigger>
143
- <SelectValue placeholder="Select status" />
144
- </SelectTrigger>}
145
- />
146
- <SelectContent>
147
- <SelectItem value="draft">Draft</SelectItem>
148
- <SelectItem value="published">Published</SelectItem>
149
- <SelectItem value="rejected">Rejected</SelectItem>
150
- </SelectContent>
151
- </Select>
152
- <FormMessage />
153
- </FormItem>
154
- )}
155
- />
156
-
157
- <div className="flex gap-2">
158
- <Button
159
- type="submit"
160
- {...form.saveButtonProps}
161
- disabled={form.formState.isSubmitting}
162
- >
163
- {form.formState.isSubmitting ? "Creating..." : "Create"}
164
- </Button>
165
- <Button
166
- type="button"
167
- variant="outline"
168
- onClick={() => navigate(-1)}
169
- >
170
- Cancel
171
- </Button>
172
- </div>
173
- </form>
174
- </Form>
175
- </CreateView>
176
- );
177
- };