@delmaredigital/payload-experiments 0.2.0-rc.3 → 0.2.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 (65) hide show
  1. package/README.md +15 -0
  2. package/dist/admin/TestsNavLink.d.ts +1 -0
  3. package/dist/admin/TestsNavLink.js +1 -0
  4. package/dist/admin/actions/ConfirmDialog.d.ts +1 -0
  5. package/dist/admin/actions/ConfirmDialog.js +7 -5
  6. package/dist/admin/components/ChipsInput.d.ts +12 -0
  7. package/dist/admin/components/ChipsInput.js +96 -0
  8. package/dist/admin/components/RootViewMessage.d.ts +5 -0
  9. package/dist/admin/components/RootViewMessage.js +15 -0
  10. package/dist/admin/components/chipsInput.css +11 -0
  11. package/dist/admin/copy/audience.d.ts +4 -0
  12. package/dist/admin/copy/audience.js +4 -0
  13. package/dist/admin/copy/goals.d.ts +44 -0
  14. package/dist/admin/copy/goals.js +46 -0
  15. package/dist/admin/results/ArmTable.js +2 -1
  16. package/dist/admin/results/CappedCell.js +2 -1
  17. package/dist/admin/results/CiChart.js +8 -7
  18. package/dist/admin/results/DataQualityPanel.js +3 -2
  19. package/dist/admin/results/VerdictCard.js +3 -2
  20. package/dist/admin/styles.css +8 -8
  21. package/dist/admin/styles.d.ts +1 -0
  22. package/dist/admin/styles.js +1 -0
  23. package/dist/admin/surface/SurfaceShell.d.ts +1 -0
  24. package/dist/admin/surface/SurfaceShell.js +1 -0
  25. package/dist/admin/surface/TestsHome.js +1 -1
  26. package/dist/admin/surface/TestsSurface.js +10 -1
  27. package/dist/admin/surface/goals/GoalForm.d.ts +7 -0
  28. package/dist/admin/surface/goals/GoalForm.js +470 -0
  29. package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +5 -0
  30. package/dist/admin/surface/goals/GoalsBreadcrumb.js +47 -0
  31. package/dist/admin/surface/goals/GoalsList.d.ts +6 -0
  32. package/dist/admin/surface/goals/GoalsList.js +135 -0
  33. package/dist/admin/surface/goals/data.d.ts +3 -0
  34. package/dist/admin/surface/goals/data.js +24 -0
  35. package/dist/admin/surface/goals/goals.css +15 -0
  36. package/dist/admin/surface/goals/goals.model.d.ts +30 -0
  37. package/dist/admin/surface/goals/goals.model.js +26 -0
  38. package/dist/admin/surface/newTest/newTest.css +6 -6
  39. package/dist/admin/surface/readiness/Banner.js +5 -4
  40. package/dist/admin/surface/results/ResultsBody.js +3 -2
  41. package/dist/admin/surface/setting/setting.css +11 -11
  42. package/dist/admin/surface/surface.css +29 -29
  43. package/dist/admin/surface/test/GoalSection.js +32 -1
  44. package/dist/admin/surface/test/TargetingSection.js +27 -10
  45. package/dist/admin/surface/test/VersionsSection.js +6 -5
  46. package/dist/admin/surface/test/testPage.css +12 -12
  47. package/dist/admin/targeting/targeting.model.d.ts +6 -1
  48. package/dist/admin/targeting/targeting.model.js +18 -2
  49. package/dist/admin/theme.css +42 -0
  50. package/dist/admin/theme.d.ts +50 -0
  51. package/dist/admin/theme.js +49 -0
  52. package/dist/admin/views/ExperimentsRootView.js +18 -18
  53. package/dist/collections/metricValidation.d.ts +3 -0
  54. package/dist/collections/metricValidation.js +9 -0
  55. package/dist/collections/metrics.js +3 -5
  56. package/dist/plugin.js +2 -2
  57. package/dist/puck/editor/CreateExperimentDrawer.d.ts +1 -0
  58. package/dist/puck/editor/CreateExperimentDrawer.js +34 -25
  59. package/dist/puck/editor/ExperimentSlotEditor.d.ts +1 -0
  60. package/dist/puck/editor/ExperimentSlotEditor.js +137 -78
  61. package/dist/puck/editor/copy.d.ts +3 -1
  62. package/dist/puck/editor/copy.js +3 -1
  63. package/dist/puck/editor/editor.css +302 -0
  64. package/dist/types/surface.d.ts +2 -1
  65. package/package.json +2 -1
