@questpie/admin 3.5.2 → 3.5.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 (195) hide show
  1. package/README.md +8 -0
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/builder/index.d.mts +1 -1
  4. package/dist/client/builder/types/collection-types.d.mts +89 -5
  5. package/dist/client/builder/types/common.d.mts +5 -0
  6. package/dist/client/builder/types/field-types.d.mts +41 -1
  7. package/dist/client/builder/view/view.d.mts +3 -2
  8. package/dist/client/components/actions/action-dialog.mjs +5 -0
  9. package/dist/client/components/admin-link.d.mts +2 -2
  10. package/dist/client/components/fields/boolean-field.mjs +2 -1
  11. package/dist/client/components/fields/date-field.mjs +2 -1
  12. package/dist/client/components/fields/datetime-field.mjs +2 -1
  13. package/dist/client/components/fields/email-field.mjs +2 -1
  14. package/dist/client/components/fields/field-utils.d.mts +11 -0
  15. package/dist/client/components/fields/field-utils.mjs +3 -1
  16. package/dist/client/components/fields/field-wrapper.mjs +3 -3
  17. package/dist/client/components/fields/number-field.mjs +2 -1
  18. package/dist/client/components/fields/object-field.mjs +2 -1
  19. package/dist/client/components/fields/relation/displays/types.mjs +3 -3
  20. package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +7 -0
  21. package/dist/client/components/fields/rich-text-editor/extensions.mjs +19 -2
  22. package/dist/client/components/fields/rich-text-editor/image-popover.mjs +6 -2
  23. package/dist/client/components/fields/rich-text-editor/image-upload.mjs +2 -1
  24. package/dist/client/components/fields/rich-text-editor/index.d.mts +5 -3
  25. package/dist/client/components/fields/rich-text-editor/index.mjs +38 -76
  26. package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +30 -7
  27. package/dist/client/components/fields/rich-text-editor/toolbar.mjs +1 -312
  28. package/dist/client/components/fields/rich-text-editor/types.d.mts +4 -0
  29. package/dist/client/components/fields/rich-text-editor/types.mjs +1 -1
  30. package/dist/client/components/fields/rich-text-editor/utils.mjs +6 -12
  31. package/dist/client/components/fields/select-field.mjs +2 -1
  32. package/dist/client/components/fields/text-field.mjs +2 -1
  33. package/dist/client/components/fields/textarea-field.mjs +2 -1
  34. package/dist/client/components/fields/time-field.mjs +2 -1
  35. package/dist/client/components/filter-builder/filter-builder-sheet.mjs +75 -22
  36. package/dist/client/components/layout/field-layout-renderer.mjs +4 -4
  37. package/dist/client/components/media/media-grid.mjs +2 -1
  38. package/dist/client/components/primitives/asset-preview.mjs +4 -2
  39. package/dist/client/components/primitives/dropzone.d.mts +100 -0
  40. package/dist/client/components/primitives/field-select-control.mjs +2 -1
  41. package/dist/client/components/ui/button.d.mts +23 -0
  42. package/dist/client/components/ui/button.mjs +2 -2
  43. package/dist/client/components/ui/dropdown-menu.d.mts +49 -0
  44. package/dist/client/components/ui/dropdown-menu.mjs +7 -19
  45. package/dist/client/components/ui/popover.mjs +1 -1
  46. package/dist/client/components/ui/search-input.d.mts +56 -0
  47. package/dist/client/components/ui/select.mjs +2 -2
  48. package/dist/client/components/ui/sheet.d.mts +40 -0
  49. package/dist/client/components/ui/table.d.mts +49 -0
  50. package/dist/client/components/ui/table.mjs +15 -1
  51. package/dist/client/components/ui/tooltip.d.mts +21 -0
  52. package/dist/client/contexts/focus-context.d.mts +2 -2
  53. package/dist/client/hooks/query-access.d.mts +9 -0
  54. package/dist/client/hooks/query-access.mjs +20 -0
  55. package/dist/client/hooks/typed-hooks.d.mts +4 -2
  56. package/dist/client/hooks/typed-hooks.mjs +30 -29
  57. package/dist/client/hooks/use-admin-config.mjs +20 -1
  58. package/dist/client/hooks/use-autosave.mjs +91 -0
  59. package/dist/client/hooks/use-collection.mjs +65 -23
  60. package/dist/client/hooks/use-reactive-fields.d.mts +1 -0
  61. package/dist/client/hooks/use-reactive-fields.mjs +16 -1
  62. package/dist/client/hooks/use-server-actions.mjs +12 -1
  63. package/dist/client/hooks/use-upload.d.mts +40 -0
  64. package/dist/client/hooks/use-upload.mjs +4 -2
  65. package/dist/client/hooks/use-view-state.mjs +15 -7
  66. package/dist/client/i18n/hooks.d.mts +20 -0
  67. package/dist/client/lib/utils.d.mts +6 -0
  68. package/dist/client/lib/view-filter-utils.mjs +30 -0
  69. package/dist/client/preview/block-scope-context.d.mts +2 -2
  70. package/dist/client/preview/preview-banner.d.mts +2 -2
  71. package/dist/client/preview/preview-field.d.mts +4 -4
  72. package/dist/client/runtime/provider.mjs +22 -3
  73. package/dist/client/scope/picker.d.mts +2 -2
  74. package/dist/client/scope/provider.d.mts +2 -2
  75. package/dist/client/styles/base.css +75 -79
  76. package/dist/client/utils/asset-url.mjs +27 -0
  77. package/dist/client/utils/build-field-definitions-from-schema.mjs +1 -0
  78. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  79. package/dist/client/views/auth/auth-layout.d.mts +3 -3
  80. package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
  81. package/dist/client/views/auth/login-form.d.mts +2 -2
  82. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  83. package/dist/client/views/auth/setup-form.d.mts +2 -2
  84. package/dist/client/views/collection/auto-form-fields.mjs +7 -6
  85. package/dist/client/views/collection/cells/primitive-cells.mjs +9 -6
  86. package/dist/client/views/collection/cells/shared/asset-thumbnail.d.mts +7 -0
  87. package/dist/client/views/collection/cells/shared/asset-thumbnail.mjs +3 -2
  88. package/dist/client/views/collection/cells/shared/cell-helpers.mjs +3 -2
  89. package/dist/client/views/collection/cells/upload-cells.mjs +2 -1
  90. package/dist/client/views/collection/columns/build-columns.mjs +3 -1
  91. package/dist/client/views/collection/document-view.d.mts +30 -0
  92. package/dist/client/views/collection/document-view.mjs +377 -0
  93. package/dist/client/views/collection/field-context.mjs +3 -2
  94. package/dist/client/views/collection/field-renderer.mjs +13 -5
  95. package/dist/client/views/collection/form-view.mjs +221 -282
  96. package/dist/client/views/collection/list-view.mjs +592 -190
  97. package/dist/client/views/collection/outline.mjs +44 -19
  98. package/dist/client/views/collection/quick-filter-bar.mjs +45 -0
  99. package/dist/client/views/collection/table-view.mjs +61 -17
  100. package/dist/client/views/globals/global-form-view.mjs +12 -9
  101. package/dist/client/views/layout/admin-layout-provider.mjs +4 -3
  102. package/dist/client/views/layout/admin-layout.mjs +108 -21
  103. package/dist/client/views/layout/admin-router.mjs +19 -3
  104. package/dist/client/views/layout/admin-sidebar.mjs +70 -20
  105. package/dist/client/views/layout/admin-theme.mjs +5 -4
  106. package/dist/client/views/layout/admin-view-layout.d.mts +36 -0
  107. package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
  108. package/dist/client/views/pages/dashboard-page.d.mts +2 -2
  109. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  110. package/dist/client/views/pages/invite-page.d.mts +2 -2
  111. package/dist/client/views/pages/login-page.d.mts +2 -2
  112. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  113. package/dist/client/views/pages/setup-page.d.mts +2 -2
  114. package/dist/client.d.mts +17 -2
  115. package/dist/client.mjs +17 -2
  116. package/dist/components/rich-text/rich-text-renderer.d.mts +5 -5
  117. package/dist/components/rich-text/rich-text-renderer.mjs +5 -2
  118. package/dist/factories.d.mts +4 -2
  119. package/dist/factories.mjs +2 -2
  120. package/dist/index.d.mts +17 -3
  121. package/dist/index.mjs +17 -2
  122. package/dist/modules/admin.d.mts +1 -1
  123. package/dist/server/adapters/index.d.mts +2 -0
  124. package/dist/server/adapters/nextjs.d.mts +1 -0
  125. package/dist/server/augmentation/actions.d.mts +9 -3
  126. package/dist/server/augmentation/dashboard.d.mts +11 -11
  127. package/dist/server/augmentation/form-layout.d.mts +16 -6
  128. package/dist/server/augmentation/index.d.mts +7 -0
  129. package/dist/server/augmentation/sidebar.d.mts +8 -8
  130. package/dist/server/augmentation/views.d.mts +4 -1
  131. package/dist/server/auth-helpers.d.mts +1 -0
  132. package/dist/server/codegen/admin-client-template.mjs +7 -6
  133. package/dist/server/fields/blocks.mjs +4 -1
  134. package/dist/server/fields/index.d.mts +1 -1
  135. package/dist/server/fields/reactive-runtime.mjs +3 -0
  136. package/dist/server/fields/rich-text.d.mts +16 -17
  137. package/dist/server/fields/rich-text.mjs +18 -7
  138. package/dist/server/i18n/messages/cs.mjs +2 -0
  139. package/dist/server/i18n/messages/de.mjs +2 -0
  140. package/dist/server/i18n/messages/en.mjs +4 -0
  141. package/dist/server/i18n/messages/es.mjs +2 -0
  142. package/dist/server/i18n/messages/fr.mjs +2 -0
  143. package/dist/server/i18n/messages/pl.mjs +2 -0
  144. package/dist/server/i18n/messages/pt.mjs +2 -0
  145. package/dist/server/i18n/messages/sk.mjs +2 -0
  146. package/dist/server/modules/admin/.generated/module.d.mts +1 -1
  147. package/dist/server/modules/admin/auth-helpers.mjs +7 -1
  148. package/dist/server/modules/admin/block/block-builder.d.mts +0 -8
  149. package/dist/server/modules/admin/block/introspection.d.mts +2 -2
  150. package/dist/server/modules/admin/block/introspection.mjs +28 -4
  151. package/dist/server/modules/admin/block/prefetch.d.mts +11 -0
  152. package/dist/server/modules/admin/block/prefetch.mjs +108 -27
  153. package/dist/server/modules/admin/client/.generated/module.d.mts +68 -67
  154. package/dist/server/modules/admin/client/.generated/module.mjs +2 -0
  155. package/dist/server/modules/admin/client/views/collection-document.d.mts +6 -0
  156. package/dist/server/modules/admin/client/views/collection-document.mjs +10 -0
  157. package/dist/server/modules/admin/collections/account.d.mts +53 -52
  158. package/dist/server/modules/admin/collections/admin-locks.d.mts +57 -56
  159. package/dist/server/modules/admin/collections/admin-preferences.d.mts +38 -37
  160. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +50 -49
  161. package/dist/server/modules/admin/collections/apikey.d.mts +76 -67
  162. package/dist/server/modules/admin/collections/assets.d.mts +37 -36
  163. package/dist/server/modules/admin/collections/session.d.mts +42 -41
  164. package/dist/server/modules/admin/collections/user.d.mts +57 -56
  165. package/dist/server/modules/admin/collections/verification.d.mts +34 -33
  166. package/dist/server/modules/admin/dto/admin-config.dto.mjs +34 -4
  167. package/dist/server/modules/admin/factories.mjs +4 -34
  168. package/dist/server/modules/admin/index.d.mts +3 -3
  169. package/dist/server/modules/admin/routes/admin-config.d.mts +4 -2
  170. package/dist/server/modules/admin/routes/admin-config.mjs +56 -24
  171. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  172. package/dist/server/modules/admin/routes/execute-action.mjs +35 -9
  173. package/dist/server/modules/admin/routes/locales.mjs +1 -1
  174. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  175. package/dist/server/modules/admin/routes/preview.mjs +6 -5
  176. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  177. package/dist/server/modules/admin/routes/reactive.mjs +2 -2
  178. package/dist/server/modules/admin/routes/route-helpers.d.mts +11 -7
  179. package/dist/server/modules/admin/routes/route-helpers.mjs +1 -1
  180. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  181. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  182. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  183. package/dist/server/modules/admin/routes/widget-data.mjs +12 -4
  184. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +45 -45
  185. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  186. package/dist/server/modules/audit/collections/audit-log.d.mts +81 -80
  187. package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
  188. package/dist/server/plugin.mjs +10 -5
  189. package/dist/server/proxy-factories.d.mts +8 -1
  190. package/dist/server/proxy-factories.mjs +33 -1
  191. package/dist/server.d.mts +3 -1
  192. package/dist/shared/types/index.d.mts +1 -0
  193. package/dist/shared/types/saved-views.types.d.mts +14 -7
  194. package/dist/shared.d.mts +3 -2
  195. package/package.json +5 -4
