@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
@@ -0,0 +1,26 @@
1
+ export const PAGE_COPY = {
2
+ search: 'Search pages by title',
3
+ more: 'More',
4
+ loading: 'Loading pages…',
5
+ empty: 'There are no pages here yet.',
6
+ error: "Couldn't load pages. Try again.",
7
+ retry: 'Try again',
8
+ draft: 'Draft',
9
+ noPath: 'This page has no address yet.',
10
+ choose: 'Choose a page',
11
+ change: 'Change',
12
+ close: 'Close',
13
+ where: 'Where it shows',
14
+ open: 'Open',
15
+ preview: 'Preview',
16
+ contentChecksOff: 'Content checks are off for this page type.',
17
+ duplicateStep: "Choose Duplicate from the document's actions. Keep the Original's title on the copy, unless the title is what you're testing.",
18
+ ownAddress: "Give the copy its own address and save it as a draft. Publish it when it's ready; the test can't start until it is.",
19
+ thenChoose: 'Then choose the page you made.',
20
+ noPage: 'No page chosen yet.'
21
+ };
22
+ export const usedPageCopy = (label)=>`This is ${label}.`;
23
+ export const usedAddressCopy = (label, path, collection)=>`Same address as ${label} (${path}). Change this page's address in ${collection}, publish it, then choose it.`;
24
+ export const openOriginalCopy = (collection)=>`Open the Original in ${collection}.`;
25
+ export const openCollectionCopy = (collection)=>`Open ${collection}`;
26
+ export const pageStepCopy = (label)=>`A page for ${label}`;
@@ -34,7 +34,9 @@ export declare const READINESS_COPY: {
34
34
  readonly full: "This test's group is full. Lower how many people take part, or create the test again without the group.";
35
35
  readonly differentPeople: "This test's group counts different people. Create the test again without the group.";
36
36
  readonly uncheckedPages: "Version pages aren't checked; make sure each one is published.";
37
- readonly noTargets: "Add a page address for each version.";
37
+ readonly chooseDifferentPage: "Choose a different page";
38
+ readonly choosePages: "Choose pages";
39
+ readonly noTargets: "Choose a page for each version.";
38
40
  readonly pageVisitors: "Whole-page tests count visitors. Create the test again counting visitors.";
39
41
  readonly blockPathRule: "Remove the page-path rule in Who takes part; page block tests run on their page.";
40
42
  readonly removeRule: "Remove rule";
@@ -45,7 +47,12 @@ export declare const READINESS_COPY: {
45
47
  readonly attributeFallback: "this attribute";
46
48
  };
47
49
  export declare const unsupportedContentCopy: (page: string) => string;
48
- export declare const unpublishedPageCopy: (path: string) => string;
50
+ export declare const pageInUseCopy: (path: string, version?: boolean) => string;
51
+ export declare const unpublishedPageCopy: (path: string, context?: {
52
+ original: boolean;
53
+ found: boolean;
54
+ collection: string;
55
+ }) => string;
49
56
  /** Old responses may include raw host errors; unknown strings use authored copy. */
50
57
  export declare const placementFailureCopy: (reason: unknown) => "Couldn't check where this test is used. Try again." | "You don't have permission to check where this test is used." | "A page used by this test no longer exists. Choose another page.";
51
58
  export declare const nextCopy: (sentence: string) => string;
@@ -59,6 +66,21 @@ export declare const attributeNext: (attribute: string) => string;
59
66
  export declare const stoppedCopy: (date: string | null) => string;
60
67
  export declare const decidedCopy: (date: string | null, winner: string) => string;
61
68
  export declare const runningCopy: (date: string | null, people: number | null, days: number | null) => string;
62
- export declare const pageGuardCopy: (name: string, status: "running" | "decided" | "archived") => string;
69
+ /** After archive the guard protects the winner and the Original; only the winner page is "the winner". */
70
+ export declare const pageGuardCopy: (name: string, status: "running" | "decided" | "archived", role?: "winner" | "original") => string;
63
71
  export declare const versionPageUnavailableCopy: (path: string, status?: string) => string;
64
72
  export declare const versionPageUncheckedCopy: (path: string, status?: string) => string;
73
+ export declare const sameAddressCopy: (label: string, other: string, path: string, collection?: string) => string;
74
+ export declare const identicalVersionCopy: (version: string) => string;
75
+ /** Keep named actions aligned with the actual transition buttons. */
76
+ export declare function placementGuardCopy(page: {
77
+ name: string;
78
+ status: string;
79
+ }, block: {
80
+ name: string;
81
+ status: string;
82
+ }, preExisting: boolean): string;
83
+ export declare function interactingTestsCopy(page: string, state: string, name: string, version?: boolean): string;
84
+ export declare const goalInSomeVersionsCopy: (name?: string) => string;
85
+ export declare function asymmetricGoalsCopy(detail: unknown): string;
86
+ export declare const placementUncheckedCopy: (page: string) => string;
@@ -34,7 +34,9 @@ export const READINESS_COPY = {
34
34
  full: "This test's group is full. Lower how many people take part, or create the test again without the group.",
35
35
  differentPeople: "This test's group counts different people. Create the test again without the group.",
36
36
  uncheckedPages: "Version pages aren't checked; make sure each one is published.",
37
- noTargets: 'Add a page address for each version.',
37
+ chooseDifferentPage: 'Choose a different page',
38
+ choosePages: 'Choose pages',
39
+ noTargets: 'Choose a page for each version.',
38
40
  pageVisitors: 'Whole-page tests count visitors. Create the test again counting visitors.',
39
41
  blockPathRule: 'Remove the page-path rule in Who takes part; page block tests run on their page.',
40
42
  // Next steps repeat the button label verbatim (testPage.ts removeRule).
@@ -46,7 +48,8 @@ export const READINESS_COPY = {
46
48
  attributeFallback: 'this attribute'
47
49
  };
48
50
  export const unsupportedContentCopy = (page)=>`${page} has more than one language, so Make it the content isn't available for it; you'll copy the winning version by hand.`;
49
- export const unpublishedPageCopy = (path)=>`${path} isn't a published page. Publish it, or pick another page.`;
51
+ export const pageInUseCopy = (path, version = false)=>version ? `${path} is already in another running or decided test. Make a new copy of the Original for this version, or finish that test first.` : `Another running or decided test already uses ${path}. Choose a different page, or finish that test first.`;
52
+ export const unpublishedPageCopy = (path, context)=>!context ? `${path} isn't a published page yet. Publish it, or choose another page.` : context.original ? `${path} isn't published, so it can't be the Original. Publish it, or choose another page.` : context.found ? `${path} is still a draft. Publish it in ${context.collection}, then start the test.` : `${path} isn't published yet, or no page has that address. Publish it in ${context.collection}, or choose another page.`;
50
53
  /** Old responses may include raw host errors; unknown strings use authored copy. */ export const placementFailureCopy = (reason)=>PLACEMENT_FAILURE_COPY[typeof reason === 'string' && Object.hasOwn(PLACEMENT_FAILURE_COPY, reason) ? reason : 'placements-failed'];
51
54
  const withoutPeriod = (value)=>value.trim().replace(/[.!?]+$/, '');
52
55
  export const nextCopy = (sentence)=>`Next: ${sentence}`;
@@ -64,6 +67,25 @@ export const runningCopy = (date, people, days)=>[
64
67
  people === null ? null : `${people.toLocaleString('en-US')} people so far`,
65
68
  days === null ? null : `about ${days} days to go`
66
69
  ].filter(Boolean).join(' · ');
67
- export const pageGuardCopy = (name, status)=>status === 'archived' ? `This page is shown at other addresses because the finished test "${name}" made it the winner. To free it, open the test and choose "Release pages".` : status === 'decided' ? `This page is part of the decided test "${name}". Choose "Make it the content" or "Discard winner" on the test before you delete, rename or unpublish it.` : `This page is part of the running test "${name}". Stop the test before you delete, rename or unpublish it.`;
70
+ /** After archive the guard protects the winner and the Original; only the winner page is "the winner". */ export const pageGuardCopy = (name, status, role = 'winner')=>status === 'archived' ? role === 'original' ? `This page's address now shows the winner of the finished test "${name}". To free it, open the test and choose "Release pages".` : `This page is shown at other addresses because the finished test "${name}" made it the winner. To free it, open the test and choose "Release pages".` : status === 'decided' ? `This page is part of the decided test "${name}". Choose "Make it the content" or "Discard winner" on the test before you delete, rename or unpublish it.` : `This page is part of the running test "${name}". Stop the test before you delete, rename or unpublish it.`;
68
71
  export const versionPageUnavailableCopy = (path, status = 'running')=>`${path} isn't published, so visitors assigned to it see an error. Republish it, or ${status === 'decided' ? 'discard the winner' : 'stop the test'}.`;
69
72
  export const versionPageUncheckedCopy = (path, status = 'running')=>`Couldn't check whether ${path} is published. Check that it is still published, or ${status === 'decided' ? 'discard the winner' : 'stop the test'}.`;
73
+ export const sameAddressCopy = (label, other, path, collection)=>`${label} has the same address (${path}) as ${other}. Choose a different page.${collection ? ` Give the copy its own address in ${collection}, publish it, then choose it.` : ''}`;
74
+ export const identicalVersionCopy = (version)=>`${version} looks the same as the Original, so the test would compare nothing. Change it before you start.`;
75
+ /** Keep named actions aligned with the actual transition buttons. */ export function placementGuardCopy(page, block, preExisting) {
76
+ const action = (test)=>test.status === 'running' ? `stop ${test.name}` : `choose "Make it the content" or "Discard winner" on ${test.name}`;
77
+ return preExisting ? `This page is in two tests that change it, ${page.name} and ${block.name}. Before you publish, ${action(page)} or ${action(block)}.` : `This page is part of the ${page.status} test ${page.name}, so a block from another test can't be added to it. Finish one of the tests first.`;
78
+ }
79
+ export function interactingTestsCopy(page, state, name, version = false) {
80
+ return `This test uses ${page}, which the ${state} test ${name} also changes. Finish one of them first${version ? `, or remove its block from ${page}` : ''}.`;
81
+ }
82
+ export const goalInSomeVersionsCopy = (name = 'This goal')=>`${name} appears in some versions but not all, so the comparison wouldn't be fair. Add it to every version, or choose another goal.`;
83
+ export function asymmetricGoalsCopy(detail) {
84
+ return (Array.isArray(detail) && detail.length ? detail : [
85
+ detail
86
+ ]).map((item)=>{
87
+ const name = item && typeof item === 'object' && 'name' in item ? item.name : undefined;
88
+ return goalInSomeVersionsCopy(typeof name === 'string' && name.trim() ? name.trim() : undefined);
89
+ }).join('\n');
90
+ }
91
+ export const placementUncheckedCopy = (page)=>`${page} couldn't be checked, so the test can't confirm every version shows the same goals. Check that you can open it.`;
@@ -22,6 +22,7 @@ export declare const RESULTS_COPY: {
22
22
  };
23
23
  readonly qualityOK: "No data quality problems found.";
24
24
  readonly previews: "Preview links";
25
+ readonly clickPrimary: "A click shows interest, not the result you want. Add that result, like a sign-up or a gift, as another goal to check that more clicks lead to more of it.";
25
26
  readonly releasePages: "Release pages";
26
27
  readonly releasePagesBody: "Every address shows its own page again. The result stays recorded.";
27
28
  readonly you: "you";
@@ -57,6 +58,8 @@ export declare const RESULTS_COPY: {
57
58
  readonly infeasible: "A clear answer would take more than 90 days at this traffic.";
58
59
  readonly noCap: "Not capped: fewer than 20 people had this outcome in the 90 days before Start, so every value counts in full.";
59
60
  readonly quality: "Data quality";
61
+ readonly missingWinner: "The stored winner is missing from this round’s results.";
62
+ readonly missingWinnerNext: "Recompute; if it persists, contact a developer before trusting these results.";
60
63
  readonly srm: "Traffic isn't splitting as set. Check for caching or a page placed twice.";
61
64
  readonly multiGroup: "People who saw more than one group were excluded.";
62
65
  readonly multiGroupNext: "Check for caching or a page placed twice before trusting the results.";
@@ -9,7 +9,7 @@ export const RESULTS_COPY = {
9
9
  sequential: 'Sequential',
10
10
  bayesian: 'Bayesian'
11
11
  },
12
- asOfDecision: (date)=>`as of the decision, ${date}`,
12
+ asOfDecision: (date)=>`numbers from ${date}, used for the decision`,
13
13
  kinds: {
14
14
  binomial: 'yes/no',
15
15
  count: 'amount',
@@ -22,6 +22,7 @@ export const RESULTS_COPY = {
22
22
  },
23
23
  qualityOK: 'No data quality problems found.',
24
24
  previews: 'Preview links',
25
+ clickPrimary: 'A click shows interest, not the result you want. Add that result, like a sign-up or a gift, as another goal to check that more clicks lead to more of it.',
25
26
  releasePages: 'Release pages',
26
27
  releasePagesBody: 'Every address shows its own page again. The result stays recorded.',
27
28
  you: 'you',
@@ -57,6 +58,8 @@ export const RESULTS_COPY = {
57
58
  infeasible: 'A clear answer would take more than 90 days at this traffic.',
58
59
  noCap: 'Not capped: fewer than 20 people had this outcome in the 90 days before Start, so every value counts in full.',
59
60
  quality: 'Data quality',
61
+ missingWinner: 'The stored winner is missing from this round’s results.',
62
+ missingWinnerNext: 'Recompute; if it persists, contact a developer before trusting these results.',
60
63
  srm: "Traffic isn't splitting as set. Check for caching or a page placed twice.",
61
64
  multiGroup: 'People who saw more than one group were excluded.',
62
65
  multiGroupNext: 'Check for caching or a page placed twice before trusting the results.',
@@ -1,8 +1,8 @@
1
1
  export declare const REVIEW_COPY: {
2
2
  readonly noBaseline: "No data yet for this metric.";
3
3
  readonly preview: "Preview";
4
+ readonly previewBlocked: "Your browser blocked the preview window. Allow pop-ups for this site, then try again.";
4
5
  readonly placedTimes: (n: number) => string;
5
- readonly noPreview: "Preview isn't set up for this page type";
6
6
  readonly raise: "Raise allocation";
7
7
  readonly addMetric: "Add a metric";
8
8
  readonly allocationBody: "Raising allocation adds people; nobody already taking part changes group. Lowering needs a new round.";
@@ -1,8 +1,8 @@
1
1
  export const REVIEW_COPY = {
2
2
  noBaseline: 'No data yet for this metric.',
3
3
  preview: 'Preview',
4
+ previewBlocked: 'Your browser blocked the preview window. Allow pop-ups for this site, then try again.',
4
5
  placedTimes: (n)=>`(placed ${n} times on this page)`,
5
- noPreview: "Preview isn't set up for this page type",
6
6
  raise: 'Raise allocation',
7
7
  addMetric: 'Add a metric',
8
8
  allocationBody: 'Raising allocation adds people; nobody already taking part changes group. Lowering needs a new round.',
@@ -10,7 +10,6 @@ export declare const SURFACE_COPY: {
10
10
  readonly dashShowTests: "Show all tests";
11
11
  readonly dashShowGoals: "Show all goals";
12
12
  readonly backToAdmin: "Back to admin";
13
- readonly limitedList: "Showing the 200 most recent tests.";
14
13
  readonly notInstalled: "The Tests screen isn't installed yet. Run `payload generate:importmap` and reload.";
15
14
  readonly noAccess: "You don't have access to tests.";
16
15
  readonly loading: "Loading tests…";
@@ -10,7 +10,6 @@ export const SURFACE_COPY = {
10
10
  dashShowTests: 'Show all tests',
11
11
  dashShowGoals: 'Show all goals',
12
12
  backToAdmin: 'Back to admin',
13
- limitedList: 'Showing the 200 most recent tests.',
14
13
  notInstalled: "The Tests screen isn't installed yet. Run `payload generate:importmap` and reload.",
15
14
  noAccess: "You don't have access to tests.",
16
15
  loading: 'Loading tests…',
@@ -1,4 +1,7 @@
1
1
  export declare const TEST_PAGE: {
2
+ readonly clickPageGroup: "On this test's page";
3
+ readonly clickGoalsNote: "Goals inside linked documents aren't shown. Goals added in code (data-xp-goal) work but aren't listed here.";
4
+ readonly clickGoalHelp: "A click shows interest, not the result you want.";
2
5
  readonly staleEdit: "Someone else changed this test. Reload to see their changes.";
3
6
  readonly reload: "Reload";
4
7
  readonly loading: "Loading test…";
@@ -54,6 +57,7 @@ export declare const TEST_PAGE: {
54
57
  readonly split: "Split evenly";
55
58
  readonly remove: "Remove version";
56
59
  readonly invalidShare: "Use a whole number from 0 to 100.";
60
+ readonly holdoutPrefix: "Holdout: ";
57
61
  readonly allocation: "How many people take part";
58
62
  readonly traffic: "How people are split";
59
63
  readonly goal: "Goal";
@@ -1,4 +1,7 @@
1
1
  export const TEST_PAGE = {
2
+ clickPageGroup: "On this test's page",
3
+ clickGoalsNote: "Goals inside linked documents aren't shown. Goals added in code (data-xp-goal) work but aren't listed here.",
4
+ clickGoalHelp: 'A click shows interest, not the result you want.',
2
5
  staleEdit: 'Someone else changed this test. Reload to see their changes.',
3
6
  reload: 'Reload',
4
7
  loading: 'Loading test…',
@@ -54,6 +57,7 @@ export const TEST_PAGE = {
54
57
  split: 'Split evenly',
55
58
  remove: 'Remove version',
56
59
  invalidShare: 'Use a whole number from 0 to 100.',
60
+ holdoutPrefix: 'Holdout: ',
57
61
  allocation: 'How many people take part',
58
62
  traffic: 'How people are split',
59
63
  goal: 'Goal',
@@ -0,0 +1,2 @@
1
+ import type { TextFieldClientProps } from 'payload';
2
+ export declare function XpGoalField({ path, field, readOnly }: TextFieldClientProps): import("react").JSX.Element;
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button, FieldError, FieldLabel, TextInput, useAuth, useConfig, useField } from '@payloadcms/ui';
4
+ import { useId } from 'react';
5
+ import { useClickGoalPicker } from '../../goals/picker/useClickGoalPicker.js';
6
+ import { CLICK_GOAL_COPY as C } from '../../goals/picker/copy.js';
7
+ export function XpGoalField({ path, field, readOnly }) {
8
+ const { value, setValue, showError } = useField({
9
+ path
10
+ });
11
+ const { permissions } = useAuth();
12
+ const { config } = useConfig();
13
+ const access = permissions?.collections?.['xp-experiments'];
14
+ const locked = Boolean(readOnly || field.admin?.readOnly);
15
+ const picker = useClickGoalPicker(config.routes.api, {
16
+ enabled: access?.update === true,
17
+ onSelect: setValue
18
+ });
19
+ const id = useId(), namePath = `${id}-name`, slugPath = `${id}-slug`;
20
+ const stopEnter = (event)=>{
21
+ if (event.key === 'Enter') {
22
+ event.preventDefault();
23
+ event.stopPropagation();
24
+ }
25
+ };
26
+ return /*#__PURE__*/ _jsxs("div", {
27
+ children: [
28
+ /*#__PURE__*/ _jsx(TextInput, {
29
+ path: path,
30
+ label: field.label ?? C.label,
31
+ description: field.admin?.description,
32
+ required: field.required,
33
+ readOnly: locked,
34
+ value: value ?? '',
35
+ showError: showError,
36
+ Error: /*#__PURE__*/ _jsx(FieldError, {
37
+ path: path,
38
+ showError: showError
39
+ }),
40
+ onChange: (event)=>{
41
+ if (!locked) setValue(event.currentTarget.value);
42
+ }
43
+ }),
44
+ picker.canManage && !locked ? /*#__PURE__*/ _jsxs(_Fragment, {
45
+ children: [
46
+ /*#__PURE__*/ _jsx("label", {
47
+ htmlFor: `${id}-picker`,
48
+ children: C.choose
49
+ }),
50
+ /*#__PURE__*/ _jsxs("select", {
51
+ id: `${id}-picker`,
52
+ value: picker.goals.some((goal)=>goal.slug === value) ? value : '',
53
+ onChange: (event)=>{
54
+ if (event.currentTarget.value) setValue(event.currentTarget.value);
55
+ },
56
+ children: [
57
+ /*#__PURE__*/ _jsx("option", {
58
+ value: "",
59
+ children: C.choose
60
+ }),
61
+ picker.goals.map((goal)=>/*#__PURE__*/ _jsx("option", {
62
+ value: goal.slug,
63
+ children: goal.name
64
+ }, goal.id))
65
+ ]
66
+ }),
67
+ picker.undefinedKey(value ?? '') ? /*#__PURE__*/ _jsx("p", {
68
+ role: "status",
69
+ children: C.undefinedKey
70
+ }) : null,
71
+ !picker.panel.open ? /*#__PURE__*/ _jsx(Button, {
72
+ type: "button",
73
+ onClick: picker.open,
74
+ children: C.newGoal
75
+ }) : /*#__PURE__*/ _jsxs("section", {
76
+ "aria-labelledby": `${id}-title`,
77
+ children: [
78
+ /*#__PURE__*/ _jsx("h3", {
79
+ id: `${id}-title`,
80
+ children: C.title
81
+ }),
82
+ /*#__PURE__*/ _jsxs("div", {
83
+ className: "field-type text",
84
+ children: [
85
+ /*#__PURE__*/ _jsx(FieldLabel, {
86
+ path: namePath,
87
+ label: C.name
88
+ }),
89
+ /*#__PURE__*/ _jsx("input", {
90
+ id: `field-${namePath}`,
91
+ value: picker.panel.name,
92
+ disabled: picker.panel.pending,
93
+ onKeyDown: stopEnter,
94
+ onChange: (event)=>picker.setName(event.currentTarget.value)
95
+ })
96
+ ]
97
+ }),
98
+ /*#__PURE__*/ _jsxs("details", {
99
+ open: picker.panel.advanced,
100
+ onToggle: (event)=>picker.setAdvanced(event.currentTarget.open),
101
+ children: [
102
+ /*#__PURE__*/ _jsx("summary", {
103
+ children: C.advanced
104
+ }),
105
+ /*#__PURE__*/ _jsxs("div", {
106
+ className: "field-type text",
107
+ children: [
108
+ /*#__PURE__*/ _jsx(FieldLabel, {
109
+ path: slugPath,
110
+ label: C.slug
111
+ }),
112
+ /*#__PURE__*/ _jsx("input", {
113
+ id: `field-${slugPath}`,
114
+ value: picker.panel.slug,
115
+ disabled: picker.panel.pending,
116
+ onKeyDown: stopEnter,
117
+ "aria-invalid": Boolean(picker.panel.errors.slug) || undefined,
118
+ "aria-describedby": picker.panel.errors.slug ? `${id}-slug-error` : undefined,
119
+ onChange: (event)=>picker.setSlug(event.currentTarget.value)
120
+ }),
121
+ picker.panel.errors.slug ? /*#__PURE__*/ _jsx("p", {
122
+ id: `${id}-slug-error`,
123
+ role: "alert",
124
+ children: picker.panel.errors.slug
125
+ }) : null
126
+ ]
127
+ })
128
+ ]
129
+ }),
130
+ picker.panel.errors.general ? /*#__PURE__*/ _jsx("p", {
131
+ role: "alert",
132
+ children: picker.panel.errors.general
133
+ }) : null,
134
+ /*#__PURE__*/ _jsx(Button, {
135
+ type: "button",
136
+ disabled: picker.panel.pending || !picker.panel.name.trim(),
137
+ onClick: ()=>void picker.create(),
138
+ children: C.createGoal
139
+ }),
140
+ /*#__PURE__*/ _jsx(Button, {
141
+ type: "button",
142
+ disabled: picker.panel.pending,
143
+ onClick: picker.cancel,
144
+ children: C.cancel
145
+ })
146
+ ]
147
+ })
148
+ ]
149
+ }) : null
150
+ ]
151
+ });
152
+ }
@@ -5,3 +5,5 @@ export declare function metricSourceOptions(sources: readonly SerializedMetricSo
5
5
  badge: "count" | "yes/no" | "amount";
6
6
  }[];
7
7
  export declare function metricSourceHint(source?: SerializedMetricSource): string | null;
8
+ /** Click goal definitions share the serialized click-source option. */
9
+ export declare function sourceFor(sourceId: string | undefined, metricSources: readonly SerializedMetricSource[]): SerializedMetricSource | undefined;
@@ -9,3 +9,7 @@ export function metricSourceOptions(sources) {
9
9
  export function metricSourceHint(source) {
10
10
  return source?.kind === 'revenue' ? METRICS_COPY.revenueHint : null;
11
11
  }
12
+ /** Click goal definitions share the serialized click-source option. */ export function sourceFor(sourceId, metricSources) {
13
+ const id = sourceId?.startsWith('click:') ? 'click:' : sourceId;
14
+ return metricSources.find((source)=>source.id === id);
15
+ }
@@ -0,0 +1,10 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ import type { ExperimentsPageCollection } from '../types/contracts.js';
3
+ export declare function serializePageCollections(collections: ExperimentsPageCollection[], req: PayloadRequest): {
4
+ copyUrl: string;
5
+ copyLabel: string;
6
+ copyHint?: string | undefined;
7
+ slug: string;
8
+ label: string;
9
+ contentChecks: boolean;
10
+ }[];
@@ -0,0 +1,20 @@
1
+ import { formatAdminURL } from 'payload/shared';
2
+ import { collectionLabel } from '../collections/labels.js';
3
+ export function serializePageCollections(collections, req) {
4
+ return collections.map((entry)=>{
5
+ const label = collectionLabel(req.payload.config.collections.find((collection)=>collection.slug === entry.slug)?.labels.plural, entry.slug, req);
6
+ return {
7
+ slug: entry.slug,
8
+ label,
9
+ contentChecks: typeof entry.contentOf === 'function',
10
+ ...entry.copyHint ? {
11
+ copyHint: entry.copyHint
12
+ } : {},
13
+ copyUrl: entry.copyUrl ?? formatAdminURL({
14
+ adminRoute: req.payload.config.routes.admin,
15
+ path: `/collections/${entry.slug}`
16
+ }),
17
+ copyLabel: entry.copyLabel ?? `Open ${label}`
18
+ };
19
+ });
20
+ }
@@ -4,6 +4,13 @@ export declare const XP_ARM_SELECT = "@delmaredigital/payload-experiments/admin/
4
4
  export declare const XP_TESTS_VIEW = "@delmaredigital/payload-experiments/admin/rsc#ExperimentsRootView";
5
5
  export declare const XP_TESTS_LIST_REDIRECT = "@delmaredigital/payload-experiments/admin/rsc#CollectionListRedirect";
6
6
  export declare const XP_TEST_EDIT_REDIRECT = "@delmaredigital/payload-experiments/admin/rsc#CollectionEditRedirect";
7
- export declare const XP_TESTS_NAV_LINK = "@delmaredigital/payload-experiments/admin/client#TestsNavLink";
7
+ export declare const XP_TESTS_NAV_LINK = "@delmaredigital/payload-experiments/admin/rsc#TestsNavLinkServer";
8
+ export declare const XP_TESTS_NAV_CLIENT = "@delmaredigital/payload-experiments/admin/client#TestsNavLink";
8
9
  export declare const XP_TESTS_SURFACE = "@delmaredigital/payload-experiments/admin/client#TestsSurface";
9
10
  export declare const XP_TESTS_DASHBOARD_CARDS = "@delmaredigital/payload-experiments/admin/client#TestsDashboardCards";
11
+ export declare const XP_PLACEMENT_STATUS = "@delmaredigital/payload-experiments/admin/client#PlacementStatus";
12
+ export declare const XP_TEST_BLOCK = "@delmaredigital/payload-experiments/admin/client#TestBlockField";
13
+ export declare const XP_BLOCK_STATUS = "@delmaredigital/payload-experiments/admin/client#BlockStatusField";
14
+ export declare const XP_BLOCK_ROW_LABEL = "@delmaredigital/payload-experiments/admin/client#BlockSlotRowLabel";
15
+ export declare const XP_COPY_ORIGINAL = "@delmaredigital/payload-experiments/admin/client#CopyOriginalField";
16
+ export declare const XP_GOAL_FIELD = "@delmaredigital/payload-experiments/admin/client#XpGoalField";
@@ -4,6 +4,13 @@ export const XP_ARM_SELECT = '@delmaredigital/payload-experiments/admin/client#A
4
4
  export const XP_TESTS_VIEW = '@delmaredigital/payload-experiments/admin/rsc#ExperimentsRootView';
5
5
  export const XP_TESTS_LIST_REDIRECT = '@delmaredigital/payload-experiments/admin/rsc#CollectionListRedirect';
6
6
  export const XP_TEST_EDIT_REDIRECT = '@delmaredigital/payload-experiments/admin/rsc#CollectionEditRedirect';
7
- export const XP_TESTS_NAV_LINK = '@delmaredigital/payload-experiments/admin/client#TestsNavLink';
7
+ export const XP_TESTS_NAV_LINK = '@delmaredigital/payload-experiments/admin/rsc#TestsNavLinkServer';
8
+ export const XP_TESTS_NAV_CLIENT = '@delmaredigital/payload-experiments/admin/client#TestsNavLink';
8
9
  export const XP_TESTS_SURFACE = '@delmaredigital/payload-experiments/admin/client#TestsSurface';
9
10
  export const XP_TESTS_DASHBOARD_CARDS = '@delmaredigital/payload-experiments/admin/client#TestsDashboardCards';
11
+ export const XP_PLACEMENT_STATUS = '@delmaredigital/payload-experiments/admin/client#PlacementStatus';
12
+ export const XP_TEST_BLOCK = '@delmaredigital/payload-experiments/admin/client#TestBlockField';
13
+ export const XP_BLOCK_STATUS = '@delmaredigital/payload-experiments/admin/client#BlockStatusField';
14
+ export const XP_BLOCK_ROW_LABEL = '@delmaredigital/payload-experiments/admin/client#BlockSlotRowLabel';
15
+ export const XP_COPY_ORIGINAL = '@delmaredigital/payload-experiments/admin/client#CopyOriginalField';
16
+ export const XP_GOAL_FIELD = '@delmaredigital/payload-experiments/admin/client#XpGoalField';
@@ -0,0 +1,2 @@
1
+ import type { PageTestStatus } from '../types/endpoints.js';
2
+ export declare function pageStatusCopy(test: PageTestStatus): string | null;
@@ -0,0 +1,19 @@
1
+ export function pageStatusCopy(test) {
2
+ if (test.pagesReleased) return null;
3
+ const winner = `${test.winnerLabel ?? 'the winner'}${test.winnerTitle ? ` (${test.winnerTitle})` : ''}`;
4
+ switch(test.status){
5
+ case 'draft':
6
+ return `This page is ${test.armLabel} in the draft test ${test.name}.`;
7
+ case 'stopped':
8
+ {
9
+ const visitors = test.role === 'original' ? 'Visitors see this page.' : !test.originalPath ? 'Visitors see the Original.' : `Visitors to ${test.originalPath === '/' ? 'the homepage (/)' : test.originalPath} see the Original.`;
10
+ return `This page is ${test.armLabel} in the stopped test ${test.name}. ${visitors}`;
11
+ }
12
+ case 'running':
13
+ return `This page is the ${test.armLabel} of a running test: ${test.name}.`;
14
+ case 'decided':
15
+ return `This page is ${test.armLabel} of a decided test; ${test.rollout ? `most visitors see ${winner}; a holdout still sees the Original` : `visitors see ${winner}`}.`;
16
+ case 'archived':
17
+ return test.winner ? `This page is also shown at the other versions' addresses because test ${test.name} made it the winner. To free it, open the test and choose "Release pages".` : test.role === 'original' ? `This page's address now shows the winner of test ${test.name}. To free it, open the test and choose "Release pages".` : `This page's address now shows ${winner}. To show this page again, open the test and choose "Release pages".`;
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ import type { DocId } from '../../types/contracts.js';
2
+ import type { PreflightResult } from '../../types/endpoints.js';
3
+ export { placementDetailKey, placementDetail, fallbackPlacementLabel } from './placementLabels.model.js';
4
+ export type { PlacementLabel } from './placementLabels.model.js';
5
+ import type { PlacementLabel } from './placementLabels.model.js';
6
+ export declare function resolvedPlacementLabel(value: unknown, match: {
7
+ id?: DocId | null;
8
+ title?: string;
9
+ editorUrl?: string;
10
+ } | undefined, adminRoute: string): PlacementLabel;
11
+ export declare function preflightPlacementLabel(value: unknown, preflight: PreflightResult, adminRoute: string): PlacementLabel;
@@ -0,0 +1,21 @@
1
+ import { formatAdminURL } from 'payload/shared';
2
+ import { placementDetail } from './placementLabels.model.js';
3
+ export { placementDetailKey, placementDetail, fallbackPlacementLabel } from './placementLabels.model.js';
4
+ export function resolvedPlacementLabel(value, match, adminRoute) {
5
+ const detail = placementDetail(value);
6
+ const id = match?.id ?? detail.id;
7
+ return {
8
+ title: match?.title?.trim() || detail.path || 'This page',
9
+ ...id != null && detail.collection ? {
10
+ href: match?.editorUrl || formatAdminURL({
11
+ adminRoute,
12
+ path: `/collections/${encodeURIComponent(detail.collection)}/${encodeURIComponent(String(id))}`
13
+ })
14
+ } : {}
15
+ };
16
+ }
17
+ export function preflightPlacementLabel(value, preflight, adminRoute) {
18
+ const detail = placementDetail(value);
19
+ const match = detail.collection ? detail.id != null ? preflight.placements.find((item)=>item.collection === detail.collection && String(item.id) === String(detail.id)) : typeof detail.path === 'string' ? preflight.pagePlacements?.find((item)=>item.collection === detail.collection && item.path === detail.path) : undefined : undefined;
20
+ return resolvedPlacementLabel(value, match, adminRoute);
21
+ }
@@ -0,0 +1,13 @@
1
+ import type { DocId } from '../../types/contracts.js';
2
+ export type PlacementDetail = {
3
+ collection?: string;
4
+ id?: DocId;
5
+ path?: string;
6
+ };
7
+ export type PlacementLabel = {
8
+ title: string;
9
+ href?: string;
10
+ };
11
+ export declare const placementDetail: (detail: unknown) => PlacementDetail;
12
+ export declare function placementDetailKey(value: unknown): string;
13
+ export declare const fallbackPlacementLabel: (value: unknown) => PlacementLabel;
@@ -0,0 +1,12 @@
1
+ export const placementDetail = (detail)=>detail && typeof detail === 'object' && !Array.isArray(detail) ? detail : {};
2
+ export function placementDetailKey(value) {
3
+ const detail = placementDetail(value);
4
+ return JSON.stringify([
5
+ detail.collection,
6
+ detail.id == null ? null : String(detail.id),
7
+ detail.path
8
+ ]);
9
+ }
10
+ export const fallbackPlacementLabel = (value)=>({
11
+ title: placementDetail(value).path || 'This page'
12
+ });
@@ -0,0 +1,12 @@
1
+ import type { PreflightResult } from '../../types/endpoints.js';
2
+ import type { PlacementLabel } from './placementLabels.js';
3
+ export { placementDetailKey } from './placementLabels.js';
4
+ /** The view owns a cache, including misses, so repeated warnings never repeat reads. */
5
+ export declare function usePlacementLabels({ details, preflight, apiRoute, adminRoute, canManage, collectionTitles }: {
6
+ details: unknown[];
7
+ preflight?: PreflightResult | null;
8
+ apiRoute: string;
9
+ adminRoute: string;
10
+ canManage: boolean;
11
+ collectionTitles?: Record<string, string>;
12
+ }): Map<string, PlacementLabel>;