@delmaredigital/payload-experiments 0.3.0-rc.1 → 0.4.0-rc.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.
Files changed (338) hide show
  1. package/README.md +172 -21
  2. package/dist/admin/MetricSourceSelect.js +3 -3
  3. package/dist/admin/PlacementStatus.d.ts +3 -0
  4. package/dist/admin/PlacementStatus.js +54 -0
  5. package/dist/admin/PreviewLinks.d.ts +2 -1
  6. package/dist/admin/PreviewLinks.js +47 -26
  7. package/dist/admin/TestsNavLink.d.ts +3 -1
  8. package/dist/admin/TestsNavLink.js +2 -2
  9. package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
  10. package/dist/admin/actions/ConfirmDialog.js +2 -2
  11. package/dist/admin/actions/DecideDialog.d.ts +2 -1
  12. package/dist/admin/actions/DecideDialog.js +2 -2
  13. package/dist/admin/api.d.ts +11 -2
  14. package/dist/admin/api.js +20 -2
  15. package/dist/admin/armSelect/ArmSelectField.js +14 -22
  16. package/dist/admin/arms/arms.model.d.ts +1 -21
  17. package/dist/admin/arms/arms.model.js +1 -153
  18. package/dist/admin/blocks/BlockPanel.d.ts +4 -0
  19. package/dist/admin/blocks/BlockPanel.js +153 -0
  20. package/dist/admin/blocks/TestBlockField.d.ts +5 -0
  21. package/dist/admin/blocks/TestBlockField.js +163 -0
  22. package/dist/admin/blocks/formApi.d.ts +43 -0
  23. package/dist/admin/blocks/formApi.js +153 -0
  24. package/dist/admin/blocks/inject.d.ts +7 -0
  25. package/dist/admin/blocks/inject.js +41 -0
  26. package/dist/admin/blocks/model.d.ts +17 -0
  27. package/dist/admin/blocks/model.js +45 -0
  28. package/dist/admin/blocks/statusCache.d.ts +7 -0
  29. package/dist/admin/blocks/statusCache.js +83 -0
  30. package/dist/admin/blocks/useBlockForm.d.ts +17 -0
  31. package/dist/admin/blocks/useBlockForm.js +46 -0
  32. package/dist/admin/copy/actions.d.ts +2 -0
  33. package/dist/admin/copy/actions.js +9 -0
  34. package/dist/admin/copy/arms.d.ts +1 -6
  35. package/dist/admin/copy/arms.js +1 -16
  36. package/dist/admin/copy/blocks.d.ts +27 -0
  37. package/dist/admin/copy/blocks.js +38 -0
  38. package/dist/admin/copy/goals.d.ts +2 -0
  39. package/dist/admin/copy/goals.js +4 -1
  40. package/dist/admin/copy/newTest.d.ts +10 -0
  41. package/dist/admin/copy/newTest.js +10 -0
  42. package/dist/admin/copy/pages.d.ts +26 -0
  43. package/dist/admin/copy/pages.js +26 -0
  44. package/dist/admin/copy/readiness.d.ts +25 -3
  45. package/dist/admin/copy/readiness.js +25 -3
  46. package/dist/admin/copy/results.d.ts +3 -0
  47. package/dist/admin/copy/results.js +4 -1
  48. package/dist/admin/copy/review.d.ts +1 -1
  49. package/dist/admin/copy/review.js +1 -1
  50. package/dist/admin/copy/surface.d.ts +0 -1
  51. package/dist/admin/copy/surface.js +0 -1
  52. package/dist/admin/copy/testPage.d.ts +4 -0
  53. package/dist/admin/copy/testPage.js +4 -0
  54. package/dist/admin/goals/XpGoalField.d.ts +2 -0
  55. package/dist/admin/goals/XpGoalField.js +152 -0
  56. package/dist/admin/metricSource.model.d.ts +2 -0
  57. package/dist/admin/metricSource.model.js +4 -0
  58. package/dist/admin/pageCollections.d.ts +10 -0
  59. package/dist/admin/pageCollections.js +20 -0
  60. package/dist/admin/paths.d.ts +8 -1
  61. package/dist/admin/paths.js +8 -1
  62. package/dist/admin/placementStatus.model.d.ts +2 -0
  63. package/dist/admin/placementStatus.model.js +19 -0
  64. package/dist/admin/placements/placementLabels.d.ts +11 -0
  65. package/dist/admin/placements/placementLabels.js +21 -0
  66. package/dist/admin/placements/placementLabels.model.d.ts +13 -0
  67. package/dist/admin/placements/placementLabels.model.js +12 -0
  68. package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
  69. package/dist/admin/placements/usePlacementLabels.js +102 -0
  70. package/dist/admin/previewLinks.model.d.ts +2 -0
  71. package/dist/admin/previewLinks.model.js +3 -0
  72. package/dist/admin/results/DataQualityPanel.js +4 -1
  73. package/dist/admin/results/legacy.d.ts +16 -0
  74. package/dist/admin/results/legacy.js +27 -0
  75. package/dist/admin/results/results.model.d.ts +9 -6
  76. package/dist/admin/results/results.model.js +22 -9
  77. package/dist/admin/results/snapshot.d.ts +14 -0
  78. package/dist/admin/results/snapshot.js +3 -0
  79. package/dist/admin/surface/TestsHome.js +43 -15
  80. package/dist/admin/surface/TestsSurface.js +5 -1
  81. package/dist/admin/surface/contracts.d.ts +6 -5
  82. package/dist/admin/surface/goals/GoalForm.js +67 -34
  83. package/dist/admin/surface/goals/GoalsList.js +3 -3
  84. package/dist/admin/surface/goals/data.js +19 -16
  85. package/dist/admin/surface/goals/goals.model.d.ts +1 -2
  86. package/dist/admin/surface/goals/goals.model.js +1 -3
  87. package/dist/admin/surface/home.model.d.ts +2 -1
  88. package/dist/admin/surface/home.model.js +8 -4
  89. package/dist/admin/surface/newTest/NewTest.js +58 -17
  90. package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
  91. package/dist/admin/surface/newTest/PageWizard.js +209 -0
  92. package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
  93. package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
  94. package/dist/admin/surface/pagePicker/PagePicker.d.ts +10 -0
  95. package/dist/admin/surface/pagePicker/PagePicker.js +172 -0
  96. package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +25 -0
  97. package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
  98. package/dist/admin/surface/readiness/ActionDialogs.js +103 -69
  99. package/dist/admin/surface/readiness/Banner.js +30 -6
  100. package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
  101. package/dist/admin/surface/readiness/readiness.model.js +95 -6
  102. package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
  103. package/dist/admin/surface/results/ReleasePages.js +3 -7
  104. package/dist/admin/surface/results/ResultsBody.js +23 -3
  105. package/dist/admin/surface/results/ResultsHeader.js +2 -5
  106. package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
  107. package/dist/admin/surface/results/recommendation.model.js +10 -3
  108. package/dist/admin/surface/results/useResults.d.ts +10 -1
  109. package/dist/admin/surface/surface.css +0 -1
  110. package/dist/admin/surface/test/AdvancedSection.js +111 -104
  111. package/dist/admin/surface/test/GoalSection.js +97 -22
  112. package/dist/admin/surface/test/LengthSection.js +1 -1
  113. package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
  114. package/dist/admin/surface/test/PageHoldout.js +67 -0
  115. package/dist/admin/surface/test/TestPage.js +2 -1
  116. package/dist/admin/surface/test/VersionsSection.js +12 -5
  117. package/dist/admin/surface/test/fields.d.ts +13 -4
  118. package/dist/admin/surface/test/fields.js +46 -14
  119. package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
  120. package/dist/admin/surface/test/jumpToSection.js +4 -3
  121. package/dist/admin/surface/test/pageParts.d.ts +4 -1
  122. package/dist/admin/surface/test/pageParts.js +79 -2
  123. package/dist/admin/surface/test/testPage.css +8 -1
  124. package/dist/admin/surface/test/testPage.model.d.ts +2 -3
  125. package/dist/admin/surface/test/testPage.model.js +1 -21
  126. package/dist/admin/surface/testDocument.model.d.ts +1 -0
  127. package/dist/admin/surface/testDocument.model.js +2 -1
  128. package/dist/admin/surface/useTestDocument.d.ts +11 -2
  129. package/dist/admin/surface/useTestDocument.js +55 -16
  130. package/dist/admin/useApi.d.ts +11 -2
  131. package/dist/admin/useApi.js +3 -5
  132. package/dist/admin/useLayerUsage.js +14 -23
  133. package/dist/admin/views/ExperimentsRootView.js +58 -16
  134. package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
  135. package/dist/admin/views/TestsNavLinkServer.js +43 -0
  136. package/dist/arms/copy.d.ts +6 -0
  137. package/dist/arms/copy.js +16 -0
  138. package/dist/arms/model.d.ts +23 -0
  139. package/dist/arms/model.js +173 -0
  140. package/dist/blocks/bindings.d.ts +3 -0
  141. package/dist/blocks/bindings.js +34 -0
  142. package/dist/blocks/createExperimentBlock.d.ts +6 -0
  143. package/dist/blocks/createExperimentBlock.js +77 -0
  144. package/dist/blocks/formState.d.ts +31 -0
  145. package/dist/blocks/formState.js +146 -0
  146. package/dist/blocks/model.d.ts +41 -0
  147. package/dist/blocks/model.js +34 -0
  148. package/dist/client/ExperimentGroup.d.ts +6 -0
  149. package/dist/client/ExperimentGroup.js +9 -0
  150. package/dist/client/ExperimentsProvider.js +20 -4
  151. package/dist/client/clickGoals.d.ts +4 -0
  152. package/dist/client/clickGoals.js +43 -0
  153. package/dist/collections/experiments/config.js +12 -2
  154. package/dist/collections/experiments/deleteGuard.d.ts +2 -1
  155. package/dist/collections/experiments/deleteGuard.js +12 -1
  156. package/dist/collections/experiments/editability.js +4 -2
  157. package/dist/collections/experiments/pickSlug.d.ts +2 -0
  158. package/dist/collections/experiments/pickSlug.js +8 -0
  159. package/dist/collections/labels.d.ts +3 -0
  160. package/dist/collections/labels.js +20 -0
  161. package/dist/collections/metrics.js +12 -2
  162. package/dist/collections/phases.js +1 -0
  163. package/dist/collections/serialize.js +7 -0
  164. package/dist/db/appends.d.ts +6 -2
  165. package/dist/db/appends.js +10 -4
  166. package/dist/db/clickGoals.d.ts +9 -0
  167. package/dist/db/clickGoals.js +22 -0
  168. package/dist/db/delete.d.ts +1 -0
  169. package/dist/db/delete.js +1 -0
  170. package/dist/db/locks.d.ts +14 -0
  171. package/dist/db/locks.js +36 -1
  172. package/dist/db/names.d.ts +4 -0
  173. package/dist/db/names.js +4 -0
  174. package/dist/db/pageBindings.d.ts +4 -0
  175. package/dist/db/pageBindings.js +17 -0
  176. package/dist/db/pageBusy.d.ts +2 -0
  177. package/dist/db/pageBusy.js +10 -0
  178. package/dist/db/pageContent.d.ts +10 -0
  179. package/dist/db/pageContent.js +79 -0
  180. package/dist/db/pageOccupancy.d.ts +12 -0
  181. package/dist/db/pageOccupancy.js +45 -0
  182. package/dist/db/placements.d.ts +33 -0
  183. package/dist/db/placements.js +54 -0
  184. package/dist/db/slugs.d.ts +8 -0
  185. package/dist/db/slugs.js +40 -0
  186. package/dist/db/snapshots.d.ts +6 -0
  187. package/dist/db/snapshots.js +9 -5
  188. package/dist/endpoints/clickKeys.d.ts +5 -0
  189. package/dist/endpoints/clickKeys.js +17 -0
  190. package/dist/endpoints/events.js +4 -1
  191. package/dist/endpoints/index.js +28 -0
  192. package/dist/endpoints/pages.d.ts +5 -0
  193. package/dist/endpoints/pages.js +120 -0
  194. package/dist/endpoints/placementStatus.d.ts +6 -0
  195. package/dist/endpoints/placementStatus.js +238 -0
  196. package/dist/endpoints/readBody.d.ts +2 -0
  197. package/dist/endpoints/readBody.js +24 -0
  198. package/dist/endpoints/results.js +5 -1
  199. package/dist/endpoints/summary.js +11 -4
  200. package/dist/endpoints/wrapBlock.d.ts +6 -0
  201. package/dist/endpoints/wrapBlock.js +76 -0
  202. package/dist/errors.d.ts +3 -3
  203. package/dist/errors.js +4 -4
  204. package/dist/events/rows.d.ts +1 -1
  205. package/dist/events/rows.js +5 -3
  206. package/dist/exports/adminClient.d.ts +5 -0
  207. package/dist/exports/adminClient.js +5 -0
  208. package/dist/exports/adminRsc.d.ts +1 -0
  209. package/dist/exports/adminRsc.js +1 -0
  210. package/dist/exports/blocks.d.ts +2 -0
  211. package/dist/exports/blocks.js +1 -0
  212. package/dist/exports/client.d.ts +1 -0
  213. package/dist/exports/client.js +1 -0
  214. package/dist/exports/index.d.ts +6 -1
  215. package/dist/exports/index.js +3 -0
  216. package/dist/exports/puck.d.ts +5 -2
  217. package/dist/exports/puck.js +3 -1
  218. package/dist/exports/puckEditor.d.ts +6 -1
  219. package/dist/exports/puckEditor.js +4 -1
  220. package/dist/exports/rsc.d.ts +4 -0
  221. package/dist/exports/rsc.js +2 -0
  222. package/dist/fields/xpGoal.d.ts +3 -0
  223. package/dist/fields/xpGoal.js +28 -0
  224. package/dist/globals/state.js +10 -0
  225. package/dist/goals/cache.d.ts +3 -0
  226. package/dist/goals/cache.js +35 -0
  227. package/dist/goals/fairness.d.ts +40 -0
  228. package/dist/goals/fairness.js +200 -0
  229. package/dist/goals/key.d.ts +4 -0
  230. package/dist/goals/key.js +3 -0
  231. package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
  232. package/dist/goals/picker/clickGoalPicker.model.js +45 -0
  233. package/dist/goals/picker/copy.d.ts +16 -0
  234. package/dist/goals/picker/copy.js +15 -0
  235. package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
  236. package/dist/goals/picker/useClickGoalPicker.js +220 -0
  237. package/dist/hooks/pageGuard.d.ts +5 -1
  238. package/dist/hooks/pageGuard.js +160 -80
  239. package/dist/lib/fetchAllDocs.d.ts +16 -0
  240. package/dist/lib/fetchAllDocs.js +56 -0
  241. package/dist/onInit.js +29 -0
  242. package/dist/options.d.ts +2 -0
  243. package/dist/options.js +43 -10
  244. package/dist/plugin.js +45 -28
  245. package/dist/puck/ExperimentPicker.js +47 -16
  246. package/dist/puck/ExperimentSlot.d.ts +51 -1
  247. package/dist/puck/ExperimentSlot.js +23 -12
  248. package/dist/puck/copy.d.ts +6 -4
  249. package/dist/puck/copy.js +6 -4
  250. package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
  251. package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
  252. package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
  253. package/dist/puck/editor/ResultsReadout.d.ts +6 -0
  254. package/dist/puck/editor/ResultsReadout.js +99 -0
  255. package/dist/puck/editor/VersionBuilder.d.ts +34 -0
  256. package/dist/puck/editor/VersionBuilder.js +317 -0
  257. package/dist/puck/editor/armWrites.d.ts +7 -0
  258. package/dist/puck/editor/armWrites.js +71 -0
  259. package/dist/puck/editor/armsCache.d.ts +14 -0
  260. package/dist/puck/editor/armsCache.js +78 -0
  261. package/dist/puck/editor/copy.d.ts +33 -1
  262. package/dist/puck/editor/copy.js +45 -8
  263. package/dist/puck/editor/createTest.d.ts +1 -1
  264. package/dist/puck/editor/createTest.js +25 -14
  265. package/dist/puck/editor/editor.chrome.css +48 -161
  266. package/dist/puck/editor/editor.css +48 -161
  267. package/dist/puck/editor/editor.model.d.ts +8 -3
  268. package/dist/puck/editor/editor.model.js +16 -10
  269. package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
  270. package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
  271. package/dist/puck/editor/integration.d.ts +10 -0
  272. package/dist/puck/editor/integration.js +30 -0
  273. package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
  274. package/dist/puck/editor/xpGoalPuckEditorField.js +134 -0
  275. package/dist/puck/experimentSlotField.d.ts +2 -0
  276. package/dist/puck/experimentSlotField.js +11 -3
  277. package/dist/puck/hostContext.d.ts +10 -0
  278. package/dist/puck/hostContext.js +12 -0
  279. package/dist/puck/integration.d.ts +6 -0
  280. package/dist/puck/integration.js +21 -0
  281. package/dist/puck/routes.d.ts +12 -0
  282. package/dist/puck/routes.js +11 -0
  283. package/dist/puck/slotConfig.d.ts +1 -1
  284. package/dist/puck/slotConfig.js +2 -2
  285. package/dist/puck/xpGoalField.d.ts +3 -0
  286. package/dist/puck/xpGoalField.js +6 -0
  287. package/dist/results/health.d.ts +4 -0
  288. package/dist/results/health.js +27 -0
  289. package/dist/results/snapshot.js +15 -2
  290. package/dist/sanitizeReadinessDetail.js +51 -1
  291. package/dist/server/metadata.js +9 -2
  292. package/dist/server/normaliseContentRelationships.d.ts +7 -0
  293. package/dist/server/normaliseContentRelationships.js +62 -0
  294. package/dist/server/pageKindSeoContract.d.ts +27 -0
  295. package/dist/server/pageKindSeoContract.js +80 -0
  296. package/dist/server/recordContentChange.d.ts +1 -1
  297. package/dist/server/recordContentChange.js +72 -13
  298. package/dist/server/resolveExperimentBlocks.d.ts +5 -0
  299. package/dist/server/resolveExperimentBlocks.js +54 -0
  300. package/dist/server/runResults.js +92 -19
  301. package/dist/server/toClientAssignments.js +2 -1
  302. package/dist/transitions/addMetric.d.ts +2 -1
  303. package/dist/transitions/addMetric.js +28 -1
  304. package/dist/transitions/checkStart.d.ts +17 -1
  305. package/dist/transitions/checkStart.js +99 -7
  306. package/dist/transitions/decide.js +40 -1
  307. package/dist/transitions/deleteExperiment.d.ts +3 -1
  308. package/dist/transitions/deleteExperiment.js +27 -1
  309. package/dist/transitions/kernel.js +4 -0
  310. package/dist/transitions/makeContent.d.ts +5 -0
  311. package/dist/transitions/makeContent.js +39 -11
  312. package/dist/transitions/pageCollection.d.ts +10 -0
  313. package/dist/transitions/pageCollection.js +20 -0
  314. package/dist/transitions/pageVersions.d.ts +10 -0
  315. package/dist/transitions/pageVersions.js +44 -0
  316. package/dist/transitions/phaseRecord.d.ts +1 -0
  317. package/dist/transitions/placementInteractions.d.ts +40 -0
  318. package/dist/transitions/placementInteractions.js +94 -0
  319. package/dist/transitions/placements.d.ts +10 -6
  320. package/dist/transitions/placements.js +156 -2
  321. package/dist/transitions/preflightData.d.ts +8 -0
  322. package/dist/transitions/preflightData.js +248 -36
  323. package/dist/transitions/resolveCaps.d.ts +1 -0
  324. package/dist/transitions/resolveCaps.js +6 -2
  325. package/dist/transitions/start.js +77 -3
  326. package/dist/types/admin.d.ts +1 -1
  327. package/dist/types/assignments.d.ts +1 -0
  328. package/dist/types/contracts.d.ts +31 -10
  329. package/dist/types/endpoints.d.ts +92 -5
  330. package/dist/types/index.d.ts +3 -0
  331. package/dist/types/puck.d.ts +7 -1
  332. package/dist/types/snapshot.d.ts +17 -4
  333. package/dist/types/surface.d.ts +13 -0
  334. package/dist/ui/Tabs.d.ts +4 -2
  335. package/dist/ui/Tabs.js +33 -26
  336. package/dist/ui/accent.d.ts +22 -3
  337. package/dist/ui/accent.js +74 -16
  338. package/package.json +12 -2