@@ -1,7 +1,33 @@
1
1
  import { useQuestpieQueryOptions } from "./use-questpie-query-options.mjs";
2
+ import { useEffect } from "react";
2
3
  import { useMutation, useQuery } from "@tanstack/react-query";
4
+ import { buildCollectionTopic } from "@questpie/tanstack-query";
3
5
 
4
6
  //#region src/client/hooks/use-collection.ts
7
+ function useCollectionRealtimeInvalidation({ client, collection, options, realtime, queryClient }) {
8
+ const topicSignature = JSON.stringify(collection ? buildCollectionTopic(collection, options) : null);
9
+ useEffect(() => {
10
+ if (!collection || !realtime) return;
11
+ const realtimeApi = client?.realtime;
12
+ if (!realtimeApi?.subscribe) return;
13
+ const topic = JSON.parse(topicSignature);
14
+ if (!topic) return;
15
+ return realtimeApi.subscribe(topic, () => {
16
+ queryClient.invalidateQueries({ queryKey: [
17
+ "questpie",
18
+ "collections",
19
+ "collections",
20
+ collection
21
+ ] });
22
+ });
23
+ }, [
24
+ client,
25
+ collection,
26
+ queryClient,
27
+ realtime,
28
+ topicSignature
29
+ ]);
30
+ }
5
31
  /**
6
32
  * Hook to fetch collection list with filters, sorting, pagination
7
33
  *
@@ -14,24 +40,32 @@ import { useMutation, useQuery } from "@tanstack/react-query";
14
40
  * ```
15
41
  */
