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 ? (
) : (
)}
)}
{error && (
{error}
)}
)
}