@aphexcms/cms-core 6.0.0 → 8.0.0
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/api/documents.d.ts +19 -0
- package/dist/api/documents.d.ts.map +1 -1
- package/dist/api/documents.js +21 -0
- package/dist/api/schemas/documents.d.ts +3 -0
- package/dist/api/schemas/documents.d.ts.map +1 -1
- package/dist/api/schemas/documents.js +12 -0
- package/dist/cli/generate-types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +133 -19
- package/dist/cli/generate-types.js.map +1 -1
- package/dist/components/AdminApp.svelte +74 -47
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +65 -2
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte +8 -6
- package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +149 -2
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +198 -125
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts +9 -2
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.svelte +1 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte +2 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavMain.svelte +9 -2
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts +2 -0
- package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +3 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/reference.d.ts +43 -0
- package/dist/db/interfaces/reference.d.ts.map +1 -0
- package/dist/db/interfaces/reference.js +4 -0
- package/dist/db/utils/reference-resolver.d.ts +4 -6
- package/dist/db/utils/reference-resolver.d.ts.map +1 -1
- package/dist/db/utils/reference-resolver.js +33 -31
- package/dist/document-refresh.svelte.d.ts +3 -0
- package/dist/document-refresh.svelte.d.ts.map +1 -0
- package/dist/document-refresh.svelte.js +21 -0
- package/dist/graphql/resolvers.d.ts.map +1 -1
- package/dist/graphql/resolvers.js +61 -1
- package/dist/graphql/schema.d.ts.map +1 -1
- package/dist/graphql/schema.js +10 -0
- package/dist/lib/api/documents.d.ts +19 -0
- package/dist/lib/api/documents.d.ts.map +1 -1
- package/dist/lib/api/documents.js +21 -0
- package/dist/lib/api/documents.js.map +1 -1
- package/dist/lib/api/schemas/documents.d.ts +3 -0
- package/dist/lib/api/schemas/documents.d.ts.map +1 -1
- package/dist/lib/api/schemas/documents.js +12 -0
- package/dist/lib/api/schemas/documents.js.map +1 -1
- package/dist/lib/db/interfaces/index.d.ts +3 -1
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/reference.d.ts +43 -0
- package/dist/lib/db/interfaces/reference.d.ts.map +1 -0
- package/dist/lib/db/interfaces/reference.js +5 -0
- package/dist/lib/db/interfaces/reference.js.map +1 -0
- package/dist/lib/db/utils/reference-resolver.d.ts +4 -6
- package/dist/lib/db/utils/reference-resolver.d.ts.map +1 -1
- package/dist/lib/db/utils/reference-resolver.js +33 -31
- package/dist/lib/db/utils/reference-resolver.js.map +1 -1
- package/dist/lib/document-refresh.svelte.d.ts +3 -0
- package/dist/lib/document-refresh.svelte.d.ts.map +1 -0
- package/dist/lib/document-refresh.svelte.js +22 -0
- package/dist/lib/document-refresh.svelte.js.map +1 -0
- package/dist/lib/graphql/resolvers.d.ts.map +1 -1
- package/dist/lib/graphql/resolvers.js +61 -1
- package/dist/lib/graphql/resolvers.js.map +1 -1
- package/dist/lib/graphql/schema.d.ts.map +1 -1
- package/dist/lib/graphql/schema.js +10 -0
- package/dist/lib/graphql/schema.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts +10 -1
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +39 -1
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/index.d.ts +29 -0
- package/dist/lib/local-api/index.d.ts.map +1 -1
- package/dist/lib/local-api/index.js +66 -2
- package/dist/lib/local-api/index.js.map +1 -1
- package/dist/lib/routes/assets-cdn.d.ts.map +1 -1
- package/dist/lib/routes/assets-cdn.js +14 -1
- package/dist/lib/routes/assets-cdn.js.map +1 -1
- package/dist/lib/schema-utils/validator.js +21 -11
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/server/api/index.d.ts.map +1 -1
- package/dist/lib/server/api/index.js +3 -1
- package/dist/lib/server/api/index.js.map +1 -1
- package/dist/lib/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents-by-id.js +23 -0
- package/dist/lib/server/api/routes/documents-by-id.js.map +1 -1
- package/dist/lib/server/api/routes/documents.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents.js +29 -1
- package/dist/lib/server/api/routes/documents.js.map +1 -1
- package/dist/lib/services/references-service.d.ts +35 -0
- package/dist/lib/services/references-service.d.ts.map +1 -0
- package/dist/lib/services/references-service.js +60 -0
- package/dist/lib/services/references-service.js.map +1 -0
- package/dist/lib/types/schemas.d.ts +8 -0
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/utils/preview.d.ts +2 -3
- package/dist/lib/utils/preview.d.ts.map +1 -1
- package/dist/lib/utils/preview.js +35 -8
- package/dist/lib/utils/preview.js.map +1 -1
- package/dist/lib/utils/reference-walk.d.ts +8 -0
- package/dist/lib/utils/reference-walk.d.ts.map +1 -0
- package/dist/lib/utils/reference-walk.js +39 -0
- package/dist/lib/utils/reference-walk.js.map +1 -0
- package/dist/local-api/collection-api.d.ts +10 -1
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +39 -1
- package/dist/local-api/index.d.ts +29 -0
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/local-api/index.js +66 -2
- package/dist/routes/assets-cdn.d.ts.map +1 -1
- package/dist/routes/assets-cdn.js +14 -1
- package/dist/schema-utils/validator.js +21 -11
- package/dist/server/api/index.d.ts.map +1 -1
- package/dist/server/api/index.js +3 -1
- package/dist/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/server/api/routes/documents-by-id.js +23 -0
- package/dist/server/api/routes/documents.d.ts.map +1 -1
- package/dist/server/api/routes/documents.js +29 -1
- package/dist/services/references-service.d.ts +35 -0
- package/dist/services/references-service.d.ts.map +1 -0
- package/dist/services/references-service.js +59 -0
- package/dist/types/schemas.d.ts +8 -0
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/preview.d.ts +2 -3
- package/dist/utils/preview.d.ts.map +1 -1
- package/dist/utils/preview.js +35 -8
- package/dist/utils/reference-walk.d.ts +8 -0
- package/dist/utils/reference-walk.d.ts.map +1 -0
- package/dist/utils/reference-walk.js +38 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA4BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;
|
|
1
|
+
{"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA4BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;AAkhDF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import { setSaveStateContext } from '../../save-state-context.svelte';
|
|
14
14
|
import { usePermissions } from '../../permissions-context.svelte';
|
|
15
15
|
import { getDefaultValueForFieldType } from '../../utils/field-defaults';
|
|
16
|
+
import { notifyDocumentChanged } from '../../document-refresh.svelte';
|
|
17
|
+
import { collectReferenceIds } from '../../utils/reference-walk';
|
|
16
18
|
import { cmsLogger } from '../../utils/logger';
|
|
17
19
|
import { toast } from 'svelte-sonner';
|
|
18
20
|
import { confirmDialog } from './confirm-dialog/confirm-dialog.svelte';
|
|
@@ -645,6 +647,7 @@
|
|
|
645
647
|
if (response?.success) {
|
|
646
648
|
lastSaved = new Date();
|
|
647
649
|
hasUnsavedChanges = false;
|
|
650
|
+
if (response.data?.id) notifyDocumentChanged(response.data.id);
|
|
648
651
|
if (isAutoSave) {
|
|
649
652
|
// Trigger validation on all fields after auto-save
|
|
650
653
|
validateAllFields(); // Update validation status
|
|
@@ -716,6 +719,47 @@
|
|
|
716
719
|
return;
|
|
717
720
|
}
|
|
718
721
|
|
|
722
|
+
// Referential integrity guard: every referenced document must itself be
|
|
723
|
+
// published. Without this, publishing a doc that points at drafts would
|
|
724
|
+
// expose dangling references in the published perspective. One batched
|
|
725
|
+
// HTTP call regardless of ref count.
|
|
726
|
+
const refIds = collectReferenceIds(documentData, schema, schemas);
|
|
727
|
+
if (refIds.length > 0) {
|
|
728
|
+
let fetched: any[] = [];
|
|
729
|
+
try {
|
|
730
|
+
const res = await documents.getMany(refIds);
|
|
731
|
+
if (res.success && res.data) fetched = res.data;
|
|
732
|
+
} catch {
|
|
733
|
+
// fall through — missing fetched IDs become "blockers" below
|
|
734
|
+
}
|
|
735
|
+
const fetchedById = new Map(fetched.map((d) => [d.id, d]));
|
|
736
|
+
const checks = refIds.map((id) => ({ id, doc: fetchedById.get(id) ?? null }));
|
|
737
|
+
const blockers = checks.filter(
|
|
738
|
+
(c) => !c.doc || (c.doc as any)._meta?.status !== 'published'
|
|
739
|
+
);
|
|
740
|
+
if (blockers.length > 0) {
|
|
741
|
+
const titles = blockers
|
|
742
|
+
.slice(0, 3)
|
|
743
|
+
.map((b) => {
|
|
744
|
+
if (!b.doc) return `Missing (${b.id.slice(0, 8)})`;
|
|
745
|
+
const d = b.doc as any;
|
|
746
|
+
const label = d.title ?? d.name ?? d.heading ?? d.label ?? b.id;
|
|
747
|
+
const type = d._meta?.type;
|
|
748
|
+
return type ? `"${label}" (${type})` : `"${label}"`;
|
|
749
|
+
})
|
|
750
|
+
.join(', ');
|
|
751
|
+
const remainder = blockers.length > 3 ? ` and ${blockers.length - 3} more` : '';
|
|
752
|
+
saveError = `Cannot publish — unpublished references: ${titles}${remainder}`;
|
|
753
|
+
toast.error(
|
|
754
|
+
`${blockers.length} referenced document${blockers.length === 1 ? '' : 's'} ${blockers.length === 1 ? 'is' : 'are'} not published`,
|
|
755
|
+
{
|
|
756
|
+
description: 'Publish the referenced documents first, then try again.'
|
|
757
|
+
}
|
|
758
|
+
);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
719
763
|
saving = true;
|
|
720
764
|
saveError = null;
|
|
721
765
|
|
|
@@ -733,6 +777,7 @@
|
|
|
733
777
|
fullDocument = response.data;
|
|
734
778
|
lastSaved = new Date();
|
|
735
779
|
publishSuccess = new Date();
|
|
780
|
+
notifyDocumentChanged(documentId);
|
|
736
781
|
cmsLogger.debug('[Document Editor]', '✅ Document published successfully');
|
|
737
782
|
cmsLogger.debug('[Document Editor]', '📄 New published hash:', response.data.publishedHash);
|
|
738
783
|
|
|
@@ -790,10 +835,27 @@
|
|
|
790
835
|
async function unpublishDocument() {
|
|
791
836
|
if (!documentId || saving) return;
|
|
792
837
|
|
|
838
|
+
// Surface published back-references so the user knows what will end up
|
|
839
|
+
// with dangling refs in the published perspective. Best-effort —
|
|
840
|
+
// network errors fall through to the standard confirm.
|
|
841
|
+
let backRefDescription =
|
|
842
|
+
'It will be removed from published queries, but the data is preserved and you can re-publish anytime.';
|
|
843
|
+
try {
|
|
844
|
+
const backRefRes = await documents.getBackReferences(documentId);
|
|
845
|
+
if (backRefRes.success && backRefRes.data) {
|
|
846
|
+
const publishedBackRefs = backRefRes.data.filter((r) => r.status === 'published');
|
|
847
|
+
if (publishedBackRefs.length > 0) {
|
|
848
|
+
const count = publishedBackRefs.length;
|
|
849
|
+
backRefDescription = `${count} published document${count === 1 ? '' : 's'} reference${count === 1 ? 's' : ''} this one — their references will be left dangling in the published perspective until you re-publish them. Continue?`;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
} catch {
|
|
853
|
+
// Silently fall through to the generic confirm; ref index may be unpopulated.
|
|
854
|
+
}
|
|
855
|
+
|
|
793
856
|
const confirmUnpublish = await confirmDialog({
|
|
794
857
|
title: 'Unpublish this document?',
|
|
795
|
-
description:
|
|
796
|
-
'It will be removed from published queries, but the data is preserved and you can re-publish anytime.',
|
|
858
|
+
description: backRefDescription,
|
|
797
859
|
confirmText: 'Unpublish',
|
|
798
860
|
variant: 'destructive'
|
|
799
861
|
});
|
|
@@ -809,6 +871,7 @@
|
|
|
809
871
|
...fullDocument,
|
|
810
872
|
_meta: { ...fullDocument?._meta, status: 'unpublished' }
|
|
811
873
|
};
|
|
874
|
+
notifyDocumentChanged(documentId);
|
|
812
875
|
toast.success('Document unpublished — you can re-publish anytime');
|
|
813
876
|
if (onUnpublished && documentId) {
|
|
814
877
|
onUnpublished(documentId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAexD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAkiDF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as AlertDialog from '@aphexcms/ui/shadcn/alert-dialog';
|
|
3
|
-
import {
|
|
3
|
+
import { buttonVariants } from '@aphexcms/ui/shadcn/button';
|
|
4
4
|
import { confirmDialogState, resolveConfirmDialog } from './confirm-dialog.svelte';
|
|
5
5
|
|
|
6
6
|
function handleOpenChange(open: boolean) {
|
|
@@ -21,15 +21,17 @@
|
|
|
21
21
|
{/if}
|
|
22
22
|
</AlertDialog.Header>
|
|
23
23
|
<AlertDialog.Footer>
|
|
24
|
-
<
|
|
24
|
+
<AlertDialog.Cancel onclick={() => resolveConfirmDialog(false)}>
|
|
25
25
|
{confirmDialogState.cancelText}
|
|
26
|
-
</
|
|
27
|
-
<
|
|
28
|
-
|
|
26
|
+
</AlertDialog.Cancel>
|
|
27
|
+
<AlertDialog.Action
|
|
28
|
+
class={confirmDialogState.variant === 'destructive'
|
|
29
|
+
? buttonVariants({ variant: 'destructive' })
|
|
30
|
+
: undefined}
|
|
29
31
|
onclick={() => resolveConfirmDialog(true)}
|
|
30
32
|
>
|
|
31
33
|
{confirmDialogState.confirmText}
|
|
32
|
-
</
|
|
34
|
+
</AlertDialog.Action>
|
|
33
35
|
</AlertDialog.Footer>
|
|
34
36
|
</AlertDialog.Content>
|
|
35
37
|
</AlertDialog.Root>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialogHost.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/confirm-dialog/ConfirmDialogHost.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConfirmDialogHost.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/confirm-dialog/ConfirmDialogHost.svelte.ts"],"names":[],"mappings":"AA4CA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,iBAAiB;;kBAA+E,CAAC;AACrF,KAAK,iBAAiB,GAAG,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAClE,eAAe,iBAAiB,CAAC"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { getSchemaContext } from '../../../schema-context.svelte';
|
|
11
11
|
import ObjectModal from '../ObjectModal.svelte';
|
|
12
12
|
import ImageField from './ImageField.svelte';
|
|
13
|
+
import ReferenceField from './ReferenceField.svelte';
|
|
13
14
|
import { getDefaultValueForFieldType } from '../../../utils/field-defaults';
|
|
14
15
|
import { DragDropProvider } from '@dnd-kit/svelte';
|
|
15
16
|
import { createSortable, isSortable } from '@dnd-kit/svelte/sortable';
|
|
@@ -29,6 +30,9 @@
|
|
|
29
30
|
import { toast } from 'svelte-sonner';
|
|
30
31
|
import AssetBrowserModal from '../AssetBrowserModal.svelte';
|
|
31
32
|
import { resolvePreviewTitle, resolvePreviewSubtitle } from '../../../utils/preview';
|
|
33
|
+
import { documents } from '../../../api/documents';
|
|
34
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
35
|
+
import { getDocumentVersion } from '../../../document-refresh.svelte';
|
|
32
36
|
|
|
33
37
|
interface Props {
|
|
34
38
|
field: ArrayFieldType;
|
|
@@ -70,8 +74,12 @@
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
const isGridLayout = $derived(field.options?.layout === 'grid');
|
|
77
|
+
const isReferenceArray = $derived(
|
|
78
|
+
field.of && field.of.length > 0 && field.of[0]?.type === 'reference'
|
|
79
|
+
);
|
|
73
80
|
const isPrimitiveArray = $derived(
|
|
74
|
-
|
|
81
|
+
!isReferenceArray &&
|
|
82
|
+
field.of &&
|
|
75
83
|
field.of.length > 0 &&
|
|
76
84
|
field.of[0]?.type &&
|
|
77
85
|
!field.of[0].fields &&
|
|
@@ -80,6 +88,48 @@
|
|
|
80
88
|
const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
|
|
81
89
|
const availableTypes = $derived(getArrayTypes(schemas, field));
|
|
82
90
|
|
|
91
|
+
// Synthetic ReferenceField definition reused for every row in an
|
|
92
|
+
// array-of-references. We forward the array's first `of[]` entry's `to` so
|
|
93
|
+
// the row picker honors the schema-author's allowed target types.
|
|
94
|
+
const referenceFieldShape = $derived(
|
|
95
|
+
isReferenceArray
|
|
96
|
+
? {
|
|
97
|
+
name: field.name,
|
|
98
|
+
type: 'reference' as const,
|
|
99
|
+
title: field.title,
|
|
100
|
+
to: (field.of?.[0] as any)?.to ?? []
|
|
101
|
+
}
|
|
102
|
+
: null
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Batched hydration cache for reference rows. Without this, each row's
|
|
106
|
+
// ReferenceField fires its own getById on mount → N round-trips for an
|
|
107
|
+
// array of N refs. Instead we fetch all populated refs in a single call
|
|
108
|
+
// and hand each row its preloaded doc. The cache invalidates whenever
|
|
109
|
+
// any row's referenced doc bumps its document-refresh version.
|
|
110
|
+
const referenceCache = new SvelteMap<string, any>();
|
|
111
|
+
$effect(() => {
|
|
112
|
+
if (!isReferenceArray) return;
|
|
113
|
+
const ids = (Array.isArray(value) ? value : [])
|
|
114
|
+
.map((item: any) => item?._ref)
|
|
115
|
+
.filter((id: any): id is string => typeof id === 'string' && id.length > 0);
|
|
116
|
+
if (ids.length === 0) return;
|
|
117
|
+
// Subscribe to each id's version so external saves trigger a refetch.
|
|
118
|
+
for (const id of ids) getDocumentVersion(id);
|
|
119
|
+
(async () => {
|
|
120
|
+
try {
|
|
121
|
+
const res = await documents.getMany(ids);
|
|
122
|
+
if (res.success && res.data) {
|
|
123
|
+
for (const doc of res.data as any[]) {
|
|
124
|
+
if (doc?.id) referenceCache.set(doc.id, doc);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
// Per-row ReferenceField will fall back to its own fetch on miss.
|
|
129
|
+
}
|
|
130
|
+
})();
|
|
131
|
+
});
|
|
132
|
+
|
|
83
133
|
// Modal state
|
|
84
134
|
let modalOpen = $state(false);
|
|
85
135
|
let editingIndex = $state<number | null>(null);
|
|
@@ -376,6 +426,29 @@
|
|
|
376
426
|
onUpdate(newArray);
|
|
377
427
|
}
|
|
378
428
|
|
|
429
|
+
// Reference array — append an empty reference row. The row renders the
|
|
430
|
+
// ReferenceField empty state (search picker), so the user picks the target
|
|
431
|
+
// document inline without an extra modal step.
|
|
432
|
+
function handleAddReference() {
|
|
433
|
+
if (readonly) return;
|
|
434
|
+
const newItem = { _type: 'reference', _key: generateKey(), _ref: null };
|
|
435
|
+
onUpdate([...keyedItems, newItem]);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function handleUpdateReference(
|
|
439
|
+
index: number,
|
|
440
|
+
newRef: { _type: 'reference'; _ref: string; _key?: string } | null
|
|
441
|
+
) {
|
|
442
|
+
if (readonly) return;
|
|
443
|
+
const newArray = [...keyedItems];
|
|
444
|
+
// Preserve the row's _key (stable across drag/drop) and only swap the _ref.
|
|
445
|
+
newArray[index] = {
|
|
446
|
+
...newArray[index],
|
|
447
|
+
_ref: newRef?._ref ?? null
|
|
448
|
+
};
|
|
449
|
+
onUpdate(newArray);
|
|
450
|
+
}
|
|
451
|
+
|
|
379
452
|
// Write-through: propagate every field change from the modal to the array immediately
|
|
380
453
|
function handleModalUpdate(updatedData: Record<string, any>) {
|
|
381
454
|
if (editingIndex === null || !editingType) return;
|
|
@@ -421,7 +494,81 @@
|
|
|
421
494
|
}
|
|
422
495
|
</script>
|
|
423
496
|
|
|
424
|
-
{#if
|
|
497
|
+
{#if isReferenceArray && referenceFieldShape}
|
|
498
|
+
<!-- Reference array — each row reuses ReferenceField. Drag handle on the
|
|
499
|
+
left, ReferenceField fills the row, an X button on empty rows lets the
|
|
500
|
+
user discard a never-picked row (populated rows use ReferenceField's
|
|
501
|
+
own context menu, which we override via onRemove to drop the row). -->
|
|
502
|
+
{#if keyedItems.length > 0}
|
|
503
|
+
{#key dndKey}
|
|
504
|
+
<DragDropProvider onDragEnd={handleDragEnd}>
|
|
505
|
+
<div class="space-y-1">
|
|
506
|
+
{#each keyedItems as item, index (item._key)}
|
|
507
|
+
{@const sortable = createSortable({ id: item._key, index, disabled: readonly })}
|
|
508
|
+
<div
|
|
509
|
+
{@attach sortable.attach}
|
|
510
|
+
class="flex items-center gap-1"
|
|
511
|
+
class:opacity-50={sortable.isDragging}
|
|
512
|
+
>
|
|
513
|
+
{#if !readonly}
|
|
514
|
+
<button
|
|
515
|
+
{@attach sortable.attachHandle}
|
|
516
|
+
class="text-muted-foreground hover:text-foreground flex h-9 w-7 shrink-0 cursor-grab items-center justify-center active:cursor-grabbing"
|
|
517
|
+
>
|
|
518
|
+
<GripVertical class="h-4 w-4" />
|
|
519
|
+
</button>
|
|
520
|
+
{/if}
|
|
521
|
+
<div class="min-w-0 flex-1">
|
|
522
|
+
<ReferenceField
|
|
523
|
+
field={referenceFieldShape}
|
|
524
|
+
value={item._ref ? { _type: 'reference', _ref: item._ref } : null}
|
|
525
|
+
onUpdate={(newRef) => handleUpdateReference(index, newRef)}
|
|
526
|
+
{onOpenReference}
|
|
527
|
+
{readonly}
|
|
528
|
+
onRemove={() => handleRemoveItem(index)}
|
|
529
|
+
preloadedDoc={item._ref ? referenceCache.get(item._ref) : undefined}
|
|
530
|
+
/>
|
|
531
|
+
</div>
|
|
532
|
+
{#if !readonly && !item._ref}
|
|
533
|
+
<DropdownMenu.Root>
|
|
534
|
+
<DropdownMenu.Trigger>
|
|
535
|
+
{#snippet child({ props })}
|
|
536
|
+
<button
|
|
537
|
+
{...props}
|
|
538
|
+
class="text-muted-foreground hover:text-foreground flex h-9 w-7 shrink-0 items-center justify-center rounded transition-colors"
|
|
539
|
+
aria-label="Row options"
|
|
540
|
+
>
|
|
541
|
+
<Ellipsis class="h-4 w-4" />
|
|
542
|
+
</button>
|
|
543
|
+
{/snippet}
|
|
544
|
+
</DropdownMenu.Trigger>
|
|
545
|
+
<DropdownMenu.Content align="end">
|
|
546
|
+
<DropdownMenu.Item
|
|
547
|
+
class="text-destructive focus:text-destructive"
|
|
548
|
+
onclick={() => handleRemoveItem(index)}
|
|
549
|
+
>
|
|
550
|
+
<Trash2 class="mr-2 h-4 w-4" />
|
|
551
|
+
Remove
|
|
552
|
+
</DropdownMenu.Item>
|
|
553
|
+
</DropdownMenu.Content>
|
|
554
|
+
</DropdownMenu.Root>
|
|
555
|
+
{/if}
|
|
556
|
+
</div>
|
|
557
|
+
{/each}
|
|
558
|
+
</div>
|
|
559
|
+
</DragDropProvider>
|
|
560
|
+
{/key}
|
|
561
|
+
{/if}
|
|
562
|
+
{#if !readonly}
|
|
563
|
+
<button
|
|
564
|
+
class="border-border/50 text-muted-foreground hover:text-foreground hover:bg-muted/50 mt-1 flex h-10 w-full items-center justify-center gap-2 rounded border border-dashed text-sm transition-colors"
|
|
565
|
+
onclick={handleAddReference}
|
|
566
|
+
>
|
|
567
|
+
<Plus class="h-4 w-4" />
|
|
568
|
+
Add reference...
|
|
569
|
+
</button>
|
|
570
|
+
{/if}
|
|
571
|
+
{:else if isPrimitiveArray}
|
|
425
572
|
<!-- Primitive array UI with DnD -->
|
|
426
573
|
{#if arrayValue.length === 0}
|
|
427
574
|
<div
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AA8BtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAq5BF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|