package/README.md CHANGED
@@ -530,6 +530,21 @@ components: { ExperimentSlot }
530
530
  components: { ExperimentSlot: ExperimentSlotEditor }
531
531
  ```
532
532
 
533
+ Load the editor stylesheet inside the Puck iframe. With Tailwind v4, add the
534
+ package import **after** Tailwind in the CSS that builds your editor iframe
535
+ stylesheet (the stylesheet listed in payload-puck's `editorStylesheets`):
536
+
537
+ ```css
538
+ @import "tailwindcss";
539
+ @import "@delmaredigital/payload-experiments/puck/editor.css";
540
+ ```
541
+
542
+ Alternatively, serve the exported `puck/editor.css` file from a public URL and
543
+ append that URL to payload-puck's `editorStylesheets`, after the host stylesheet.
544
+ The editor CSS includes its own light and dark palette, selected by
545
+ `data-theme="dark"` or the `dark` class on the iframe's `<html>`. The client component's CSS import alone
546
+ does not reach an iframe configured with `syncHostStyles: false`.
547
+
533
548
  Resolve the Puck data on the server with `resolveExperimentData` before
534
549
  rendering it. The render component shows only the selected group's slot. The
535
550
  editor lets you select a block experiment, edit each group's content, copy the
@@ -1 +1,2 @@
1
+ import './theme.css';
1
2
  export declare function TestsNavLink(): import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import './theme.css';
3
4
  import { Link, useConfig } from '@payloadcms/ui';
4
5
  import { formatAdminURL } from 'payload/shared';
5
6
  import { usePathname } from 'next/navigation.js';
@@ -1,3 +1,4 @@
1
+ import '../theme.css';
1
2
  import type { ReactNode } from 'react';
2
3
  import type { DocId } from '../../types/contracts.js';
3
4
  import type { ExperimentAction } from './actions.model.js';
@@ -1,5 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import '../theme.css';
4
+ import { theme } from '../theme.js';
3
5
  import { useEffect, useId, useRef, useState } from 'react';
4
6
  import { createPortal } from 'react-dom';
5
7
  import { Button, TextInput } from '@payloadcms/ui';
@@ -24,7 +26,7 @@ export function DialogFrame({ title, onClose, children }) {
24
26
  position: 'fixed',
25
27
  inset: 0,
26
28
  zIndex: 1000,
27
- background: 'var(--theme-overlay)',
29
+ background: theme.color.overlay,
28
30
  display: 'grid',
29
31
  placeItems: 'center'
30
32
  },
@@ -35,14 +37,14 @@ export function DialogFrame({ title, onClose, children }) {
35
37
  "aria-labelledby": heading,
36
38
  tabIndex: -1,
37
39
  style: {
38
- background: 'var(--theme-bg)',
39
- color: 'var(--theme-text)',
40
- border: '1px solid var(--theme-elevation-200)',
40
+ background: theme.color.bg,
41
+ color: theme.color.text,
42
+ border: `1px solid ${theme.color.borderStrong}`,
41
43
  padding: '2rem',
42
44
  width: 'min(36rem, 90vw)',
43
45
  maxHeight: '90vh',
44
46
  overflow: 'auto',
45
- borderRadius: 'var(--style-radius-s)'
47
+ borderRadius: theme.radius.small
46
48
  },
47
49
  onKeyDown: (event)=>{
48
50
  if (event.defaultPrevented) return;
@@ -0,0 +1,12 @@
1
+ import '../theme.css';
2
+ import './chipsInput.css';
3
+ export interface ChipsInputProps {
4
+ value: string[];
5
+ onChange: (value: string[]) => void;
6
+ label: string;
7
+ description?: string;
8
+ placeholder?: string;
9
+ disabled?: boolean;
10
+ id: string;
11
+ }
12
+ export declare function ChipsInput({ value, onChange, label, description, placeholder, disabled, id }: ChipsInputProps): import("react").JSX.Element;
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState } from 'react';
4
+ import { AUDIENCE_COPY } from '../copy/audience.js';
5
+ import '../theme.css';
6
+ import './chipsInput.css';
7
+ export function ChipsInput({ value, onChange, label, description, placeholder, disabled = false, id }) {
8
+ const [draft, setDraft] = useState('');
9
+ const input = useRef(null);
10
+ const commit = (text)=>{
11
+ if (disabled) return;
12
+ const next = [
13
+ ...new Set([
14
+ ...value,
15
+ ...text.split(',')
16
+ ].map((item)=>item.trim()).filter(Boolean))
17
+ ];
18
+ if (next.length !== value.length || next.some((item, index)=>item !== value[index])) onChange(next);
19
+ setDraft('');
20
+ };
21
+ const remove = (index)=>{
22
+ if (disabled) return;
23
+ onChange(value.filter((_, i)=>i !== index));
24
+ input.current?.focus();
25
+ };
26
+ return /*#__PURE__*/ _jsxs("div", {
27
+ className: "xp-chips",
28
+ role: "group",
29
+ "aria-labelledby": `${id}-label`,
30
+ "aria-disabled": disabled,
31
+ children: [
32
+ /*#__PURE__*/ _jsx("label", {
33
+ id: `${id}-label`,
34
+ htmlFor: id,
35
+ children: label
36
+ }),
37
+ /*#__PURE__*/ _jsxs("div", {
38
+ className: "xp-chips-control",
39
+ children: [
40
+ value.map((item, index)=>/*#__PURE__*/ _jsxs("span", {
41
+ className: "xp-chips-item",
42
+ children: [
43
+ /*#__PURE__*/ _jsx("span", {
44
+ children: item
45
+ }),
46
+ /*#__PURE__*/ _jsx("button", {
47
+ type: "button",
48
+ disabled: disabled,
49
+ "aria-label": AUDIENCE_COPY.removeValue(item),
50
+ onClick: ()=>remove(index),
51
+ children: AUDIENCE_COPY.removeSymbol
52
+ })
53
+ ]
54
+ }, item)),
55
+ /*#__PURE__*/ _jsx("input", {
56
+ ref: input,
57
+ id: id,
58
+ type: "text",
59
+ value: draft,
60
+ disabled: disabled,
61
+ placeholder: placeholder,
62
+ "aria-describedby": description ? `${id}-description` : undefined,
63
+ onChange: (event)=>setDraft(event.target.value),
64
+ onBlur: ()=>{
65
+ if (draft.trim()) commit(draft);
66
+ },
67
+ onKeyDown: (event)=>{
68
+ if (disabled || event.nativeEvent.isComposing) return;
69
+ if (event.key === 'Enter' || event.key === ',') {
70
+ event.preventDefault();
71
+ commit(draft);
72
+ } else if (event.key === 'Tab' && draft.trim()) commit(draft);
73
+ else if (event.key === 'Backspace' && !draft && value.length) {
74
+ event.preventDefault();
75
+ remove(value.length - 1);
76
+ }
77
+ },
78
+ onPaste: (event)=>{
79
+ if (disabled) return;
80
+ const pasted = event.clipboardData.getData('text');
81
+ if (!pasted.includes(',')) return;
82
+ event.preventDefault();
83
+ const start = event.currentTarget.selectionStart ?? draft.length;
84
+ const end = event.currentTarget.selectionEnd ?? start;
85
+ commit(draft.slice(0, start) + pasted + draft.slice(end));
86
+ }
87
+ })
88
+ ]
89
+ }),
90
+ description ? /*#__PURE__*/ _jsx("p", {
91
+ id: `${id}-description`,
92
+ children: description
93
+ }) : null
94
+ ]
95
+ });
96
+ }
@@ -0,0 +1,5 @@
1
+ import '../theme.css';
2
+ export declare function RootViewMessage({ children, alert }: {
3
+ children: string;
4
+ alert?: boolean;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import '../theme.css';
4
+ import { theme } from '../theme.js';
5
+ export function RootViewMessage({ children, alert = false }) {
6
+ return /*#__PURE__*/ _jsx("div", {
7
+ role: alert ? 'alert' : undefined,
8
+ style: {
9
+ padding: theme.space[4],
10
+ color: theme.color.text,
11
+ background: theme.color.bg
12
+ },
13
+ children: children
14
+ });
15
+ }
@@ -0,0 +1,11 @@
1
+ .xp-chips { display: grid; gap: var(--xp-space-2); margin-block: var(--xp-space-4); color: var(--xp-text); font-family: var(--xp-font-body); }
2
+ .xp-chips-control { display: flex; align-items: center; flex-wrap: wrap; gap: var(--xp-space-1); padding: var(--xp-space-2); border: 1px solid var(--xp-border-control); border-radius: var(--xp-radius); background: var(--xp-input-bg); }
3
+ .xp-chips-control:focus-within { outline: 2px solid var(--xp-text); outline-offset: 2px; }
4
+ .xp-chips-item { display: inline-flex; align-items: center; gap: var(--xp-space-1); padding-inline-start: var(--xp-space-2); border-radius: var(--xp-radius); background: var(--xp-surface-raised); overflow-wrap: anywhere; max-width: 100%; }
5
+ .xp-chips .xp-chips-item button { padding: var(--xp-space-1) var(--xp-space-2); border: 0; background: transparent; color: var(--xp-text); font: inherit; cursor: pointer; }
6
+ .xp-chips .xp-chips-control input { flex: 1; min-width: 8rem; width: 0; border: 0; padding: var(--xp-space-1); background: transparent; color: var(--xp-text); font: inherit; }
7
+ .xp-chips input:focus { outline: none; }
8
+ .xp-chips button:focus-visible { outline: 2px solid var(--xp-text); outline-offset: 2px; }
9
+ .xp-chips[aria-disabled='true'] { opacity: .5; }
10
+ .xp-chips[aria-disabled='true'] button { cursor: default; }
11
+ .xp-chips p { margin: 0; color: var(--xp-text-muted); font-size: var(--xp-font-size-small); }
@@ -1,4 +1,8 @@
1
1
  export declare const AUDIENCE_COPY: {
2
+ readonly listPlaceholder: "Type a value and press Enter";
3
+ readonly listDescription: "Add one value per chip, for example each page path.";
4
+ readonly removeValue: (value: string) => string;
5
+ readonly removeSymbol: "×";
2
6
  readonly emptyListNote: "Add at least one value — an empty list can't be saved.";
3
7
  };
4
8
  export declare const STRING_OP_LABELS: {
@@ -1,4 +1,8 @@
1
1
  export const AUDIENCE_COPY = {
2
+ listPlaceholder: 'Type a value and press Enter',
3
+ listDescription: 'Add one value per chip, for example each page path.',
4
+ removeValue: (value)=>`Remove ${value}`,
5
+ removeSymbol: '×',
2
6
  emptyListNote: "Add at least one value — an empty list can't be saved."
3
7
  };
4
8
  export const STRING_OP_LABELS = {
@@ -0,0 +1,44 @@
1
+ export declare const GOALS_COPY: {
2
+ readonly title: "Goals";
3
+ readonly newGoal: "New goal";
4
+ readonly name: "Name";
5
+ readonly source: "Measured by";
6
+ readonly directionColumn: "Better when";
7
+ readonly direction: "Better when it goes";
8
+ readonly increase: "Goes up";
9
+ readonly decrease: "Goes down";
10
+ readonly usedBy: "Used by";
11
+ readonly status: "Status";
12
+ readonly inUse: "In use";
13
+ readonly unused: "Not used";
14
+ readonly empty: "A goal is an outcome you want a test to improve, such as a registration or a gift.";
15
+ readonly loading: "Loading goals…";
16
+ readonly loadError: "Couldn't load goals. Try again.";
17
+ readonly retry: "Try again";
18
+ readonly slug: "Short name";
19
+ readonly slugHelp: "A unique short name for this goal.";
20
+ readonly slugLocked: "The short name stays the same once a test uses this goal.";
21
+ readonly chooseSource: "Choose an outcome";
22
+ readonly window: "Count results for (hours)";
23
+ readonly minimum: "Minimum completions before deciding";
24
+ readonly advanced: "Advanced";
25
+ readonly capping: "Limit very large values";
26
+ readonly capType: "Limit by";
27
+ readonly capValue: "Limit value";
28
+ readonly noCap: "No limit";
29
+ readonly percentile: "Based on past amounts";
30
+ readonly absolute: "Fixed amount";
31
+ readonly capHelp: "For past amounts, enter a number such as 0.99 to limit only the largest 1%; or enter a fixed amount.";
32
+ readonly threshold: "Warn when it gets worse by (%)";
33
+ readonly save: "Save";
34
+ readonly saving: "Saving…";
35
+ readonly cancel: "Cancel";
36
+ readonly remove: "Delete";
37
+ readonly thresholdError: "Enter a percentage of zero or more, or leave blank for 5%.";
38
+ readonly deleting: "Deleting…";
39
+ readonly saveError: "Couldn't save the goal. Try again.";
40
+ readonly deleteError: "Couldn't delete the goal. Try again.";
41
+ readonly breadcrumb: "Breadcrumb";
42
+ readonly missing: "This goal could not be found.";
43
+ };
44
+ export declare function goalUsedBy(count: number): string;
@@ -0,0 +1,46 @@
1
+ export const GOALS_COPY = {
2
+ title: 'Goals',
3
+ newGoal: 'New goal',
4
+ name: 'Name',
5
+ source: 'Measured by',
6
+ directionColumn: 'Better when',
7
+ direction: 'Better when it goes',
8
+ increase: 'Goes up',
9
+ decrease: 'Goes down',
10
+ usedBy: 'Used by',
11
+ status: 'Status',
12
+ inUse: 'In use',
13
+ unused: 'Not used',
14
+ empty: 'A goal is an outcome you want a test to improve, such as a registration or a gift.',
15
+ loading: 'Loading goals…',
16
+ loadError: "Couldn't load goals. Try again.",
17
+ retry: 'Try again',
18
+ slug: 'Short name',
19
+ slugHelp: 'A unique short name for this goal.',
20
+ slugLocked: 'The short name stays the same once a test uses this goal.',
21
+ chooseSource: 'Choose an outcome',
22
+ window: 'Count results for (hours)',
23
+ minimum: 'Minimum completions before deciding',
24
+ advanced: 'Advanced',
25
+ capping: 'Limit very large values',
26
+ capType: 'Limit by',
27
+ capValue: 'Limit value',
28
+ noCap: 'No limit',
29
+ percentile: 'Based on past amounts',
30
+ absolute: 'Fixed amount',
31
+ capHelp: 'For past amounts, enter a number such as 0.99 to limit only the largest 1%; or enter a fixed amount.',
32
+ threshold: 'Warn when it gets worse by (%)',
33
+ save: 'Save',
34
+ saving: 'Saving…',
35
+ cancel: 'Cancel',
36
+ remove: 'Delete',
37
+ thresholdError: 'Enter a percentage of zero or more, or leave blank for 5%.',
38
+ deleting: 'Deleting…',
39
+ saveError: "Couldn't save the goal. Try again.",
40
+ deleteError: "Couldn't delete the goal. Try again.",
41
+ breadcrumb: 'Breadcrumb',
42
+ missing: 'This goal could not be found.'
43
+ };
44
+ export function goalUsedBy(count) {
45
+ return `Used by ${count} ${count === 1 ? 'test' : 'tests'}`;
46
+ }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { theme } from '../theme.js';
3
4
  import { RESULTS_COPY } from '../copy/results.js';
4
5
  import { CappedCell } from './CappedCell.js';
5
6
  export function ArmTable({ rows, caption, method, metricKind }) {
@@ -10,7 +11,7 @@ export function ArmTable({ rows, caption, method, metricKind }) {
10
11
  return /*#__PURE__*/ _jsx("div", {
11
12
  className: "xp-arm-table",
12
13
  style: {
13
- color: 'var(--theme-text)',
14
+ color: theme.color.text,
14
15
  overflowX: 'auto'
15
16
  },
16
17
  children: /*#__PURE__*/ _jsxs("table", {
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { theme } from '../theme.js';
3
4
  export function CappedCell({ model }) {
4
5
  return /*#__PURE__*/ _jsxs("div", {
5
6
  className: "xp-capped-cell",
6
7
  style: {
7
- color: 'var(--theme-text)'
8
+ color: theme.color.text
8
9
  },
9
10
  children: [
10
11
  /*#__PURE__*/ _jsx("span", {
@@ -1,12 +1,13 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { theme } from '../theme.js';
3
4
  import { RESULTS_COPY as C, chartSummary, contentChangeTitle } from '../copy/results.js';
4
5
  import { chartModel } from './chart.model.js';
5
6
  import { formatPercent } from './format.js';
6
7
  const colors = [
7
- 'var(--theme-elevation-800)',
8
- 'var(--theme-elevation-600)',
9
- 'var(--theme-elevation-400)'
8
+ theme.color.textStrong,
9
+ theme.color.textMuted,
10
+ theme.color.borderAccent
10
11
  ];
11
12
  const date = (t)=>new Intl.DateTimeFormat('en-US', {
12
13
  dateStyle: 'medium',
@@ -28,7 +29,7 @@ export function CiChart({ series, metricId, metricName, phase, contentChanges })
28
29
  return /*#__PURE__*/ _jsxs("section", {
29
30
  className: "xp-ci-chart",
30
31
  style: {
31
- color: 'var(--theme-text)'
32
+ color: theme.color.text
32
33
  },
33
34
  children: [
34
35
  /*#__PURE__*/ _jsx("h2", {
@@ -50,7 +51,7 @@ export function CiChart({ series, metricId, metricName, phase, contentChanges })
50
51
  x2: 680,
51
52
  y1: y(0),
52
53
  y2: y(0),
53
- stroke: "var(--theme-elevation-500)",
54
+ stroke: theme.color.accent,
54
55
  strokeDasharray: "4 4"
55
56
  }),
56
57
  [
@@ -146,7 +147,7 @@ export function CiChart({ series, metricId, metricName, phase, contentChanges })
146
147
  x2: x(marker.t),
147
148
  y1: 20,
148
149
  y2: 230,
149
- stroke: "var(--theme-text)",
150
+ stroke: theme.color.text,
150
151
  strokeDasharray: "2 4"
151
152
  })
152
153
  ]
@@ -156,7 +157,7 @@ export function CiChart({ series, metricId, metricName, phase, contentChanges })
156
157
  /*#__PURE__*/ _jsx("ul", {
157
158
  children: model.variants.map((variant, index)=>/*#__PURE__*/ _jsxs("li", {
158
159
  style: {
159
- color: 'var(--theme-text)'
160
+ color: theme.color.text
160
161
  },
161
162
  children: [
162
163
  /*#__PURE__*/ _jsx("span", {
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { theme } from '../theme.js';
3
4
  import { RESULTS_COPY } from '../copy/results.js';
4
5
  export function DataQualityPanel({ items }) {
5
6
  if (items.length === 0) return null;
@@ -7,8 +8,8 @@ export function DataQualityPanel({ items }) {
7
8
  className: "xp-data-quality",
8
9
  "aria-label": RESULTS_COPY.quality,
9
10
  style: {
10
- color: 'var(--theme-text)',
11
- background: 'var(--theme-elevation-50)',
11
+ color: theme.color.text,
12
+ background: theme.color.surface,
12
13
  padding: '1rem'
13
14
  },
14
15
  children: [
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { theme } from '../theme.js';
3
4
  export function VerdictCard({ model, guardrails = [], power, collecting, sinceShipping }) {
4
5
  return /*#__PURE__*/ _jsxs("section", {
5
6
  className: "xp-verdict-card",
6
7
  style: {
7
- color: 'var(--theme-text)',
8
- background: 'var(--theme-elevation-50)',
8
+ color: theme.color.text,
9
+ background: theme.color.surface,
9
10
  padding: '1rem'
10
11
  },
11
12
  children: [
@@ -1,10 +1,10 @@
1
1
  /* Admin styles for the experiments plugin; loaded by src/admin/styles.ts. */
2
2
  .xp-status-pill { font-weight: 600; }
3
- .xp-status-pill--neutral { background: var(--theme-elevation-150); color: var(--theme-elevation-800); }
4
- .xp-status-pill--success { background: var(--theme-success-150); color: var(--theme-success-850); }
5
- .xp-status-pill--warning { background: var(--theme-warning-150); color: var(--theme-warning-850); }
6
- .xp-status-pill--info { background: var(--theme-elevation-200); color: var(--theme-elevation-900); }
7
- .xp-status-pill--muted { background: var(--theme-elevation-100); color: var(--theme-elevation-600); }
8
- .xp-locked-notice, .xp-notice { padding: 0.75rem; border-inline-start: 3px solid var(--theme-elevation-400); background: var(--theme-elevation-50); color: var(--theme-elevation-800); }
9
- .xp-notice--warning { border-color: var(--theme-warning-500); background: var(--theme-warning-100); }
10
- .xp-notice--error { border-color: var(--theme-error-500); background: var(--theme-error-100); }
3
+ .xp-status-pill--neutral { background: var(--xp-border); color: var(--xp-text-strong); }
4
+ .xp-status-pill--success { background: var(--xp-success-bg); color: var(--xp-success-text-strong); }
5
+ .xp-status-pill--warning { background: var(--xp-warning-bg); color: var(--xp-warning-text-strong); }
6
+ .xp-status-pill--info { background: var(--xp-border-strong); color: var(--xp-text-emphasis); }
7
+ .xp-status-pill--muted { background: var(--xp-surface-raised); color: var(--xp-text-muted); }
8
+ .xp-locked-notice, .xp-notice { padding: 0.75rem; border-inline-start: 3px solid var(--xp-border-accent); background: var(--xp-surface); color: var(--xp-text-strong); }
9
+ .xp-notice--warning { border-color: var(--xp-warning); background: var(--xp-warning-surface); }
10
+ .xp-notice--error { border-color: var(--xp-error); background: var(--xp-error-surface); }
@@ -1 +1,2 @@
1
+ import './theme.css';
1
2
  import './styles.css';
@@ -1,2 +1,3 @@
1
1
  // Load admin styles from mounted components so barrels stay free of side effects.
2
+ import './theme.css';
2
3
  import './styles.css';
@@ -1,3 +1,4 @@
1
+ import '../theme.css';
1
2
  import type { ReactNode } from 'react';
2
3
  import './surface.css';
3
4
  export declare function SurfaceShell({ adminRoute, children }: {
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import '../theme.css';
3
4
  import { Link } from '@payloadcms/ui';
4
5
  import { formatAdminURL } from 'payload/shared';
5
6
  import { SURFACE_COPY } from '../copy/surface.js';
@@ -91,7 +91,7 @@ export function TestsHome({ adminRoute, apiRoute }) {
91
91
  /*#__PURE__*/ _jsx(Link, {
92
92
  href: formatAdminURL({
93
93
  adminRoute,
94
- path: '/collections/xp-metrics'
94
+ path: '/experiments/goals'
95
95
  }),
96
96
  children: SURFACE_COPY.goals
97
97
  })
@@ -4,6 +4,8 @@ import { NewTest } from './newTest/NewTest.js';
4
4
  import { TestPage } from './test/TestPage.js';
5
5
  import { ResultsRoute } from './ResultsRoute.js';
6
6
  import { SurfaceShell } from './SurfaceShell.js';
7
+ import { GoalForm } from './goals/GoalForm.js';
8
+ import { GoalsList } from './goals/GoalsList.js';
7
9
  import { TestsHome } from './TestsHome.js';
8
10
  export function TestsSurface(props) {
9
11
  const surface = {
@@ -22,7 +24,14 @@ export function TestsSurface(props) {
22
24
  children: props.route === 'home' ? /*#__PURE__*/ _jsx(TestsHome, {
23
25
  adminRoute: props.adminRoute,
24
26
  apiRoute: props.apiRoute
25
- }) : props.route === 'new' ? /*#__PURE__*/ _jsx(NewTest, {
27
+ }) : props.route === 'goals' ? /*#__PURE__*/ _jsx(GoalsList, {
28
+ surface: surface,
29
+ returnToTest: props.returnToTest
30
+ }) : props.route === 'goalNew' || props.route === 'goal' ? /*#__PURE__*/ _jsx(GoalForm, {
31
+ surface: surface,
32
+ id: props.route === 'goal' ? props.id : undefined,
33
+ returnToTest: props.returnToTest
34
+ }, props.route === 'goalNew' ? 'new' : props.id) : props.route === 'new' ? /*#__PURE__*/ _jsx(NewTest, {
26
35
  surface: surface,
27
36
  pages: props.pages
28
37
  }) : props.id === undefined ? null : props.route === 'test' ? /*#__PURE__*/ _jsx(TestPage, {
@@ -0,0 +1,7 @@
1
+ import type { SurfaceProps } from '../contracts.js';
2
+ import './goals.css';
3
+ export declare function GoalForm({ surface, id, returnToTest }: {
4
+ surface: SurfaceProps;
5
+ id?: string;
6
+ returnToTest?: string;
7
+ }): import("react").JSX.Element;