@@ -1,11 +1,13 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { useRouter } from 'next/navigation.js';
5
5
  import { formatAdminURL } from 'payload/shared';
6
6
  import { GOALS_COPY as C, goalDeleteLocked, goalInUse } from '../../copy/goals.js';
7
- import { metricSourceHint, metricSourceOptions } from '../../metricSource.model.js';
7
+ import { metricSourceHint, metricSourceOptions, sourceFor } from '../../metricSource.model.js';
8
8
  import { validateCappingType, validateCappingValue } from '../../../collections/metricValidation.js';
9
+ import { CLICK_KEY } from '../../../goals/key.js';
10
+ import { clickGoalSlug } from '../../../goals/picker/clickGoalPicker.model.js';
9
11
  import { goalSlug, goalUsage } from './goals.model.js';
10
12
  import { GoalResponseError, goalPages, goalResponse } from './data.js';
11
13
  import { SURFACE_COPY } from '../../copy/surface.js';
@@ -55,7 +57,7 @@ export function GoalForm({ surface, id, returnToTest }) {
55
57
  const advanced = useRef(null);
56
58
  const [confirmDelete, setConfirmDelete] = useState(false);
57
59
  useEffect(()=>{
58
- if (advanced.current && (fieldErrors.capType || fieldErrors.capValue || fieldErrors.warningPercent)) advanced.current.open = true;
60
+ if (advanced.current && (fieldErrors.slug || fieldErrors.capType || fieldErrors.capValue || fieldErrors.warningPercent)) advanced.current.open = true;
59
61
  }, [
60
62
  fieldErrors
61
63
  ]);
@@ -84,7 +86,7 @@ export function GoalForm({ surface, id, returnToTest }) {
84
86
  ]);
