diff --git a/design-system/templates/chicorytv-admin/MultiCollections.jsx b/design-system/templates/chicorytv-admin/MultiCollections.jsx index 6bb0f0129..8320add89 100644 --- a/design-system/templates/chicorytv-admin/MultiCollections.jsx +++ b/design-system/templates/chicorytv-admin/MultiCollections.jsx @@ -18,12 +18,20 @@ ]; const EDIT_ITEMS = [ - { kind: "manual", name: "Friends", scheduleAsGroup: true }, - { kind: "manual", name: "Seinfeld", scheduleAsGroup: true }, - { kind: "manual", name: "The Office (US)", scheduleAsGroup: false }, - { kind: "smart", name: "Sitcoms — Unwatched, Short", scheduleAsGroup: false }, + { kind: "manual", name: "Friends", scheduleAsGroup: true, weight: 3 }, + { kind: "manual", name: "Seinfeld", scheduleAsGroup: true, weight: 2 }, + { kind: "manual", name: "The Office (US)", scheduleAsGroup: false, weight: 1 }, + { kind: "smart", name: "Sitcoms — Unwatched, Short", scheduleAsGroup: false, weight: 1 }, ]; + // Per-source weight bounds mirror the API's MultiCollectionItemWeight validator (1..1000, #404). + const WEIGHT_MIN = 1; + const WEIGHT_MAX = 1000; + const clampWeight = (raw) => { + const n = Math.trunc(Number(raw)); + return Number.isFinite(n) ? Math.min(WEIGHT_MAX, Math.max(WEIGHT_MIN, n)) : WEIGHT_MIN; + }; + const flushRow = (first) => ({ display: "flex", alignItems: "center", @@ -73,7 +81,7 @@ ); } - function EditItemRow({ item, first }) { + function EditItemRow({ item, first, weight, pct, onWeight }) { const [checked, setChecked] = React.useState(item.scheduleAsGroup); return (
@@ -92,6 +100,31 @@ {item.name} {item.kind === "smart" ? "Smart" : "Manual"} + {/* Per-source weight (#404): relative share of airtime under the Weighted Shuffle order. + Bounded 1..1000 (API validator); the % is a display of the integer share (3:1 → 75/25). */} +
+ Weight + onWeight(e.target.value)} + onBlur={(e) => onWeight(String(clampWeight(e.target.value)))} + style={{ + width: 62, + height: 30, + padding: "0 8px", + borderRadius: "var(--radius-sm)", + border: "1px solid var(--border-hairline)", + background: "var(--ctv-bg-sunken)", + color: "var(--text-primary)", + font: "var(--text-sm)/1 var(--font-sans)", + }} + /> + {pct}% +
) : ( - items.map((item, i) => ) + + {items.map((item, i) => ( + setWeightAt(i, v)} + /> + ))} + {/* fair-share footer (#404): fair-share is not a separate mode — it is Weighted Shuffle + with all weights left at 1, so this resets rather than writing a different order. */} +
+ + Weights set each source’s share of airtime under the Weighted Shuffle playback order (set on the schedule item). Equal weights = fair share. + + +
+
)}