@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.
- package/README.md +202 -26
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +50 -21
- package/dist/admin/TestsNavLink.d.ts +3 -1
- package/dist/admin/TestsNavLink.js +2 -2
- package/dist/admin/actions/ConfirmDialog.d.ts +7 -4
- package/dist/admin/actions/ConfirmDialog.js +37 -82
- package/dist/admin/actions/DecideDialog.d.ts +3 -1
- package/dist/admin/actions/DecideDialog.js +30 -13
- package/dist/admin/actions/MakeContentResult.d.ts +13 -2
- package/dist/admin/actions/MakeContentResult.js +115 -16
- package/dist/admin/actions/actions.model.d.ts +2 -4
- package/dist/admin/actions/actions.model.js +2 -3
- package/dist/admin/allocation.model.js +3 -5
- package/dist/admin/api.d.ts +10 -4
- package/dist/admin/api.js +11 -7
- package/dist/admin/armSelect/ArmSelectField.js +14 -22
- package/dist/admin/arms/arms.model.d.ts +1 -21
- package/dist/admin/arms/arms.model.js +1 -153
- package/dist/admin/copy/actions.d.ts +39 -3
- package/dist/admin/copy/actions.js +40 -4
- package/dist/admin/copy/armSelect.js +5 -5
- package/dist/admin/copy/arms.d.ts +1 -6
- package/dist/admin/copy/arms.js +1 -16
- package/dist/admin/copy/audience.d.ts +2 -1
- package/dist/admin/copy/audience.js +3 -2
- package/dist/admin/copy/common.d.ts +13 -6
- package/dist/admin/copy/common.js +14 -8
- package/dist/admin/copy/goals.d.ts +21 -6
- package/dist/admin/copy/goals.js +27 -6
- package/dist/admin/copy/readiness.d.ts +22 -0
- package/dist/admin/copy/readiness.js +21 -0
- package/dist/admin/copy/recommendation.d.ts +0 -3
- package/dist/admin/copy/recommendation.js +0 -3
- package/dist/admin/copy/results.d.ts +43 -2
- package/dist/admin/copy/results.js +44 -3
- package/dist/admin/copy/review.d.ts +2 -2
- package/dist/admin/copy/review.js +2 -2
- package/dist/admin/copy/setting.d.ts +2 -0
- package/dist/admin/copy/setting.js +2 -0
- package/dist/admin/copy/surface.d.ts +7 -5
- package/dist/admin/copy/surface.js +9 -5
- package/dist/admin/copy/testPage.d.ts +10 -0
- package/dist/admin/copy/testPage.js +10 -0
- package/dist/admin/format/date.d.ts +2 -0
- package/dist/admin/format/date.js +10 -0
- package/dist/admin/paths.d.ts +2 -1
- package/dist/admin/paths.js +2 -1
- package/dist/admin/previewLinks.model.d.ts +4 -2
- package/dist/admin/previewLinks.model.js +8 -7
- package/dist/admin/results/CiChart.js +11 -21
- package/dist/admin/results/DataQualityPanel.d.ts +4 -2
- package/dist/admin/results/DataQualityPanel.js +30 -17
- package/dist/admin/results/DataTable.d.ts +16 -0
- package/dist/admin/results/DataTable.js +152 -0
- package/dist/admin/results/StatTile.d.ts +7 -0
- package/dist/admin/results/StatTile.js +22 -0
- package/dist/admin/results/VerdictHero.d.ts +13 -0
- package/dist/admin/results/{VerdictCard.js → VerdictHero.js} +15 -12
- package/dist/admin/results/legacy.d.ts +16 -0
- package/dist/admin/results/legacy.js +27 -0
- package/dist/admin/results/releasePages.model.d.ts +17 -0
- package/dist/admin/results/releasePages.model.js +10 -0
- package/dist/admin/results/results.css +64 -0
- package/dist/admin/results/results.model.d.ts +11 -7
- package/dist/admin/results/results.model.js +24 -7
- package/dist/admin/results/snapshot.d.ts +14 -0
- package/dist/admin/results/snapshot.js +3 -0
- package/dist/admin/surface/ResultsRoute.d.ts +1 -1
- package/dist/admin/surface/ResultsRoute.js +59 -34
- package/dist/admin/surface/SurfaceShell.d.ts +11 -2
- package/dist/admin/surface/SurfaceShell.js +49 -14
- package/dist/admin/surface/TestsHome.d.ts +6 -1
- package/dist/admin/surface/TestsHome.js +182 -114
- package/dist/admin/surface/TestsSurface.js +56 -16
- package/dist/admin/surface/goals/GoalForm.js +341 -308
- package/dist/admin/surface/goals/GoalsList.js +123 -89
- package/dist/admin/surface/goals/data.d.ts +8 -2
- package/dist/admin/surface/goals/data.js +24 -16
- package/dist/admin/surface/goals/goals.css +10 -13
- package/dist/admin/surface/home.model.d.ts +8 -8
- package/dist/admin/surface/home.model.js +23 -12
- package/dist/admin/surface/newTest/NewTest.js +9 -9
- package/dist/admin/surface/pageBlock/parts.js +31 -26
- package/dist/admin/surface/readiness/ActionDialogs.js +47 -19
- package/dist/admin/surface/readiness/Banner.d.ts +3 -0
- package/dist/admin/surface/readiness/Banner.js +58 -33
- package/dist/admin/surface/readiness/SaveStatus.d.ts +1 -0
- package/dist/admin/surface/readiness/SaveStatus.js +6 -3
- package/dist/admin/surface/readiness/readiness.model.d.ts +2 -0
- package/dist/admin/surface/readiness/readiness.model.js +25 -9
- package/dist/admin/surface/readiness/useTransition.d.ts +1 -0
- package/dist/admin/surface/readiness/useTransition.js +2 -1
- package/dist/admin/surface/results/ReleasePages.d.ts +14 -0
- package/dist/admin/surface/results/ReleasePages.js +116 -0
- package/dist/admin/surface/results/ResultsBody.d.ts +11 -1
- package/dist/admin/surface/results/ResultsBody.js +277 -105
- package/dist/admin/surface/results/ResultsHeader.d.ts +10 -0
- package/dist/admin/surface/results/ResultsHeader.js +177 -0
- package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
- package/dist/admin/surface/results/recommendation.model.js +12 -9
- package/dist/admin/surface/results/useResults.d.ts +21 -3
- package/dist/admin/surface/results/useResults.js +2 -1
- package/dist/admin/surface/setting/parts.js +75 -88
- package/dist/admin/surface/setting/setting.css +3 -15
- package/dist/admin/surface/surface.css +61 -47
- package/dist/admin/surface/test/AdvancedSection.js +55 -48
- package/dist/admin/surface/test/GoalSection.js +46 -47
- package/dist/admin/surface/test/LengthSection.js +46 -34
- package/dist/admin/surface/test/Rail.d.ts +4 -0
- package/dist/admin/surface/test/Rail.js +83 -0
- package/dist/admin/surface/test/ReadinessBar.d.ts +5 -0
- package/dist/admin/surface/test/ReadinessBar.js +33 -0
- package/dist/admin/surface/test/Section.d.ts +1 -1
- package/dist/admin/surface/test/Section.js +36 -16
- package/dist/admin/surface/test/TargetingSection.js +168 -142
- package/dist/admin/surface/test/TestPage.d.ts +2 -1
- package/dist/admin/surface/test/TestPage.js +146 -68
- package/dist/admin/surface/test/VersionsSection.js +151 -120
- package/dist/admin/surface/test/fields.d.ts +11 -4
- package/dist/admin/surface/test/fields.js +84 -85
- package/dist/admin/surface/test/jumpToSection.d.ts +3 -0
- package/dist/admin/surface/test/jumpToSection.js +10 -0
- package/dist/admin/surface/test/pageParts.js +9 -3
- package/dist/admin/surface/test/testPage.css +81 -14
- package/dist/admin/surface/test/testPage.model.d.ts +2 -3
- package/dist/admin/surface/test/testPage.model.js +1 -21
- package/dist/admin/surface/useTestDocument.js +12 -5
- package/dist/admin/targeting/targeting.model.d.ts +1 -3
- package/dist/admin/targeting/targeting.model.js +3 -8
- package/dist/admin/theme.css +115 -36
- package/dist/admin/theme.d.ts +38 -0
- package/dist/admin/theme.js +38 -1
- package/dist/admin/useApi.d.ts +17 -2
- package/dist/admin/useLayerUsage.js +14 -23
- package/dist/admin/views/ExperimentsRootView.js +12 -2
- package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
- package/dist/admin/views/TestsNavLinkServer.js +43 -0
- package/dist/arms/copy.d.ts +6 -0
- package/dist/arms/copy.js +16 -0
- package/dist/arms/model.d.ts +23 -0
- package/dist/arms/model.js +173 -0
- package/dist/collections/experiments/config.js +25 -0
- package/dist/collections/experiments/lockHook.js +3 -1
- package/dist/collections/identities.js +6 -1
- package/dist/collections/metrics.js +7 -0
- package/dist/collections/metricsGuards.d.ts +9 -0
- package/dist/collections/metricsGuards.js +33 -0
- package/dist/contentDone.d.ts +4 -0
- package/dist/contentDone.js +5 -0
- package/dist/db/appends.d.ts +6 -2
- package/dist/db/appends.js +10 -4
- package/dist/db/identities.d.ts +8 -7
- package/dist/db/identities.js +9 -6
- package/dist/db/locks.d.ts +6 -0
- package/dist/db/locks.js +13 -1
- package/dist/db/metricUsage.d.ts +19 -0
- package/dist/db/metricUsage.js +54 -0
- package/dist/db/names.d.ts +5 -0
- package/dist/db/names.js +5 -0
- package/dist/db/pageOccupancy.d.ts +12 -0
- package/dist/db/pageOccupancy.js +45 -0
- package/dist/db/publishedContent.d.ts +10 -0
- package/dist/db/publishedContent.js +43 -0
- package/dist/db/results.d.ts +2 -0
- package/dist/db/results.js +6 -2
- package/dist/db/snapshots.d.ts +18 -6
- package/dist/db/snapshots.js +25 -8
- package/dist/db/sweep.js +11 -6
- package/dist/endpoints/index.js +19 -1
- package/dist/endpoints/results.js +5 -0
- package/dist/endpoints/sanitizeReadinessDetail.d.ts +1 -0
- package/dist/endpoints/sanitizeReadinessDetail.js +2 -0
- package/dist/endpoints/summary.js +20 -2
- package/dist/endpoints/transitions.d.ts +15 -0
- package/dist/endpoints/transitions.js +36 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +28 -0
- package/dist/exports/adminRsc.d.ts +1 -0
- package/dist/exports/adminRsc.js +1 -0
- package/dist/exports/index.d.ts +2 -1
- package/dist/exports/index.js +1 -0
- package/dist/exports/puck.d.ts +2 -1
- package/dist/exports/puck.js +1 -1
- package/dist/exports/puckEditor.d.ts +4 -1
- package/dist/exports/puckEditor.js +3 -1
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +2 -0
- package/dist/hooks/pageGuard.d.ts +5 -0
- package/dist/hooks/pageGuard.js +139 -0
- package/dist/lib/fetchAllDocs.d.ts +16 -0
- package/dist/lib/fetchAllDocs.js +56 -0
- package/dist/log.js +6 -3
- package/dist/manifest/compile.d.ts +5 -0
- package/dist/manifest/compile.js +5 -1
- package/dist/options.js +16 -0
- package/dist/plugin.js +30 -2
- package/dist/puck/ExperimentPicker.js +47 -16
- package/dist/puck/ExperimentSlot.d.ts +51 -1
- package/dist/puck/ExperimentSlot.js +23 -12
- package/dist/puck/copy.d.ts +6 -5
- package/dist/puck/copy.js +6 -5
- package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
- package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
- package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
- package/dist/puck/editor/ResultsReadout.d.ts +6 -0
- package/dist/puck/editor/ResultsReadout.js +99 -0
- package/dist/puck/editor/VersionBuilder.d.ts +34 -0
- package/dist/puck/editor/VersionBuilder.js +317 -0
- package/dist/puck/editor/armWrites.d.ts +7 -0
- package/dist/puck/editor/armWrites.js +71 -0
- package/dist/puck/editor/armsCache.d.ts +14 -0
- package/dist/puck/editor/armsCache.js +78 -0
- package/dist/puck/editor/copy.d.ts +33 -1
- package/dist/puck/editor/copy.js +45 -8
- package/dist/puck/editor/createTest.d.ts +1 -1
- package/dist/puck/editor/createTest.js +25 -14
- package/dist/puck/editor/editor.chrome.css +121 -0
- package/dist/puck/editor/editor.css +288 -204
- package/dist/puck/editor/editor.model.d.ts +4 -1
- package/dist/puck/editor/editor.model.js +10 -7
- package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
- package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
- package/dist/puck/experimentSlotField.d.ts +2 -0
- package/dist/puck/experimentSlotField.js +11 -3
- package/dist/puck/hostContext.d.ts +10 -0
- package/dist/puck/hostContext.js +12 -0
- package/dist/puck/routes.d.ts +12 -0
- package/dist/puck/routes.js +11 -0
- package/dist/puck/slotConfig.d.ts +1 -1
- package/dist/puck/slotConfig.js +2 -2
- package/dist/results/aggregate.js +4 -3
- package/dist/results/analyse.d.ts +4 -1
- package/dist/results/analyse.js +27 -11
- package/dist/results/cuped.js +3 -2
- package/dist/results/identityCredit.d.ts +15 -7
- package/dist/results/identityCredit.js +32 -33
- package/dist/results/rolloutUnits.js +2 -1
- package/dist/results/snapshot.js +8 -2
- package/dist/results/srm.js +2 -1
- package/dist/sanitizeReadinessDetail.d.ts +7 -0
- package/dist/sanitizeReadinessDetail.js +63 -0
- package/dist/server/linkIdentity.d.ts +10 -1
- package/dist/server/linkIdentity.js +17 -15
- package/dist/server/metadata.d.ts +5 -0
- package/dist/server/metadata.js +18 -3
- package/dist/server/normaliseContentRelationships.d.ts +7 -0
- package/dist/server/normaliseContentRelationships.js +62 -0
- package/dist/server/recordContentChange.d.ts +1 -1
- package/dist/server/recordContentChange.js +17 -8
- package/dist/server/runResults.d.ts +3 -2
- package/dist/server/runResults.js +157 -35
- package/dist/targeting/values.d.ts +4 -0
- package/dist/targeting/values.js +10 -0
- package/dist/transitions/addMetric.js +8 -0
- package/dist/transitions/archive.js +2 -3
- package/dist/transitions/checkStart.d.ts +10 -3
- package/dist/transitions/checkStart.js +25 -0
- package/dist/transitions/contentFields.d.ts +7 -0
- package/dist/transitions/contentFields.js +20 -0
- package/dist/transitions/decide.js +22 -2
- package/dist/transitions/dismissContentEntry.d.ts +8 -0
- package/dist/transitions/dismissContentEntry.js +45 -0
- package/dist/transitions/makeContent.d.ts +5 -2
- package/dist/transitions/makeContent.js +217 -118
- package/dist/transitions/phaseRecord.d.ts +8 -2
- package/dist/transitions/placements.d.ts +3 -6
- package/dist/transitions/placements.js +12 -1
- package/dist/transitions/preflightData.d.ts +9 -1
- package/dist/transitions/preflightData.js +138 -20
- package/dist/transitions/releasePages.d.ts +8 -0
- package/dist/transitions/releasePages.js +26 -0
- package/dist/transitions/restoreContentEntry.d.ts +8 -0
- package/dist/transitions/restoreContentEntry.js +47 -0
- package/dist/transitions/start.js +62 -7
- package/dist/types/contracts.d.ts +24 -4
- package/dist/types/endpoints.d.ts +34 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/reasons.d.ts +3 -0
- package/dist/types/reasons.js +1 -0
- package/dist/types/snapshot.d.ts +6 -1
- package/dist/ui/Banner.d.ts +2 -0
- package/dist/ui/Banner.js +2 -0
- package/dist/ui/Breadcrumb.d.ts +7 -0
- package/dist/ui/Breadcrumb.js +25 -0
- package/dist/ui/Button.d.ts +15 -0
- package/dist/ui/Button.js +32 -0
- package/dist/ui/Card.d.ts +12 -0
- package/dist/ui/Card.js +40 -0
- package/dist/ui/Checkbox.d.ts +2 -0
- package/dist/ui/Checkbox.js +2 -0
- package/dist/{admin/components → ui}/ChipsInput.d.ts +4 -3
- package/dist/{admin/components → ui}/ChipsInput.js +20 -12
- package/dist/ui/Dialog.d.ts +13 -0
- package/dist/ui/Dialog.js +85 -0
- package/dist/ui/EmptyState.d.ts +2 -0
- package/dist/ui/EmptyState.js +2 -0
- package/dist/ui/Feedback.d.ts +24 -0
- package/dist/ui/Feedback.js +40 -0
- package/dist/ui/Inputs.d.ts +34 -0
- package/dist/ui/Inputs.js +225 -0
- package/dist/ui/NumberInput.d.ts +2 -0
- package/dist/ui/NumberInput.js +2 -0
- package/dist/ui/Range.d.ts +2 -0
- package/dist/ui/Range.js +2 -0
- package/dist/ui/SearchInput.d.ts +2 -0
- package/dist/ui/SearchInput.js +2 -0
- package/dist/ui/SectionCard.d.ts +2 -0
- package/dist/ui/SectionCard.js +2 -0
- package/dist/ui/Select.d.ts +2 -0
- package/dist/ui/Select.js +2 -0
- package/dist/ui/StatusChip.d.ts +2 -0
- package/dist/ui/StatusChip.js +2 -0
- package/dist/ui/Tabs.d.ts +16 -0
- package/dist/ui/Tabs.js +61 -0
- package/dist/ui/TextInput.d.ts +2 -0
- package/dist/ui/TextInput.js +2 -0
- package/dist/ui/Textarea.d.ts +2 -0
- package/dist/ui/Textarea.js +2 -0
- package/dist/ui/Toggle.d.ts +2 -0
- package/dist/ui/Toggle.js +2 -0
- package/dist/ui/accent.d.ts +40 -0
- package/dist/ui/accent.js +139 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/ui.css +92 -0
- package/package.json +5 -2
- package/dist/admin/components/chipsInput.css +0 -11
- package/dist/admin/results/ArmTable.d.ts +0 -8
- package/dist/admin/results/ArmTable.js +0 -113
- package/dist/admin/results/VerdictCard.d.ts +0 -9
- package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +0 -5
- package/dist/admin/surface/goals/GoalsBreadcrumb.js +0 -47
- package/dist/admin/surface/test/TestHeader.d.ts +0 -4
- package/dist/admin/surface/test/TestHeader.js +0 -70
|
@@ -1,99 +1,45 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import '
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Button, TextInput } from '@payloadcms/ui';
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
4
|
+
import { Button } from '../../ui/Button.js';
|
|
5
|
+
import { TextInput } from '../../ui/Inputs.js';
|
|
6
|
+
import { Dialog } from '../../ui/Dialog.js';
|
|
8
7
|
import { expectedConfirm, matchesConfirm } from '../../transitions/confirmPhrases.js';
|
|
9
8
|
import { useApi } from '../useApi.js';
|
|
10
9
|
import { reloadPage } from '../reload.js';
|
|
11
10
|
import { ACTIONS_COPY as C } from '../copy/actions.js';
|
|
12
11
|
import { API_MESSAGES } from '../copy/common.js';
|
|
13
12
|
import { MakeContentResult } from './MakeContentResult.js';
|
|
14
|
-
export function DialogFrame({ title, onClose, children }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}, []);
|
|
24
|
-
return /*#__PURE__*/ createPortal(/*#__PURE__*/ _jsx("div", {
|
|
25
|
-
style: {
|
|
26
|
-
position: 'fixed',
|
|
27
|
-
inset: 0,
|
|
28
|
-
zIndex: 1000,
|
|
29
|
-
background: theme.color.overlay,
|
|
30
|
-
display: 'grid',
|
|
31
|
-
placeItems: 'center'
|
|
32
|
-
},
|
|
33
|
-
children: /*#__PURE__*/ _jsxs("div", {
|
|
34
|
-
ref: ref,
|
|
35
|
-
role: "dialog",
|
|
36
|
-
"aria-modal": "true",
|
|
37
|
-
"aria-labelledby": heading,
|
|
38
|
-
tabIndex: -1,
|
|
39
|
-
style: {
|
|
40
|
-
background: theme.color.bg,
|
|
41
|
-
color: theme.color.text,
|
|
42
|
-
border: `1px solid ${theme.color.borderStrong}`,
|
|
43
|
-
padding: '2rem',
|
|
44
|
-
width: 'min(36rem, 90vw)',
|
|
45
|
-
maxHeight: '90vh',
|
|
46
|
-
overflow: 'auto',
|
|
47
|
-
borderRadius: theme.radius.small
|
|
48
|
-
},
|
|
49
|
-
onKeyDown: (event)=>{
|
|
50
|
-
if (event.defaultPrevented) return;
|
|
51
|
-
if (event.key === 'Escape') {
|
|
52
|
-
event.preventDefault();
|
|
53
|
-
event.stopPropagation();
|
|
54
|
-
onClose();
|
|
55
|
-
}
|
|
56
|
-
if (event.key === 'Tab') {
|
|
57
|
-
const items = Array.from(ref.current?.querySelectorAll('button:not(:disabled), input:not(:disabled), textarea:not(:disabled), select:not(:disabled), [tabindex="0"]') ?? []);
|
|
58
|
-
const first = items[0], last = items.at(-1);
|
|
59
|
-
if (!first) {
|
|
60
|
-
event.preventDefault();
|
|
61
|
-
ref.current?.focus();
|
|
62
|
-
} else if (event.shiftKey && (document.activeElement === first || document.activeElement === ref.current)) {
|
|
63
|
-
event.preventDefault();
|
|
64
|
-
last?.focus();
|
|
65
|
-
} else if (!event.shiftKey && (document.activeElement === last || document.activeElement === ref.current)) {
|
|
66
|
-
event.preventDefault();
|
|
67
|
-
first.focus();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
children: [
|
|
72
|
-
/*#__PURE__*/ _jsx("h2", {
|
|
73
|
-
id: heading,
|
|
74
|
-
children: title
|
|
75
|
-
}),
|
|
76
|
-
children
|
|
77
|
-
]
|
|
78
|
-
})
|
|
79
|
-
}), document.body);
|
|
13
|
+
export function DialogFrame({ title, onClose, children, initialFocus, dismissible = true }) {
|
|
14
|
+
return /*#__PURE__*/ _jsx(Dialog, {
|
|
15
|
+
open: true,
|
|
16
|
+
dismissible: dismissible,
|
|
17
|
+
initialFocus: initialFocus,
|
|
18
|
+
title: title,
|
|
19
|
+
onClose: onClose,
|
|
20
|
+
children: children
|
|
21
|
+
});
|
|
80
22
|
}
|
|
81
|
-
export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
23
|
+
export function ConfirmDialog({ id, action, winnerLabel, onClose, canManage = false, isDecided = false }) {
|
|
82
24
|
const api = useApi();
|
|
83
25
|
const [typed, setTyped] = useState('');
|
|
84
26
|
const [busy, setBusy] = useState(false);
|
|
27
|
+
const [reportBusy, setReportBusy] = useState(false);
|
|
85
28
|
const inFlight = useRef(false);
|
|
86
29
|
const [error, setError] = useState(null);
|
|
87
30
|
const [content, setContent] = useState(null);
|
|
88
31
|
const phrase = action.confirm.phrase ? expectedConfirm(action.confirm.phrase.action, winnerLabel) : null;
|
|
89
32
|
const enabled = !busy && (!phrase || matchesConfirm(phrase, typed));
|
|
90
|
-
async function submit() {
|
|
33
|
+
async function submit(finish = false) {
|
|
91
34
|
if (inFlight.current || !enabled) return;
|
|
92
35
|
inFlight.current = true;
|
|
93
36
|
setBusy(true);
|
|
94
37
|
setError(null);
|
|
95
38
|
let result;
|
|
96
|
-
if (
|
|
39
|
+
if (finish) result = await api.archive(id, {
|
|
40
|
+
mode: 'after-content'
|
|
41
|
+
});
|
|
42
|
+
else if (action.archive) result = await api.archive(id, {
|
|
97
43
|
...action.archive,
|
|
98
44
|
...phrase ? {
|
|
99
45
|
confirmation: typed.trim()
|
|
@@ -112,7 +58,7 @@ export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
|
112
58
|
}
|
|
113
59
|
result = response;
|
|
114
60
|
}
|
|
115
|
-
if (result.ok && action.id !== 'make-content') {
|
|
61
|
+
if (result.ok && (finish || action.id !== 'make-content')) {
|
|
116
62
|
reloadPage();
|
|
117
63
|
return;
|
|
118
64
|
}
|
|
@@ -126,18 +72,27 @@ export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
|
126
72
|
});
|
|
127
73
|
}
|
|
128
74
|
function close() {
|
|
129
|
-
if (busy) return;
|
|
75
|
+
if (busy || reportBusy) return;
|
|
130
76
|
if (content) reloadPage();
|
|
131
77
|
onClose();
|
|
132
78
|
}
|
|
133
79
|
return /*#__PURE__*/ _jsxs(DialogFrame, {
|
|
80
|
+
dismissible: !busy && !reportBusy,
|
|
81
|
+
initialFocus: action.id === 'start' ? undefined : 'cancel',
|
|
134
82
|
title: action.confirm.title,
|
|
135
83
|
onClose: close,
|
|
136
84
|
children: [
|
|
137
85
|
content ? /*#__PURE__*/ _jsx(MakeContentResult, {
|
|
138
86
|
result: content,
|
|
139
|
-
|
|
140
|
-
|
|
87
|
+
id: id,
|
|
88
|
+
api: api,
|
|
89
|
+
canManage: canManage,
|
|
90
|
+
isDecided: content.doc?.status ? content.doc.status === 'decided' : isDecided,
|
|
91
|
+
onRetry: ()=>submit(),
|
|
92
|
+
onFinish: ()=>submit(true),
|
|
93
|
+
busy: busy,
|
|
94
|
+
onBusyChange: setReportBusy,
|
|
95
|
+
onActionStart: ()=>setError(null)
|
|
141
96
|
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
142
97
|
children: [
|
|
143
98
|
/*#__PURE__*/ _jsx("p", {
|
|
@@ -155,8 +110,7 @@ export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
|
155
110
|
]
|
|
156
111
|
}),
|
|
157
112
|
/*#__PURE__*/ _jsx(TextInput, {
|
|
158
|
-
|
|
159
|
-
hasMany: false,
|
|
113
|
+
id: "xp-confirmation",
|
|
160
114
|
label: C.confirmation,
|
|
161
115
|
value: typed,
|
|
162
116
|
readOnly: busy,
|
|
@@ -177,7 +131,7 @@ export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
|
177
131
|
},
|
|
178
132
|
children: action.label
|
|
179
133
|
}),
|
|
180
|
-
error?.retryable ? /*#__PURE__*/ _jsx(Button, {
|
|
134
|
+
error?.retryable && !content ? /*#__PURE__*/ _jsx(Button, {
|
|
181
135
|
disabled: !enabled,
|
|
182
136
|
onClick: ()=>{
|
|
183
137
|
void submit();
|
|
@@ -185,7 +139,8 @@ export function ConfirmDialog({ id, action, winnerLabel, onClose }) {
|
|
|
185
139
|
children: C.retry
|
|
186
140
|
}) : null,
|
|
187
141
|
/*#__PURE__*/ _jsx(Button, {
|
|
188
|
-
|
|
142
|
+
"data-dialog-cancel": true,
|
|
143
|
+
disabled: busy || reportBusy,
|
|
189
144
|
onClick: close,
|
|
190
145
|
children: content ? C.done : C.cancel
|
|
191
146
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { DocId } from '../../types/contracts.js';
|
|
2
|
+
import type { PreflightResult } from '../../types/endpoints.js';
|
|
2
3
|
import type { ActionGroup } from './actions.model.js';
|
|
3
|
-
export declare function DecideDialog({ id, arms, onClose }: {
|
|
4
|
+
export declare function DecideDialog({ id, arms, checks, onClose }: {
|
|
4
5
|
id: DocId;
|
|
5
6
|
arms: readonly ActionGroup[];
|
|
7
|
+
checks?: PreflightResult['checks'];
|
|
6
8
|
onClose: () => void;
|
|
7
9
|
}): import("react").JSX.Element;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { Button
|
|
4
|
+
import { Button } from '../../ui/Button.js';
|
|
5
|
+
import { Select, Textarea, TextInput } from '../../ui/Inputs.js';
|
|
6
|
+
import { unsupportedContentCopy } from '../copy/readiness.js';
|
|
5
7
|
import { useApi } from '../useApi.js';
|
|
6
8
|
import { reloadPage } from '../reload.js';
|
|
9
|
+
import { RESULTS_COPY } from '../copy/results.js';
|
|
7
10
|
import { ACTIONS_COPY as C, decideCopy } from '../copy/actions.js';
|
|
8
11
|
import { decideState } from './decide.model.js';
|
|
9
12
|
import { DialogFrame } from './ConfirmDialog.js';
|
|
10
|
-
export function DecideDialog({ id, arms, onClose }) {
|
|
13
|
+
export function DecideDialog({ id, arms, checks = [], onClose }) {
|
|
11
14
|
const api = useApi();
|
|
12
15
|
const [snapshot, setSnapshot] = useState(null);
|
|
13
16
|
const [chosenArm, setChosenArm] = useState(null);
|
|
@@ -85,6 +88,8 @@ export function DecideDialog({ id, arms, onClose }) {
|
|
|
85
88
|
const enabled = !busy && !loading && !loadFailed && state.canSubmit;
|
|
86
89
|
const label = state.options.find((option)=>option.value === chosenArm)?.label;
|
|
87
90
|
return /*#__PURE__*/ _jsxs(DialogFrame, {
|
|
91
|
+
dismissible: !busy,
|
|
92
|
+
initialFocus: "cancel",
|
|
88
93
|
title: C.decide,
|
|
89
94
|
onClose: ()=>{
|
|
90
95
|
if (!busy) onClose();
|
|
@@ -94,27 +99,39 @@ export function DecideDialog({ id, arms, onClose }) {
|
|
|
94
99
|
role: "status",
|
|
95
100
|
children: C.loading
|
|
96
101
|
}) : null,
|
|
97
|
-
/*#__PURE__*/
|
|
98
|
-
|
|
102
|
+
/*#__PURE__*/ _jsxs(Select, {
|
|
103
|
+
id: "xp-winner",
|
|
99
104
|
name: "xp-winner",
|
|
100
105
|
label: C.group,
|
|
101
|
-
options: state.options,
|
|
102
106
|
value: chosenArm ?? '',
|
|
103
|
-
|
|
107
|
+
disabled: busy || loading,
|
|
104
108
|
required: true,
|
|
105
|
-
onChange: (
|
|
106
|
-
setChosenArm(
|
|
109
|
+
onChange: (event)=>{
|
|
110
|
+
setChosenArm(event.target.value || null);
|
|
107
111
|
setTyped('');
|
|
108
|
-
}
|
|
112
|
+
},
|
|
113
|
+
children: [
|
|
114
|
+
/*#__PURE__*/ _jsx("option", {
|
|
115
|
+
value: "",
|
|
116
|
+
children: RESULTS_COPY.chooseGroup
|
|
117
|
+
}),
|
|
118
|
+
state.options.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
119
|
+
value: option.value,
|
|
120
|
+
children: option.label
|
|
121
|
+
}, option.value))
|
|
122
|
+
]
|
|
109
123
|
}),
|
|
110
124
|
label ? /*#__PURE__*/ _jsx("p", {
|
|
111
125
|
children: decideCopy(label, arms.some((group)=>group.key === 'holdout'), chosenArm === 'control')
|
|
112
126
|
}) : null,
|
|
127
|
+
checks.filter((check)=>check.code === 'make-content-unsupported' && check.warning).map((check)=>/*#__PURE__*/ _jsx("p", {
|
|
128
|
+
children: unsupportedContentCopy(typeof check.detail === 'string' ? check.detail : 'This page')
|
|
129
|
+
}, check.code)),
|
|
113
130
|
state.reason ? /*#__PURE__*/ _jsx("p", {
|
|
114
131
|
children: state.reason
|
|
115
132
|
}) : null,
|
|
116
|
-
/*#__PURE__*/ _jsx(
|
|
117
|
-
|
|
133
|
+
/*#__PURE__*/ _jsx(Textarea, {
|
|
134
|
+
id: "xp-note",
|
|
118
135
|
label: C.note,
|
|
119
136
|
value: note,
|
|
120
137
|
required: state.guarded,
|
|
@@ -133,8 +150,7 @@ export function DecideDialog({ id, arms, onClose }) {
|
|
|
133
150
|
]
|
|
134
151
|
}),
|
|
135
152
|
/*#__PURE__*/ _jsx(TextInput, {
|
|
136
|
-
|
|
137
|
-
hasMany: false,
|
|
153
|
+
id: "xp-decide-confirmation",
|
|
138
154
|
label: C.confirmation,
|
|
139
155
|
value: typed,
|
|
140
156
|
readOnly: busy,
|
|
@@ -164,6 +180,7 @@ export function DecideDialog({ id, arms, onClose }) {
|
|
|
164
180
|
children: C.retry
|
|
165
181
|
}) : null,
|
|
166
182
|
/*#__PURE__*/ _jsx(Button, {
|
|
183
|
+
"data-dialog-cancel": true,
|
|
167
184
|
disabled: busy,
|
|
168
185
|
onClick: onClose,
|
|
169
186
|
children: C.cancel
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import type { DocId } from '../../types/contracts.js';
|
|
1
2
|
import type { MakeContentResult as Result } from '../../types/endpoints.js';
|
|
2
|
-
|
|
3
|
+
import type { createApi } from '../api.js';
|
|
4
|
+
type Props = {
|
|
3
5
|
result: Result;
|
|
4
6
|
onRetry: () => void | Promise<void>;
|
|
5
7
|
busy?: boolean;
|
|
6
|
-
|
|
8
|
+
id?: DocId;
|
|
9
|
+
api?: ReturnType<typeof createApi>;
|
|
10
|
+
canManage?: boolean;
|
|
11
|
+
isDecided?: boolean;
|
|
12
|
+
onFinish?: () => void | Promise<void>;
|
|
13
|
+
onBusyChange?: (busy: boolean) => void;
|
|
14
|
+
onActionStart?: () => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function MakeContentResult({ result, onRetry, busy, id, api, canManage, isDecided, onFinish, onBusyChange, onActionStart }: Props): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -1,49 +1,148 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useState } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
3
|
+
import { useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
+
import { contentDone } from '../../contentDone.js';
|
|
5
|
+
import { Button } from '../../ui/Button.js';
|
|
6
|
+
import { Banner } from '../../ui/Banner.js';
|
|
7
|
+
import { API_MESSAGES } from '../copy/common.js';
|
|
8
|
+
import { ACTIONS_COPY as C, contentReportCopy } from '../copy/actions.js';
|
|
9
|
+
export function MakeContentResult({ result, onRetry, busy = false, id, api, canManage = false, isDecided = false, onFinish, onBusyChange, onActionStart }) {
|
|
7
10
|
const inFlight = useRef(false);
|
|
11
|
+
const container = useRef(null);
|
|
12
|
+
const focusTarget = useRef(null);
|
|
8
13
|
const [pending, setPending] = useState(false);
|
|
9
|
-
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const [report, setReport] = useState({
|
|
16
|
+
source: result,
|
|
17
|
+
docs: result.docs
|
|
18
|
+
});
|
|
19
|
+
// A Retry supplies a new report. Entry actions use the authoritative returned doc.
|
|
20
|
+
if (report.source !== result) setReport({
|
|
21
|
+
source: result,
|
|
22
|
+
docs: result.docs
|
|
23
|
+
});
|
|
24
|
+
const docs = report.source === result ? report.docs : result.docs;
|
|
25
|
+
const editable = canManage && isDecided;
|
|
26
|
+
useLayoutEffect(()=>{
|
|
27
|
+
if (busy || pending || !focusTarget.current) return;
|
|
28
|
+
const target = focusTarget.current === 'finish' ? container.current?.querySelector('[data-finish-archiving]') : focusTarget.current;
|
|
29
|
+
target?.focus();
|
|
30
|
+
focusTarget.current = null;
|
|
31
|
+
}, [
|
|
32
|
+
docs,
|
|
33
|
+
busy,
|
|
34
|
+
pending
|
|
35
|
+
]);
|
|
36
|
+
async function run(action) {
|
|
10
37
|
if (inFlight.current || busy) return;
|
|
11
38
|
inFlight.current = true;
|
|
12
39
|
setPending(true);
|
|
40
|
+
setError(null);
|
|
41
|
+
onActionStart?.();
|
|
42
|
+
onBusyChange?.(true);
|
|
13
43
|
try {
|
|
14
|
-
await
|
|
44
|
+
await action();
|
|
45
|
+
} catch {
|
|
46
|
+
setError(API_MESSAGES.network);
|
|
15
47
|
} finally{
|
|
16
48
|
inFlight.current = false;
|
|
17
49
|
setPending(false);
|
|
50
|
+
onBusyChange?.(false);
|
|
18
51
|
}
|
|
19
52
|
}
|
|
20
|
-
|
|
53
|
+
async function changeEntry(entry, button) {
|
|
54
|
+
if (!editable || !api || id === undefined) return;
|
|
55
|
+
const response = await (entry.dismissedAt ? api.restoreContentEntry : api.dismissContentEntry)(id, {
|
|
56
|
+
collection: entry.collection,
|
|
57
|
+
id: entry.id
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
setError(response.message);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const decision = response.data?.doc.decision;
|
|
64
|
+
const updated = decision?.contentReport?.docs?.find((doc)=>doc.collection === entry.collection && String(doc.id) === String(entry.id));
|
|
65
|
+
if (!updated) {
|
|
66
|
+
setError(API_MESSAGES.unreadable);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const updatedDocs = docs.map((doc)=>doc.collection === entry.collection && String(doc.id) === String(entry.id) ? updated : doc);
|
|
70
|
+
focusTarget.current = !entry.dismissedAt && !contentDone(docs) && contentDone(updatedDocs) && onFinish ? 'finish' : button;
|
|
71
|
+
setReport((current)=>({
|
|
72
|
+
...current,
|
|
73
|
+
docs: updatedDocs
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
const retryable = docs.some((doc)=>!doc.ok && !doc.dismissedAt);
|
|
21
77
|
return /*#__PURE__*/ _jsxs("div", {
|
|
78
|
+
ref: container,
|
|
22
79
|
"aria-live": "polite",
|
|
23
80
|
children: [
|
|
81
|
+
error ? /*#__PURE__*/ _jsx(Banner, {
|
|
82
|
+
tone: "error",
|
|
83
|
+
children: error
|
|
84
|
+
}) : null,
|
|
85
|
+
result.doc?.status === 'archived' ? /*#__PURE__*/ _jsx("p", {
|
|
86
|
+
children: C.contentArchived
|
|
87
|
+
}) : null,
|
|
24
88
|
/*#__PURE__*/ _jsx("ul", {
|
|
25
|
-
children:
|
|
89
|
+
children: docs.map((doc, index)=>{
|
|
90
|
+
const label = 'title' in doc && typeof doc.title === 'string' && doc.title || `${doc.collection}/${doc.id}`;
|
|
91
|
+
return /*#__PURE__*/ _jsxs("li", {
|
|
26
92
|
children: [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
93
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
94
|
+
children: [
|
|
95
|
+
label,
|
|
96
|
+
": ",
|
|
97
|
+
doc.ok ? C.updated : doc.dismissedAt ? C.dismissed : C.failed,
|
|
98
|
+
!doc.dismissedAt && (doc.detail !== undefined || doc.error !== undefined || !doc.ok) ? ` — ${contentReportCopy(doc.detail ?? doc.error, doc.ok)}` : ''
|
|
99
|
+
]
|
|
100
|
+
}),
|
|
101
|
+
!doc.ok && editable && api && id !== undefined ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
102
|
+
children: [
|
|
103
|
+
doc.dismissedAt ? ' · ' : ' ',
|
|
104
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
105
|
+
variant: "quiet",
|
|
106
|
+
size: "sm",
|
|
107
|
+
"aria-disabled": busy || pending || undefined,
|
|
108
|
+
"aria-label": doc.dismissedAt ? C.undoLabel(label) : C.dismissLabel(label),
|
|
109
|
+
onClick: (event)=>{
|
|
110
|
+
const button = event.currentTarget;
|
|
111
|
+
void run(()=>changeEntry(doc, button));
|
|
112
|
+
},
|
|
113
|
+
children: doc.dismissedAt ? C.undo : C.dismiss
|
|
114
|
+
})
|
|
115
|
+
]
|
|
116
|
+
}) : null
|
|
31
117
|
]
|
|
32
|
-
}, `${doc.collection}/${doc.id}/${index}`)
|
|
118
|
+
}, `${doc.collection}/${doc.id}/${index}`);
|
|
119
|
+
})
|
|
33
120
|
}),
|
|
34
|
-
|
|
121
|
+
editable && retryable ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
35
122
|
children: [
|
|
123
|
+
/*#__PURE__*/ _jsx("p", {
|
|
124
|
+
children: C.dismissHelp
|
|
125
|
+
}),
|
|
36
126
|
/*#__PURE__*/ _jsx("p", {
|
|
37
127
|
children: C.partial
|
|
38
128
|
}),
|
|
39
129
|
/*#__PURE__*/ _jsx(Button, {
|
|
40
|
-
disabled: busy || pending,
|
|
130
|
+
"aria-disabled": busy || pending || undefined,
|
|
41
131
|
onClick: ()=>{
|
|
42
|
-
void
|
|
132
|
+
void run(onRetry);
|
|
43
133
|
},
|
|
44
134
|
children: C.retry
|
|
45
135
|
})
|
|
46
136
|
]
|
|
137
|
+
}) : null,
|
|
138
|
+
editable && contentDone(docs) && onFinish ? /*#__PURE__*/ _jsx(Button, {
|
|
139
|
+
"data-finish-archiving": true,
|
|
140
|
+
variant: "primary",
|
|
141
|
+
disabled: busy || pending,
|
|
142
|
+
onClick: ()=>{
|
|
143
|
+
void run(onFinish);
|
|
144
|
+
},
|
|
145
|
+
children: C.finish
|
|
47
146
|
}) : null
|
|
48
147
|
]
|
|
49
148
|
});
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { ExperimentKind, Status } from '../../types/contracts.js';
|
|
2
|
-
import type { ArchiveBody,
|
|
2
|
+
import type { ArchiveBody, ContentReport } from '../../types/endpoints.js';
|
|
3
3
|
export type ActionGroup = {
|
|
4
4
|
key: string;
|
|
5
5
|
label?: string;
|
|
6
6
|
kind?: string;
|
|
7
7
|
};
|
|
8
|
-
export type ContentReport
|
|
9
|
-
docs: MakeContentResult['docs'];
|
|
10
|
-
} | MakeContentResult['docs'];
|
|
8
|
+
export type { ContentReport } from '../../types/endpoints.js';
|
|
11
9
|
export type ActionDecision = {
|
|
12
10
|
winnerArm?: string | null;
|
|
13
11
|
holdoutReleasedAt?: string | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { contentDone } from '../../contentDone.js';
|
|
1
2
|
import { armLabel } from '../labels.js';
|
|
2
3
|
import { ACTIONS_COPY as C, hardCodedLabel, makeContentCopy, releaseCopy } from '../copy/actions.js';
|
|
3
4
|
import { REVIEW_COPY } from '../copy/review.js';
|
|
@@ -45,8 +46,6 @@ export function availableActions({ status, kind, arms, decision, canManage }) {
|
|
|
45
46
|
const winner = arms.find((group)=>group.key === decision?.winnerArm);
|
|
46
47
|
const label = actionGroupLabel(decision?.winnerArm ?? 'control', winner?.label);
|
|
47
48
|
const rolloutOpen = arms.some((group)=>group.key === 'holdout') && Boolean(decision?.winnerArm) && decision?.winnerArm !== 'control' && !decision?.holdoutReleasedAt;
|
|
48
|
-
const report = decision?.contentReport;
|
|
49
|
-
const docs = Array.isArray(report) ? report : report?.docs;
|
|
50
49
|
return [
|
|
51
50
|
...rolloutOpen ? [
|
|
52
51
|
action('release-holdout', C.release, releaseCopy(label))
|
|
@@ -54,7 +53,7 @@ export function availableActions({ status, kind, arms, decision, canManage }) {
|
|
|
54
53
|
...kind !== 'code' ? [
|
|
55
54
|
action('make-content', C.makeContent, makeContentCopy(label, kind), 'primary')
|
|
56
55
|
] : [],
|
|
57
|
-
...
|
|
56
|
+
...contentDone(decision?.contentReport) ? [
|
|
58
57
|
action('after-content', C.finish, C.finishBody, 'secondary', {
|
|
59
58
|
mode: 'after-content'
|
|
60
59
|
})
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { armLabel } from './labels.js';
|
|
2
|
-
import {
|
|
3
|
-
import { eachVersion, groupShare, holdoutClause, layerSentence, participation, peopleLabel, uncounted } from './copy/audience.js';
|
|
2
|
+
import { eachVersion, groupShare, holdoutClause, holdoutRemainder, layerSentence, participation, peopleLabel, uncounted } from './copy/audience.js';
|
|
4
3
|
const percent = (value)=>String(Math.round(value * 10) / 10);
|
|
5
4
|
export function allocationSentence({ allocation, unitType, arms, layer, status }) {
|
|
6
5
|
const groups = arms.filter((group)=>group.kind !== 'holdout');
|
|
@@ -8,10 +7,9 @@ export function allocationSentence({ allocation, unitType, arms, layer, status }
|
|
|
8
7
|
const equal = groups.every((group)=>group.weight === groups[0].weight);
|
|
9
8
|
const shares = equal ? eachVersion(percent(allocation * groups[0].weight / 100)) : groups.map((group)=>groupShare(percent(allocation * group.weight / 100), armLabel(group.key, group.label))).join(', ');
|
|
10
9
|
const holdout = arms.find((group)=>group.kind === 'holdout');
|
|
11
|
-
let sentence = participation(percent(allocation), peopleLabel[unitType], shares) + (holdout ? holdoutClause(percent(holdout.weight)) : uncounted);
|
|
12
|
-
if ((layer || status === 'running') && !sentence.endsWith('.')) sentence += '.';
|
|
10
|
+
let sentence = participation(percent(allocation), peopleLabel[unitType], shares) + (holdout ? holdoutClause(percent(holdout.weight)) : '') + (allocation < 100 ? holdout ? holdoutRemainder : uncounted : '');
|
|
11
|
+
if ((holdout || layer || status === 'running') && !sentence.endsWith('.')) sentence += '.';
|
|
13
12
|
if (layer) sentence += layerSentence(layer.name, layer.used, layer.free);
|
|
14
|
-
if (status === 'running') sentence += ` ${LOCKED_MESSAGES.runningAllocation}`;
|
|
15
13
|
return sentence;
|
|
16
14
|
}
|
|
17
15
|
// Payload arrays are flattened into row fields; their parent value is a row count.
|
package/dist/admin/api.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DocId } from '../types/contracts.js';
|
|
2
|
-
import type { AddMetricBody, AllocateBody, ArchiveBody, DecideBody, MakeContentResult, PreflightResult, PreviewTokenResult, RefusalCode, ResultsResponse, SummaryResponse, TransitionSuccess } from '../types/endpoints.js';
|
|
2
|
+
import type { RestoreContentEntryBody, RestoreContentEntryResult, DismissContentEntryBody, DismissContentEntryResult, AddMetricBody, AllocateBody, ArchiveBody, DecideBody, MakeContentResult, PreflightResult, PreviewTokenResult, RefusalCode, ResultsResponse, SummaryResponse, TransitionSuccess } from '../types/endpoints.js';
|
|
3
|
+
import { refusalMessage } from './copy/common.js';
|
|
3
4
|
export type ApiResult<T> = {
|
|
4
5
|
ok: true;
|
|
5
6
|
data: T | undefined;
|
|
@@ -10,23 +11,28 @@ export type ApiResult<T> = {
|
|
|
10
11
|
message: string;
|
|
11
12
|
retryable: boolean;
|
|
12
13
|
};
|
|
14
|
+
type RequestOptions = NonNullable<Parameters<typeof refusalMessage>[3]>;
|
|
13
15
|
export declare function createApi(apiBase: string): {
|
|
14
16
|
start: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
15
|
-
allocate: (id: DocId, body: AllocateBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
16
|
-
addMetric: (id: DocId, body: AddMetricBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
17
|
+
allocate: (id: DocId, body: AllocateBody, options?: RequestOptions) => Promise<ApiResult<TransitionSuccess>>;
|
|
18
|
+
addMetric: (id: DocId, body: AddMetricBody, options?: RequestOptions) => Promise<ApiResult<TransitionSuccess>>;
|
|
17
19
|
stop: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
18
20
|
decide: (id: DocId, body: DecideBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
19
21
|
releaseHoldout: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
22
|
+
releasePages: (id: DocId) => Promise<ApiResult<TransitionSuccess>>;
|
|
20
23
|
makeContent: (id: DocId) => Promise<ApiResult<MakeContentResult>>;
|
|
24
|
+
dismissContentEntry: (id: DocId, body: DismissContentEntryBody) => Promise<ApiResult<DismissContentEntryResult>>;
|
|
25
|
+
restoreContentEntry: (id: DocId, body: RestoreContentEntryBody) => Promise<ApiResult<RestoreContentEntryResult>>;
|
|
21
26
|
archive: (id: DocId, body: ArchiveBody) => Promise<ApiResult<TransitionSuccess>>;
|
|
22
27
|
preflight: (id: DocId) => Promise<ApiResult<PreflightResult>>;
|
|
23
28
|
previewToken: (id: DocId, arm: string) => Promise<ApiResult<PreviewTokenResult>>;
|
|
24
29
|
results: (id: DocId, options?: {
|
|
25
30
|
phase?: number;
|
|
26
31
|
series?: boolean;
|
|
27
|
-
}) => Promise<ApiResult<ResultsResponse>>;
|
|
32
|
+
} & RequestOptions) => Promise<ApiResult<ResultsResponse>>;
|
|
28
33
|
recompute: (id: DocId) => Promise<ApiResult<{
|
|
29
34
|
snapshotId: string;
|
|
30
35
|
}>>;
|
|
31
36
|
summary: (ids: readonly DocId[]) => Promise<ApiResult<SummaryResponse>>;
|
|
32
37
|
};
|
|
38
|
+
export {};
|
package/dist/admin/api.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { API_MESSAGES, refusalMessage } from './copy/common.js';
|
|
2
|
+
import { CONTENT_ENTRY_REFUSALS } from './copy/actions.js';
|
|
2
3
|
export function createApi(apiBase) {
|
|
3
4
|
const base = `${apiBase.replace(/\/$/, '')}/xp/experiments`;
|
|
4
5
|
const route = (id, action)=>`${base}/${encodeURIComponent(id)}/${action}`;
|
|
5
|
-
async function request(path, method, body) {
|
|
6
|
+
async function request(path, method, body, options) {
|
|
6
7
|
try {
|
|
7
8
|
const response = await fetch(path(), {
|
|
8
9
|
method,
|
|
@@ -28,7 +29,7 @@ export function createApi(apiBase) {
|
|
|
28
29
|
...response.status === 501 ? {
|
|
29
30
|
code: 'not-implemented'
|
|
30
31
|
} : {},
|
|
31
|
-
message: refusalMessage(undefined, response.status),
|
|
32
|
+
message: refusalMessage(undefined, response.status, undefined, options),
|
|
32
33
|
retryable: response.status >= 500 && response.status !== 501
|
|
33
34
|
};
|
|
34
35
|
}
|
|
@@ -46,7 +47,7 @@ export function createApi(apiBase) {
|
|
|
46
47
|
...code ? {
|
|
47
48
|
code
|
|
48
49
|
} : {},
|
|
49
|
-
message: refusalMessage(code, response.status, typeof error?.message === 'string' ? error.message : undefined),
|
|
50
|
+
message: refusalMessage(code, response.status, typeof error?.message === 'string' ? error.message : undefined, options),
|
|
50
51
|
retryable: code === 'commit-unverified' || response.status >= 500 && response.status !== 501
|
|
51
52
|
};
|
|
52
53
|
} catch {
|
|
@@ -58,15 +59,18 @@ export function createApi(apiBase) {
|
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
const post = (id, action, body)=>request(()=>route(id, action), 'POST', body);
|
|
62
|
+
const post = (id, action, body, options)=>request(()=>route(id, action), 'POST', body, options);
|
|
62
63
|
return {
|
|
63
64
|
start: (id)=>post(id, 'start'),
|
|
64
|
-
allocate: (id, body)=>post(id, 'allocate', body),
|
|
65
|
-
addMetric: (id, body)=>post(id, 'add-metric', body),
|
|
65
|
+
allocate: (id, body, options)=>post(id, 'allocate', body, options),
|
|
66
|
+
addMetric: (id, body, options)=>post(id, 'add-metric', body, options),
|
|
66
67
|
stop: (id)=>post(id, 'stop'),
|
|
67
68
|
decide: (id, body)=>post(id, 'decide', body),
|
|
68
69
|
releaseHoldout: (id)=>post(id, 'release-holdout'),
|
|
70
|
+
releasePages: (id)=>post(id, 'release-pages'),
|
|
69
71
|
makeContent: (id)=>post(id, 'make-content'),
|
|
72
|
+
dismissContentEntry: (id, body)=>post(id, 'content-report/dismiss', body, CONTENT_ENTRY_REFUSALS),
|
|
73
|
+
restoreContentEntry: (id, body)=>post(id, 'content-report/restore', body, CONTENT_ENTRY_REFUSALS),
|
|
70
74
|
archive: (id, body)=>post(id, 'archive', body),
|
|
71
75
|
preflight: (id)=>request(()=>route(id, 'preflight'), 'GET'),
|
|
72
76
|
previewToken: (id, arm)=>request(()=>`${route(id, 'preview-token')}?arm=${encodeURIComponent(arm)}`, 'GET'),
|
|
@@ -76,7 +80,7 @@ export function createApi(apiBase) {
|
|
|
76
80
|
if (options.series) query.set('series', '1');
|
|
77
81
|
const qs = query.toString();
|
|
78
82
|
return `${route(id, 'results')}${qs ? `?${qs}` : ''}`;
|
|
79
|
-
}, 'GET'),
|
|
83
|
+
}, 'GET', undefined, options),
|
|
80
84
|
recompute: (id)=>post(id, 'recompute'),
|
|
81
85
|
summary: async (ids)=>{
|
|
82
86
|
const data = {};
|