@delmaredigital/payload-experiments 0.3.0-rc.4 → 0.4.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -27
- package/dist/admin/MetricSourceSelect.js +3 -3
- package/dist/admin/PlacementStatus.d.ts +3 -0
- package/dist/admin/PlacementStatus.js +54 -0
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +2 -2
- package/dist/admin/TestsDashboardCards.d.ts +1 -0
- package/dist/admin/TestsDashboardCards.js +4 -8
- package/dist/admin/TestsNavLink.d.ts +1 -0
- package/dist/admin/TestsNavLink.js +4 -10
- package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
- package/dist/admin/actions/ConfirmDialog.js +2 -2
- package/dist/admin/actions/DecideDialog.d.ts +2 -1
- package/dist/admin/actions/DecideDialog.js +2 -2
- package/dist/admin/api.d.ts +18 -2
- package/dist/admin/api.js +47 -2
- package/dist/admin/blocks/BlockPanel.d.ts +4 -0
- package/dist/admin/blocks/BlockPanel.js +199 -0
- package/dist/admin/blocks/TestBlockField.d.ts +5 -0
- package/dist/admin/blocks/TestBlockField.js +163 -0
- package/dist/admin/blocks/formApi.d.ts +46 -0
- package/dist/admin/blocks/formApi.js +159 -0
- package/dist/admin/blocks/inject.d.ts +7 -0
- package/dist/admin/blocks/inject.js +41 -0
- package/dist/admin/blocks/model.d.ts +17 -0
- package/dist/admin/blocks/model.js +45 -0
- package/dist/admin/blocks/statusCache.d.ts +9 -0
- package/dist/admin/blocks/statusCache.js +92 -0
- package/dist/admin/blocks/useBlockForm.d.ts +17 -0
- package/dist/admin/blocks/useBlockForm.js +46 -0
- package/dist/admin/copy/actions.d.ts +2 -0
- package/dist/admin/copy/actions.js +9 -0
- package/dist/admin/copy/blocks.d.ts +43 -0
- package/dist/admin/copy/blocks.js +64 -0
- package/dist/admin/copy/common.d.ts +1 -0
- package/dist/admin/copy/common.js +1 -1
- package/dist/admin/copy/goals.d.ts +2 -0
- package/dist/admin/copy/goals.js +4 -1
- package/dist/admin/copy/newTest.d.ts +10 -0
- package/dist/admin/copy/newTest.js +10 -0
- package/dist/admin/copy/pages.d.ts +24 -0
- package/dist/admin/copy/pages.js +24 -0
- package/dist/admin/copy/readiness.d.ts +23 -3
- package/dist/admin/copy/readiness.js +24 -3
- package/dist/admin/copy/results.d.ts +1 -0
- package/dist/admin/copy/results.js +1 -0
- package/dist/admin/copy/testPage.d.ts +4 -0
- package/dist/admin/copy/testPage.js +4 -0
- package/dist/admin/goals/XpGoalField.d.ts +4 -0
- package/dist/admin/goals/XpGoalField.js +168 -0
- package/dist/admin/goals/xpGoalField.css +48 -0
- package/dist/admin/metricSource.model.d.ts +2 -0
- package/dist/admin/metricSource.model.js +4 -0
- package/dist/admin/pageCollections.d.ts +10 -0
- package/dist/admin/pageCollections.js +20 -0
- package/dist/admin/paths.d.ts +9 -0
- package/dist/admin/paths.js +9 -0
- package/dist/admin/placementStatus.model.d.ts +2 -0
- package/dist/admin/placementStatus.model.js +19 -0
- package/dist/admin/placements/placementLabels.d.ts +11 -0
- package/dist/admin/placements/placementLabels.js +21 -0
- package/dist/admin/placements/placementLabels.model.d.ts +13 -0
- package/dist/admin/placements/placementLabels.model.js +12 -0
- package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
- package/dist/admin/placements/usePlacementLabels.js +102 -0
- package/dist/admin/results/DataQualityPanel.js +4 -1
- package/dist/admin/results/results.model.d.ts +3 -0
- package/dist/admin/results/results.model.js +8 -4
- package/dist/admin/surface/TestsHome.js +39 -10
- package/dist/admin/surface/TestsSurface.js +7 -6
- package/dist/admin/surface/contracts.d.ts +6 -5
- package/dist/admin/surface/goals/GoalForm.js +71 -44
- package/dist/admin/surface/goals/GoalsList.js +5 -4
- package/dist/admin/surface/goals/data.js +4 -1
- package/dist/admin/surface/goals/goals.model.d.ts +1 -2
- package/dist/admin/surface/goals/goals.model.js +1 -3
- package/dist/admin/surface/home.model.d.ts +3 -3
- package/dist/admin/surface/home.model.js +8 -4
- package/dist/admin/surface/newTest/NewTest.js +56 -17
- package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
- package/dist/admin/surface/newTest/PageWizard.js +214 -0
- package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
- package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
- package/dist/admin/surface/pagePicker/PageCombobox.d.ts +10 -0
- package/dist/admin/surface/pagePicker/PageCombobox.js +395 -0
- package/dist/admin/surface/pagePicker/pagePicker.css +10 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +27 -0
- package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
- package/dist/admin/surface/readiness/ActionDialogs.js +99 -64
- package/dist/admin/surface/readiness/Banner.js +30 -6
- package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
- package/dist/admin/surface/readiness/readiness.model.js +92 -9
- package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
- package/dist/admin/surface/results/ReleasePages.js +3 -7
- package/dist/admin/surface/results/ResultsBody.js +21 -2
- package/dist/admin/surface/results/ResultsHeader.js +5 -13
- package/dist/admin/surface/results/useResults.d.ts +20 -1
- package/dist/admin/surface/test/AdvancedSection.js +111 -104
- package/dist/admin/surface/test/GoalSection.js +94 -14
- package/dist/admin/surface/test/LengthSection.js +1 -1
- package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
- package/dist/admin/surface/test/PageHoldout.js +67 -0
- package/dist/admin/surface/test/TestPage.js +5 -9
- package/dist/admin/surface/test/VersionsSection.js +12 -5
- package/dist/admin/surface/test/fields.d.ts +13 -4
- package/dist/admin/surface/test/fields.js +46 -14
- package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
- package/dist/admin/surface/test/jumpToSection.js +4 -3
- package/dist/admin/surface/test/pageParts.d.ts +4 -1
- package/dist/admin/surface/test/pageParts.js +49 -1
- package/dist/admin/surface/test/testPage.css +3 -1
- package/dist/admin/surface/testDocument.model.d.ts +1 -0
- package/dist/admin/surface/testDocument.model.js +2 -1
- package/dist/admin/surface/useTestDocument.d.ts +15 -2
- package/dist/admin/surface/useTestDocument.js +53 -15
- package/dist/admin/surfaceUrls.d.ts +7 -0
- package/dist/admin/surfaceUrls.js +19 -0
- package/dist/admin/theme.css +2 -0
- package/dist/admin/useApi.d.ts +21 -2
- package/dist/admin/useApi.js +3 -5
- package/dist/admin/views/CollectionListRedirect.d.ts +1 -0
- package/dist/admin/views/CollectionListRedirect.js +1 -1
- package/dist/admin/views/ExperimentsRootView.js +28 -133
- package/dist/admin/views/GoalEditRedirect.d.ts +4 -0
- package/dist/admin/views/GoalEditRedirect.js +14 -0
- package/dist/admin/views/GoalsCollectionListView.d.ts +2 -0
- package/dist/admin/views/GoalsCollectionListView.js +4 -0
- package/dist/admin/views/TestsCollectionListView.d.ts +2 -0
- package/dist/admin/views/TestsCollectionListView.js +4 -0
- package/dist/admin/views/TestsNavLinkServer.d.ts +1 -0
- package/dist/admin/views/TestsNavLinkServer.js +1 -1
- package/dist/admin/views/buildSurfaceProps.d.ts +8 -0
- package/dist/admin/views/buildSurfaceProps.js +143 -0
- package/dist/admin/views/renderCollectionList.d.ts +2 -0
- package/dist/admin/views/renderCollectionList.js +34 -0
- package/dist/admin/views/renderTestsSurface.d.ts +3 -0
- package/dist/admin/views/renderTestsSurface.js +34 -0
- package/dist/blocks/bindings.d.ts +3 -0
- package/dist/blocks/bindings.js +34 -0
- package/dist/blocks/createExperimentBlock.d.ts +6 -0
- package/dist/blocks/createExperimentBlock.js +77 -0
- package/dist/blocks/formState.d.ts +31 -0
- package/dist/blocks/formState.js +146 -0
- package/dist/blocks/model.d.ts +41 -0
- package/dist/blocks/model.js +34 -0
- package/dist/client/ExperimentGroup.d.ts +6 -0
- package/dist/client/ExperimentGroup.js +9 -0
- package/dist/client/ExperimentsProvider.js +20 -4
- package/dist/client/clickGoals.d.ts +4 -0
- package/dist/client/clickGoals.js +43 -0
- package/dist/collections/experiments/config.js +21 -7
- package/dist/collections/experiments/deleteGuard.d.ts +2 -1
- package/dist/collections/experiments/deleteGuard.js +12 -1
- package/dist/collections/experiments/editability.js +4 -2
- package/dist/collections/experiments/pickSlug.d.ts +2 -0
- package/dist/collections/experiments/pickSlug.js +8 -0
- package/dist/collections/labels.d.ts +3 -0
- package/dist/collections/labels.js +20 -0
- package/dist/collections/metrics.js +28 -6
- package/dist/collections/phases.js +1 -0
- package/dist/collections/serialize.js +7 -0
- package/dist/db/clickGoals.d.ts +9 -0
- package/dist/db/clickGoals.js +22 -0
- package/dist/db/delete.d.ts +1 -0
- package/dist/db/delete.js +1 -0
- package/dist/db/locks.d.ts +8 -0
- package/dist/db/locks.js +27 -4
- package/dist/db/names.d.ts +4 -0
- package/dist/db/names.js +4 -0
- package/dist/db/pageBindings.d.ts +4 -0
- package/dist/db/pageBindings.js +17 -0
- package/dist/db/pageBusy.d.ts +2 -0
- package/dist/db/pageBusy.js +10 -0
- package/dist/db/pageContent.d.ts +10 -0
- package/dist/db/pageContent.js +79 -0
- package/dist/db/placements.d.ts +33 -0
- package/dist/db/placements.js +54 -0
- package/dist/db/slugs.d.ts +8 -0
- package/dist/db/slugs.js +40 -0
- package/dist/db/snapshots.d.ts +6 -0
- package/dist/db/snapshots.js +8 -5
- package/dist/endpoints/clickKeys.d.ts +5 -0
- package/dist/endpoints/clickKeys.js +17 -0
- package/dist/endpoints/events.js +4 -1
- package/dist/endpoints/index.js +28 -0
- package/dist/endpoints/pages.d.ts +5 -0
- package/dist/endpoints/pages.js +120 -0
- package/dist/endpoints/placementStatus.d.ts +6 -0
- package/dist/endpoints/placementStatus.js +238 -0
- package/dist/endpoints/readBody.d.ts +2 -0
- package/dist/endpoints/readBody.js +24 -0
- package/dist/endpoints/results.js +5 -1
- package/dist/endpoints/summary.js +11 -4
- package/dist/endpoints/wrapBlock.d.ts +6 -0
- package/dist/endpoints/wrapBlock.js +76 -0
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +4 -4
- package/dist/events/rows.d.ts +1 -1
- package/dist/events/rows.js +5 -3
- package/dist/exports/adminClient.d.ts +5 -0
- package/dist/exports/adminClient.js +5 -0
- package/dist/exports/adminRsc.d.ts +3 -0
- package/dist/exports/adminRsc.js +3 -0
- package/dist/exports/blocks.d.ts +2 -0
- package/dist/exports/blocks.js +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +1 -0
- package/dist/exports/index.d.ts +6 -1
- package/dist/exports/index.js +3 -0
- package/dist/exports/puck.d.ts +3 -1
- package/dist/exports/puck.js +2 -0
- package/dist/exports/puckEditor.d.ts +3 -0
- package/dist/exports/puckEditor.js +2 -0
- package/dist/exports/rsc.d.ts +3 -0
- package/dist/exports/rsc.js +2 -0
- package/dist/fields/xpGoal.d.ts +3 -0
- package/dist/fields/xpGoal.js +16 -0
- package/dist/globals/state.js +10 -0
- package/dist/goals/cache.d.ts +3 -0
- package/dist/goals/cache.js +35 -0
- package/dist/goals/fairness.d.ts +40 -0
- package/dist/goals/fairness.js +200 -0
- package/dist/goals/key.d.ts +4 -0
- package/dist/goals/key.js +3 -0
- package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
- package/dist/goals/picker/clickGoalPicker.model.js +45 -0
- package/dist/goals/picker/copy.d.ts +16 -0
- package/dist/goals/picker/copy.js +15 -0
- package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
- package/dist/goals/picker/useClickGoalPicker.js +220 -0
- package/dist/goals/validate.d.ts +3 -0
- package/dist/goals/validate.js +14 -0
- package/dist/hooks/pageGuard.d.ts +5 -1
- package/dist/hooks/pageGuard.js +157 -95
- package/dist/hooks/puckGoals.d.ts +3 -0
- package/dist/hooks/puckGoals.js +38 -0
- package/dist/onInit.js +29 -0
- package/dist/options.d.ts +2 -0
- package/dist/options.js +41 -1
- package/dist/plugin.js +50 -43
- package/dist/puck/copy.d.ts +1 -0
- package/dist/puck/copy.js +1 -0
- package/dist/puck/editor/editor.chrome.css +14 -0
- package/dist/puck/editor/editor.css +106 -0
- package/dist/puck/editor/editor.model.d.ts +4 -2
- package/dist/puck/editor/editor.model.js +6 -3
- package/dist/puck/editor/integration.d.ts +10 -0
- package/dist/puck/editor/integration.js +30 -0
- package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
- package/dist/puck/editor/xpGoalPuckEditorField.js +142 -0
- package/dist/puck/integration.d.ts +6 -0
- package/dist/puck/integration.js +21 -0
- package/dist/puck/xpGoalField.d.ts +3 -0
- package/dist/puck/xpGoalField.js +6 -0
- package/dist/results/health.d.ts +4 -0
- package/dist/results/health.js +27 -0
- package/dist/results/snapshot.js +15 -2
- package/dist/sanitizeReadinessDetail.js +43 -0
- package/dist/server/pageKindSeoContract.d.ts +27 -0
- package/dist/server/pageKindSeoContract.js +80 -0
- package/dist/server/recordContentChange.js +58 -8
- package/dist/server/resolveExperimentBlocks.d.ts +5 -0
- package/dist/server/resolveExperimentBlocks.js +54 -0
- package/dist/server/runResults.js +56 -15
- package/dist/server/toClientAssignments.js +2 -1
- package/dist/transitions/addMetric.d.ts +2 -1
- package/dist/transitions/addMetric.js +28 -1
- package/dist/transitions/checkStart.d.ts +14 -1
- package/dist/transitions/checkStart.js +92 -8
- package/dist/transitions/decide.js +25 -0
- package/dist/transitions/deleteExperiment.d.ts +3 -1
- package/dist/transitions/deleteExperiment.js +27 -1
- package/dist/transitions/kernel.js +4 -0
- package/dist/transitions/makeContent.d.ts +5 -0
- package/dist/transitions/makeContent.js +39 -11
- package/dist/transitions/pageCollection.d.ts +10 -0
- package/dist/transitions/pageCollection.js +20 -0
- package/dist/transitions/pageVersions.d.ts +10 -0
- package/dist/transitions/pageVersions.js +44 -0
- package/dist/transitions/phaseRecord.d.ts +1 -0
- package/dist/transitions/placementInteractions.d.ts +40 -0
- package/dist/transitions/placementInteractions.js +94 -0
- package/dist/transitions/placements.d.ts +8 -0
- package/dist/transitions/placements.js +148 -1
- package/dist/transitions/preflightData.d.ts +2 -2
- package/dist/transitions/preflightData.js +137 -5
- package/dist/transitions/resolveCaps.d.ts +1 -0
- package/dist/transitions/resolveCaps.js +6 -2
- package/dist/transitions/start.js +46 -1
- package/dist/types/admin.d.ts +1 -1
- package/dist/types/assignments.d.ts +1 -0
- package/dist/types/contracts.d.ts +15 -0
- package/dist/types/endpoints.d.ts +92 -5
- package/dist/types/index.d.ts +3 -0
- package/dist/types/puck.d.ts +7 -1
- package/dist/types/snapshot.d.ts +17 -4
- package/dist/types/surface.d.ts +13 -0
- package/dist/ui/ui.css +4 -0
- package/package.json +8 -1
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { accentStyles } from '../../ui/accent.js';
|
|
3
1
|
import { formatAdminURL } from 'payload/shared';
|
|
4
2
|
import { notFound, redirect } from 'next/navigation.js';
|
|
5
|
-
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
|
|
6
|
-
import { serializeAttributeKeys, serializeFlagPoints, serializeMetricSources } from '../../collections/serialize.js';
|
|
7
|
-
import { puckCollections } from '../../options.js';
|
|
8
3
|
import { xpOptionsFromReq } from '../../options/fromReq.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
4
|
+
import { surfaceUrls } from '../surfaceUrls.js';
|
|
5
|
+
import { buildSurfaceProps } from './buildSurfaceProps.js';
|
|
6
|
+
import { renderTestsSurface } from './renderTestsSurface.js';
|
|
12
7
|
export async function ExperimentsRootView({ initPageResult, params, searchParams }) {
|
|
13
8
|
const { req } = initPageResult;
|
|
14
9
|
const adminRoute = req.payload.config.routes.admin;
|
|
@@ -26,6 +21,9 @@ export async function ExperimentsRootView({ initPageResult, params, searchParams
|
|
|
26
21
|
} else if (tail.length === 1) {
|
|
27
22
|
route = 'test';
|
|
28
23
|
id = tail[0];
|
|
24
|
+
} else if (tail.length === 2 && tail[0] === 'by-slug') {
|
|
25
|
+
route = 'test';
|
|
26
|
+
id = tail[1];
|
|
29
27
|
} else if (tail.length === 2 && tail[1] === 'results') {
|
|
30
28
|
route = 'results';
|
|
31
29
|
id = tail[0];
|
|
@@ -46,140 +44,37 @@ export async function ExperimentsRootView({ initPageResult, params, searchParams
|
|
|
46
44
|
path: `${login}?redirect=${encodeURIComponent(currentPath)}`
|
|
47
45
|
}));
|
|
48
46
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const options = xpOptionsFromReq(req);
|
|
52
|
-
async function permits(access) {
|
|
47
|
+
if (route === 'home' || route === 'goals') {
|
|
48
|
+
let canManage = false;
|
|
53
49
|
try {
|
|
54
|
-
|
|
50
|
+
canManage = await xpOptionsFromReq(req).access.manage({
|
|
55
51
|
req
|
|
56
52
|
}) === true;
|
|
57
|
-
} catch {
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
} catch {}
|
|
54
|
+
if (!canManage) return renderTestsSurface(req, null);
|
|
55
|
+
return redirect(route === 'home' ? surfaceUrls.home(adminRoute, await searchParams) : surfaceUrls.goals(adminRoute, await searchParams));
|
|
60
56
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const canReadResults = options.access.readResults ? await permits(options.access.readResults) : canManage;
|
|
64
|
-
if (routeAccess({
|
|
57
|
+
const clientProps = await buildSurfaceProps({
|
|
58
|
+
req,
|
|
65
59
|
route,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
const flagPoints = serializeFlagPoints(options);
|
|
75
|
-
// xp-state is manager-readable only. Results-only readers do not edit settings.
|
|
76
|
-
if (canManage && flagPoints.length) {
|
|
77
|
-
const state = await req.payload.findGlobal({
|
|
78
|
-
slug: 'xp-state',
|
|
60
|
+
id,
|
|
61
|
+
searchParams
|
|
62
|
+
});
|
|
63
|
+
if (clientProps?.canManage && id && tail[0] === 'by-slug') {
|
|
64
|
+
const result = await req.payload.find({
|
|
65
|
+
collection: 'xp-experiments',
|
|
79
66
|
req,
|
|
80
67
|
overrideAccess: false,
|
|
81
|
-
depth: 0
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const registrations = puckCollections(options);
|
|
87
|
-
const pages = route === 'new' && canManage && options.puck?.enabled && registrations.length ? (await Promise.all(registrations.map(async ({ slug: collection })=>{
|
|
88
|
-
try {
|
|
89
|
-
const config = req.payload.config.collections.find((item)=>item.slug === collection);
|
|
90
|
-
const useAsTitle = config?.admin.useAsTitle ?? 'id';
|
|
91
|
-
const label = config?.labels.plural;
|
|
92
|
-
// Keep /rsc dependency-safe: translations is not a declared runtime dependency,
|
|
93
|
-
// so a per-language label picks the request language, then English, then any string.
|
|
94
|
-
const byLanguage = label && typeof label === 'object' ? label : {};
|
|
95
|
-
const collectionLabel = typeof label === 'string' ? label : [
|
|
96
|
-
byLanguage[req.i18n?.language ?? 'en'],
|
|
97
|
-
byLanguage.en,
|
|
98
|
-
...Object.values(byLanguage)
|
|
99
|
-
].find((value)=>typeof value === 'string') ?? collection;
|
|
100
|
-
const result = await req.payload.find({
|
|
101
|
-
collection,
|
|
102
|
-
req,
|
|
103
|
-
overrideAccess: false,
|
|
104
|
-
depth: 0,
|
|
105
|
-
limit: 50,
|
|
106
|
-
sort: '-updatedAt',
|
|
107
|
-
draft: true,
|
|
108
|
-
select: {
|
|
109
|
-
[useAsTitle]: true
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
return result.docs.map((doc)=>{
|
|
113
|
-
const id = doc.id;
|
|
114
|
-
const customUrl = options.admin?.editorUrl?.({
|
|
115
|
-
collection,
|
|
116
|
-
id,
|
|
117
|
-
draft: true
|
|
118
|
-
});
|
|
119
|
-
const title = doc[useAsTitle];
|
|
120
|
-
return {
|
|
121
|
-
collection,
|
|
122
|
-
collectionLabel,
|
|
123
|
-
id,
|
|
124
|
-
title: typeof title === 'string' || typeof title === 'number' ? String(title) : String(id),
|
|
125
|
-
editorUrl: typeof customUrl === 'string' ? customUrl : formatAdminURL({
|
|
126
|
-
adminRoute,
|
|
127
|
-
path: `/collections/${collection}/${encodeURIComponent(String(id))}`
|
|
128
|
-
})
|
|
129
|
-
};
|
|
130
|
-
});
|
|
131
|
-
} catch {
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
}))).flat() : undefined;
|
|
135
|
-
const descriptions = options.admin?.descriptions;
|
|
136
|
-
const returnToTest = (await searchParams)?.return;
|
|
137
|
-
const clientProps = {
|
|
138
|
-
...typeof returnToTest === 'string' && /^[\w-]+$/.test(returnToTest) ? {
|
|
139
|
-
returnToTest
|
|
140
|
-
} : {},
|
|
141
|
-
route,
|
|
142
|
-
...id === undefined ? {} : {
|
|
143
|
-
id
|
|
144
|
-
},
|
|
145
|
-
adminRoute,
|
|
146
|
-
apiRoute: req.payload.config.routes.api,
|
|
147
|
-
canManage,
|
|
148
|
-
canReadResults,
|
|
149
|
-
isAdmin,
|
|
150
|
-
flagPoints,
|
|
151
|
-
...pages === undefined ? {} : {
|
|
152
|
-
pages
|
|
153
|
-
},
|
|
154
|
-
attributeKeys: serializeAttributeKeys(options),
|
|
155
|
-
metricSources: serializeMetricSources(options),
|
|
156
|
-
...descriptions ? {
|
|
157
|
-
admin: {
|
|
158
|
-
descriptions: {
|
|
159
|
-
...descriptions
|
|
68
|
+
depth: 0,
|
|
69
|
+
limit: 1,
|
|
70
|
+
where: {
|
|
71
|
+
slug: {
|
|
72
|
+
equals: id
|
|
160
73
|
}
|
|
161
74
|
}
|
|
162
|
-
} : {}
|
|
163
|
-
};
|
|
164
|
-
function TestsSurfaceFallback() {
|
|
165
|
-
return /*#__PURE__*/ _jsx(RootViewMessage, {
|
|
166
|
-
alert: true,
|
|
167
|
-
children: SURFACE_COPY.notInstalled
|
|
168
75
|
});
|
|
76
|
+
if (!result.docs[0]) return notFound();
|
|
77
|
+
clientProps.id = String(result.docs[0].id);
|
|
169
78
|
}
|
|
170
|
-
|
|
171
|
-
const surface = RenderServerComponent({
|
|
172
|
-
Component: XP_TESTS_SURFACE,
|
|
173
|
-
Fallback: TestsSurfaceFallback,
|
|
174
|
-
importMap: req.payload.importMap,
|
|
175
|
-
clientProps
|
|
176
|
-
});
|
|
177
|
-
return css ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
178
|
-
children: [
|
|
179
|
-
/*#__PURE__*/ _jsx("style", {
|
|
180
|
-
children: css
|
|
181
|
-
}),
|
|
182
|
-
surface
|
|
183
|
-
]
|
|
184
|
-
}) : surface;
|
|
79
|
+
return renderTestsSurface(req, clientProps);
|
|
185
80
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { formatAdminURL } from 'payload/shared';
|
|
2
|
+
import { redirect } from 'next/navigation.js';
|
|
3
|
+
export async function GoalEditRedirect({ initPageResult, params, routeSegments }) {
|
|
4
|
+
const resolved = await params;
|
|
5
|
+
const segments = routeSegments ?? (Array.isArray(resolved?.segments) ? resolved.segments : []);
|
|
6
|
+
const collectionIndex = segments.indexOf('xp-metrics');
|
|
7
|
+
const tail = collectionIndex >= 0 ? segments.slice(collectionIndex + 1) : segments;
|
|
8
|
+
const id = tail[0] ?? resolved?.id ?? initPageResult.docID;
|
|
9
|
+
const path = id === undefined || id === 'create' ? '/experiments/goals/new' : `/experiments/goals/${encodeURIComponent(String(id))}`;
|
|
10
|
+
return redirect(formatAdminURL({
|
|
11
|
+
adminRoute: initPageResult.req.payload.config.routes.admin,
|
|
12
|
+
path
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { ServerProps } from 'payload';
|
|
2
|
+
/** @deprecated Retained for existing import maps until 0.5.0. */
|
|
2
3
|
export declare function TestsNavLinkServer({ payload, user, permissions }: Pick<ServerProps, 'payload' | 'user' | 'permissions'>): Promise<string | number | bigint | boolean | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
|
|
2
2
|
import { SURFACE_COPY } from '../copy/surface.js';
|
|
3
3
|
import { XP_TESTS_NAV_CLIENT } from '../paths.js';
|
|
4
|
-
export async function TestsNavLinkServer({ payload, user, permissions }) {
|
|
4
|
+
/** @deprecated Retained for existing import maps until 0.5.0. */ export async function TestsNavLinkServer({ payload, user, permissions }) {
|
|
5
5
|
const testPermissions = permissions?.collections?.['xp-experiments'];
|
|
6
6
|
if (!user?.collection || testPermissions?.update !== true) return null;
|
|
7
7
|
const result = await payload.find({
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AdminViewServerProps, PayloadRequest } from 'payload';
|
|
2
|
+
import type { TestsRoute, TestsSurfaceProps } from '../../types/surface.js';
|
|
3
|
+
export declare function buildSurfaceProps({ req, route, id, searchParams }: {
|
|
4
|
+
req: PayloadRequest;
|
|
5
|
+
route: TestsRoute;
|
|
6
|
+
id?: string;
|
|
7
|
+
searchParams: AdminViewServerProps['searchParams'];
|
|
8
|
+
}): Promise<TestsSurfaceProps | null>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { flattenTopLevelFields, formatAdminURL } from 'payload/shared';
|
|
2
|
+
import { serializeAttributeKeys, serializeFlagPoints, serializeMetricSources } from '../../collections/serialize.js';
|
|
3
|
+
import { serializePageCollections } from '../pageCollections.js';
|
|
4
|
+
import { collectionLabel as resolveLabel } from '../../collections/labels.js';
|
|
5
|
+
import { documentEditor, puckCollections } from '../../options.js';
|
|
6
|
+
import { xpOptionsFromReq } from '../../options/fromReq.js';
|
|
7
|
+
import { routeAccess } from './access.model.js';
|
|
8
|
+
export async function buildSurfaceProps({ req, route, id, searchParams }) {
|
|
9
|
+
if (!req.user) return null;
|
|
10
|
+
const adminRoute = req.payload.config.routes.admin;
|
|
11
|
+
const options = xpOptionsFromReq(req);
|
|
12
|
+
async function permits(access) {
|
|
13
|
+
try {
|
|
14
|
+
return await access({
|
|
15
|
+
req
|
|
16
|
+
}) === true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const canManage = await permits(options.access.manage);
|
|
22
|
+
const isAdmin = options.access.admin ? await permits(options.access.admin) : false;
|
|
23
|
+
const canReadResults = options.access.readResults ? await permits(options.access.readResults) : canManage;
|
|
24
|
+
if (routeAccess({
|
|
25
|
+
route,
|
|
26
|
+
hasUser: true,
|
|
27
|
+
canManage,
|
|
28
|
+
canReadResults
|
|
29
|
+
}) !== 'ok') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const flagPoints = serializeFlagPoints(options);
|
|
33
|
+
// xp-state is manager-readable only. Results-only readers do not edit settings.
|
|
34
|
+
if (canManage && flagPoints.length) {
|
|
35
|
+
const state = await req.payload.findGlobal({
|
|
36
|
+
slug: 'xp-state',
|
|
37
|
+
req,
|
|
38
|
+
overrideAccess: false,
|
|
39
|
+
depth: 0
|
|
40
|
+
});
|
|
41
|
+
const defaults = state.flagDefaults;
|
|
42
|
+
for (const point of flagPoints)point.effectiveDefaults = defaults?.[point.id]?.params ?? point.defaultParams;
|
|
43
|
+
}
|
|
44
|
+
const registrations = [
|
|
45
|
+
...new Set([
|
|
46
|
+
...options.puck?.enabled ? puckCollections(options) : [],
|
|
47
|
+
...options.blocks?.collections ?? []
|
|
48
|
+
].map((entry)=>entry.slug))
|
|
49
|
+
];
|
|
50
|
+
const pages = route === 'new' && canManage && registrations.length ? (await Promise.all(registrations.map(async (collection)=>{
|
|
51
|
+
try {
|
|
52
|
+
const config = req.payload.config.collections.find((item)=>item.slug === collection);
|
|
53
|
+
const useAsTitle = config?.admin.useAsTitle ?? 'id';
|
|
54
|
+
const collectionLabel = resolveLabel(config?.labels.plural, collection, req);
|
|
55
|
+
// appliesTo may inspect any stored field; ordinary Puck lists keep their title-only projection.
|
|
56
|
+
const predicateFields = options.blocks?.collections.some((entry)=>entry.slug === collection) ? Object.fromEntries(flattenTopLevelFields(config?.fields ?? []).map((field)=>[
|
|
57
|
+
field.name,
|
|
58
|
+
true
|
|
59
|
+
])) : {};
|
|
60
|
+
const result = await req.payload.find({
|
|
61
|
+
collection,
|
|
62
|
+
req,
|
|
63
|
+
overrideAccess: false,
|
|
64
|
+
depth: 0,
|
|
65
|
+
limit: 50,
|
|
66
|
+
sort: '-updatedAt',
|
|
67
|
+
draft: true,
|
|
68
|
+
select: {
|
|
69
|
+
[useAsTitle]: true,
|
|
70
|
+
...predicateFields
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// The new-test list uses the latest draft's editing intent, as the "Test a block" field condition does (§4.1).
|
|
74
|
+
return result.docs.map((doc)=>{
|
|
75
|
+
const editor = documentEditor(options, collection, doc);
|
|
76
|
+
if (editor !== 'puck' && editor !== 'blocks') return null;
|
|
77
|
+
const id = doc.id;
|
|
78
|
+
const customUrl = options.admin?.editorUrl?.({
|
|
79
|
+
collection,
|
|
80
|
+
id,
|
|
81
|
+
draft: true,
|
|
82
|
+
editor,
|
|
83
|
+
doc
|
|
84
|
+
});
|
|
85
|
+
const title = doc[useAsTitle];
|
|
86
|
+
return {
|
|
87
|
+
collection,
|
|
88
|
+
collectionLabel,
|
|
89
|
+
id,
|
|
90
|
+
editor,
|
|
91
|
+
title: typeof title === 'string' || typeof title === 'number' ? String(title) : String(id),
|
|
92
|
+
editorUrl: typeof customUrl === 'string' ? customUrl : formatAdminURL({
|
|
93
|
+
adminRoute,
|
|
94
|
+
path: `/collections/${collection}/${encodeURIComponent(String(id))}`
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
}).filter((page)=>page !== null);
|
|
98
|
+
} catch {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}))).flat() : undefined;
|
|
102
|
+
const descriptions = options.admin?.descriptions;
|
|
103
|
+
const returnToTest = (await searchParams)?.return;
|
|
104
|
+
return {
|
|
105
|
+
...typeof returnToTest === 'string' && /^[\w-]+$/.test(returnToTest) ? {
|
|
106
|
+
returnToTest
|
|
107
|
+
} : {},
|
|
108
|
+
route,
|
|
109
|
+
...id === undefined ? {} : {
|
|
110
|
+
id
|
|
111
|
+
},
|
|
112
|
+
adminRoute,
|
|
113
|
+
apiRoute: req.payload.config.routes.api,
|
|
114
|
+
canManage,
|
|
115
|
+
canReadResults,
|
|
116
|
+
isAdmin,
|
|
117
|
+
flagPoints,
|
|
118
|
+
...pages === undefined ? {} : {
|
|
119
|
+
pages
|
|
120
|
+
},
|
|
121
|
+
pageCollections: serializePageCollections(options.pages?.collections ?? [], req),
|
|
122
|
+
collectionTitles: Object.fromEntries(req.payload.config.collections.flatMap((collection)=>{
|
|
123
|
+
const title = collection.admin.useAsTitle;
|
|
124
|
+
return title && title !== 'id' ? [
|
|
125
|
+
[
|
|
126
|
+
collection.slug,
|
|
127
|
+
title
|
|
128
|
+
]
|
|
129
|
+
] : [];
|
|
130
|
+
})),
|
|
131
|
+
userId: req.user.id,
|
|
132
|
+
userCollection: req.payload.config.admin.user,
|
|
133
|
+
attributeKeys: serializeAttributeKeys(options),
|
|
134
|
+
metricSources: serializeMetricSources(options),
|
|
135
|
+
...descriptions ? {
|
|
136
|
+
admin: {
|
|
137
|
+
descriptions: {
|
|
138
|
+
...descriptions
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} : {}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { ListViewServerProps } from 'payload';
|
|
2
|
+
export declare function renderCollectionList({ payload, searchParams }: ListViewServerProps, route: 'home' | 'goals'): Promise<string | number | bigint | boolean | Iterable<import("react").ReactNode> | import("react").JSX.Element | null | undefined>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createLocalReq } from 'payload';
|
|
2
|
+
import { formatAdminURL } from 'payload/shared';
|
|
3
|
+
import { headers } from 'next/headers.js';
|
|
4
|
+
import { redirect } from 'next/navigation.js';
|
|
5
|
+
import { surfaceUrls } from '../surfaceUrls.js';
|
|
6
|
+
import { buildSurfaceProps } from './buildSurfaceProps.js';
|
|
7
|
+
import { renderTestsSurface } from './renderTestsSurface.js';
|
|
8
|
+
export async function renderCollectionList({ payload, searchParams }, route) {
|
|
9
|
+
const requestHeaders = await headers();
|
|
10
|
+
// Authentication, not an access-checked operation; host access callbacks need the full session user.
|
|
11
|
+
const { user } = await payload.auth({
|
|
12
|
+
headers: requestHeaders
|
|
13
|
+
});
|
|
14
|
+
const adminRoute = payload.config.routes.admin;
|
|
15
|
+
if (!user) {
|
|
16
|
+
const { login } = payload.config.admin.routes;
|
|
17
|
+
const currentPath = route === 'home' ? surfaceUrls.home(adminRoute, await searchParams) : surfaceUrls.goals(adminRoute, await searchParams);
|
|
18
|
+
return redirect(formatAdminURL({
|
|
19
|
+
adminRoute,
|
|
20
|
+
path: `${login}?redirect=${encodeURIComponent(currentPath)}`
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
const req = await createLocalReq({
|
|
24
|
+
user,
|
|
25
|
+
req: {
|
|
26
|
+
headers: requestHeaders
|
|
27
|
+
}
|
|
28
|
+
}, payload);
|
|
29
|
+
return renderTestsSurface(req, await buildSurfaceProps({
|
|
30
|
+
req,
|
|
31
|
+
route,
|
|
32
|
+
searchParams
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
import type { TestsSurfaceProps } from '../../types/surface.js';
|
|
3
|
+
export declare function renderTestsSurface(req: PayloadRequest, clientProps: TestsSurfaceProps | null): Promise<string | number | bigint | boolean | Iterable<import("react").ReactNode> | import("react").JSX.Element | null | undefined>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
|
|
3
|
+
import { accentStyles } from '../../ui/accent.js';
|
|
4
|
+
import { xpOptionsFromReq } from '../../options/fromReq.js';
|
|
5
|
+
import { XP_TESTS_SURFACE } from '../paths.js';
|
|
6
|
+
import { SURFACE_COPY } from '../copy/surface.js';
|
|
7
|
+
export async function renderTestsSurface(req, clientProps) {
|
|
8
|
+
// Keep the server export importable in plain Node; Next loads the CSS-bearing client boundary on render.
|
|
9
|
+
const { RootViewMessage } = await import('../components/RootViewMessage.js');
|
|
10
|
+
if (!clientProps) return /*#__PURE__*/ _jsx(RootViewMessage, {
|
|
11
|
+
children: SURFACE_COPY.noAccess
|
|
12
|
+
});
|
|
13
|
+
function TestsSurfaceFallback() {
|
|
14
|
+
return /*#__PURE__*/ _jsx(RootViewMessage, {
|
|
15
|
+
alert: true,
|
|
16
|
+
children: SURFACE_COPY.notInstalled
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const css = accentStyles(xpOptionsFromReq(req).theme?.accent);
|
|
20
|
+
const surface = RenderServerComponent({
|
|
21
|
+
Component: XP_TESTS_SURFACE,
|
|
22
|
+
Fallback: TestsSurfaceFallback,
|
|
23
|
+
importMap: req.payload.importMap,
|
|
24
|
+
clientProps
|
|
25
|
+
});
|
|
26
|
+
return css ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
27
|
+
children: [
|
|
28
|
+
/*#__PURE__*/ _jsx("style", {
|
|
29
|
+
children: css
|
|
30
|
+
}),
|
|
31
|
+
surface
|
|
32
|
+
]
|
|
33
|
+
}) : surface;
|
|
34
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ResolvedOptions } from '../types/contracts.js';
|
|
2
|
+
/** Same document-kind rule as the scan; Payload blocks are top level, Puck is recursive. */
|
|
3
|
+
export declare function placementSlugs(options: Pick<ResolvedOptions, 'blocks' | 'puck'>, collection: string, doc: Record<string, unknown> | undefined): string[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { documentEditor, puckCollections } from '../options.js';
|
|
2
|
+
import { EXPERIMENT_BLOCK_SLUG, hostBlocks, isObject } from './model.js';
|
|
3
|
+
/** Same document-kind rule as the scan; Payload blocks are top level, Puck is recursive. */ export function placementSlugs(options, collection, doc) {
|
|
4
|
+
if (!doc) return [];
|
|
5
|
+
const editor = documentEditor(options, collection, doc);
|
|
6
|
+
if (editor === 'blocks') {
|
|
7
|
+
const registration = options.blocks?.collections.find((r)=>r.slug === collection);
|
|
8
|
+
if (!registration) return [];
|
|
9
|
+
return [
|
|
10
|
+
...new Set(hostBlocks(doc[registration.field]).flatMap((block)=>block.blockType === (registration.blockSlug ?? EXPERIMENT_BLOCK_SLUG) && typeof block.experiment === 'string' ? [
|
|
11
|
+
block.experiment
|
|
12
|
+
] : []))
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
if (editor !== 'puck') return [];
|
|
16
|
+
const registration = puckCollections(options).find((r)=>r.slug === collection);
|
|
17
|
+
const found = new Set();
|
|
18
|
+
function walk(v) {
|
|
19
|
+
if (Array.isArray(v)) {
|
|
20
|
+
v.forEach(walk);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!isObject(v)) return;
|
|
24
|
+
if (v.type === 'ExperimentSlot' && isObject(v.props)) {
|
|
25
|
+
const ref = v.props.experiment, slug = typeof ref === 'string' ? ref : isObject(ref) ? ref.slug : undefined;
|
|
26
|
+
if (typeof slug === 'string') found.add(slug);
|
|
27
|
+
}
|
|
28
|
+
Object.values(v).forEach(walk);
|
|
29
|
+
}
|
|
30
|
+
if (registration) walk(doc[registration.field]);
|
|
31
|
+
return [
|
|
32
|
+
...found
|
|
33
|
+
];
|
|
34
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { XP_BLOCK_STATUS, XP_BLOCK_ROW_LABEL, XP_COPY_ORIGINAL } from '../admin/paths.js';
|
|
2
|
+
import { BLOCK_SLOTS, EXPERIMENT_BLOCK_SLUG } from './model.js';
|
|
3
|
+
export function validateExperimentSlots(value) {
|
|
4
|
+
if (!Array.isArray(value)) return true;
|
|
5
|
+
const slots = value.map((row)=>row?.slot);
|
|
6
|
+
return new Set(slots).size === slots.length ? true : 'Each version appears once';
|
|
7
|
+
}
|
|
8
|
+
export function createExperimentBlock({ blocks, slug = EXPERIMENT_BLOCK_SLUG }) {
|
|
9
|
+
if (blocks.some((block)=>block.slug === slug)) throw new Error('Experiment blocks cannot be nested.');
|
|
10
|
+
return {
|
|
11
|
+
slug,
|
|
12
|
+
labels: {
|
|
13
|
+
singular: 'Experiment',
|
|
14
|
+
plural: 'Experiments'
|
|
15
|
+
},
|
|
16
|
+
fields: [
|
|
17
|
+
{
|
|
18
|
+
name: 'xpBlockStatus',
|
|
19
|
+
type: 'ui',
|
|
20
|
+
admin: {
|
|
21
|
+
components: {
|
|
22
|
+
Field: XP_BLOCK_STATUS
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'experiment',
|
|
28
|
+
label: 'Test',
|
|
29
|
+
type: 'text',
|
|
30
|
+
required: true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'slots',
|
|
34
|
+
label: 'Versions',
|
|
35
|
+
type: 'array',
|
|
36
|
+
validate: validateExperimentSlots,
|
|
37
|
+
admin: {
|
|
38
|
+
components: {
|
|
39
|
+
RowLabel: XP_BLOCK_ROW_LABEL
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
fields: [
|
|
43
|
+
{
|
|
44
|
+
name: 'xpCopyOriginal',
|
|
45
|
+
type: 'ui',
|
|
46
|
+
admin: {
|
|
47
|
+
components: {
|
|
48
|
+
Field: XP_COPY_ORIGINAL
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'slot',
|
|
54
|
+
label: 'Version',
|
|
55
|
+
type: 'select',
|
|
56
|
+
required: true,
|
|
57
|
+
options: BLOCK_SLOTS.map((value, index)=>({
|
|
58
|
+
value,
|
|
59
|
+
label: [
|
|
60
|
+
'Original',
|
|
61
|
+
'Variant A',
|
|
62
|
+
'Variant B',
|
|
63
|
+
'Variant C'
|
|
64
|
+
][index]
|
|
65
|
+
}))
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'blocks',
|
|
69
|
+
label: 'Content',
|
|
70
|
+
type: 'blocks',
|
|
71
|
+
blocks
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Block, Field, SanitizedConfig } from 'payload';
|
|
2
|
+
import { type ExperimentBlock, type HostBlock } from './model.js';
|
|
3
|
+
type RowConfig = {
|
|
4
|
+
blocks?: Block[];
|
|
5
|
+
localization?: SanitizedConfig['localization'];
|
|
6
|
+
};
|
|
7
|
+
type RowsField = Extract<Field, {
|
|
8
|
+
type: 'array' | 'blocks';
|
|
9
|
+
}>;
|
|
10
|
+
/** Mirror Payload 3.90.2's collections/operations/utilities/copyDataWithFreshRowIDs.js:
|
|
11
|
+
* walk configured row/subfield containers only. Mint ids for array/blocks rows, leaving
|
|
12
|
+
* opaque values (including json, richText, relationships and uploads) unchanged.
|
|
13
|
+
* Global block definitions resolve blockReferences; inline slot blocks retain host configs.
|
|
14
|
+
*/
|
|
15
|
+
export declare function freshBlockIds<T>(value: T, field: RowsField, config?: RowConfig, mint?: () => string): T;
|
|
16
|
+
export declare function wrapExperimentBlock(block: HostBlock, experiment: string, field: RowsField, config?: RowConfig, blockSlug?: string): ExperimentBlock;
|
|
17
|
+
export declare function syncExperimentSlots(block: ExperimentBlock, arms: readonly {
|
|
18
|
+
key: string;
|
|
19
|
+
kind?: string;
|
|
20
|
+
slot?: string | null;
|
|
21
|
+
}[], field: RowsField, config?: RowConfig): ExperimentBlock;
|
|
22
|
+
export declare function copyOriginalIntoVersion(block: ExperimentBlock, slot: string, field: RowsField, config?: RowConfig): ExperimentBlock;
|
|
23
|
+
export declare function unwrapExperimentBlock(block: HostBlock, field: RowsField, slot?: string, config?: RowConfig): HostBlock[];
|
|
24
|
+
/** Config coordinates include unnamed layout containers. No field component is injected here. */
|
|
25
|
+
export declare function findRegisteredBlocksField(fields: readonly Field[], name: string): {
|
|
26
|
+
field: Extract<Field, {
|
|
27
|
+
type: 'blocks';
|
|
28
|
+
}>;
|
|
29
|
+
path: Array<string | number>;
|
|
30
|
+
} | undefined;
|
|
31
|
+
export {};
|