@questpie/admin 3.4.1 → 3.5.1
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/dist/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/builder/types/action-types.d.mts +2 -2
- package/dist/client/components/actions/action-dialog.mjs +7 -7
- package/dist/client/components/blocks/block-canvas.mjs +1 -1
- package/dist/client/components/blocks/block-editor-layout.mjs +2 -2
- package/dist/client/components/blocks/block-insert-button.mjs +3 -3
- package/dist/client/components/blocks/block-item-menu.mjs +9 -9
- package/dist/client/components/blocks/block-item.mjs +5 -5
- package/dist/client/components/blocks/block-library-sidebar.mjs +3 -3
- package/dist/client/components/fields/array-field.mjs +2 -2
- package/dist/client/components/fields/date-field.mjs +6 -5
- package/dist/client/components/fields/datetime-field.mjs +6 -1
- package/dist/client/components/fields/object-array-field.mjs +2 -2
- package/dist/client/components/fields/relation/displays/cards-display.mjs +1 -1
- package/dist/client/components/fields/relation/displays/grid-display.mjs +1 -1
- package/dist/client/components/fields/relation/displays/list-display.mjs +3 -3
- package/dist/client/components/fields/relation/displays/table-display.mjs +1 -1
- package/dist/client/components/fields/relation-picker.mjs +3 -3
- package/dist/client/components/fields/relation-select.mjs +2 -2
- package/dist/client/components/filter-builder/filter-builder-sheet.mjs +16 -16
- package/dist/client/components/history-sidebar.mjs +12 -4
- package/dist/client/components/layout/field-layout-renderer.mjs +8 -3
- package/dist/client/components/media/media-grid.mjs +2 -2
- package/dist/client/components/preview/live-preview-mode.mjs +4 -4
- package/dist/client/components/preview/preview-pane.mjs +4 -4
- package/dist/client/components/primitives/asset-preview.mjs +5 -5
- package/dist/client/components/primitives/dropzone.mjs +1 -1
- package/dist/client/components/ui/kbd.mjs +1 -1
- package/dist/client/components/ui/scroll-fade.mjs +4 -4
- package/dist/client/components/ui/sidebar.mjs +1 -1
- package/dist/client/components/ui/skeleton.mjs +1 -1
- package/dist/client/components/ui/table.mjs +1 -1
- package/dist/client/components/widgets/quick-actions-widget.mjs +6 -6
- package/dist/client/components/widgets/timeline-widget.mjs +3 -3
- package/dist/client/components/widgets/value-widget.mjs +1 -1
- package/dist/client/components/widgets/widget-skeletons.mjs +2 -2
- package/dist/client/hooks/typed-hooks.mjs +66 -21
- package/dist/client/hooks/use-collection.mjs +48 -7
- package/dist/client/hooks/use-server-actions.mjs +1 -0
- package/dist/client/i18n/date-locale.mjs +0 -14
- package/dist/client/preview/diff.mjs +4 -1
- package/dist/client/preview/patch.mjs +1 -1
- package/dist/client/preview/paths.mjs +85 -0
- package/dist/client/runtime/translations-provider.mjs +1 -1
- package/dist/client/scope/picker.d.mts +2 -2
- package/dist/client/scope/provider.d.mts +2 -2
- package/dist/client/styles/base.css +51 -1
- package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
- package/dist/client/views/auth/auth-layout.d.mts +3 -3
- package/dist/client/views/auth/reset-password-form.d.mts +2 -2
- package/dist/client/views/auth/setup-form.d.mts +2 -2
- package/dist/client/views/collection/auto-form-fields.mjs +1 -1
- package/dist/client/views/collection/cells/primitive-cells.mjs +2 -2
- package/dist/client/views/collection/cells/upload-cells.mjs +2 -2
- package/dist/client/views/collection/form-view.mjs +45 -26
- package/dist/client/views/collection/table-view.mjs +33 -20
- package/dist/client/views/collection/view-skeletons.mjs +37 -38
- package/dist/client/views/common/global-search.mjs +3 -3
- package/dist/client/views/dashboard/widget-card.mjs +7 -7
- package/dist/client/views/layout/admin-router.mjs +84 -37
- package/dist/client/views/layout/admin-sidebar.mjs +22 -21
- package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
- package/dist/client/views/pages/dashboard-page.d.mts +2 -2
- package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
- package/dist/client/views/pages/invite-page.d.mts +2 -2
- package/dist/client/views/pages/login-page.d.mts +2 -2
- package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
- package/dist/factories.d.mts +0 -2
- package/dist/server/adapters/nextjs.d.mts +0 -1
- package/dist/server/augmentation/actions.d.mts +2 -0
- package/dist/server/i18n/messages/cs.mjs +2 -0
- package/dist/server/i18n/messages/de.mjs +2 -0
- package/dist/server/i18n/messages/en.mjs +2 -0
- package/dist/server/i18n/messages/es.mjs +2 -0
- package/dist/server/i18n/messages/fr.mjs +2 -0
- package/dist/server/i18n/messages/pl.mjs +2 -0
- package/dist/server/i18n/messages/pt.mjs +2 -0
- package/dist/server/i18n/messages/sk.mjs +2 -0
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +49 -49
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +36 -36
- package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
- package/dist/server/modules/admin/collections/assets.d.mts +34 -34
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +14 -14
- package/dist/server/modules/admin/collections/user.mjs +9 -9
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/execute-action.mjs +3 -2
- package/dist/server/modules/admin/routes/preview.d.mts +11 -11
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/setup.d.mts +7 -7
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +23 -23
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +37 -37
- package/dist/server.d.mts +0 -2
- package/package.json +3 -3
- package/dist/server/adapters/index.d.mts +0 -2
- package/dist/server/auth-helpers.d.mts +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScopePickerProps } from "./types.mjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime27 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):
|
|
51
|
+
}: ScopePickerProps): react_jsx_runtime27.JSX.Element;
|
|
52
52
|
//#endregion
|
|
53
53
|
export { ScopePicker };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ScopeContextValue, ScopeProviderProps } from "./types.mjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime26 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):
|
|
32
|
+
}: ScopeProviderProps): react_jsx_runtime26.JSX.Element;
|
|
33
33
|
/**
|
|
34
34
|
* Hook to access the current scope context.
|
|
35
35
|
*
|
|
@@ -718,6 +718,51 @@
|
|
|
718
718
|
}
|
|
719
719
|
}
|
|
720
720
|
|
|
721
|
+
/* =============================================================================
|
|
722
|
+
Loading States
|
|
723
|
+
============================================================================= */
|
|
724
|
+
|
|
725
|
+
.qa-skeleton {
|
|
726
|
+
animation:
|
|
727
|
+
qa-skeleton-fade-in var(--motion-duration-base) var(--motion-ease-enter)
|
|
728
|
+
120ms both,
|
|
729
|
+
qa-skeleton-breathe 1.6s ease-in-out 120ms infinite;
|
|
730
|
+
background-color: color-mix(in srgb, var(--muted) 86%, var(--foreground) 14%);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.qa-table-view-skeleton,
|
|
734
|
+
.qa-router-skeleton,
|
|
735
|
+
.qa-form-view-skeleton {
|
|
736
|
+
min-height: min(36rem, calc(100dvh - 14rem));
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
@keyframes qa-skeleton-fade-in {
|
|
740
|
+
from {
|
|
741
|
+
opacity: 0;
|
|
742
|
+
}
|
|
743
|
+
to {
|
|
744
|
+
opacity: 1;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
@keyframes qa-skeleton-breathe {
|
|
749
|
+
0%,
|
|
750
|
+
100% {
|
|
751
|
+
background-color: color-mix(
|
|
752
|
+
in srgb,
|
|
753
|
+
var(--muted) 86%,
|
|
754
|
+
var(--foreground) 14%
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
50% {
|
|
758
|
+
background-color: color-mix(
|
|
759
|
+
in srgb,
|
|
760
|
+
var(--muted) 68%,
|
|
761
|
+
var(--foreground) 32%
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
721
766
|
/* =============================================================================
|
|
722
767
|
Base Styles
|
|
723
768
|
============================================================================= */
|
|
@@ -936,9 +981,14 @@
|
|
|
936
981
|
.animate-field-glow,
|
|
937
982
|
.animate-realtime-pulse,
|
|
938
983
|
.animate-realtime-delete,
|
|
939
|
-
.animate-spin
|
|
984
|
+
.animate-spin,
|
|
985
|
+
.qa-skeleton {
|
|
940
986
|
animation: none !important;
|
|
941
987
|
}
|
|
988
|
+
|
|
989
|
+
.qa-skeleton {
|
|
990
|
+
opacity: 1;
|
|
991
|
+
}
|
|
942
992
|
}
|
|
943
993
|
|
|
944
994
|
/* =============================================================================
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/client/views/auth/accept-invite-form.d.ts
|
|
4
4
|
/**
|
|
@@ -67,6 +67,6 @@ declare function AcceptInviteForm({
|
|
|
67
67
|
className,
|
|
68
68
|
error,
|
|
69
69
|
minPasswordLength
|
|
70
|
-
}: AcceptInviteFormProps):
|
|
70
|
+
}: AcceptInviteFormProps): react_jsx_runtime0.JSX.Element;
|
|
71
71
|
//#endregion
|
|
72
72
|
export { AcceptInviteForm };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime1 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/client/views/auth/auth-layout.d.ts
|
|
5
5
|
|
|
@@ -26,7 +26,7 @@ declare function AuthDefaultLogo({
|
|
|
26
26
|
brandName
|
|
27
27
|
}: {
|
|
28
28
|
brandName: string;
|
|
29
|
-
}):
|
|
29
|
+
}): react_jsx_runtime1.JSX.Element;
|
|
30
30
|
/**
|
|
31
31
|
* Minimal split layout for authentication pages (login, register, forgot password, etc.)
|
|
32
32
|
*
|
|
@@ -50,6 +50,6 @@ declare function AuthDefaultLogo({
|
|
|
50
50
|
* </AuthLayout>
|
|
51
51
|
* ```
|
|
52
52
|
*/
|
|
53
|
-
declare function AuthLayout(props: AuthLayoutProps):
|
|
53
|
+
declare function AuthLayout(props: AuthLayoutProps): react_jsx_runtime1.JSX.Element;
|
|
54
54
|
//#endregion
|
|
55
55
|
export { AuthDefaultLogo, AuthLayout, AuthLayoutProps };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime5 from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/client/views/auth/reset-password-form.d.ts
|
|
4
4
|
/**
|
|
@@ -60,6 +60,6 @@ declare function ResetPasswordForm({
|
|
|
60
60
|
minPasswordLength,
|
|
61
61
|
className,
|
|
62
62
|
error
|
|
63
|
-
}: ResetPasswordFormProps):
|
|
63
|
+
}: ResetPasswordFormProps): react_jsx_runtime5.JSX.Element;
|
|
64
64
|
//#endregion
|
|
65
65
|
export { ResetPasswordForm };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react_jsx_runtime6 from "react/jsx-runtime";
|
|
2
2
|
|
|
3
3
|
//#region src/client/views/auth/setup-form.d.ts
|
|
4
4
|
/**
|
|
@@ -55,6 +55,6 @@ declare function SetupForm({
|
|
|
55
55
|
className,
|
|
56
56
|
error,
|
|
57
57
|
minPasswordLength
|
|
58
|
-
}: SetupFormProps):
|
|
58
|
+
}: SetupFormProps): react_jsx_runtime6.JSX.Element;
|
|
59
59
|
//#endregion
|
|
60
60
|
export { SetupForm, SetupFormValues };
|
|
@@ -481,7 +481,7 @@ function AutoFormFields({ app: _cms, collection, mode = "collection", config, re
|
|
|
481
481
|
children: /* @__PURE__ */ jsx("div", {
|
|
482
482
|
className: "bg-surface-low/45 flex min-h-0 flex-col rounded-md @2xl:h-full",
|
|
483
483
|
children: /* @__PURE__ */ jsx("div", {
|
|
484
|
-
className: "
|
|
484
|
+
className: "min-h-0 scrollbar-thin space-y-4 overflow-y-auto p-3 @2xl:flex-1",
|
|
485
485
|
children: /* @__PURE__ */ jsx(SidebarRenderer, {
|
|
486
486
|
sidebar: formConfig.sidebar,
|
|
487
487
|
fields,
|
|
@@ -183,7 +183,7 @@ function SelectCell({ value, fieldDef }) {
|
|
|
183
183
|
const options = getSelectOptions(fieldDef);
|
|
184
184
|
return /* @__PURE__ */ jsx("span", {
|
|
185
185
|
className: "inline-flex max-w-[300px] flex-wrap gap-1",
|
|
186
|
-
children: values.map((item
|
|
186
|
+
children: values.map((item) => {
|
|
187
187
|
const option = findSelectOption(options, item);
|
|
188
188
|
const icon = resolveIconElement(option?.icon);
|
|
189
189
|
return /* @__PURE__ */ jsxs(Badge, {
|
|
@@ -196,7 +196,7 @@ function SelectCell({ value, fieldDef }) {
|
|
|
196
196
|
t,
|
|
197
197
|
locale
|
|
198
198
|
})]
|
|
199
|
-
},
|
|
199
|
+
}, String(item));
|
|
200
200
|
})
|
|
201
201
|
});
|
|
202
202
|
}
|
|
@@ -79,11 +79,11 @@ function UploadManyCell({ value }) {
|
|
|
79
79
|
return /* @__PURE__ */ jsxs("div", {
|
|
80
80
|
className: "flex items-center gap-1",
|
|
81
81
|
children: [/* @__PURE__ */ jsx("div", {
|
|
82
|
-
className: "flex
|
|
82
|
+
className: "flex",
|
|
83
83
|
children: imageAssets.map((asset, index) => /* @__PURE__ */ jsx("img", {
|
|
84
84
|
src: asset.url,
|
|
85
85
|
alt: asset.filename || "Asset",
|
|
86
|
-
className:
|
|
86
|
+
className: `image-outline bg-background size-6 rounded object-cover${index > 0 ? " -ms-2" : ""}`
|
|
87
87
|
}, asset.id || index))
|
|
88
88
|
}), remaining > 0 && /* @__PURE__ */ jsxs("span", {
|
|
89
89
|
className: "text-muted-foreground ml-1 text-xs tabular-nums",
|
|
@@ -9,6 +9,7 @@ import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
|
|
|
9
9
|
import { Label } from "../../components/ui/label.mjs";
|
|
10
10
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../../components/ui/dialog.mjs";
|
|
11
11
|
import { Badge } from "../../components/ui/badge.mjs";
|
|
12
|
+
import { useAdminConfig } from "../../hooks/use-admin-config.mjs";
|
|
12
13
|
import { RenderProfiler } from "../../lib/render-profiler.mjs";
|
|
13
14
|
import { scrollFieldIntoView } from "../../contexts/focus-context.mjs";
|
|
14
15
|
import { Checkbox } from "../../components/ui/checkbox.mjs";
|
|
@@ -33,6 +34,7 @@ import { useServerActions } from "../../hooks/use-server-actions.mjs";
|
|
|
33
34
|
import { useTransitionStage } from "../../hooks/use-transition-stage.mjs";
|
|
34
35
|
import { applyPatchBatchImmutable, cloneSnapshot } from "../../preview/patch.mjs";
|
|
35
36
|
import { diffSnapshot, diffSnapshotAtPath } from "../../preview/diff.mjs";
|
|
37
|
+
import { resolveKnownPreviewPath } from "../../preview/paths.mjs";
|
|
36
38
|
import { detectManyToManyRelations, hasManyToManyRelations } from "../../utils/detect-relations.mjs";
|
|
37
39
|
import { shouldHandleAdminShortcut } from "../../utils/keyboard-shortcuts.mjs";
|
|
38
40
|
import { AdminViewHeader } from "../layout/admin-view-layout.mjs";
|
|
@@ -168,11 +170,12 @@ const FormStateRefBridge = React.memo(function FormStateRefBridge$1({ control, o
|
|
|
168
170
|
}, [isSubmitting, onSubmittingChange]);
|
|
169
171
|
return null;
|
|
170
172
|
});
|
|
171
|
-
const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1({ form, previewRef, enabled }) {
|
|
173
|
+
const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1({ form, previewRef, enabled, schema, blocks }) {
|
|
172
174
|
const previousSnapshotRef = React.useRef(null);
|
|
173
175
|
const snapshotVersionRef = React.useRef(void 0);
|
|
176
|
+
const subscribedFieldNames = React.useMemo(() => Object.keys(schema?.fields ?? {}).filter(isSafePreviewEditPath), [schema]);
|
|
174
177
|
React.useEffect(() => {
|
|
175
|
-
if (!enabled) {
|
|
178
|
+
if (!enabled || subscribedFieldNames.length === 0) {
|
|
176
179
|
previousSnapshotRef.current = null;
|
|
177
180
|
snapshotVersionRef.current = void 0;
|
|
178
181
|
return;
|
|
@@ -194,37 +197,50 @@ const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1({ form, prev
|
|
|
194
197
|
if (pendingOps.size === 0 || animationFrame !== null) return;
|
|
195
198
|
animationFrame = window.requestAnimationFrame(flushPendingOps);
|
|
196
199
|
};
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
previousSnapshotRef.current
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
200
|
+
const unsubscribe = form.subscribe({
|
|
201
|
+
name: subscribedFieldNames,
|
|
202
|
+
exact: false,
|
|
203
|
+
formState: { values: true },
|
|
204
|
+
callback: ({ values, name }) => {
|
|
205
|
+
const previousSnapshot = previousSnapshotRef.current;
|
|
206
|
+
const nextValues = values;
|
|
207
|
+
if (!previousSnapshot) {
|
|
208
|
+
const nextSnapshot = cloneSnapshot(nextValues);
|
|
209
|
+
previousSnapshotRef.current = nextSnapshot;
|
|
210
|
+
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const changedPath = resolveKnownPreviewPath(name, {
|
|
214
|
+
schema,
|
|
215
|
+
blocks,
|
|
216
|
+
values: nextValues,
|
|
217
|
+
previousValues: previousSnapshot
|
|
218
|
+
});
|
|
219
|
+
let ops = changedPath ? diffSnapshotAtPath(previousSnapshot, nextValues, changedPath) : diffSnapshot(previousSnapshot, nextValues);
|
|
220
|
+
if (ops.some((op) => !op.path)) {
|
|
221
|
+
const nextSnapshot = cloneSnapshot(nextValues);
|
|
222
|
+
previousSnapshotRef.current = nextSnapshot;
|
|
223
|
+
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot);
|
|
224
|
+
pendingOps.clear();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
ops = ops.filter((op) => op.path);
|
|
228
|
+
if (ops.length === 0) return;
|
|
229
|
+
previousSnapshotRef.current = applyPatchBatchImmutable(previousSnapshot, ops);
|
|
230
|
+
queuePatchOps(ops);
|
|
214
231
|
}
|
|
215
|
-
ops = ops.filter((op) => op.path);
|
|
216
|
-
if (ops.length === 0) return;
|
|
217
|
-
previousSnapshotRef.current = applyPatchBatchImmutable(previousSnapshot, ops);
|
|
218
|
-
queuePatchOps(ops);
|
|
219
232
|
});
|
|
220
233
|
return () => {
|
|
221
234
|
if (animationFrame !== null) window.cancelAnimationFrame(animationFrame);
|
|
222
|
-
|
|
235
|
+
unsubscribe();
|
|
223
236
|
};
|
|
224
237
|
}, [
|
|
238
|
+
blocks,
|
|
225
239
|
enabled,
|
|
226
240
|
form,
|
|
227
|
-
previewRef
|
|
241
|
+
previewRef,
|
|
242
|
+
schema,
|
|
243
|
+
subscribedFieldNames
|
|
228
244
|
]);
|
|
229
245
|
return null;
|
|
230
246
|
});
|
|
@@ -379,6 +395,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
379
395
|
const resolveText = useResolveText();
|
|
380
396
|
const isEditMode = !!id;
|
|
381
397
|
const { fields: resolvedFields, schema, isLoading: isFieldsLoading } = useCollectionFields(collection, { fallbackFields: config?.fields });
|
|
398
|
+
const { data: adminConfig } = useAdminConfig();
|
|
382
399
|
const resolvedFormConfig = React.useMemo(() => viewConfig ?? (config?.form)?.["~config"] ?? config?.form ?? schema?.admin?.form, [
|
|
383
400
|
viewConfig,
|
|
384
401
|
config?.form,
|
|
@@ -1214,7 +1231,9 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
1214
1231
|
/* @__PURE__ */ jsx(PreviewPatchBridge, {
|
|
1215
1232
|
form,
|
|
1216
1233
|
previewRef,
|
|
1217
|
-
enabled: isLivePreviewOpen && !!previewUrl && !isBlocked
|
|
1234
|
+
enabled: isLivePreviewOpen && !!previewUrl && !isBlocked && !!schema,
|
|
1235
|
+
schema,
|
|
1236
|
+
blocks: adminConfig?.blocks
|
|
1218
1237
|
}),
|
|
1219
1238
|
/* @__PURE__ */ jsx(ReactiveFieldsManager, {
|
|
1220
1239
|
collection,
|
|
@@ -203,6 +203,13 @@ function getStickyLeftOffset(columns, index) {
|
|
|
203
203
|
return left + getColumnSize(column, columnIndex === 0 ? 40 : 360);
|
|
204
204
|
}, 0);
|
|
205
205
|
}
|
|
206
|
+
function reconcileOrderIds(orderIds, itemIds) {
|
|
207
|
+
const knownIds = new Set(itemIds);
|
|
208
|
+
const next = orderIds.filter((id) => knownIds.has(id));
|
|
209
|
+
const nextIds = new Set(next);
|
|
210
|
+
for (const id of itemIds) if (!nextIds.has(id)) next.push(id);
|
|
211
|
+
return next;
|
|
212
|
+
}
|
|
206
213
|
function getActionReferenceType(reference) {
|
|
207
214
|
if (typeof reference === "string") return reference;
|
|
208
215
|
if (typeof reference === "function") {
|
|
@@ -473,7 +480,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
473
480
|
groupBy: defaultGroupBy
|
|
474
481
|
}, collection, user?.id);
|
|
475
482
|
const effectiveRealtime = viewState.config.realtime ?? resolvedRealtime;
|
|
476
|
-
const visibleColumnsForExpansion =
|
|
483
|
+
const visibleColumnsForExpansion = viewState.config.visibleColumns.length > 0 ? viewState.config.visibleColumns : defaultColumns;
|
|
477
484
|
const expandedFields = useMemo(() => autoExpandFields({
|
|
478
485
|
fields: resolvedFields,
|
|
479
486
|
list: resolvedListConfig,
|
|
@@ -802,31 +809,19 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
802
809
|
searchData?.docs,
|
|
803
810
|
listData?.docs
|
|
804
811
|
]);
|
|
812
|
+
const itemIds = useMemo(() => items.map((item) => String(item.id)), [items]);
|
|
805
813
|
const { isHighlighted } = useRealtimeHighlight(items, { enabled: effectiveRealtime && !isSearching });
|
|
806
814
|
const { getLock, isLocked: isDocLocked } = useLocks({
|
|
807
815
|
resourceType: "collection",
|
|
808
816
|
resource: collection,
|
|
809
817
|
realtime: effectiveRealtime
|
|
810
818
|
});
|
|
811
|
-
React.useEffect(() => {
|
|
812
|
-
if (!isReorderMode) {
|
|
813
|
-
setOptimisticOrderIds(null);
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
setOptimisticOrderIds((current) => {
|
|
817
|
-
const itemIds = items.map((item) => String(item.id));
|
|
818
|
-
if (!current) return itemIds;
|
|
819
|
-
const knownIds = new Set(itemIds);
|
|
820
|
-
const next = current.filter((id) => knownIds.has(id));
|
|
821
|
-
for (const id of itemIds) if (!next.includes(id)) next.push(id);
|
|
822
|
-
return next;
|
|
823
|
-
});
|
|
824
|
-
}, [isReorderMode, items]);
|
|
825
819
|
const filteredItems = useMemo(() => {
|
|
826
820
|
if (!isReorderMode || !optimisticOrderIds) return items;
|
|
821
|
+
const orderedIds = reconcileOrderIds(optimisticOrderIds, itemIds);
|
|
827
822
|
const itemsById = new Map(items.map((item) => [String(item.id), item]));
|
|
828
823
|
const seen = /* @__PURE__ */ new Set();
|
|
829
|
-
const ordered =
|
|
824
|
+
const ordered = orderedIds.map((id) => {
|
|
830
825
|
const item = itemsById.get(id);
|
|
831
826
|
if (item) seen.add(id);
|
|
832
827
|
return item;
|
|
@@ -838,6 +833,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
838
833
|
return ordered;
|
|
839
834
|
}, [
|
|
840
835
|
isReorderMode,
|
|
836
|
+
itemIds,
|
|
841
837
|
items,
|
|
842
838
|
optimisticOrderIds
|
|
843
839
|
]);
|
|
@@ -860,13 +856,22 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
860
856
|
desc: nextSort.direction === "desc"
|
|
861
857
|
}]);
|
|
862
858
|
viewState.setSort(nextSort);
|
|
859
|
+
setOptimisticOrderIds(itemIds);
|
|
863
860
|
setIsReorderMode(true);
|
|
864
861
|
return;
|
|
865
862
|
}
|
|
866
|
-
|
|
863
|
+
if (isReorderMode) {
|
|
864
|
+
setOptimisticOrderIds(null);
|
|
865
|
+
setIsReorderMode(false);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
setOptimisticOrderIds(itemIds);
|
|
869
|
+
setIsReorderMode(true);
|
|
867
870
|
}, [
|
|
868
871
|
canReorder,
|
|
869
872
|
isOrderSortActive,
|
|
873
|
+
isReorderMode,
|
|
874
|
+
itemIds,
|
|
870
875
|
orderDirection,
|
|
871
876
|
viewState
|
|
872
877
|
]);
|
|
@@ -877,9 +882,17 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
877
882
|
filters: []
|
|
878
883
|
});
|
|
879
884
|
};
|
|
885
|
+
const exitReorderMode = React.useCallback(() => {
|
|
886
|
+
setOptimisticOrderIds(null);
|
|
887
|
+
setIsReorderMode(false);
|
|
888
|
+
}, []);
|
|
880
889
|
React.useEffect(() => {
|
|
881
|
-
if (isReorderMode && !canReorder)
|
|
882
|
-
}, [
|
|
890
|
+
if (isReorderMode && !canReorder) exitReorderMode();
|
|
891
|
+
}, [
|
|
892
|
+
canReorder,
|
|
893
|
+
exitReorderMode,
|
|
894
|
+
isReorderMode
|
|
895
|
+
]);
|
|
883
896
|
const table = useReactTable({
|
|
884
897
|
data: filteredItems,
|
|
885
898
|
columns: visibleColumnDefs,
|
|
@@ -1226,7 +1239,7 @@ function TableViewInner({ collection, config, viewConfig, navigate, basePath = "
|
|
|
1226
1239
|
}), /* @__PURE__ */ jsx(Button, {
|
|
1227
1240
|
variant: "ghost",
|
|
1228
1241
|
size: "xs",
|
|
1229
|
-
onClick:
|
|
1242
|
+
onClick: exitReorderMode,
|
|
1230
1243
|
children: t("common.done")
|
|
1231
1244
|
})]
|
|
1232
1245
|
}),
|
|
@@ -13,8 +13,8 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
const TABLE_SKELETON_COLUMNS = [
|
|
14
14
|
{
|
|
15
15
|
width: 40,
|
|
16
|
-
header: "mx-auto
|
|
17
|
-
cells: ["mx-auto
|
|
16
|
+
header: "mx-auto size-4",
|
|
17
|
+
cells: ["mx-auto size-4"]
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
width: 360,
|
|
@@ -66,7 +66,7 @@ function getColumnSizeStyle(width) {
|
|
|
66
66
|
function getSkeletonWidth(widths, rowIndex) {
|
|
67
67
|
return widths[rowIndex % widths.length] ?? widths[0] ?? "h-4 w-20";
|
|
68
68
|
}
|
|
69
|
-
function TableSkeletonRows({ rows =
|
|
69
|
+
function TableSkeletonRows({ rows = 12 }) {
|
|
70
70
|
return /* @__PURE__ */ jsx(Fragment, { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx(TableRow, {
|
|
71
71
|
className: "hover:bg-transparent",
|
|
72
72
|
children: TABLE_SKELETON_COLUMNS.map((column, columnIndex) => {
|
|
@@ -88,7 +88,7 @@ function TableSkeletonShell() {
|
|
|
88
88
|
className: "table-fixed",
|
|
89
89
|
style: { width: TABLE_SKELETON_COLUMNS.reduce((total, column) => total + column.width, 0) },
|
|
90
90
|
children: [
|
|
91
|
-
/* @__PURE__ */ jsx("colgroup", { children: TABLE_SKELETON_COLUMNS.map((column
|
|
91
|
+
/* @__PURE__ */ jsx("colgroup", { children: TABLE_SKELETON_COLUMNS.map((column) => /* @__PURE__ */ jsx("col", { style: { width: column.width } }, `${column.width}-${column.header}`)) }),
|
|
92
92
|
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, {
|
|
93
93
|
className: "hover:bg-transparent",
|
|
94
94
|
children: TABLE_SKELETON_COLUMNS.map((column, index) => {
|
|
@@ -101,7 +101,7 @@ function TableSkeletonShell() {
|
|
|
101
101
|
variant: "text",
|
|
102
102
|
className: column.header
|
|
103
103
|
})
|
|
104
|
-
},
|
|
104
|
+
}, `${column.width}-${column.header}`);
|
|
105
105
|
})
|
|
106
106
|
}) }),
|
|
107
107
|
/* @__PURE__ */ jsx(TableBody, { children: /* @__PURE__ */ jsx(TableSkeletonRows, {}) })
|
|
@@ -187,44 +187,43 @@ function TableViewSkeleton() {
|
|
|
187
187
|
*/
|
|
188
188
|
function FormViewSkeleton() {
|
|
189
189
|
return /* @__PURE__ */ jsx("div", {
|
|
190
|
-
className: "qa-form-view-skeleton
|
|
190
|
+
className: "qa-form-view-skeleton qa-form-view w-full",
|
|
191
|
+
"aria-busy": "true",
|
|
191
192
|
children: /* @__PURE__ */ jsxs("div", {
|
|
192
193
|
className: "space-y-6",
|
|
193
|
-
children: [
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ jsx("span", {
|
|
196
|
+
className: "sr-only",
|
|
197
|
+
children: "Loading form view"
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsx(AdminViewHeader, {
|
|
200
|
+
title: /* @__PURE__ */ jsx(Skeleton, {
|
|
201
|
+
variant: "text",
|
|
202
|
+
className: "h-7 w-48 max-w-full"
|
|
203
|
+
}),
|
|
204
|
+
meta: /* @__PURE__ */ jsx(Skeleton, {
|
|
205
|
+
variant: "text",
|
|
206
|
+
className: "h-3 w-36 max-w-full"
|
|
207
|
+
}),
|
|
208
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-20" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-24" })] })
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsxs("div", {
|
|
211
|
+
className: "space-y-6",
|
|
197
212
|
children: [
|
|
198
|
-
/* @__PURE__ */ jsx(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}),
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
213
|
+
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
214
|
+
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
215
|
+
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
216
|
+
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
217
|
+
/* @__PURE__ */ jsxs("div", {
|
|
218
|
+
className: "space-y-2",
|
|
219
|
+
children: [/* @__PURE__ */ jsx(Skeleton, {
|
|
220
|
+
variant: "text",
|
|
221
|
+
className: "h-4 w-32"
|
|
222
|
+
}), /* @__PURE__ */ jsx(Skeleton, { className: "h-32 w-full" })]
|
|
206
223
|
})
|
|
207
224
|
]
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-20" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-24" })]
|
|
211
|
-
})]
|
|
212
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
213
|
-
className: "space-y-6",
|
|
214
|
-
children: [
|
|
215
|
-
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
216
|
-
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
217
|
-
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
218
|
-
/* @__PURE__ */ jsx(FormFieldSkeleton, {}),
|
|
219
|
-
/* @__PURE__ */ jsxs("div", {
|
|
220
|
-
className: "space-y-2",
|
|
221
|
-
children: [/* @__PURE__ */ jsx(Skeleton, {
|
|
222
|
-
variant: "text",
|
|
223
|
-
className: "h-4 w-32"
|
|
224
|
-
}), /* @__PURE__ */ jsx(Skeleton, { className: "h-32 w-full" })]
|
|
225
|
-
})
|
|
226
|
-
]
|
|
227
|
-
})]
|
|
225
|
+
})
|
|
226
|
+
]
|
|
228
227
|
})
|
|
229
228
|
});
|
|
230
229
|
}
|
|
@@ -94,8 +94,8 @@ const SearchGroup = React.memo(function SearchGroup$1({ title, items, selectedIn
|
|
|
94
94
|
"data-selected": isSelected,
|
|
95
95
|
className: cn("item-surface flex w-full items-center gap-3 border-transparent px-3 py-2.5 text-sm transition-colors outline-none", isSelected ? "border-border bg-accent text-accent-foreground" : "hover:border-border hover:bg-accent hover:text-accent-foreground"),
|
|
96
96
|
children: [item.icon && /* @__PURE__ */ jsx("span", {
|
|
97
|
-
className: "text-muted-foreground flex
|
|
98
|
-
children: resolveIconElement(item.icon, { className: "
|
|
97
|
+
className: "text-muted-foreground flex size-4 shrink-0 items-center justify-center",
|
|
98
|
+
children: resolveIconElement(item.icon, { className: "size-4" })
|
|
99
99
|
}), /* @__PURE__ */ jsxs("div", {
|
|
100
100
|
className: "flex min-w-0 flex-col items-start",
|
|
101
101
|
children: [item.highlights?.title ? /* @__PURE__ */ jsx("span", {
|
|
@@ -372,7 +372,7 @@ function GlobalSearch({ isOpen, onClose, navigate, basePath: basePathProp }) {
|
|
|
372
372
|
className: "flex items-center justify-center gap-2",
|
|
373
373
|
children: [/* @__PURE__ */ jsx(Icon, {
|
|
374
374
|
icon: "ph:spinner",
|
|
375
|
-
className: "
|
|
375
|
+
className: "size-4 animate-spin"
|
|
376
376
|
}), /* @__PURE__ */ jsx("span", { children: t("globalSearch.searching") })]
|
|
377
377
|
}) : t("globalSearch.noResults")
|
|
378
378
|
})
|
|
@@ -52,7 +52,7 @@ function WidgetCardError({ error, variant = "default", onRetry }) {
|
|
|
52
52
|
onClick: onRetry,
|
|
53
53
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
54
54
|
icon: "ph:arrow-clockwise",
|
|
55
|
-
className: "
|
|
55
|
+
className: "size-3.5"
|
|
56
56
|
})
|
|
57
57
|
}) })]
|
|
58
58
|
}), /* @__PURE__ */ jsx(CardContent, {
|
|
@@ -106,7 +106,7 @@ function WidgetCard({ title, description, icon, variant = "default", isLoading,
|
|
|
106
106
|
className: "qa-widget-card__header shrink-0",
|
|
107
107
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
108
108
|
className: "flex min-w-0 items-center gap-2",
|
|
109
|
-
children: [resolveIconElement(icon, { className: "
|
|
109
|
+
children: [resolveIconElement(icon, { className: "size-4 text-muted-foreground" }), /* @__PURE__ */ jsxs("div", {
|
|
110
110
|
className: "min-w-0 flex-1",
|
|
111
111
|
children: [title && /* @__PURE__ */ jsx(CardTitle, {
|
|
112
112
|
className: "truncate text-sm font-medium",
|
|
@@ -124,19 +124,19 @@ function WidgetCard({ title, description, icon, variant = "default", isLoading,
|
|
|
124
124
|
size: "icon-xs",
|
|
125
125
|
onClick: actions[0].onClick,
|
|
126
126
|
title: actions[0].label,
|
|
127
|
-
children: resolveIconElement(actions[0].icon, { className: "
|
|
127
|
+
children: resolveIconElement(actions[0].icon, { className: "size-3.5" })
|
|
128
128
|
}, actions[0].id), actions.length > 1 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button, {
|
|
129
129
|
variant: "ghost",
|
|
130
130
|
size: "icon-xs",
|
|
131
131
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
132
132
|
icon: "ph:dots-three-vertical",
|
|
133
|
-
className: "
|
|
133
|
+
className: "size-3.5"
|
|
134
134
|
})
|
|
135
135
|
}) }), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
136
136
|
align: "end",
|
|
137
137
|
children: actions.slice(1).map((action) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
138
138
|
onClick: action.onClick,
|
|
139
|
-
children: [resolveIconElement(action.icon, { className: "
|
|
139
|
+
children: [resolveIconElement(action.icon, { className: "size-3.5 mr-2" }), action.label]
|
|
140
140
|
}, action.id))
|
|
141
141
|
})] })] }),
|
|
142
142
|
onRefresh && /* @__PURE__ */ jsx(Button, {
|
|
@@ -147,7 +147,7 @@ function WidgetCard({ title, description, icon, variant = "default", isLoading,
|
|
|
147
147
|
disabled: isRefreshing,
|
|
148
148
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
149
149
|
icon: "ph:arrow-clockwise",
|
|
150
|
-
className: cn("
|
|
150
|
+
className: cn("size-3.5", isRefreshing && "animate-spin")
|
|
151
151
|
})
|
|
152
152
|
}),
|
|
153
153
|
onExpand && /* @__PURE__ */ jsx(Button, {
|
|
@@ -157,7 +157,7 @@ function WidgetCard({ title, description, icon, variant = "default", isLoading,
|
|
|
157
157
|
title: t("ui.expand"),
|
|
158
158
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
159
159
|
icon: "ph:arrows-out-simple",
|
|
160
|
-
className: "
|
|
160
|
+
className: "size-3.5"
|
|
161
161
|
})
|
|
162
162
|
})
|
|
163
163
|
]
|