import React from 'react' const TYPE_LABELS = { style: 'Style', layout: 'Layout', background: 'Background', typography: 'Typography', starter: 'Starter', } const TYPE_ICONS = { style: 'fa-palette', layout: 'fa-table-columns', background: 'fa-image', typography: 'fa-font', starter: 'fa-star', } function PresetCard({ preset, onApply, onDelete, applying }) { return (
) } export default function NovaCardPresetPicker({ presets = {}, endpoints = {}, cardId = null, onApplyPatch, onPresetsChange, activeType = null, }) { const [selectedType, setSelectedType] = React.useState(activeType || 'style') const [applyingId, setApplyingId] = React.useState(null) const [capturing, setCapturing] = React.useState(false) const [captureName, setCaptureName] = React.useState('') const [captureType, setCaptureType] = React.useState('style') const [showCaptureForm, setShowCaptureForm] = React.useState(false) const [error, setError] = React.useState(null) const typeKeys = Object.keys(TYPE_LABELS) const listedPresets = Array.isArray(presets[selectedType]) ? presets[selectedType] : [] async function handleApply(preset) { if (!cardId || !endpoints.presetApplyPattern) return const url = endpoints.presetApplyPattern .replace('__PRESET__', preset.id) .replace('__CARD__', cardId) setApplyingId(preset.id) setError(null) try { const res = await fetch(url, { method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json' }, }) const data = await res.json() if (!res.ok) throw new Error(data?.message || 'Failed to apply preset') if (data?.project_patch && onApplyPatch) { onApplyPatch(data.project_patch) } } catch (err) { setError(err.message || 'Failed to apply preset') } finally { setApplyingId(null) } } async function handleDelete(preset) { if (!endpoints.presetDestroyPattern) return const url = endpoints.presetDestroyPattern.replace('__PRESET__', preset.id) if (!window.confirm(`Delete preset "${preset.name}"?`)) return try { const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '' const res = await fetch(url, { method: 'DELETE', headers: { 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'X-CSRF-TOKEN': csrfToken, }, }) if (!res.ok) throw new Error('Failed to delete preset') onPresetsChange?.() } catch (err) { setError(err.message || 'Failed to delete preset') } } async function handleCapture(e) { e.preventDefault() if (!cardId || !endpoints.capturePresetPattern || !captureName.trim()) return const url = endpoints.capturePresetPattern.replace('__CARD__', cardId) setCapturing(true) setError(null) try { const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '' const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'X-CSRF-TOKEN': csrfToken, }, body: JSON.stringify({ name: captureName.trim(), preset_type: captureType }), }) const data = await res.json() if (!res.ok) throw new Error(data?.message || 'Failed to capture preset') setCaptureName('') setShowCaptureForm(false) onPresetsChange?.() } catch (err) { setError(err.message || 'Failed to capture preset') } finally { setCapturing(false) } } return (
{/* Type tabs */}
{typeKeys.map((type) => ( ))}
{/* Preset list */} {listedPresets.length > 0 ? (
{listedPresets.map((preset) => ( ))}
) : (

No {TYPE_LABELS[selectedType]?.toLowerCase()} presets saved yet.

)} {/* Capture from current card */} {cardId && endpoints.capturePresetPattern && (
{showCaptureForm ? (
setCaptureName(e.target.value)} placeholder="Preset name…" maxLength={64} required className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-slate-500 outline-none focus:border-sky-400/40" />
) : ( )}
)} {error && (

{error}

)}
) }