85
87
  if (loaded.id == null) throw new Error(C.missing);
86
88
  if (controller.signal.aborted) return;
87
- const defaultCap = surface.metricSources.some((source)=>source.id === loaded.source && source.kind === 'revenue') && loaded.capping?.type == null && loaded.capping?.value == null;
89
+ const defaultCap = sourceFor(loaded.source, surface.metricSources)?.kind === 'revenue' && loaded.capping?.type == null && loaded.capping?.value == null;
88
90
  setGoal(loaded);
89
91
  setUsedBy(goalUsage(tests).get(loaded.id) ?? 0);
90
92
  setDraft({
@@ -107,7 +109,8 @@ export function GoalForm({ surface, id, returnToTest }) {
107
109
  id,
108
110
  attempt
109
111
  ]);
110
- const source = surface.metricSources.find((source)=>source.id === draft.source);
112
+ const source = sourceFor(draft.source, surface.metricSources);
113
+ const click = draft.source.startsWith('click:');
111
114
  const hint = metricSourceHint(source);
112
115
  const options = metricSourceOptions(surface.metricSources);
113
116
  const set = (key, value)=>setDraft((current)=>({
@@ -116,11 +119,14 @@ export function GoalForm({ surface, id, returnToTest }) {
116
119
  }));
117
120
  async function submit() {
118
121
  if (inFlight.current || !ready || !surface.canManage) return;
119
- const capValue = draft.capValue.trim() === '' ? null : Number(draft.capValue);
120
- const typeError = validateCappingType(draft.capType || null, capValue, source?.kind);
121
- const valueError = validateCappingValue(capValue, draft.capType || null);
122
+ const capValue = click || draft.capValue.trim() === '' ? null : Number(draft.capValue);
123
+ const typeError = validateCappingType(click ? null : draft.capType || null, capValue, source?.kind);
124
+ const valueError = validateCappingValue(capValue, click ? null : draft.capType || null);
122
125
  const warningPercent = draft.warningPercent.trim() === '' ? 5 : Number(draft.warningPercent);
123
126
  const errors = {
127
+ ...click && !CLICK_KEY.test(draft.slug) ? {
128
+ slug: C.slugRule
129
+ } : {},
124
130
  ...typeError !== true ? {
125
131
  capType: typeError
126
132
  } : {},
@@ -141,12 +147,12 @@ export function GoalForm({ surface, id, returnToTest }) {
141
147
  const body = {
142
148
  name: draft.name,
143
149
  slug: usedBy && goal ? goal.slug : draft.slug,
144
- source: usedBy && goal ? goal.source : draft.source,
145
- direction: draft.direction,
150
+ source: usedBy && goal ? goal.source : click ? `click:${draft.slug}` : draft.source,
151
+ direction: click ? 'increase' : draft.direction,
146
152
  attributionWindowHours: draft.attributionWindowHours,
147
153
  minSampleSize: draft.minSampleSize,
148
154
  capping: {
149
- type: draft.capType || null,
155
+ type: click ? null : draft.capType || null,
150
156
  value: capValue
151
157
  },
152
158
  guardrailThreshold: warningPercent / 100
@@ -170,11 +176,11 @@ export function GoalForm({ surface, id, returnToTest }) {
170
176
  })}`
171
177
  }) : listUrl);
172
178
  } catch (cause) {
173
- setError(cause instanceof Error ? cause.message : C.saveError);
179
+ setError(cause instanceof GoalResponseError ? cause.message : C.saveError);
174
180
  if (cause instanceof GoalResponseError) setFieldErrors(Object.fromEntries(Object.entries(cause.fields).flatMap(([path, message])=>fieldNames[path] ? [
175
181
  [
176
182
  fieldNames[path],
177
- message
183
+ path === 'slug' ? C.duplicateSlug : message
178
184
  ]
179
185
  ] : [])));
180
186
  inFlight.current = false;
@@ -272,41 +278,54 @@ export function GoalForm({ surface, id, returnToTest }) {
272
278
  ...current,
273
279
  name,
274
280
  ...!slugEdited && !usedBy ? {
275
- slug: goalSlug(name)
281
+ slug: click ? clickGoalSlug(name) : goalSlug(name)
276
282
  } : {}
277
283
  }));
284
+ if (click && !slugEdited && !usedBy) setFieldErrors((current)=>({
285
+ ...current,
286
+ slug: clickGoalSlug(name) ? undefined : C.slugRule
287
+ }));
278
288
  }
279
289
  }),
280
- /*#__PURE__*/ _jsx(TextInput, {
281
- id: "xp-goal-slug",
282
- label: C.slug,
283
- required: true,
284
- value: draft.slug,
285
- readOnly: usedBy > 0,
286
- help: usedBy ? C.slugLocked : C.slugHelp,
287
- error: fieldErrors.slug,
288
- onChange: (event)=>{
289
- setSlugEdited(true);
290
- set('slug', event.currentTarget.value);
291
- }
292
- }),
290
+ !click ? /*#__PURE__*/ _jsx(_Fragment, {
291
+ children: /*#__PURE__*/ _jsx(TextInput, {
292
+ id: "xp-goal-slug",
293
+ label: C.slug,
294
+ required: true,
295
+ value: draft.slug,
296
+ readOnly: usedBy > 0,
297
+ help: usedBy ? C.slugLocked : C.slugHelp,
298
+ error: fieldErrors.slug,
299
+ onChange: (event)=>{
300
+ setSlugEdited(true);
301
+ set('slug', event.currentTarget.value);
302
+ }
303
+ })
304
+ }) : null,
293
305
  /*#__PURE__*/ _jsxs(Select, {
294
306
  id: "xp-goal-source",
295
307
  label: C.source,
296
308
  required: true,
297
- value: draft.source,
309
+ value: click ? 'click:' : draft.source,
298
310
  disabled: usedBy > 0,
299
311
  help: usedBy ? C.sourceLocked : hint || undefined,
300
312
  error: fieldErrors.source,
301
313
  onChange: (event)=>{
302
314
  const sourceId = event.currentTarget.value;
303
- const revenue = surface.metricSources.find((source)=>source.id === sourceId)?.kind === 'revenue';
315
+ const revenue = sourceFor(sourceId, surface.metricSources)?.kind === 'revenue';
304
316
  setDraft((current)=>({
305
317
  ...current,
306
318
  source: sourceId,
319
+ ...!slugEdited && !usedBy ? {
320
+ slug: sourceId === 'click:' ? clickGoalSlug(current.name) : goalSlug(current.name)
321
+ } : {},
307
322
  capType: revenue ? 'percentile' : '',
308
323
  capValue: revenue ? '0.99' : ''
309
324
  }));
325
+ if (sourceId === 'click:' && !slugEdited && !usedBy) setFieldErrors((current)=>({
326
+ ...current,
327
+ slug: clickGoalSlug(draft.name) ? undefined : C.slugRule
328
+ }));
310
329
  },
311
330
  children: [
312
331
  /*#__PURE__*/ _jsx("option", {
@@ -315,15 +334,15 @@ export function GoalForm({ surface, id, returnToTest }) {
315
334
  }),
316
335
  options.map((option)=>/*#__PURE__*/ _jsx("option", {
317
336
  value: option.value,
318
- children: option.label
337
+ children: option.value === 'click:' ? sourceFor(option.value, surface.metricSources)?.label : option.label
319
338
  }, option.value)),
320
- draft.source && !options.some((option)=>option.value === draft.source) ? /*#__PURE__*/ _jsx("option", {
339
+ !click && draft.source && !options.some((option)=>option.value === draft.source) ? /*#__PURE__*/ _jsx("option", {
321
340
  value: draft.source,
322
341
  children: draft.source
323
342
  }) : null
324
343
  ]
325
344
  }),
326
- /*#__PURE__*/ _jsxs(Select, {
345
+ !click ? /*#__PURE__*/ _jsxs(Select, {
327
346
  id: "xp-goal-direction",
328
347
  label: C.direction,
329
348
  value: draft.direction,
@@ -339,7 +358,7 @@ export function GoalForm({ surface, id, returnToTest }) {
339
358
  children: C.decrease
340
359
  })
341
360
  ]
342
- }),
361
+ }) : null,
343
362
  /*#__PURE__*/ _jsx(NumberInput, {
344
363
  id: "xp-goal-window",
345
364
  label: C.window,
@@ -370,7 +389,21 @@ export function GoalForm({ surface, id, returnToTest }) {
370
389
  /*#__PURE__*/ _jsxs("div", {
371
390
  className: "xp-goal-advanced-fields",
372
391
  children: [
373
- /*#__PURE__*/ _jsxs("fieldset", {
392
+ click ? /*#__PURE__*/ _jsx(_Fragment, {
393
+ children: /*#__PURE__*/ _jsx(TextInput, {
394
+ id: "xp-goal-slug",
395
+ label: C.slug,
396
+ required: true,
397
+ value: draft.slug,
398
+ readOnly: usedBy > 0,
399
+ help: usedBy ? C.slugLocked : C.slugHelp,
400
+ error: fieldErrors.slug,
401
+ onChange: (event)=>{
402
+ setSlugEdited(true);
403
+ set('slug', event.currentTarget.value);
404
+ }
405
+ })
406
+ }) : /*#__PURE__*/ _jsxs("fieldset", {
374
407
  children: [
375
408
  /*#__PURE__*/ _jsx("legend", {
376
409
  children: C.capping
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { sourceFor } from '../../metricSource.model.js';
3
4
  import { useEffect, useState } from 'react';
4
- import { Link } from '@payloadcms/ui';
5
5
  import { formatAdminURL } from 'payload/shared';
6
6
  import { Banner, EmptyState } from '../../../ui/Feedback.js';
7
7
  import { Button } from '../../../ui/Button.js';
@@ -111,7 +111,7 @@ export function GoalsList({ surface, returnToTest }) {
111
111
  className: "xp-tests-list",
112
112
  children: goals.map((goal)=>{
113
113
  const count = usage.get(goal.id) ?? 0;
114
- const kind = surface.metricSources.find((source)=>source.id === goal.source)?.kind;
114
+ const kind = sourceFor(goal.source, surface.metricSources)?.kind;
115
115
  const label = kind && Object.hasOwn(METRIC_KIND_LABELS, kind) ? METRIC_KIND_LABELS[kind] : undefined;
116
116
  const href = url(`/experiments/goals/${encodeURIComponent(String(goal.id))}${returnQuery}`);
117
117
  return /*#__PURE__*/ _jsxs("article", {
@@ -123,7 +123,7 @@ export function GoalsList({ surface, returnToTest }) {
123
123
  /*#__PURE__*/ _jsxs("div", {
124
124
  className: "xp-tests-row-name",
125
125
  children: [
126
- /*#__PURE__*/ _jsx(Link, {
126
+ /*#__PURE__*/ _jsx("a", {
127
127
  href: href,
128
128
  children: goal.name
129
129
  }),
@@ -1,3 +1,4 @@
1
+ import { DocsResponseError, fetchAllDocs } from '../../../lib/fetchAllDocs.js';
1
2
  import { refusalMessage } from '../../copy/common.js';
2
3
  import { GOALS_COPY as C } from '../../copy/goals.js';
3
4
  import { payloadFieldErrors } from '../testDocument.model.js';
@@ -9,24 +10,26 @@ export class GoalResponseError extends Error {
9
10
  }
10
11
  /** Map REST refusals while retaining Payload's field validation details. */ export async function goalResponse(response, fallback, options) {
11
12
  const body = await response.json().catch(()=>undefined);
12
- if (!response.ok) {
13
- const fields = payloadFieldErrors(body, '_section', fallback);
14
- const first = body?.errors?.[0];
15
- throw new GoalResponseError(refusalMessage(first?.code, response.status, Object.values(fields).join(' ') || fallback, options), fields);
16
- }
13
+ if (!response.ok) goalRefusal(body, response.status, fallback, options);
17
14
  if (body == null) throw new Error(fallback);
18
15
  return body;
19
16
  }
17
+ function goalRefusal(body, status, fallback, options) {
18
+ const fields = payloadFieldErrors(body, '_section', fallback);
19
+ const first = body?.errors?.[0];
20
+ throw new GoalResponseError(refusalMessage(first?.code, status, undefined, {
21
+ ...options,
22
+ fallback
23
+ }), fields);
24
+ }
20
25
  export async function goalPages(base, collection, signal) {
21
- const all = [];
22
- for(let page = 1;; page++){
23
- const response = await fetch(`${base}/${collection}?depth=0&limit=100&page=${page}`, {
24
- credentials: 'include',
25
- signal
26
- });
27
- const body = await goalResponse(response, C.loadError);
28
- if (!Array.isArray(body.docs)) throw new Error(C.loadError);
29
- all.push(...body.docs);
30
- if (!body.hasNextPage) return all;
31
- }
26
+ const result = await fetchAllDocs(`${base}/${collection}?depth=0`, {
27
+ credentials: 'include',
28
+ signal
29
+ }).catch((cause)=>{
30
+ if (cause instanceof DocsResponseError) throw new Error(C.loadError);
31
+ throw cause;
32
+ });
33
+ if (!result.ok) goalRefusal(result.body, result.status, C.loadError);
34
+ return result.docs;
32
35
  }
@@ -26,5 +26,4 @@ export interface GoalUsageTest {
26
26
  }
27
27
  /** Count each experiment once per goal, retaining the API's id types. */
28
28
  export declare function goalUsage(tests: readonly GoalUsageTest[]): Map<DocId, number>;
29
- export declare function goalSlug(name: string): string;
30
- export {};
29
+ export { goalSlug } from '../../../goals/picker/clickGoalPicker.model.js';
@@ -21,6 +21,4 @@
21
21
  tests.size
22
22
  ]));
23
23
  }
24
- export function goalSlug(name) {
25
- return name.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
26
- }
24
+ export { goalSlug } from '../../../goals/picker/clickGoalPicker.model.js';
@@ -7,6 +7,7 @@ export interface HomeTest {
7
7
  kind: ExperimentKind;
8
8
  status: Status;
9
9
  unitType: UnitType;
10
+ updatedAt?: string | null;
10
11
  flagPoint?: string | null;
11
12
  analysis?: {
12
13
  plannedDurationDays?: number | null;
@@ -25,7 +26,7 @@ export declare function groupTests(tests: readonly HomeTest[], summaries: Summar
25
26
  label: "Needs attention" | "Running" | "Decided" | "Draft" | "Archived" | "Ready to start";
26
27
  tests: HomeTest[];
27
28
  }[];
28
- export declare function homeCard(test: HomeTest, summary: Summary, adminRoute: string, flagPoints?: ReadinessContext['flagPoints']): {
29
+ export declare function homeCard(test: HomeTest, summary: Summary, adminRoute: string, flagPoints?: ReadinessContext['flagPoints'], goalNames?: Readonly<Record<string, string>>): {
29
30
  kind: "Whole page" | "Page block" | "Setting";
30
31
  pill: {
31
32
  label: string;
@@ -3,7 +3,7 @@ import { formatDate } from '../format/date.js';
3
3
  import { armLabel } from '../labels.js';
4
4
  import { statusPill } from '../status.model.js';
5
5
  import { decidedLine, runningLine, SURFACE_COPY } from '../copy/surface.js';
6
- import { nextCopy } from '../copy/readiness.js';
6
+ import { asymmetricGoalsCopy, nextCopy, READINESS_COPY } from '../copy/readiness.js';
7
7
  import { READINESS } from './readiness/readiness.model.js';
8
8
  import { sanitizeReadinessDetail } from '../../sanitizeReadinessDetail.js';
9
9
  export function healthDot(test, summary) {
@@ -30,7 +30,7 @@ export function groupTests(tests, summaries, search = '') {
30
30
  const number = (value)=>value != null && Number.isFinite(value) ? new Intl.NumberFormat('en-US', {
31
31
  maximumFractionDigits: 0
32
32
  }).format(value) : '—';
33
- export function homeCard(test, summary, adminRoute, flagPoints) {
33
+ export function homeCard(test, summary, adminRoute, flagPoints, goalNames = {}) {
34
34
  const results = [
35
35
  'running',
36
36
  'stopped',
@@ -45,12 +45,16 @@ export function homeCard(test, summary, adminRoute, flagPoints) {
45
45
  const next = summary?.next;
46
46
  if ((test.status === 'draft' || test.status === 'stopped') && summary?.ready !== true && next && Object.hasOwn(READINESS, next.code)) {
47
47
  // Keep unexpected text out of authored copy, including responses from older servers.
48
- const sentence = READINESS[next.code].next(sanitizeReadinessDetail(next.code, next.detail), {
48
+ const detail = sanitizeReadinessDetail(next.code, next.detail);
49
+ const first = Array.isArray(detail) ? detail[0] : undefined;
50
+ const sentence = next.code === 'goal-in-some-versions' ? asymmetricGoalsCopy({
51
+ name: first?.metricId == null ? undefined : goalNames[String(first.metricId)]
52
+ }) : READINESS[next.code].next(detail, {
49
53
  ...test,
50
54
  arms: test.arms ?? [],
51
55
  flagPoints
52
56
  });
53
- if (sentence) line = nextCopy(sentence);
57
+ if (sentence) line = nextCopy(next.code === 'page-no-targets' ? READINESS_COPY.choosePages : sentence);
54
58
  }
55
59
  if (test.status === 'running') line = runningLine(number(summary?.days), number(test.analysis?.plannedDurationDays), number(summary?.exposures));
56
60
  if (test.status === 'decided') {
@@ -1,14 +1,18 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { fetchAllDocs } from '../../../lib/fetchAllDocs.js';
3
4
  import { useEffect, useRef, useState } from 'react';
4
5
  import { useRouter } from 'next/navigation.js';
5
6
  import { formatAdminURL } from 'payload/shared';
6
7
  import { NEW_TEST_COPY as C } from '../../copy/newTest.js';
7
8
  import { CreateRefused, createTest } from '../../../puck/editor/createTest.js';
9
+ import { PageWizard } from './PageWizard.js';
8
10
  import './newTest.css';
9
11
  export function NewTest({ surface, pages }) {
10
12
  const router = useRouter();
11
13
  const [step, setStep] = useState('kind');
14
+ const [kind, setKind] = useState('flag');
15
+ const [created, setCreated] = useState();
12
16
  const [pointId, setPointId] = useState('');
13
17
  const point = surface.flagPoints.find((item)=>item.id === pointId);
14
18
  const [unitType, setUnitType] = useState('visitor');
@@ -28,14 +32,13 @@ export function NewTest({ surface, pages }) {
28
32
  setLayerState('loading');
29
33
  void (async ()=>{
30
34
  try {
31
- const response = await fetch(`${surface.apiRoute.replace(/\/$/, '')}/xp-layers?depth=0&limit=100`, {
35
+ const response = await fetchAllDocs(`${surface.apiRoute.replace(/\/$/, '')}/xp-layers?depth=0`, {
32
36
  credentials: 'include',
33
37
  signal: controller.signal
34
38
  });
35
- const body = await response.json();
36
- if (!response.ok || !Array.isArray(body?.docs)) throw new Error('groups');
39
+ if (!response.ok) throw new Error('groups');
37
40
  if (!controller.signal.aborted) {
38
- setLayers(body.docs);
41
+ setLayers(response.docs);
39
42
  setLayerState('ready');
40
43
  }
41
44
  } catch {
@@ -50,23 +53,32 @@ export function NewTest({ surface, pages }) {
50
53
  layerRetry
51
54
  ]);
52
55
  async function submit() {
53
- if (inFlight.current || !point || !name.trim()) return;
56
+ if (inFlight.current || kind === 'flag' && !point || !name.trim()) return;
54
57
  inFlight.current = true;
55
58
  setPending(true);
56
59
  setError('');
57
60
  try {
58
61
  const { id } = await createTest(surface.apiRoute, {
59
- kind: 'flag',
60
- unitType,
62
+ kind,
63
+ unitType: kind === 'page' ? 'visitor' : unitType,
61
64
  name,
62
- flagPoint: point.id,
63
- ...layer === undefined ? {} : {
65
+ ...kind === 'flag' ? {
66
+ flagPoint: point.id
67
+ } : surface.pageCollections?.length === 1 ? {
68
+ pageCollection: surface.pageCollections[0].slug
69
+ } : {},
70
+ ...kind !== 'flag' || layer === undefined ? {} : {
64
71
  layer
65
72
  },
66
73
  ...hypothesis.trim() ? {
67
74
  hypothesis
68
75
  } : {}
69
76
  });
77
+ if (kind === 'page') {
78
+ setCreated(id);
79
+ setPending(false);
80
+ return;
81
+ }
70
82
  router.push(formatAdminURL({
71
83
  adminRoute: surface.adminRoute,
72
84
  path: `/experiments/${encodeURIComponent(String(id))}`
@@ -77,6 +89,10 @@ export function NewTest({ surface, pages }) {
77
89
  setPending(false);
78
90
  }
79
91
  }
92
+ if (created !== undefined) return /*#__PURE__*/ _jsx(PageWizard, {
93
+ id: created,
94
+ surface: surface
95
+ });
80
96
  const collections = [
81
97
  ...new Set(pages?.map((page)=>page.collection))
82
98
  ];
@@ -102,15 +118,37 @@ export function NewTest({ surface, pages }) {
102
118
  /*#__PURE__*/ _jsx("span", {
103
119
  children: C.blockMeaning
104
120
  }),
105
- surface.descriptions?.block ? /*#__PURE__*/ _jsx("span", {
106
- children: surface.descriptions.block
121
+ surface.admin?.descriptions?.block ? /*#__PURE__*/ _jsx("span", {
122
+ children: surface.admin?.descriptions?.block
123
+ }) : null
124
+ ]
125
+ }) : null,
126
+ surface.pageCollections?.length ? /*#__PURE__*/ _jsxs("button", {
127
+ type: "button",
128
+ className: "xp-new-test-card",
129
+ onClick: ()=>{
130
+ setKind('page');
131
+ setStep('name');
132
+ },
133
+ children: [
134
+ /*#__PURE__*/ _jsx("strong", {
135
+ children: C.page
136
+ }),
137
+ /*#__PURE__*/ _jsx("span", {
138
+ children: C.pageMeaning
139
+ }),
140
+ surface.admin?.descriptions?.page ? /*#__PURE__*/ _jsx("span", {
141
+ children: surface.admin?.descriptions?.page
107
142
  }) : null
108
143
  ]
109
144
  }) : null,
110
145
  surface.flagPoints.length > 0 ? /*#__PURE__*/ _jsxs("button", {
111
146
  type: "button",
112
147
  className: "xp-new-test-card",
113
- onClick: ()=>setStep('setting'),
148
+ onClick: ()=>{
149
+ setKind('flag');
150
+ setStep('setting');
151
+ },
114
152
  children: [
115
153
  /*#__PURE__*/ _jsx("strong", {
116
154
  children: C.setting
@@ -118,14 +156,14 @@ export function NewTest({ surface, pages }) {
118
156
  /*#__PURE__*/ _jsx("span", {
119
157
  children: C.settingMeaning
120
158
  }),
121
- surface.descriptions?.setting ? /*#__PURE__*/ _jsx("span", {
122
- children: surface.descriptions.setting
159
+ surface.admin?.descriptions?.setting ? /*#__PURE__*/ _jsx("span", {
160
+ children: surface.admin?.descriptions.setting
123
161
  }) : null
124
162
  ]
125
163
  }) : null
126
164
  ]
127
165
  }),
128
- pages === undefined && !surface.flagPoints.length ? /*#__PURE__*/ _jsx("p", {
166
+ pages === undefined && !surface.flagPoints.length && !surface.pageCollections?.length ? /*#__PURE__*/ _jsx("p", {
129
167
  children: C.unavailable
130
168
  }) : null
131
169
  ]
@@ -156,7 +194,10 @@ export function NewTest({ surface, pages }) {
156
194
  /*#__PURE__*/ _jsx("a", {
157
195
  href: page.editorUrl,
158
196
  children: C.open
159
- })
197
+ }),
198
+ page.editor === 'blocks' ? /*#__PURE__*/ _jsx("p", {
199
+ children: C.blocksHandoff
200
+ }) : null
160
201
  ]
161
202
  }, `${typeof page.id}:${page.id}`))
162
203
  }),
@@ -337,7 +378,7 @@ export function NewTest({ surface, pages }) {
337
378
  }));
338
379
  else {
339
380
  setError('');
340
- setStep(step === 'name' ? 'setting' : 'kind');
381
+ setStep(step === 'name' && kind === 'flag' ? 'setting' : 'kind');
341
382
  }
342
383
  },
343
384
  children: C.back
@@ -0,0 +1,7 @@
1
+ import type { DocId } from '../../../types/contracts.js';
2
+ import type { SurfaceProps } from '../contracts.js';
3
+ /** One session stays mounted across Collection, all page steps and Review. */
4
+ export declare function PageWizard({ id, surface }: {
5
+ id: DocId;
6
+ surface: SurfaceProps;
7
+ }): import("react").JSX.Element;