16
42
  function useCollectionList(collection, options, queryOptions, realtimeOptions) {
17
- const { queryOpts, locale } = useQuestpieQueryOptions();
43
+ const { queryOpts, queryClient, locale, client } = useQuestpieQueryOptions();
18
44
  const findOptions = {
19
45
  ...options,
20
46
  locale
21
47
  };
48
+ const baseQuery = collection ? queryOpts.collections[collection].find(findOptions, { realtime: realtimeOptions?.realtime }) : {
49
+ queryKey: [
50
+ "questpie",
51
+ "collections",
52
+ "__none__",
53
+ "find"
54
+ ],
55
+ queryFn: () => ({
56
+ docs: [],
57
+ totalDocs: 0
58
+ })
59
+ };
60
+ useCollectionRealtimeInvalidation({
61
+ client,
62
+ collection: collection ? String(collection) : void 0,
63
+ options: findOptions,
64
+ realtime: realtimeOptions?.realtime,
65
+ queryClient
66
+ });
22
67
  return useQuery({
23
- ...collection ? queryOpts.collections[collection].find(findOptions, { realtime: realtimeOptions?.realtime }) : {
24
- queryKey: [
25
- "questpie",
26
- "collections",
27
- "__none__",
28
- "find"
29
- ],
30
- queryFn: () => ({
31
- docs: [],
32
- totalDocs: 0
33
- })
34
- },
68
+ ...baseQuery,
35
69
  enabled: !!collection && (queryOptions?.enabled ?? true),
36
70
  ...queryOptions
37
71
  });
@@ -54,21 +88,29 @@ function useCollectionList(collection, options, queryOptions, realtimeOptions) {
54
88
  * ```
55
89
  */
56
90
  function useCollectionCount(collection, options, queryOptions, realtimeOptions) {
57
- const { queryOpts, locale } = useQuestpieQueryOptions();
91
+ const { queryOpts, queryClient, locale, client } = useQuestpieQueryOptions();
58
92
  const countOptions = {
59
93
  ...options,
60
94
  locale
61
95
  };
96
+ const baseQuery = collection ? queryOpts.collections[collection].count(countOptions, { realtime: realtimeOptions?.realtime }) : {
97
+ queryKey: [
98
+ "questpie",
99
+ "collections",
100
+ "__none__",
101
+ "count"
102
+ ],
103
+ queryFn: () => 0
104
+ };
105
+ useCollectionRealtimeInvalidation({
106
+ client,
107
+ collection: collection ? String(collection) : void 0,
108
+ options: countOptions,
109
+ realtime: realtimeOptions?.realtime,
110
+ queryClient
111
+ });
62
112
  return useQuery({
63
- ...collection ? queryOpts.collections[collection].count(countOptions, { realtime: realtimeOptions?.realtime }) : {
64
- queryKey: [
65
- "questpie",
66
- "collections",
67
- "__none__",
68
- "count"
69
- ],
70
- queryFn: () => 0
71
- },
113
+ ...baseQuery,
72
114
  enabled: !!collection && (queryOptions?.enabled ?? true),
73
115
  ...queryOptions
74
116
  });
@@ -1,3 +1,4 @@
1
+ import "react";
1
2
  import { UseFormReturn } from "react-hook-form";
2
3
  import { FieldReactiveSchema } from "questpie/client";
3
4
 
@@ -10,6 +10,8 @@ import { useFormContext, useWatch } from "react-hook-form";
10
10
  * Watches form changes and triggers server-side reactive handlers.
11
11
  * Supports batched RPC calls with debouncing.
12
12
  */
13
+ const EMPTY_REACTIVE_FIELD_STATE = {};
14
+ const ReactiveFieldStatesContext = React.createContext({});
13
15
  /**
14
16
  * Get sibling data for a field path (for fields inside arrays)
15
17
  */
@@ -66,6 +68,19 @@ function getChangedDeps(prevDeps, nextDeps) {
66
68
  for (const [dep, value] of Object.entries(nextDeps)) if (prevDeps[dep] !== value) changed.push(dep);
67
69
  return changed;
68
70
  }
71
+ function mergeReactiveFieldState(base, reactive) {
72
+ return {
73
+ hidden: base.hidden === true || reactive?.hidden === true,
74
+ readOnly: base.readOnly === true || reactive?.readOnly === true,
75
+ disabled: base.disabled === true || reactive?.disabled === true
76
+ };
77
+ }
78
+ function ReactiveFieldStatesProvider({ fieldStates, children }) {
79
+ return React.createElement(ReactiveFieldStatesContext.Provider, { value: fieldStates }, children);
80
+ }
81
+ function useReactiveFieldState(fieldPath) {
82
+ return React.useContext(ReactiveFieldStatesContext)[fieldPath] ?? EMPTY_REACTIVE_FIELD_STATE;
83
+ }
69
84
  /**
70
85
  * Hook to manage reactive field states.
71
86
  * Watches form changes and triggers server-side handlers when dependencies change.
@@ -198,4 +213,4 @@ function useReactiveFields({ collection, mode = "collection", reactiveConfigs, d
198
213
  }
199
214
 
200
215
  //#endregion
201
- export { useReactiveFields };
216
+ export { ReactiveFieldStatesProvider, mergeReactiveFieldState, useReactiveFieldState, useReactiveFields };
@@ -14,6 +14,13 @@ import * as React from "react";
14
14
  * Server actions have their handlers stripped during serialization; this hook
15
15
  * creates client-side wrappers that execute actions via the server API.
16
16
  */
17
+ var ServerActionValidationError = class extends Error {
18
+ constructor(message, fieldErrors) {
19
+ super(message);
20
+ this.fieldErrors = fieldErrors;
21
+ this.name = "ServerActionValidationError";
22
+ }
23
+ };
17
24
  function getActionErrorMessage(response, t) {
18
25
  if (response.error) return response.error;
19
26
  if (response.result?.toast?.message) return response.result.toast.message;
@@ -106,7 +113,11 @@ function mapServerAction(serverAction, collection, fieldRegistry, client, locale
106
113
  data,
107
114
  locale
108
115
  });
109
- if (!response.success || response.result?.type === "error") throw new Error(getActionErrorMessage(response, t));
116
+ if (!response.success || response.result?.type === "error") {
117
+ const message = getActionErrorMessage(response, t);
118
+ if (response.result?.errors) throw new ServerActionValidationError(message, response.result.errors);
119
+ throw new Error(message);
120
+ }
110
121
  await applyServerActionEffects(response.result, ctx);
111
122
  return response.result?.toast?.message;
112
123
  }
@@ -0,0 +1,40 @@
1
+ //#region src/client/hooks/use-upload.d.ts
2
+ interface Asset {
3
+ id: string;
4
+ key: string;
5
+ filename: string;
6
+ mimeType: string;
7
+ size: number;
8
+ visibility: "public" | "private";
9
+ url?: string;
10
+ width?: number | null;
11
+ height?: number | null;
12
+ alt?: string | null;
13
+ caption?: string | null;
14
+ createdAt?: string;
15
+ updatedAt?: string;
16
+ }
17
+ interface UploadOptions {
18
+ to?: string;
19
+ /**
20
+ * Optional destination path/folder forwarded to the collection's upload so a
21
+ * blob upload lands inside a folder (e.g. an `assets` row's required `path`).
22
+ */
23
+ path?: string;
24
+ onProgress?: (progress: number) => void;
25
+ signal?: AbortSignal;
26
+ }
27
+ interface UploadManyOptions extends UploadOptions {
28
+ onProgress?: (progress: number, fileIndex?: number) => void;
29
+ }
30
+ interface UseUploadReturn {
31
+ upload: (file: File, options?: UploadOptions) => Promise<Asset>;
32
+ uploadMany: (files: File[], options?: UploadManyOptions) => Promise<Asset[]>;
33
+ isUploading: boolean;
34
+ progress: number;
35
+ error: Error | null;
36
+ reset: () => void;
37
+ }
38
+ declare function useUpload(): UseUploadReturn;
39
+ //#endregion
40
+ export { Asset, useUpload };
@@ -29,7 +29,7 @@ function useUpload() {
29
29
  }, [uploadCollections]);
30
30
  const uploadMutation = useMutation({
31
31
  mutationFn: async ({ file, options }) => {
32
- const { to, onProgress, signal } = options ?? {};
32
+ const { to, path, onProgress, signal } = options ?? {};
33
33
  const targetCollection = resolveTargetCollection(to);
34
34
  if (!targetCollection) throw new Error(getMissingCollectionMessage());
35
35
  const collectionApi = client.collections[targetCollection];
@@ -38,6 +38,7 @@ function useUpload() {
38
38
  return {
39
39
  asset: await collectionApi.upload(file, {
40
40
  signal,
41
+ path,
41
42
  onProgress: (p) => {
42
43
  setProgress(p);
43
44
  onProgress?.(p);
@@ -56,7 +57,7 @@ function useUpload() {
56
57
  });
57
58
  const uploadManyMutation = useMutation({
58
59
  mutationFn: async ({ files, options }) => {
59
- const { to, onProgress, signal } = options ?? {};
60
+ const { to, path, onProgress, signal } = options ?? {};
60
61
  const targetCollection = resolveTargetCollection(to);
61
62
  if (!targetCollection) throw new Error(getMissingCollectionMessage());
62
63
  if (files.length === 0) return {
@@ -68,6 +69,7 @@ function useUpload() {
68
69
  setProgress(0);
69
70
  const results = await collectionApi.uploadMany(files, {
70
71
  signal,
72
+ path,
71
73
  onProgress: (p, fileIndex) => {
72
74
  setProgress(p);
73
75
  onProgress?.(p, fileIndex);
@@ -1,5 +1,6 @@
1
1
  import { useAdminStore } from "../runtime/provider.mjs";
2
2
  import { getAdminPreferenceQueryKey, useSetAdminPreference } from "./use-admin-preferences.mjs";
3
+ import { cloneFilters, filtersEqual, sortConfigEqual } from "../lib/view-filter-utils.mjs";
3
4
  import { useCallback, useEffect, useMemo, useRef } from "react";
4
5
  import { useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
5
6
 
@@ -102,7 +103,7 @@ function useViewState(defaultColumns, initialConfig, collectionName, userId) {
102
103
  }
103
104
  };
104
105
  return {
105
- filters: initialConfig?.filters ?? [],
106
+ filters: cloneFilters(initialConfig?.filters ?? []),
106
107
  sortConfig: initialConfig?.sortConfig ?? null,
107
108
  visibleColumns: defaultColumns,
108
109
  groupBy: initialConfig?.groupBy ?? null,
@@ -273,17 +274,24 @@ function useViewState(defaultColumns, initialConfig, collectionName, userId) {
273
274
  resetConfig: useCallback(() => {
274
275
  setConfig({
275
276
  ...EMPTY_CONFIG,
276
- visibleColumns: defaultColumns
277
+ filters: cloneFilters(initialConfig?.filters ?? []),
278
+ sortConfig: initialConfig?.sortConfig ?? null,
279
+ visibleColumns: defaultColumns,
280
+ groupBy: initialConfig?.groupBy ?? null,
281
+ realtime: initialConfig?.realtime,
282
+ includeDeleted: initialConfig?.includeDeleted ?? false
277
283
  });
278
- }, [setConfig, defaultColumns]),
284
+ }, [
285
+ setConfig,
286
+ defaultColumns,
287
+ initialConfig
288
+ ]),
279
289
  hasChanges: useMemo(() => {
280
- return config.filters.length > 0 || config.sortConfig !== null || (config.groupBy ?? null) !== (initialConfig?.groupBy ?? null) || (config.collapsedGroups?.length ?? 0) > 0 || config.realtime !== initialConfig?.realtime || (config.includeDeleted ?? false) !== (initialConfig?.includeDeleted ?? false) || config.pagination?.page !== 1 || config.pagination?.pageSize !== 25 || JSON.stringify([...config.visibleColumns].sort()) !== JSON.stringify([...defaultColumns].sort());
290
+ return !filtersEqual(config.filters, initialConfig?.filters ?? []) || !sortConfigEqual(config.sortConfig ?? null, initialConfig?.sortConfig ?? null) || (config.groupBy ?? null) !== (initialConfig?.groupBy ?? null) || (config.collapsedGroups?.length ?? 0) > 0 || config.realtime !== initialConfig?.realtime || (config.includeDeleted ?? false) !== (initialConfig?.includeDeleted ?? false) || config.pagination?.page !== 1 || config.pagination?.pageSize !== 25 || JSON.stringify([...config.visibleColumns].sort()) !== JSON.stringify([...defaultColumns].sort());
281
291
  }, [
282
292
  config,
283
293
  defaultColumns,
284
- initialConfig?.includeDeleted,
285
- initialConfig?.groupBy,
286
- initialConfig?.realtime
294
+ initialConfig
287
295
  ]),
288
296
  isLoading: false
289
297
  };
@@ -0,0 +1,20 @@
1
+ import { I18nText } from "./types.mjs";
2
+ import "react";
3
+ import "date-fns";
4
+
5
+ //#region src/client/i18n/hooks.d.ts
6
+
7
+ /**
8
+ * Resolve I18nText to string
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * function Label({ text }: { text: I18nText }) {
13
+ * const resolve = useResolveText();
14
+ * return <span>{resolve(text)}</span>;
15
+ * }
16
+ * ```
17
+ */
18
+ declare function useResolveText(): (text: I18nText | ((values: Record<string, any>) => I18nText) | undefined, fallback?: string, contextValues?: Record<string, any>) => string;
19
+ //#endregion
20
+ export { useResolveText };
@@ -0,0 +1,6 @@
1
+ import { ClassValue } from "clsx";
2
+
3
+ //#region src/client/lib/utils.d.ts
4
+ declare function cn(...inputs: ClassValue[]): string;
5
+ //#endregion
6
+ export { cn };
@@ -0,0 +1,30 @@
1
+ //#region src/client/lib/view-filter-utils.ts
2
+ function filterValueEqual(a, b) {
3
+ if (Array.isArray(a) || Array.isArray(b)) {
4
+ if (!Array.isArray(a) || !Array.isArray(b)) return false;
5
+ if (a.length !== b.length) return false;
6
+ return a.every((value, index) => value === b[index]);
7
+ }
8
+ return a === b;
9
+ }
10
+ function filtersEqual(a = [], b = []) {
11
+ if (a.length !== b.length) return false;
12
+ return a.every((filter, index) => {
13
+ const other = b[index];
14
+ return filter.id === other.id && filter.field === other.field && filter.operator === other.operator && filterValueEqual(filter.value, other.value);
15
+ });
16
+ }
17
+ function cloneFilters(filters) {
18
+ return filters.map((filter) => ({
19
+ ...filter,
20
+ value: Array.isArray(filter.value) ? [...filter.value] : filter.value
21
+ }));
22
+ }
23
+ function sortConfigEqual(a, b) {
24
+ if (a === b) return true;
25
+ if (!a || !b) return false;
26
+ return a.field === b.field && a.direction === b.direction;
27
+ }
28
+
29
+ //#endregion
30
+ export { cloneFilters, filtersEqual, sortConfigEqual };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as react_jsx_runtime20 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime55 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/preview/block-scope-context.d.ts
5
5
 
@@ -35,7 +35,7 @@ declare function BlockScopeProvider({
35
35
  blockId,
36
36
  basePath,
37
37
  children
38
- }: BlockScopeProviderProps): react_jsx_runtime20.JSX.Element;
38
+ }: BlockScopeProviderProps): react_jsx_runtime55.JSX.Element;
39
39
  /**
40
40
  * Get current block scope context.
41
41
  *
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime21 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime56 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/client/preview/preview-banner.d.ts
4
4
 
@@ -40,6 +40,6 @@ declare function PreviewBanner({
40
40
  isPreviewMode,
41
41
  className,
42
42
  exitPreviewUrl
43
- }: PreviewBannerProps): react_jsx_runtime21.JSX.Element | null;
43
+ }: PreviewBannerProps): react_jsx_runtime56.JSX.Element | null;
44
44
  //#endregion
45
45
  export { PreviewBanner, PreviewBannerProps };
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as react_jsx_runtime22 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime57 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/preview/preview-field.d.ts
5
5
 
@@ -68,7 +68,7 @@ declare function PreviewProvider({
68
68
  }) => void;
69
69
  onFieldValueEdited?: (payload: PreviewFieldValueEditedPayload) => void;
70
70
  children: React.ReactNode;
71
- }): react_jsx_runtime22.JSX.Element;
71
+ }): react_jsx_runtime57.JSX.Element;
72
72
  /**
73
73
  * Hook to access preview context.
74
74
  */
