@delmaredigital/payload-experiments 0.2.0-rc.7 → 0.3.0-rc.4

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 (336) hide show
  1. package/README.md +202 -26
  2. package/dist/admin/PreviewLinks.d.ts +2 -1
  3. package/dist/admin/PreviewLinks.js +50 -21
  4. package/dist/admin/TestsNavLink.d.ts +3 -1
  5. package/dist/admin/TestsNavLink.js +2 -2
  6. package/dist/admin/actions/ConfirmDialog.d.ts +7 -4
  7. package/dist/admin/actions/ConfirmDialog.js +37 -82
  8. package/dist/admin/actions/DecideDialog.d.ts +3 -1
  9. package/dist/admin/actions/DecideDialog.js +30 -13
  10. package/dist/admin/actions/MakeContentResult.d.ts +13 -2
  11. package/dist/admin/actions/MakeContentResult.js +115 -16
  12. package/dist/admin/actions/actions.model.d.ts +2 -4
  13. package/dist/admin/actions/actions.model.js +2 -3
  14. package/dist/admin/allocation.model.js +3 -5
  15. package/dist/admin/api.d.ts +10 -4
  16. package/dist/admin/api.js +11 -7
  17. package/dist/admin/armSelect/ArmSelectField.js +14 -22
  18. package/dist/admin/arms/arms.model.d.ts +1 -21
  19. package/dist/admin/arms/arms.model.js +1 -153
  20. package/dist/admin/copy/actions.d.ts +39 -3
  21. package/dist/admin/copy/actions.js +40 -4
  22. package/dist/admin/copy/armSelect.js +5 -5
  23. package/dist/admin/copy/arms.d.ts +1 -6
  24. package/dist/admin/copy/arms.js +1 -16
  25. package/dist/admin/copy/audience.d.ts +2 -1
  26. package/dist/admin/copy/audience.js +3 -2
  27. package/dist/admin/copy/common.d.ts +13 -6
  28. package/dist/admin/copy/common.js +14 -8
  29. package/dist/admin/copy/goals.d.ts +21 -6
  30. package/dist/admin/copy/goals.js +27 -6
  31. package/dist/admin/copy/readiness.d.ts +22 -0
  32. package/dist/admin/copy/readiness.js +21 -0
  33. package/dist/admin/copy/recommendation.d.ts +0 -3
  34. package/dist/admin/copy/recommendation.js +0 -3
  35. package/dist/admin/copy/results.d.ts +43 -2
  36. package/dist/admin/copy/results.js +44 -3
  37. package/dist/admin/copy/review.d.ts +2 -2
  38. package/dist/admin/copy/review.js +2 -2
  39. package/dist/admin/copy/setting.d.ts +2 -0
  40. package/dist/admin/copy/setting.js +2 -0
  41. package/dist/admin/copy/surface.d.ts +7 -5
  42. package/dist/admin/copy/surface.js +9 -5
  43. package/dist/admin/copy/testPage.d.ts +10 -0
  44. package/dist/admin/copy/testPage.js +10 -0
  45. package/dist/admin/format/date.d.ts +2 -0
  46. package/dist/admin/format/date.js +10 -0
  47. package/dist/admin/paths.d.ts +2 -1
  48. package/dist/admin/paths.js +2 -1
  49. package/dist/admin/previewLinks.model.d.ts +4 -2
  50. package/dist/admin/previewLinks.model.js +8 -7
  51. package/dist/admin/results/CiChart.js +11 -21
  52. package/dist/admin/results/DataQualityPanel.d.ts +4 -2
  53. package/dist/admin/results/DataQualityPanel.js +30 -17
  54. package/dist/admin/results/DataTable.d.ts +16 -0
  55. package/dist/admin/results/DataTable.js +152 -0
  56. package/dist/admin/results/StatTile.d.ts +7 -0
  57. package/dist/admin/results/StatTile.js +22 -0
  58. package/dist/admin/results/VerdictHero.d.ts +13 -0
  59. package/dist/admin/results/{VerdictCard.js → VerdictHero.js} +15 -12
  60. package/dist/admin/results/legacy.d.ts +16 -0
  61. package/dist/admin/results/legacy.js +27 -0
  62. package/dist/admin/results/releasePages.model.d.ts +17 -0
  63. package/dist/admin/results/releasePages.model.js +10 -0
  64. package/dist/admin/results/results.css +64 -0
  65. package/dist/admin/results/results.model.d.ts +11 -7
  66. package/dist/admin/results/results.model.js +24 -7
  67. package/dist/admin/results/snapshot.d.ts +14 -0
  68. package/dist/admin/results/snapshot.js +3 -0
  69. package/dist/admin/surface/ResultsRoute.d.ts +1 -1
  70. package/dist/admin/surface/ResultsRoute.js +59 -34
  71. package/dist/admin/surface/SurfaceShell.d.ts +11 -2
  72. package/dist/admin/surface/SurfaceShell.js +49 -14
  73. package/dist/admin/surface/TestsHome.d.ts +6 -1
  74. package/dist/admin/surface/TestsHome.js +182 -114
  75. package/dist/admin/surface/TestsSurface.js +56 -16
  76. package/dist/admin/surface/goals/GoalForm.js +341 -308
  77. package/dist/admin/surface/goals/GoalsList.js +123 -89
  78. package/dist/admin/surface/goals/data.d.ts +8 -2
  79. package/dist/admin/surface/goals/data.js +24 -16
  80. package/dist/admin/surface/goals/goals.css +10 -13
  81. package/dist/admin/surface/home.model.d.ts +8 -8
  82. package/dist/admin/surface/home.model.js +23 -12
  83. package/dist/admin/surface/newTest/NewTest.js +9 -9
  84. package/dist/admin/surface/pageBlock/parts.js +31 -26
  85. package/dist/admin/surface/readiness/ActionDialogs.js +47 -19
  86. package/dist/admin/surface/readiness/Banner.d.ts +3 -0
  87. package/dist/admin/surface/readiness/Banner.js +58 -33
  88. package/dist/admin/surface/readiness/SaveStatus.d.ts +1 -0
  89. package/dist/admin/surface/readiness/SaveStatus.js +6 -3
  90. package/dist/admin/surface/readiness/readiness.model.d.ts +2 -0
  91. package/dist/admin/surface/readiness/readiness.model.js +25 -9
  92. package/dist/admin/surface/readiness/useTransition.d.ts +1 -0
  93. package/dist/admin/surface/readiness/useTransition.js +2 -1
  94. package/dist/admin/surface/results/ReleasePages.d.ts +14 -0
  95. package/dist/admin/surface/results/ReleasePages.js +116 -0
  96. package/dist/admin/surface/results/ResultsBody.d.ts +11 -1
  97. package/dist/admin/surface/results/ResultsBody.js +277 -105
  98. package/dist/admin/surface/results/ResultsHeader.d.ts +10 -0
  99. package/dist/admin/surface/results/ResultsHeader.js +177 -0
  100. package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
  101. package/dist/admin/surface/results/recommendation.model.js +12 -9
  102. package/dist/admin/surface/results/useResults.d.ts +21 -3
  103. package/dist/admin/surface/results/useResults.js +2 -1
  104. package/dist/admin/surface/setting/parts.js +75 -88
  105. package/dist/admin/surface/setting/setting.css +3 -15
  106. package/dist/admin/surface/surface.css +61 -47
  107. package/dist/admin/surface/test/AdvancedSection.js +55 -48
  108. package/dist/admin/surface/test/GoalSection.js +46 -47
  109. package/dist/admin/surface/test/LengthSection.js +46 -34
  110. package/dist/admin/surface/test/Rail.d.ts +4 -0
  111. package/dist/admin/surface/test/Rail.js +83 -0
  112. package/dist/admin/surface/test/ReadinessBar.d.ts +5 -0
  113. package/dist/admin/surface/test/ReadinessBar.js +33 -0
  114. package/dist/admin/surface/test/Section.d.ts +1 -1
  115. package/dist/admin/surface/test/Section.js +36 -16
  116. package/dist/admin/surface/test/TargetingSection.js +168 -142
  117. package/dist/admin/surface/test/TestPage.d.ts +2 -1
  118. package/dist/admin/surface/test/TestPage.js +146 -68
  119. package/dist/admin/surface/test/VersionsSection.js +151 -120
  120. package/dist/admin/surface/test/fields.d.ts +11 -4
  121. package/dist/admin/surface/test/fields.js +84 -85
  122. package/dist/admin/surface/test/jumpToSection.d.ts +3 -0
  123. package/dist/admin/surface/test/jumpToSection.js +10 -0
  124. package/dist/admin/surface/test/pageParts.js +9 -3
  125. package/dist/admin/surface/test/testPage.css +81 -14
  126. package/dist/admin/surface/test/testPage.model.d.ts +2 -3
  127. package/dist/admin/surface/test/testPage.model.js +1 -21
  128. package/dist/admin/surface/useTestDocument.js +12 -5
  129. package/dist/admin/targeting/targeting.model.d.ts +1 -3
  130. package/dist/admin/targeting/targeting.model.js +3 -8
  131. package/dist/admin/theme.css +115 -36
  132. package/dist/admin/theme.d.ts +38 -0
  133. package/dist/admin/theme.js +38 -1
  134. package/dist/admin/useApi.d.ts +17 -2
  135. package/dist/admin/useLayerUsage.js +14 -23
  136. package/dist/admin/views/ExperimentsRootView.js +12 -2
  137. package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
  138. package/dist/admin/views/TestsNavLinkServer.js +43 -0
  139. package/dist/arms/copy.d.ts +6 -0
  140. package/dist/arms/copy.js +16 -0
  141. package/dist/arms/model.d.ts +23 -0
  142. package/dist/arms/model.js +173 -0
  143. package/dist/collections/experiments/config.js +25 -0
  144. package/dist/collections/experiments/lockHook.js +3 -1
  145. package/dist/collections/identities.js +6 -1
  146. package/dist/collections/metrics.js +7 -0
  147. package/dist/collections/metricsGuards.d.ts +9 -0
  148. package/dist/collections/metricsGuards.js +33 -0
  149. package/dist/contentDone.d.ts +4 -0
  150. package/dist/contentDone.js +5 -0
  151. package/dist/db/appends.d.ts +6 -2
  152. package/dist/db/appends.js +10 -4
  153. package/dist/db/identities.d.ts +8 -7
  154. package/dist/db/identities.js +9 -6
  155. package/dist/db/locks.d.ts +6 -0
  156. package/dist/db/locks.js +13 -1
  157. package/dist/db/metricUsage.d.ts +19 -0
  158. package/dist/db/metricUsage.js +54 -0
  159. package/dist/db/names.d.ts +5 -0
  160. package/dist/db/names.js +5 -0
  161. package/dist/db/pageOccupancy.d.ts +12 -0
  162. package/dist/db/pageOccupancy.js +45 -0
  163. package/dist/db/publishedContent.d.ts +10 -0
  164. package/dist/db/publishedContent.js +43 -0
  165. package/dist/db/results.d.ts +2 -0
  166. package/dist/db/results.js +6 -2
  167. package/dist/db/snapshots.d.ts +18 -6
  168. package/dist/db/snapshots.js +25 -8
  169. package/dist/db/sweep.js +11 -6
  170. package/dist/endpoints/index.js +19 -1
  171. package/dist/endpoints/results.js +5 -0
  172. package/dist/endpoints/sanitizeReadinessDetail.d.ts +1 -0
  173. package/dist/endpoints/sanitizeReadinessDetail.js +2 -0
  174. package/dist/endpoints/summary.js +20 -2
  175. package/dist/endpoints/transitions.d.ts +15 -0
  176. package/dist/endpoints/transitions.js +36 -0
  177. package/dist/errors.d.ts +8 -0
  178. package/dist/errors.js +28 -0
  179. package/dist/exports/adminRsc.d.ts +1 -0
  180. package/dist/exports/adminRsc.js +1 -0
  181. package/dist/exports/index.d.ts +2 -1
  182. package/dist/exports/index.js +1 -0
  183. package/dist/exports/puck.d.ts +2 -1
  184. package/dist/exports/puck.js +1 -1
  185. package/dist/exports/puckEditor.d.ts +4 -1
  186. package/dist/exports/puckEditor.js +3 -1
  187. package/dist/exports/rsc.d.ts +6 -1
  188. package/dist/exports/rsc.js +2 -0
  189. package/dist/hooks/pageGuard.d.ts +5 -0
  190. package/dist/hooks/pageGuard.js +139 -0
  191. package/dist/lib/fetchAllDocs.d.ts +16 -0
  192. package/dist/lib/fetchAllDocs.js +56 -0
  193. package/dist/log.js +6 -3
  194. package/dist/manifest/compile.d.ts +5 -0
  195. package/dist/manifest/compile.js +5 -1
  196. package/dist/options.js +16 -0
  197. package/dist/plugin.js +30 -2
  198. package/dist/puck/ExperimentPicker.js +47 -16
  199. package/dist/puck/ExperimentSlot.d.ts +51 -1
  200. package/dist/puck/ExperimentSlot.js +23 -12
  201. package/dist/puck/copy.d.ts +6 -5
  202. package/dist/puck/copy.js +6 -5
  203. package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
  204. package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
  205. package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
  206. package/dist/puck/editor/ResultsReadout.d.ts +6 -0
  207. package/dist/puck/editor/ResultsReadout.js +99 -0
  208. package/dist/puck/editor/VersionBuilder.d.ts +34 -0
  209. package/dist/puck/editor/VersionBuilder.js +317 -0
  210. package/dist/puck/editor/armWrites.d.ts +7 -0
  211. package/dist/puck/editor/armWrites.js +71 -0
  212. package/dist/puck/editor/armsCache.d.ts +14 -0
  213. package/dist/puck/editor/armsCache.js +78 -0
  214. package/dist/puck/editor/copy.d.ts +33 -1
  215. package/dist/puck/editor/copy.js +45 -8
  216. package/dist/puck/editor/createTest.d.ts +1 -1
  217. package/dist/puck/editor/createTest.js +25 -14
  218. package/dist/puck/editor/editor.chrome.css +121 -0
  219. package/dist/puck/editor/editor.css +288 -204
  220. package/dist/puck/editor/editor.model.d.ts +4 -1
  221. package/dist/puck/editor/editor.model.js +10 -7
  222. package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
  223. package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
  224. package/dist/puck/experimentSlotField.d.ts +2 -0
  225. package/dist/puck/experimentSlotField.js +11 -3
  226. package/dist/puck/hostContext.d.ts +10 -0
  227. package/dist/puck/hostContext.js +12 -0
  228. package/dist/puck/routes.d.ts +12 -0
  229. package/dist/puck/routes.js +11 -0
  230. package/dist/puck/slotConfig.d.ts +1 -1
  231. package/dist/puck/slotConfig.js +2 -2
  232. package/dist/results/aggregate.js +4 -3
  233. package/dist/results/analyse.d.ts +4 -1
  234. package/dist/results/analyse.js +27 -11
  235. package/dist/results/cuped.js +3 -2
  236. package/dist/results/identityCredit.d.ts +15 -7
  237. package/dist/results/identityCredit.js +32 -33
  238. package/dist/results/rolloutUnits.js +2 -1
  239. package/dist/results/snapshot.js +8 -2
  240. package/dist/results/srm.js +2 -1
  241. package/dist/sanitizeReadinessDetail.d.ts +7 -0
  242. package/dist/sanitizeReadinessDetail.js +63 -0
  243. package/dist/server/linkIdentity.d.ts +10 -1
  244. package/dist/server/linkIdentity.js +17 -15
  245. package/dist/server/metadata.d.ts +5 -0
  246. package/dist/server/metadata.js +18 -3
  247. package/dist/server/normaliseContentRelationships.d.ts +7 -0
  248. package/dist/server/normaliseContentRelationships.js +62 -0
  249. package/dist/server/recordContentChange.d.ts +1 -1
  250. package/dist/server/recordContentChange.js +17 -8
  251. package/dist/server/runResults.d.ts +3 -2
  252. package/dist/server/runResults.js +157 -35
  253. package/dist/targeting/values.d.ts +4 -0
  254. package/dist/targeting/values.js +10 -0
  255. package/dist/transitions/addMetric.js +8 -0
  256. package/dist/transitions/archive.js +2 -3
  257. package/dist/transitions/checkStart.d.ts +10 -3
  258. package/dist/transitions/checkStart.js +25 -0
  259. package/dist/transitions/contentFields.d.ts +7 -0
  260. package/dist/transitions/contentFields.js +20 -0
  261. package/dist/transitions/decide.js +22 -2
  262. package/dist/transitions/dismissContentEntry.d.ts +8 -0
  263. package/dist/transitions/dismissContentEntry.js +45 -0
  264. package/dist/transitions/makeContent.d.ts +5 -2
  265. package/dist/transitions/makeContent.js +217 -118
  266. package/dist/transitions/phaseRecord.d.ts +8 -2
  267. package/dist/transitions/placements.d.ts +3 -6
  268. package/dist/transitions/placements.js +12 -1
  269. package/dist/transitions/preflightData.d.ts +9 -1
  270. package/dist/transitions/preflightData.js +138 -20
  271. package/dist/transitions/releasePages.d.ts +8 -0
  272. package/dist/transitions/releasePages.js +26 -0
  273. package/dist/transitions/restoreContentEntry.d.ts +8 -0
  274. package/dist/transitions/restoreContentEntry.js +47 -0
  275. package/dist/transitions/start.js +62 -7
  276. package/dist/types/contracts.d.ts +24 -4
  277. package/dist/types/endpoints.d.ts +34 -3
  278. package/dist/types/index.d.ts +1 -0
  279. package/dist/types/index.js +1 -0
  280. package/dist/types/reasons.d.ts +3 -0
  281. package/dist/types/reasons.js +1 -0
  282. package/dist/types/snapshot.d.ts +6 -1
  283. package/dist/ui/Banner.d.ts +2 -0
  284. package/dist/ui/Banner.js +2 -0
  285. package/dist/ui/Breadcrumb.d.ts +7 -0
  286. package/dist/ui/Breadcrumb.js +25 -0
  287. package/dist/ui/Button.d.ts +15 -0
  288. package/dist/ui/Button.js +32 -0
  289. package/dist/ui/Card.d.ts +12 -0
  290. package/dist/ui/Card.js +40 -0
  291. package/dist/ui/Checkbox.d.ts +2 -0
  292. package/dist/ui/Checkbox.js +2 -0
  293. package/dist/{admin/components → ui}/ChipsInput.d.ts +4 -3
  294. package/dist/{admin/components → ui}/ChipsInput.js +20 -12
  295. package/dist/ui/Dialog.d.ts +13 -0
  296. package/dist/ui/Dialog.js +85 -0
  297. package/dist/ui/EmptyState.d.ts +2 -0
  298. package/dist/ui/EmptyState.js +2 -0
  299. package/dist/ui/Feedback.d.ts +24 -0
  300. package/dist/ui/Feedback.js +40 -0
  301. package/dist/ui/Inputs.d.ts +34 -0
  302. package/dist/ui/Inputs.js +225 -0
  303. package/dist/ui/NumberInput.d.ts +2 -0
  304. package/dist/ui/NumberInput.js +2 -0
  305. package/dist/ui/Range.d.ts +2 -0
  306. package/dist/ui/Range.js +2 -0
  307. package/dist/ui/SearchInput.d.ts +2 -0
  308. package/dist/ui/SearchInput.js +2 -0
  309. package/dist/ui/SectionCard.d.ts +2 -0
  310. package/dist/ui/SectionCard.js +2 -0
  311. package/dist/ui/Select.d.ts +2 -0
  312. package/dist/ui/Select.js +2 -0
  313. package/dist/ui/StatusChip.d.ts +2 -0
  314. package/dist/ui/StatusChip.js +2 -0
  315. package/dist/ui/Tabs.d.ts +16 -0
  316. package/dist/ui/Tabs.js +61 -0
  317. package/dist/ui/TextInput.d.ts +2 -0
  318. package/dist/ui/TextInput.js +2 -0
  319. package/dist/ui/Textarea.d.ts +2 -0
  320. package/dist/ui/Textarea.js +2 -0
  321. package/dist/ui/Toggle.d.ts +2 -0
  322. package/dist/ui/Toggle.js +2 -0
  323. package/dist/ui/accent.d.ts +40 -0
  324. package/dist/ui/accent.js +139 -0
  325. package/dist/ui/index.d.ts +9 -0
  326. package/dist/ui/index.js +9 -0
  327. package/dist/ui/ui.css +92 -0
  328. package/package.json +5 -2
  329. package/dist/admin/components/chipsInput.css +0 -11
  330. package/dist/admin/results/ArmTable.d.ts +0 -8
  331. package/dist/admin/results/ArmTable.js +0 -113
  332. package/dist/admin/results/VerdictCard.d.ts +0 -9
  333. package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +0 -5
  334. package/dist/admin/surface/goals/GoalsBreadcrumb.js +0 -47
  335. package/dist/admin/surface/test/TestHeader.d.ts +0 -4
  336. package/dist/admin/surface/test/TestHeader.js +0 -70
