@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,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
|
|
5
|
-
import {
|
|
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(
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
201
|
-
|
|
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.
|
|
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 '
|
|
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(
|
|
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
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
if (!result
|
|
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.
|
|
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__*/
|
|
85
|
-
|
|
86
|
-
children:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
133
|
+
variant: item.tone === 'primary' || bar && item.id === 'stop' ? 'primary' : 'secondary',
|
|
109
134
|
disabled: disabled,
|
|
110
135
|
onClick: ()=>{
|
|
111
136
|
if (disabled) return;
|
|
@@ -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 '
|
|
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
|
|
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:
|
|
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
|
|
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;
|
|
@@ -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 {};
|