@@ -107,7 +107,7 @@ declare function PreviewField({
107
107
  style,
108
108
  onClick,
109
109
  onValueCommit
110
- }: PreviewFieldProps): react_jsx_runtime22.JSX.Element;
110
+ }: PreviewFieldProps): react_jsx_runtime57.JSX.Element;
111
111
  /**
112
112
  * Standalone PreviewField that works without context.
113
113
  * Useful when you can't use PreviewProvider.
@@ -131,6 +131,6 @@ declare function StandalonePreviewField({
131
131
  blockId?: string;
132
132
  fieldType?: "regular" | "block" | "relation";
133
133
  }) => void;
134
- }): react_jsx_runtime22.JSX.Element;
134
+ }): react_jsx_runtime57.JSX.Element;
135
135
  //#endregion
136
136
  export { PreviewField, PreviewFieldProps, PreviewProvider, StandalonePreviewField, usePreviewContext };
@@ -19,6 +19,13 @@ const CONTENT_LOCALE_COOKIE = "questpie_content_locale";
19
19
  /** Cookie max age (1 year) */
20
20
  const LOCALE_COOKIE_MAX_AGE = 3600 * 24 * 365;
21
21
  const LEGACY_LOCALE_COOKIE = "questpie_locale";
22
+ const PUBLIC_BRANDING_PATHS = [
23
+ "/login",
24
+ "/forgot-password",
25
+ "/reset-password",
26
+ "/accept-invite",
27
+ "/setup"
28
+ ];
22
29
  function setCookie(name, value) {
23
30
  if (typeof document === "undefined") return;
24
31
  document.cookie = `${name}=${value}; path=/; max-age=${LOCALE_COOKIE_MAX_AGE}; SameSite=Lax`;
@@ -201,13 +208,25 @@ function applyFavicon(href) {
201
208
  }
202
209
  link.href = href;
203
210
  }
