import React from 'react' import { Head, usePage } from '@inertiajs/react' import CollectionCard from '../../components/profile/collections/CollectionCard' import ShareToast from '../../components/ui/ShareToast' function getCsrfToken() { if (typeof document === 'undefined') return '' return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' } async function requestJson(url, { method = 'POST', body } = {}) { const response = await fetch(url, { method, credentials: 'same-origin', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken(), 'X-Requested-With': 'XMLHttpRequest', }, body: body ? JSON.stringify(body) : undefined, }) const payload = await response.json().catch(() => ({})) if (!response.ok) { throw new Error(payload?.message || 'Request failed.') } return payload } function isoToLocalInput(value) { if (!value) return '' const date = new Date(value) if (Number.isNaN(date.getTime())) return '' const local = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)) return local.toISOString().slice(0, 16) } function titleize(value) { return String(value || '') .split('_') .filter(Boolean) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(' ') } function Field({ label, help, children }) { return ( ) } function StatCard({ label, value, tone = 'sky' }) { const toneClasses = { sky: 'border-sky-300/15 bg-sky-400/10 text-sky-100', amber: 'border-amber-300/15 bg-amber-400/10 text-amber-100', emerald: 'border-emerald-300/15 bg-emerald-400/10 text-emerald-100', } return (
{label}
{value}
) } function sortAssignments(items) { return [...items].sort((left, right) => { const keyCompare = String(left.program_key || '').localeCompare(String(right.program_key || '')) if (keyCompare !== 0) return keyCompare return (Number(right.priority) || 0) - (Number(left.priority) || 0) }) } function buildHooksForm(collection) { return { experiment_key: collection?.experiment_key || '', experiment_treatment: collection?.experiment_treatment || '', placement_variant: collection?.placement_variant || '', ranking_mode_variant: collection?.ranking_mode_variant || '', collection_pool_version: collection?.collection_pool_version || '', test_label: collection?.test_label || '', promotion_tier: collection?.promotion_tier || '', partner_key: collection?.partner_key || '', trust_tier: collection?.trust_tier || '', sponsorship_state: collection?.sponsorship_state || '', ownership_domain: collection?.ownership_domain || '', commercial_review_state: collection?.commercial_review_state || '', legal_review_state: collection?.legal_review_state || '', placement_eligibility: Boolean(collection?.placement_eligibility), } } function buildDiagnostics(collection) { if (!collection) { return null } return { collection_id: Number(collection.id), workflow_state: collection.workflow_state || null, health_state: collection.health_state || null, placement_eligibility: Boolean(collection.placement_eligibility), experiment_key: collection.experiment_key || null, experiment_treatment: collection.experiment_treatment || null, placement_variant: collection.placement_variant || null, ranking_mode_variant: collection.ranking_mode_variant || null, collection_pool_version: collection.collection_pool_version || null, test_label: collection.test_label || null, partner_key: collection.partner_key || null, trust_tier: collection.trust_tier || null, promotion_tier: collection.promotion_tier || null, sponsorship_state: collection.sponsorship_state || null, ownership_domain: collection.ownership_domain || null, commercial_review_state: collection.commercial_review_state || null, legal_review_state: collection.legal_review_state || null, ranking_bucket: collection.ranking_bucket || null, recommendation_tier: collection.recommendation_tier || null, last_health_check_at: collection.last_health_check_at || null, last_recommendation_refresh_at: collection.last_recommendation_refresh_at || null, } } function buildProgramUrl(pattern, programKey) { if (!pattern || !programKey) return null return pattern.replace('__PROGRAM__', String(programKey)) } export default function CollectionStaffProgramming() { const { props } = usePage() const initialCollectionOptions = Array.isArray(props.collectionOptions) ? props.collectionOptions : [] const initialAssignments = Array.isArray(props.assignments) ? props.assignments : [] const baseProgramKeys = Array.isArray(props.programKeyOptions) ? props.programKeyOptions : [] const initialMergeQueue = props.mergeQueue || { summary: {}, pending: [], recent: [] } const observabilitySummary = props.observabilitySummary || { counts: {}, watchlist: [], generated_at: null } const endpoints = props.endpoints || {} const historyPattern = props.historyPattern || '' const seo = props.seo || {} const viewer = props.viewer || {} const [assignments, setAssignments] = React.useState(sortAssignments(initialAssignments)) const [collectionOverrides, setCollectionOverrides] = React.useState({}) const collectionOptions = React.useMemo(() => initialCollectionOptions.map((collection) => collectionOverrides[collection.id] || collection), [collectionOverrides, initialCollectionOptions]) const [mergeQueue, setMergeQueue] = React.useState(initialMergeQueue) const [previewCollections, setPreviewCollections] = React.useState([]) const [diagnostics, setDiagnostics] = React.useState({ eligibility: null, duplicates: null, recommendations: null, }) const [notice, setNotice] = React.useState('') const [toast, setToast] = React.useState({ id: 0, visible: false, message: '', variant: 'success' }) const [busy, setBusy] = React.useState('') const [queueBusy, setQueueBusy] = React.useState({}) const [selectedCollectionId, setSelectedCollectionId] = React.useState(initialCollectionOptions[0]?.id || '') const [previewForm, setPreviewForm] = React.useState({ program_key: baseProgramKeys[0] || '', limit: 8, }) const [assignmentForm, setAssignmentForm] = React.useState({ id: null, collection_id: collectionOptions[0]?.id || '', program_key: baseProgramKeys[0] || '', campaign_key: '', placement_scope: '', starts_at: '', ends_at: '', priority: 0, notes: '', }) const selectedCollection = React.useMemo(() => collectionOptions.find((collection) => String(collection.id) === String(selectedCollectionId)) || null, [collectionOptions, selectedCollectionId]) const [hooksForm, setHooksForm] = React.useState(buildHooksForm(initialCollectionOptions[0] || null)) const [hooksDiagnostics, setHooksDiagnostics] = React.useState(buildDiagnostics(initialCollectionOptions[0] || null)) const programKeyOptions = React.useMemo(() => { return Array.from(new Set([ ...baseProgramKeys, ...assignments.map((assignment) => assignment.program_key).filter(Boolean), ...collectionOptions.map((collection) => collection.program_key).filter(Boolean), assignmentForm.program_key || null, previewForm.program_key || null, ].filter(Boolean))).sort((left, right) => String(left).localeCompare(String(right))) }, [assignmentForm.program_key, assignments, baseProgramKeys, collectionOptions, previewForm.program_key]) React.useEffect(() => { if (!assignmentForm.collection_id && collectionOptions[0]?.id) { setAssignmentForm((current) => ({ ...current, collection_id: collectionOptions[0].id })) } if (!selectedCollectionId && collectionOptions[0]?.id) { setSelectedCollectionId(collectionOptions[0].id) } }, [assignmentForm.collection_id, collectionOptions, selectedCollectionId]) React.useEffect(() => { if (!assignmentForm.program_key && programKeyOptions[0]) { setAssignmentForm((current) => ({ ...current, program_key: programKeyOptions[0] })) } if (!previewForm.program_key && programKeyOptions[0]) { setPreviewForm((current) => ({ ...current, program_key: programKeyOptions[0] })) } }, [assignmentForm.program_key, previewForm.program_key, programKeyOptions]) React.useEffect(() => { setMergeQueue(initialMergeQueue) }, [initialMergeQueue]) React.useEffect(() => { setHooksForm(buildHooksForm(selectedCollection)) setHooksDiagnostics(buildDiagnostics(selectedCollection)) }, [selectedCollection]) function showToast(message, variant = 'success') { setToast({ id: Date.now() + Math.random(), visible: true, message, variant, }) } function resetAssignmentForm() { setAssignmentForm({ id: null, collection_id: collectionOptions[0]?.id || '', program_key: programKeyOptions[0] || '', campaign_key: '', placement_scope: '', starts_at: '', ends_at: '', priority: 0, notes: '', }) } function hydrateAssignment(assignment) { setAssignmentForm({ id: assignment.id, collection_id: assignment.collection?.id || '', program_key: assignment.program_key || '', campaign_key: assignment.campaign_key || '', placement_scope: assignment.placement_scope || '', starts_at: isoToLocalInput(assignment.starts_at), ends_at: isoToLocalInput(assignment.ends_at), priority: assignment.priority || 0, notes: assignment.notes || '', }) } async function handleAssignmentSubmit(event) { event.preventDefault() setBusy('assignment') setNotice('') try { const url = assignmentForm.id ? endpoints.updatePattern?.replace('__PROGRAM__', String(assignmentForm.id)) : endpoints.store const payload = await requestJson(url, { method: assignmentForm.id ? 'PATCH' : 'POST', body: { collection_id: Number(assignmentForm.collection_id), program_key: assignmentForm.program_key, campaign_key: assignmentForm.campaign_key || null, placement_scope: assignmentForm.placement_scope || null, starts_at: assignmentForm.starts_at ? new Date(assignmentForm.starts_at).toISOString() : null, ends_at: assignmentForm.ends_at ? new Date(assignmentForm.ends_at).toISOString() : null, priority: Number(assignmentForm.priority || 0), notes: assignmentForm.notes || null, }, }) setAssignments((current) => sortAssignments([...current.filter((item) => item.id !== payload.assignment.id), payload.assignment])) setNotice(assignmentForm.id ? 'Programming assignment updated.' : 'Programming assignment created.') resetAssignmentForm() } catch (error) { setNotice(error.message || 'Failed to save programming assignment.') } finally { setBusy('') } } async function handlePreview(event) { event.preventDefault() setBusy('preview') setNotice('') try { const payload = await requestJson(endpoints.preview, { method: 'POST', body: { program_key: previewForm.program_key, limit: Number(previewForm.limit || 8), }, }) setPreviewCollections(Array.isArray(payload.collections) ? payload.collections : []) setNotice('Preview refreshed.') } catch (error) { setNotice(error.message || 'Failed to preview this program key.') } finally { setBusy('') } } async function runDiagnostic(kind) { const endpointMap = { eligibility: endpoints.refreshEligibility, duplicates: endpoints.duplicateScan, recommendations: endpoints.refreshRecommendations, } const url = endpointMap[kind] if (!url) { return } setBusy(kind) setNotice('') try { const payload = await requestJson(url, { method: 'POST', body: { collection_id: selectedCollectionId ? Number(selectedCollectionId) : null, }, }) setDiagnostics((current) => ({ ...current, [kind]: payload.result || null })) setNotice(payload?.result?.message || `${titleize(kind)} queued.`) } catch (error) { setNotice(error.message || `Failed to run ${kind}.`) } finally { setBusy('') } } async function handleQueueAction(kind, item) { const sourceId = item?.source?.id const targetId = item?.target?.id if (!sourceId || !targetId) { return } const endpointMap = { canonicalize: endpoints.canonicalizeCandidate, merge: endpoints.mergeCandidate, reject: endpoints.rejectCandidate, } const confirmationMap = { canonicalize: `Designate "${item.target?.title || 'this collection'}" as the canonical target for "${item.source?.title || 'this collection'}"?`, merge: `Merge "${item.source?.title || 'this collection'}" into "${item.target?.title || 'this collection'}" from the staff queue?`, reject: `Mark "${item.target?.title || 'this collection'}" as not a duplicate of "${item.source?.title || 'this collection'}"?`, } const url = endpointMap[kind] if (!url) { return } if (!window.confirm(confirmationMap[kind] || 'Continue?')) { return } setQueueBusy((current) => ({ ...current, [item.id]: kind })) try { const payload = await requestJson(url, { method: 'POST', body: { source_collection_id: Number(sourceId), target_collection_id: Number(targetId), }, }) if (payload?.mergeQueue) { setMergeQueue(payload.mergeQueue) } showToast(payload?.message || `${titleize(kind)} action completed.`, 'success') } catch (error) { showToast(error.message || `Failed to ${kind} this queue item.`, 'error') } finally { setQueueBusy((current) => { const next = { ...current } delete next[item.id] return next }) } } async function handleHooksSubmit(event) { event.preventDefault() if (!selectedCollectionId || !endpoints.metadataUpdate) { return } setBusy('hooks') setNotice('') try { const payload = await requestJson(endpoints.metadataUpdate, { method: 'POST', body: { collection_id: Number(selectedCollectionId), experiment_key: hooksForm.experiment_key || null, experiment_treatment: hooksForm.experiment_treatment || null, placement_variant: hooksForm.placement_variant || null, ranking_mode_variant: hooksForm.ranking_mode_variant || null, collection_pool_version: hooksForm.collection_pool_version || null, test_label: hooksForm.test_label || null, promotion_tier: hooksForm.promotion_tier || null, partner_key: viewer.isAdmin ? (hooksForm.partner_key || null) : undefined, trust_tier: viewer.isAdmin ? (hooksForm.trust_tier || null) : undefined, sponsorship_state: viewer.isAdmin ? (hooksForm.sponsorship_state || null) : undefined, ownership_domain: viewer.isAdmin ? (hooksForm.ownership_domain || null) : undefined, commercial_review_state: viewer.isAdmin ? (hooksForm.commercial_review_state || null) : undefined, legal_review_state: viewer.isAdmin ? (hooksForm.legal_review_state || null) : undefined, placement_eligibility: Boolean(hooksForm.placement_eligibility), }, }) if (payload?.collection?.id) { setCollectionOverrides((current) => ({ ...current, [payload.collection.id]: payload.collection, })) } setHooksDiagnostics(payload?.diagnostics || buildDiagnostics(payload?.collection || selectedCollection)) setHooksForm(buildHooksForm(payload?.collection || selectedCollection)) setNotice(payload?.message || 'Experiment and program governance hooks updated.') } catch (error) { setNotice(error.message || 'Failed to update experiment and program governance hooks.') } finally { setBusy('') } } const totalPrograms = Array.from(new Set(assignments.map((assignment) => assignment.program_key).filter(Boolean))).length const eligibleAssignments = assignments.filter((assignment) => assignment.collection?.placement_eligibility === true).length return ( <> {seo.title || 'Collection Programming — Skinbase Nova'} {seo.canonical ? : null} setToast((current) => ({ ...current, visible: false }))} />