@@ -1,8 +1,10 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { DocsResponseError, fetchAllDocs } from '../../../lib/fetchAllDocs.js';
3
4
  import { useEffect, useState } from 'react';
4
- import { Button, TextInput } from '@payloadcms/ui';
5
- import { DialogFrame } from '../../actions/ConfirmDialog.js';
5
+ import { Button } from '../../../ui/Button.js';
6
+ import { TextInput } from '../../../ui/Inputs.js';
7
+ import { Dialog } from '../../../ui/Dialog.js';
6
8
  import { MakeContentResult } from '../../actions/MakeContentResult.js';
7
9
  import { expectedConfirm, matchesConfirm } from '../../../transitions/confirmPhrases.js';
8
10
  import { ACTIONS_COPY as A } from '../../copy/actions.js';
@@ -15,14 +17,18 @@ import { useTransition } from './useTransition.js';
15
17
  const api = createApi(surface.apiRoute);
16
18
  const [typed, setTyped] = useState('');
17
19
  const [content, setContent] = useState(null);
20
+ const [reportBusy, setReportBusy] = useState(false);
18
21
  const transition = useTransition(test, onClose);
19
22
  const phrase = action.confirm.phrase ? expectedConfirm(action.confirm.phrase.action, winnerLabel) : null;