211
+ function isPublicBrandingPath(basePath) {
212
+ if (typeof window === "undefined") return false;
213
+ const currentPath = window.location.pathname.replace(/\/+$/, "");
214
+ const normalizedBase = basePath.replace(/\/+$/, "");
215
+ return PUBLIC_BRANDING_PATHS.some((path) => {
216
+ const fullPath = `${normalizedBase}${path}`.replace(/\/+$/, "");
217
+ return currentPath === fullPath || currentPath.startsWith(`${fullPath}/`);
218
+ });
219
+ }
204
220
  function BrandingSync() {
205
221
  const store = useContext(AdminStoreContext);
206
222
  useEffect(() => {
207
223
  if (!store) return;
208
- const client = store.getState().client;
209
- if (!client || !client.routes?.getAdminConfig) return;
210
- client.routes.getAdminConfig().then((config) => {
224
+ const state = store.getState();
225
+ const routes = state.client?.routes;
226
+ if (!routes) return;
227
+ const configRequest = isPublicBrandingPath(state.basePath) && typeof routes.getPublicAdminConfig === "function" ? routes.getPublicAdminConfig() : routes.getAdminConfig?.();
228
+ if (!configRequest) return;
229
+ configRequest.then((config) => {
211
230
  const branding = config?.branding;
212
231
  if (!branding) return;
213
232
  const next = {};
@@ -1,5 +1,5 @@
1
1
  import { ScopePickerProps } from "./types.mjs";
2
- import * as react_jsx_runtime26 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime60 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/scope/picker.d.ts
5
5
 
@@ -48,6 +48,6 @@ declare function ScopePicker({
48
48
  clearText,
49
49
  className,
50
50
  compact
51
- }: ScopePickerProps): react_jsx_runtime26.JSX.Element;
51
+ }: ScopePickerProps): react_jsx_runtime60.JSX.Element;
52
52
  //#endregion
53
53
  export { ScopePicker };
@@ -1,5 +1,5 @@
1
1
  import { ScopeContextValue, ScopeProviderProps } from "./types.mjs";
2
- import * as react_jsx_runtime27 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime61 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/client/scope/provider.d.ts
5
5
 
@@ -29,7 +29,7 @@ declare function ScopeProvider({
29
29
  headerName,
30
30
  storageKey,
31
31
  defaultScope
32
- }: ScopeProviderProps): react_jsx_runtime27.JSX.Element;
32
+ }: ScopeProviderProps): react_jsx_runtime61.JSX.Element;
33
33
  /**
34
34
  * Hook to access the current scope context.
35
35
  *