20
23
  const ready = action.id !== 'start' || Boolean(test.preflight && !test.preflightPending && !test.preflightError && test.preflight.checks.every((check)=>check.ok));
21
24
  const enabled = ready && !transition.busy && !test.hasUnsaved && test.saving !== 'saving' && (!phrase || matchesConfirm(phrase, typed));
22
- async function submit() {
25
+ async function submit(finish = false) {
23
26
  if (!enabled || !test.doc) return;
24
27
  const id = test.doc.id;
25
28
  await transition.run(async ()=>{
29
+ if (finish) return api.archive(id, {
30
+ mode: 'after-content'
31
+ });
26
32
  if (action.archive) return api.archive(id, {
27
33
  ...action.archive,
28
34
  ...phrase ? {
@@ -44,22 +50,32 @@ import { useTransition } from './useTransition.js';
44
50
  message: API_MESSAGES.unreadable
45
51
  } : result;
46
52
  }, {
47
- reloadOnSuccess: action.id !== 'make-content'
53
+ reloadOnSuccess: finish || action.id !== 'make-content'
48
54
  });
49
55
  }
50
56
  function close() {
51
- if (transition.busy) return;
57
+ if (transition.busy || reportBusy) return;
52
58
  if (content) void test.reload();
53
59
  onClose();
54
60
  }
55
- return /*#__PURE__*/ _jsxs(DialogFrame, {
61
+ return /*#__PURE__*/ _jsxs(Dialog, {
62
+ open: true,
63
+ initialFocus: action.id === 'start' ? undefined : 'cancel',
64
+ dismissible: !transition.busy && !reportBusy,
56
65
  title: action.confirm.title,
57
66
  onClose: close,
58
67
  children: [
59
68
  content ? /*#__PURE__*/ _jsx(MakeContentResult, {
60
69
  result: content,
61
- onRetry: submit,
62
- busy: transition.busy
70
+ id: test.doc?.id,
71
+ api: api,
72
+ canManage: surface.canManage,
73
+ isDecided: (content.doc?.status ?? test.doc?.status) === 'decided',
74
+ onRetry: ()=>submit(),
75
+ onFinish: ()=>submit(true),
76
+ busy: transition.busy,
77
+ onBusyChange: setReportBusy,
78
+ onActionStart: transition.clearError
63
79
  }) : /*#__PURE__*/ _jsxs(_Fragment, {
64
80
  children: [
65
81
  /*#__PURE__*/ _jsx("p", {
@@ -77,7 +93,7 @@ import { useTransition } from './useTransition.js';
77
93
  ]
78
94
  }),
79
95
  /*#__PURE__*/ _jsx(TextInput, {
80
- path: "xp-confirmation",
96
+ id: "xp-confirmation",
81
97
  label: A.confirmation,
82
98
  value: typed,
83
99
  readOnly: transition.busy,
@@ -99,7 +115,8 @@ import { useTransition } from './useTransition.js';
99
115
  children: action.label
100
116
  }) : null,
101
117
  /*#__PURE__*/ _jsx(Button, {
102
- disabled: transition.busy,
118
+ "data-dialog-cancel": true,
119
+ disabled: transition.busy || reportBusy,
103
120
  onClick: close,
104
121
  children: content ? A.done : A.cancel
105
122
  })
@@ -113,7 +130,9 @@ import { useTransition } from './useTransition.js';
113
130
  const allocation = Number(value);
114
131
  const valid = value.trim() !== '' && Number.isInteger(allocation) && allocation > current && allocation <= 100;
115
132
  const disabled = transition.busy || test.hasUnsaved || test.saving === 'saving';
116
- return /*#__PURE__*/ _jsxs(DialogFrame, {
133
+ return /*#__PURE__*/ _jsxs(Dialog, {
134
+ open: true,
135
+ dismissible: !transition.busy,
117
136
  title: C.allocate,
118
137
  onClose: ()=>{
119
138
  if (!transition.busy) onClose();
@@ -146,8 +165,11 @@ import { useTransition } from './useTransition.js';
146
165
  /*#__PURE__*/ _jsx(Button, {
147
166
  disabled: disabled || !valid,
148
167
  onClick: ()=>{
168
+ // Not-found can also mean the experiment is missing; use round copy for the missing phase record.
149
169
  if (valid && test.doc) void transition.run(()=>createApi(surface.apiRoute).allocate(test.doc.id, {
150
170
  allocation
171
+ }, {
172
+ notFound: API_MESSAGES.roundChanged
151
173
  }));
152
174
  },
153
175
  children: C.allocate
@@ -171,16 +193,14 @@ import { useTransition } from './useTransition.js';
171
193
  const controller = new AbortController();
172
194
  void (async ()=>{
173
195
  try {
174
- const response = await fetch(`${surface.apiRoute.replace(/\/$/, '')}/xp-metrics?depth=0&limit=100`, {
196
+ const response = await fetchAllDocs(`${surface.apiRoute.replace(/\/$/, '')}/xp-metrics?depth=0`, {
175
197
  credentials: 'include',
176
198
  signal: controller.signal
177
199
  });
178
200
  if (!response.ok) throw new Error(refusalMessage(undefined, response.status));
179
- const data = await response.json();
180
- if (!Array.isArray(data.docs)) throw new Error(API_MESSAGES.unreadable);
181
- if (!controller.signal.aborted) setMetrics(data.docs);
201
+ if (!controller.signal.aborted) setMetrics(response.docs);
182
202
  } catch (cause) {
183
- if (!controller.signal.aborted) setLoadError(cause instanceof Error ? cause.message : API_MESSAGES.network);
203
+ if (!controller.signal.aborted) setLoadError(cause instanceof DocsResponseError ? API_MESSAGES.unreadable : cause instanceof Error ? cause.message : API_MESSAGES.network);
184
204
  } finally{
185
205
  if (!controller.signal.aborted) setLoading(false);
186
206
  }
@@ -197,12 +217,17 @@ import { useTransition } from './useTransition.js';
197
217
  const available = metrics.filter((item)=>!attached.has(String(item.id)));
198
218
  const selected = available.find((item)=>String(item.id) === metric);
199
219
  const disabled = transition.busy || test.hasUnsaved || test.saving === 'saving';
200
- return /*#__PURE__*/ _jsxs(DialogFrame, {
201
- title: C.addMetric,
220
+ return /*#__PURE__*/ _jsxs(Dialog, {
221
+ open: true,
222
+ dismissible: !transition.busy,
223
+ title: C.addGoal,
202
224
  onClose: ()=>{
203
225
  if (!transition.busy) onClose();
204
226
  },
205
227
  children: [
228
+ /*#__PURE__*/ _jsx("p", {
229
+ children: C.addMetric
230
+ }),
206
231
  loading ? /*#__PURE__*/ _jsx("p", {
207
232
  role: "status",
208
233
  children: R.loadingMetrics
@@ -257,12 +282,15 @@ import { useTransition } from './useTransition.js';
257
282
  /*#__PURE__*/ _jsx(Button, {
258
283
  disabled: disabled || loading || !!loadError || !selected,
259
284
  onClick: ()=>{
285
+ // Goal and phase lookups share not-found; prefer the more likely missing goal without reading server text.
260
286
  if (selected && test.doc) void transition.run(()=>createApi(surface.apiRoute).addMetric(test.doc.id, {
261
287
  metric: selected.id,
262
288
  role
289
+ }, {
290
+ notFound: API_MESSAGES.goalNotFound
263
291
  }));
264
292
  },
265
- children: C.addMetric
293
+ children: C.addGoal
266
294
  }),
267
295
  /*#__PURE__*/ _jsx(Button, {
268
296
  disabled: transition.busy,
@@ -1,2 +1,5 @@
1
1
  import type { SectionProps } from '../contracts.js';
2
2
  export declare function Banner(props: SectionProps): import("react").JSX.Element | null;
3
+ export declare function ReadinessContent({ test, surface, resultsVersion, bar }: SectionProps & {
4
+ bar?: boolean;
5
+ }): import("react").JSX.Element;
@@ -1,12 +1,13 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { theme } from '../../theme.js';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
3
  import { useEffect, useState } from 'react';
5
- import { Button } from '@payloadcms/ui';
4
+ import { Button } from '../../../ui/Button.js';
5
+ import { SaveStatus, saveStatusMessage } from './SaveStatus.js';
6
+ import { jumpToSection } from '../test/jumpToSection.js';
6
7
  import { actionGroupLabel, availableActions } from '../../actions/actions.model.js';
7
8
  import { createApi } from '../../api.js';
8
9
  import { READINESS_COPY as C, decidedCopy, nextCopy, runningCopy, stoppedCopy } from '../../copy/readiness.js';
9
- import { daysRemaining, displayDate, nextStep } from './readiness.model.js';
10
+ import { READINESS, daysRemaining, displayDate, nextStep } from './readiness.model.js';
10
11
  import { AddMetricAction, AllocateAction, ConfirmAction } from './ActionDialogs.js';
11
12
  import { useTransition } from './useTransition.js';
12
13
  const RUNNING_ACTIONS = [
@@ -15,11 +16,11 @@ const RUNNING_ACTIONS = [
15
16
  'allocate'
16
17
  ];
17
18
  export function Banner(props) {
18
- return props.test.doc ? /*#__PURE__*/ _jsx(BannerContent, {
19
+ return props.test.doc ? /*#__PURE__*/ _jsx(ReadinessContent, {
19
20
  ...props
20
21
  }, `${props.test.doc.id}/${props.test.doc.status}`) : null;
21
22
  }
22
- function BannerContent({ test, surface, resultsVersion }) {
23
+ export function ReadinessContent({ test, surface, resultsVersion, bar = false }) {
23
24
  const [selected, setSelected] = useState(null);
24
25
  const [people, setPeople] = useState(null);
25
26
  const transition = useTransition(test);
@@ -28,10 +29,10 @@ function BannerContent({ test, surface, resultsVersion }) {
28
29
  let active = true;
29
30
  // The id/status key resets the count; refreshes retain it while loading.
30
31
  if (surface.canReadResults && doc.status === 'running') {
31
- void createApi(surface.apiRoute).summary([
32
- doc.id
33
- ]).then((result)=>{
34
- if (!result.ok) return;
32
+ void Promise.resolve().then(()=>active ? createApi(surface.apiRoute).summary([
33
+ doc.id
34
+ ]) : undefined).then((result)=>{
35
+ if (!result?.ok) return;
35
36
  const count = result.data?.[doc.id]?.exposures;
36
37
  if (active) setPeople(typeof count === 'number' && Number.isFinite(count) ? count : null);
37
38
  }).catch(()=>{});
@@ -64,48 +65,72 @@ function BannerContent({ test, surface, resultsVersion }) {
64
65
  canManage: surface.canManage
65
66
  }).filter((action)=>setup ? action.id === 'start' : doc.status === 'running' ? RUNNING_ACTIONS.includes(action.id) : true).sort((a, b)=>doc.status === 'running' ? RUNNING_ACTIONS.indexOf(a.id) - RUNNING_ACTIONS.indexOf(b.id) : 0).map((action)=>action.id === 'add-metric' ? {
66
67
  ...action,
67
- label: C.addMetric
68
+ label: C.addGoal
68
69
  } : action.id === 'allocate' ? {
69
70
  ...action,
70
71
  label: C.allocate
71
72
  } : action);
72
73
  const action = actions.find((item)=>item.id === selected);
73
- const sentence = setup ? ready ? C.ready : next ? nextCopy(next.sentence) : test.preflightError ? C.checkError : C.checking : doc.status === 'running' ? runningCopy(displayDate(doc.startedAt), people, daysRemaining(doc, Date.now())) : doc.status === 'decided' ? decidedCopy(displayDate(doc.decidedAt), winner) : C.archived;
74
+ const sentence = setup ? bar && test.preflightError ? null : ready ? C.ready : next ? nextCopy(next.sentence) : test.preflightError ? C.checkError : C.checking : doc.status === 'running' ? runningCopy(displayDate(doc.startedAt), people, daysRemaining(doc, Date.now())) : doc.status === 'decided' ? decidedCopy(displayDate(doc.decidedAt), winner) : C.archived;
74
75
  return /*#__PURE__*/ _jsxs("div", {
75
- className: `xp-readiness xp-readiness--${setup && ready ? 'ready' : 'neutral'}`,
76
- style: {
77
- padding: '1rem',
78
- border: `1px solid ${theme.color.borderStrong}`,
79
- borderRadius: theme.radius.small,
80
- background: setup && ready ? theme.color.successSurface : theme.color.surface,
81
- color: theme.color.text
82
- },
76
+ className: `xp-readiness xp-readiness--${setup && ready ? 'ready' : 'neutral'}${bar ? ' xp-readiness--bar xp-tests-container' : ''}`,
83
77
  children: [
84
- /*#__PURE__*/ _jsx("p", {
85
- role: "status",
86
- children: setup && next && !ready && next.section !== 'header' ? /*#__PURE__*/ _jsx("a", {
87
- href: `#section-${next.section}`,
88
- children: sentence
89
- }) : sentence
78
+ /*#__PURE__*/ _jsxs("div", {
79
+ className: "xp-readiness__state",
80
+ children: [
81
+ bar ? /*#__PURE__*/ _jsxs(_Fragment, {
82
+ children: [
83
+ /*#__PURE__*/ _jsx(SaveStatus, {
84
+ test: test
85
+ }),
86
+ sentence && (saveStatusMessage(test) || test.preflightError) ? /*#__PURE__*/ _jsx("span", {
87
+ className: "xp-readiness__separator",
88
+ "aria-hidden": "true",
89
+ children: "·"
90
+ }) : null
91
+ ]
92
+ }) : null,
93
+ sentence ? /*#__PURE__*/ _jsx("p", {
94
+ role: "status",
95
+ children: setup && next && !ready && next.section !== 'header' ? /*#__PURE__*/ _jsxs("a", {
96
+ href: `#section-${next.section}`,
97
+ onClick: (event)=>{
98
+ event.preventDefault();
99
+ if (next.section !== 'header') jumpToSection(next.section);
100
+ },
101
+ children: [
102
+ sentence,
103
+ next.nextStep ? /*#__PURE__*/ _jsxs(_Fragment, {
104
+ children: [
105
+ /*#__PURE__*/ _jsx("br", {}),
106
+ /*#__PURE__*/ _jsx("span", {
107
+ children: next.nextStep
108
+ })
109
+ ]
110
+ }) : null
111
+ ]
112
+ }) : sentence
113
+ }) : null
114
+ ]
90
115
  }),
116
+ setup && test.preflight?.checks.some((check)=>check.code === 'page-targets-unchecked' && check.warning) ? /*#__PURE__*/ _jsx("p", {
117
+ children: C.uncheckedPages
118
+ }) : null,
119
+ test.preflight?.checks.filter((check)=>check.code === 'make-content-unsupported' && check.warning).map((check)=>/*#__PURE__*/ _jsx("p", {
120
+ children: READINESS[check.code].next(check.detail, doc)
121
+ }, check.code)),
91
122
  doc.status === 'stopped' ? /*#__PURE__*/ _jsx("p", {
92
123
  children: stoppedCopy(displayDate(doc.stoppedAt))
93
124
  }) : null,
94
125
  /*#__PURE__*/ _jsx("div", {
95
126
  className: "xp-readiness__actions",
96
- style: {
97
- display: 'flex',
98
- gap: '0.75rem',
99
- flexWrap: 'wrap',
100
- alignItems: 'center'
101
- },
102
127
  children: actions.map((item)=>{
103
128
  const reason = item.id === 'start' ? startReason : unsaved ? C.saveFirst : item.id === 'allocate' && doc.allocation >= 100 ? C.everyone : undefined;
104
129
  const disabled = transition.busy || !!selected || !!reason;
105
130
  return /*#__PURE__*/ _jsx("span", {
106
131
  title: reason,
107
132
  children: /*#__PURE__*/ _jsx(Button, {
108
- buttonStyle: item.tone === 'primary' ? 'primary' : 'secondary',
133
+ variant: item.tone === 'primary' || bar && item.id === 'stop' ? 'primary' : 'secondary',
109
134
  disabled: disabled,
110
135
  onClick: ()=>{
111
136
  if (disabled) return;
@@ -1,4 +1,5 @@
1
1
  import type { TestDocApi } from '../contracts.js';
2
+ export declare function saveStatusMessage(test: TestDocApi): string | null;
2
3
  export declare function SaveStatus({ test }: {
3
4
  test: TestDocApi;
4
5
  }): import("react").JSX.Element;
@@ -1,8 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useRef, useState } from 'react';
4
- import { Button } from '@payloadcms/ui';
4
+ import { Button } from '../../../ui/Button.js';
5
5
  import { READINESS_COPY as C } from '../../copy/readiness.js';
6
+ export function saveStatusMessage(test) {
7
+ return test.saving === 'saving' ? C.saving : test.saving === 'saved' || test.saving === 'idle' ? C.saved : test.saving === 'error' && !test.stale ? C.saveError : null;
8
+ }
6
9
  export function SaveStatus({ test }) {
7
10
  const inFlight = useRef(false);
8
11
  const [retrying, setRetrying] = useState(false);
@@ -17,7 +20,7 @@ export function SaveStatus({ test }) {
17
20
  setRetrying(false);
18
21
  }
19
22
  }
20
- const message = test.saving === 'saving' ? C.saving : test.saving === 'saved' ? C.saved : test.saving === 'error' && !test.stale ? C.saveError : null;
23
+ const message = saveStatusMessage(test);
21
24
  return /*#__PURE__*/ _jsxs("div", {
22
25
  className: "xp-save-status",
23
26
  "aria-live": "polite",
@@ -30,7 +33,7 @@ export function SaveStatus({ test }) {
30
33
  children: [
31
34
  /*#__PURE__*/ _jsx("span", {
32
35
  role: "alert",
33
- children: C.checkError
36
+ children: test.preflightError
34
37
  }),
35
38
  ' ',
36
39
  /*#__PURE__*/ _jsx("span", {
@@ -9,8 +9,10 @@ export type ReadinessContext = Pick<TestDocument, 'arms' | 'unitType' | 'flagPoi
9
9
  export declare const READINESS: Record<ReadinessCode, {
10
10
  section: SectionId | 'header';
11
11
  next(detail: unknown, ctx: ReadinessContext): string | null;
12
+ nextStep?: string;
12
13
  }>;
13
14
  export declare function nextStep(preflight: PreflightResult | null, ctx: ReadinessContext): {
15
+ nextStep?: string | undefined;
14
16
  section: "header" | SectionId;
15
17
  sentence: string;
16
18
  code: ReadinessCode;
@@ -1,5 +1,6 @@
1
+ import { formatDate } from '../../format/date.js';
1
2
  import { actionGroupLabel } from '../../actions/actions.model.js';
2
- import { READINESS_COPY as C, attributeNext, contentNext, goalPeopleNext, parameterNext, placementNext, ruleNext, versionsNext } from '../../copy/readiness.js';
3
+ import { unsupportedContentCopy, versionPageUnavailableCopy, versionPageUncheckedCopy, placementFailureCopy, pageInUseCopy, unpublishedPageCopy, READINESS_COPY as C, attributeNext, contentNext, goalPeopleNext, parameterNext, placementNext, ruleNext, versionsNext } from '../../copy/readiness.js';
3
4
  import { SECTION_ORDER } from '../contracts.js';
4
5
  const object = (value)=>value && typeof value === 'object' && !Array.isArray(value) ? value : {};
5
6
  const first = (value)=>object(Array.isArray(value) ? value[0] : value);
@@ -15,8 +16,9 @@ const row = (section, next)=>({
15
16
  });
16
17
  const versions = row('versions', (detail)=>versionsNext(detailText(detail, C.versionFallback)));
17
18
  /** Exhaustive mapping of the checkStart ReadinessCode union, including all ten invariant checks. */ export const READINESS = {
19
+ 'make-content-unsupported': row('header', (detail)=>unsupportedContentCopy(text(detail, 'This page'))),
18
20
  'invalid-status': row('header', ()=>null),
19
- 'no-placements': row('what', ()=>C.noPlacements),
21
+ 'no-placements': row('what', (detail)=>detail === undefined ? C.noPlacements : placementFailureCopy(detail)),
20
22
  'empty-slot': row('versions', (detail, ctx)=>{
21
23
  const placements = Array.isArray(detail) ? detail : [];
22
24
  const missing = ctx.arms.find((item)=>item.kind !== 'holdout' && placements.some((placement)=>{
@@ -34,6 +36,7 @@ const versions = row('versions', (detail)=>versionsNext(detailText(detail, C.ver
34
36
  }),
35
37
  'flag-point-busy': row('what', ()=>C.busy),
36
38
  'flag-placement-missing': row('versions', (detail, ctx)=>{
39
+ if (typeof detail === 'string') return placementFailureCopy(detail);
37
40
  const value = object(detail);
38
41
  const placement = first(value.placements);
39
42
  const missing = Array.isArray(value.missing) ? value.missing[0] : undefined;
@@ -46,8 +49,23 @@ const versions = row('versions', (detail)=>versionsNext(detailText(detail, C.ver
46
49
  'no-reach-per-day': row('length', ()=>C.noReach),
47
50
  'layer-full': row('versions', ()=>C.full),
48
51
  'layer-unit-mismatch': row('header', ()=>C.differentPeople),
52
+ 'page-in-use': {
53
+ ...row('versions', (detail)=>{
54
+ const paths = object(detail).paths;
55
+ return pageInUseCopy(text(Array.isArray(paths) ? paths[0] : undefined, 'this page'));
56
+ }),
57
+ nextStep: C.chooseDifferentPage
58
+ },
49
59
  'page-no-targets': row('versions', ()=>C.noTargets),
60
+ 'page-target-unpublished': row('versions', (detail)=>unpublishedPageCopy(text(detail, 'This page'))),
61
+ 'page-targets-unchecked': row('versions', ()=>C.uncheckedPages),
62
+ 'version-page-unavailable': row('versions', (detail)=>versionPageUnavailableCopy(text(detail, 'This page'))),
63
+ 'version-page-unchecked': row('versions', (detail)=>versionPageUncheckedCopy(text(detail, 'This page'))),
50
64
  'page-user-unit': row('header', ()=>C.pageVisitors),
65
+ 'block-path-rule': {
66
+ ...row('who', ()=>C.blockPathRule),
67
+ nextStep: C.removeRule
68
+ },
51
69
  'invalid-targeting-rule': row('who', (detail)=>{
52
70
  const attributes = object(detail).attributes;
53
71
  return ruleNext(detailText(detail, Array.isArray(attributes) && attributes.length ? attributes.map(String).join(', ') : C.ruleFallback));
@@ -79,7 +97,10 @@ export function nextStep(preflight, ctx) {
79
97
  if (sentence) return {
80
98
  section,
81
99
  sentence,
82
- code: check.code
100
+ code: check.code,
101
+ ...mapping.nextStep ? {
102
+ nextStep: mapping.nextStep
103
+ } : {}
83
104
  };
84
105
  }
85
106
  }
@@ -93,12 +114,7 @@ export function sectionDone(test) {
93
114
  }
94
115
  export function displayDate(value) {
95
116
  if (typeof value !== 'string' || !Number.isFinite(Date.parse(value))) return null;
96
- return new Intl.DateTimeFormat('en-US', {
97
- year: 'numeric',
98
- month: 'short',
99
- day: 'numeric',
100
- timeZone: 'UTC'
101
- }).format(new Date(value));
117
+ return formatDate(value);
102
118
  }
103
119
  export function daysRemaining(doc, now) {
104
120
  const days = doc.analysis?.plannedDurationDays;
@@ -7,4 +7,5 @@ export declare function useTransition(test: TestDocApi, onSuccess?: () => void):
7
7
  run: (request: () => Promise<ApiResult<unknown>>, { reloadOnSuccess }?: {
8
8
  reloadOnSuccess?: boolean | undefined;
9
9
  }) => Promise<void>;
10
+ clearError: () => void;
10
11
  };
@@ -29,6 +29,7 @@ import { API_MESSAGES } from '../../copy/common.js';
29
29
  return {
30
30
  busy,
31
31
  error,
32
- run
32
+ run,
33
+ clearError: ()=>setError(null)
33
34
  };
34
35
  }
@@ -0,0 +1,14 @@
1
+ import type { DocId } from '../../../types/contracts.js';
2
+ import { type PageReleaseDecision } from '../../results/releasePages.model.js';
3
+ export declare function ReleasePagesDialog({ id, apiRoute, onClose, onSuccess, onRefusal }: {
4
+ id: DocId;
5
+ apiRoute: string;
6
+ onClose(): void;
7
+ onSuccess(): void;
8
+ onRefusal(message: string): void;
9
+ }): import("react").JSX.Element;
10
+ /** An inaccessible or deleted author never hides the recorded release date. */
11
+ export declare function PagesReleased({ decision, apiRoute }: {
12
+ decision: PageReleaseDecision;
13
+ apiRoute: string;
14
+ }): import("react").JSX.Element;
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { useAuth, useConfig } from '@payloadcms/ui';
5
+ import { Button } from '../../../ui/Button.js';
6
+ import { Dialog } from '../../../ui/Dialog.js';
7
+ import { createApi } from '../../api.js';
8
+ import { ACTIONS_COPY } from '../../copy/actions.js';
9
+ import { RESULTS_COPY as C, pagesReleasedCopy } from '../../copy/results.js';
10
+ import { formatDate } from '../../format/date.js';
11
+ import { pagesReleasedBy } from '../../results/releasePages.model.js';
12
+ export function ReleasePagesDialog({ id, apiRoute, onClose, onSuccess, onRefusal }) {
13
+ const [busy, setBusy] = useState(false);
14
+ const inFlight = useRef(false);
15
+ const mounted = useRef(true);
16
+ useEffect(()=>{
17
+ mounted.current = true;
18
+ return ()=>{
19
+ mounted.current = false;
20
+ };
21
+ }, []);
22
+ async function submit() {
23
+ if (inFlight.current) return;
24
+ inFlight.current = true;
25
+ setBusy(true);
26
+ const result = await createApi(apiRoute).releasePages(id);
27
+ if (!mounted.current) return;
28
+ onClose();
29
+ if (result.ok) onSuccess();
30
+ else onRefusal(result.message);
31
+ }
32
+ return /*#__PURE__*/ _jsxs(Dialog, {
33
+ open: true,
34
+ title: C.releasePages,
35
+ initialFocus: "cancel",
36
+ dismissible: !busy,
37
+ onClose: ()=>{
38
+ if (!inFlight.current) onClose();
39
+ },
40
+ children: [
41
+ /*#__PURE__*/ _jsx("p", {
42
+ children: C.releasePagesBody
43
+ }),
44
+ /*#__PURE__*/ _jsxs("div", {
45
+ className: "xp-ui-button-group",
46
+ children: [
47
+ /*#__PURE__*/ _jsx(Button, {
48
+ "data-dialog-cancel": true,
49
+ disabled: busy,
50
+ onClick: onClose,
51
+ children: ACTIONS_COPY.cancel
52
+ }),
53
+ /*#__PURE__*/ _jsx(Button, {
54
+ variant: "primary",
55
+ busy: busy,
56
+ onClick: ()=>{
57
+ void submit();
58
+ },
59
+ children: C.releasePages
60
+ })
61
+ ]
62
+ })
63
+ ]
64
+ });
65
+ }
66
+ /** An inaccessible or deleted author never hides the recorded release date. */ export function PagesReleased({ decision, apiRoute }) {
67
+ const { user } = useAuth();
68
+ const { config } = useConfig();
69
+ const collection = config.admin.user;
70
+ const by = decision.pagesReleasedBy;
71
+ const [resolved, setResolved] = useState(null);
72
+ const key = JSON.stringify([
73
+ apiRoute,
74
+ collection,
75
+ by
76
+ ]);
77
+ const own = pagesReleasedBy(by, user?.id);
78
+ useEffect(()=>{
79
+ if (!by || own || !collection) return;
80
+ const controller = new AbortController();
81
+ const query = new URLSearchParams({
82
+ depth: '0',
83
+ 'select[name]': 'true',
84
+ 'select[email]': 'true'
85
+ });
86
+ void (async ()=>{
87
+ try {
88
+ const response = await fetch(`${apiRoute.replace(/\/$/, '')}/${encodeURIComponent(collection)}/${encodeURIComponent(by)}?${query}`, {
89
+ method: 'GET',
90
+ credentials: 'include',
91
+ signal: controller.signal
92
+ });
93
+ if (!response.ok) return;
94
+ const author = await response.json();
95
+ if (!controller.signal.aborted && author) setResolved({
96
+ key,
97
+ author: {
98
+ name: typeof author.name === 'string' ? author.name : null,
99
+ email: typeof author.email === 'string' ? author.email : null
100
+ }
101
+ });
102
+ } catch {}
103
+ })();
104
+ return ()=>controller.abort();
105
+ }, [
106
+ apiRoute,
107
+ by,
108
+ collection,
109
+ key,
110
+ own
111
+ ]);
112
+ const name = own ?? pagesReleasedBy(by, user?.id, resolved?.key === key ? resolved.author : null);
113
+ return /*#__PURE__*/ _jsx("p", {
114
+ children: pagesReleasedCopy(formatDate(decision.pagesReleasedAt), name)
115
+ });
116
+ }
@@ -1,10 +1,20 @@
1
1
  import '../../styles.js';
2
+ import type { ReactNode } from 'react';
2
3
  import type { DocId } from '../../../types/contracts.js';
4
+ import type { ResultsResponse } from '../../../types/endpoints.js';
3
5
  import type { SurfaceProps } from '../contracts.js';
6
+ export type ResultsPageParts = {
7
+ children: ReactNode;
8
+ actions: ReactNode;
9
+ released: ReactNode;
10
+ data?: ResultsResponse;
11
+ };
4
12
  type ResultsBodyProps = {
13
+ renderPage?: (parts: ResultsPageParts) => ReactNode;
5
14
  id: DocId;
6
15
  surface: SurfaceProps;
7
16
  onRecomputed?: () => void;
17
+ onPagesReleased?: () => void;
8
18
  };
9
- export declare function ResultsBody({ id, surface, onRecomputed }: ResultsBodyProps): import("react").JSX.Element;
19
+ export declare function ResultsBody({ id, surface, onRecomputed, onPagesReleased, renderPage }: ResultsBodyProps): import("react").JSX.Element;
10
20
  export {};