/* ════════════════════════════════════════════════ Dashboard — modul Konten generik + Donasi + Pengaturan ════════════════════════════════════════════════ */ /* Skema field per jenis konten */ const PAGE_OPTS = [ { value: 'articles', label: 'Halaman Artikel' }, { value: 'donasi', label: 'Halaman Donasi' }, { value: 'register', label: 'Pendaftaran Santri' }, { value: 'programs', label: 'Halaman Program' }, { value: 'kegiatan', label: 'Halaman Kegiatan' }, { value: 'toko', label: 'Toko Buku' }, { value: 'about', label: 'Tentang Kami' }, { value: 'contact', label: 'Kontak' }, ]; const ACCENT_OPTS = [ { value: '#1F6B47', label: 'Hijau (Sage)' }, { value: '#1B2F6E', label: 'Biru (Navy)' }, { value: '#C26F12', label: 'Oranye' }, { value: '#8B5A2B', label: 'Coklat' }, ]; const KIND_GLYPH = { banner: 'scroll', program: 'cap', teacher: 'mosque', testimonial: 'scroll', fasilitas: 'mosque', kegiatan: 'calendar', buku: 'book' }; const KIND_SCHEMA = { banner: { title: 'Banner Beranda', singular: 'Banner', icon: '🖼️', fields: [ { key: 'title', label: 'Judul Banner', type: 'text', primary: true }, { key: 'subtitle', label: 'Subjudul / Deskripsi', type: 'textarea' }, { key: 'tag', label: 'Label kecil (mis. Artikel Baru)', type: 'text' }, { key: 'image_url', label: 'Gambar Latar', type: 'image' }, { key: 'cta_label', label: 'Teks Tombol (mis. Baca Selengkapnya)', type: 'text' }, { key: 'cta_target', label: 'Tombol Menuju Halaman', type: 'select', options: PAGE_OPTS }, { key: 'accent', label: 'Warna Aksen', type: 'select', options: ACCENT_OPTS }, ], }, program: { title: 'Program', singular: 'Program', icon: '🎓', fields: [ { key: 'name', label: 'Nama Program', type: 'text', primary: true }, { key: 'icon', label: 'Ikon (emoji)', type: 'text' }, { key: 'desc', label: 'Deskripsi', type: 'textarea' }, { key: 'schedule', label: 'Jadwal', type: 'text' }, { key: 'levels', label: 'Level (pisahkan koma)', type: 'list' }, { key: 'banner_url', label: 'Gambar Banner (opsional)', type: 'image' }, { key: 'youtube_url', label: 'Link Video YouTube (opsional)', type: 'text' }, { key: 'featured', label: 'Tampilkan di Beranda?', type: 'toggle' }, { key: 'banner_home', label: 'Jadikan Banner Slideshow Beranda?', type: 'toggle' }, ], }, teacher: { title: 'Pengajar', singular: 'Pengajar', icon: '👤', fields: [ { key: 'name', label: 'Nama', type: 'text', primary: true }, { key: 'title', label: 'Jabatan / Keahlian', type: 'text' }, { key: 'bio', label: 'Bio singkat', type: 'textarea' }, { key: 'photo_url', label: 'Foto', type: 'image' }, { key: 'tags', label: 'Keahlian (pisahkan koma)', type: 'list' }, ], }, testimonial: { title: 'Testimoni', singular: 'Testimoni', icon: '⭐', fields: [ { key: 'name', label: 'Nama', type: 'text', primary: true }, { key: 'role', label: 'Peran (mis. Wali Santri)', type: 'text' }, { key: 'text', label: 'Isi Testimoni', type: 'textarea' }, { key: 'stars', label: 'Bintang (1-5)', type: 'number' }, ], }, fasilitas: { title: 'Fasilitas', singular: 'Fasilitas', icon: '🏫', fields: [ { key: 'caption', label: 'Nama Fasilitas', type: 'text', primary: true }, { key: 'img_url', label: 'Foto', type: 'image' }, { key: 'emoji', label: 'Ikon (emoji)', type: 'text' }, ], }, kegiatan: { title: 'Kegiatan', singular: 'Kegiatan', icon: '📅', fields: [ { key: 'title', label: 'Judul Kegiatan', type: 'text', primary: true }, { key: 'date', label: 'Tanggal', type: 'text' }, { key: 'time', label: 'Waktu (opsional)', type: 'text' }, { key: 'location', label: 'Lokasi (opsional)', type: 'text' }, { key: 'desc', label: 'Deskripsi', type: 'textarea' }, { key: 'img_url', label: 'Foto', type: 'image' }, { key: 'youtube_url', label: 'Link Video YouTube (opsional)', type: 'text' }, { key: 'featured', label: 'Tampilkan di Beranda?', type: 'toggle' }, { key: 'banner_home', label: 'Jadikan Banner Slideshow Beranda?', type: 'toggle' }, ], }, buku: { title: 'Toko Buku', singular: 'Buku', icon: '🛒', fields: [ { key: 'title', label: 'Judul Buku', type: 'text', primary: true }, { key: 'author', label: 'Penulis', type: 'text' }, { key: 'price', label: 'Harga (Rp)', type: 'number' }, { key: 'originalPrice', label: 'Harga Coret (opsional)', type: 'number' }, { key: 'desc', label: 'Deskripsi', type: 'textarea' }, { key: 'cover_url', label: 'Sampul', type: 'image' }, { key: 'emoji', label: 'Ikon (emoji)', type: 'text' }, { key: 'stock', label: 'Stok', type: 'number' }, { key: 'categories', label: 'Kategori (pisahkan koma)', type: 'list' }, { key: 'featured', label: 'Unggulan (badge)?', type: 'toggle' }, { key: 'banner_home', label: 'Jadikan Banner Slideshow Beranda?', type: 'toggle' }, ], }, }; function ContentListModule({ section, kind, toast }) { const schema = KIND_SCHEMA[kind]; const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [modal, setModal] = useState(null); const [form, setForm] = useState({}); const [saving, setSaving] = useState(false); const load = () => { setLoading(true); // mula-mula dari data lokal sebagai fallback const seed = (window.MQA || {})[section] || []; api.get(`content.php?action=get_section§ion=${section}`).then(r => { const arr = (r.ok && r.data && Array.isArray(r.data.items)) ? r.data.items : seed; setItems(arr); }).catch(() => setItems(seed)).finally(() => setLoading(false)); }; useEffect(load, [section]); const persist = async (next) => { setItems(next); const r = await api.post('content.php?action=save', { section, data: { items: next } }); if (r.ok) toast('Tersimpan'); else toast('Gagal menyimpan'); }; const primaryKey = schema.fields.find(f => f.primary).key; const blank = () => { const o = {}; schema.fields.forEach(f => o[f.key] = f.type === 'list' ? [] : f.type === 'toggle' ? false : ''); return o; }; const openAdd = () => { setForm(blank()); setModal({}); }; const openEdit = (it, i) => { setForm({ ...it }); setModal({ i }); }; const save = async () => { if (!form[primaryKey]) return; setSaving(true); // normalisasi number/list const clean = { ...form }; schema.fields.forEach(f => { if (f.type === 'number') clean[f.key] = clean[f.key] === '' || clean[f.key] == null ? null : Number(clean[f.key]); }); let next; if (modal.i != null) next = items.map((x, i) => i === modal.i ? { ...x, ...clean } : x); else next = [{ id: Date.now(), ...clean }, ...items]; await persist(next); setSaving(false); setModal(null); }; const del = async (i) => { if (!window.confirm('Hapus item ini?')) return; await persist(items.filter((_, j) => j !== i)); }; return (

Kelola {schema.title.toLowerCase()} yang tampil di situs.

+ Tambah {schema.singular}
{loading ?
Memuat…
: items.length === 0 ?
Belum ada {schema.title.toLowerCase()}.
:
{items.map((it, i) => { const img = it.cover_url || it.img_url || it.photo_url; return (
{img ? : }
{it[primaryKey]}
{it.desc || it.text || it.title || it.role || it.author || it.schedule || ''}
openEdit(it, i)} style={{ flex: 1 }}>Edit del(i)}>Hapus
); })}
} setModal(null)} title={modal && modal.i != null ? `Edit ${schema.singular}` : `Tambah ${schema.singular}`} width="560px"> {modal && schema.fields.map(f => { const val = form[f.key]; if (f.type === 'image') return setForm(s => ({ ...s, [f.key]: v }))} ratio={kind === 'buku' || kind === 'teacher' ? '3/4' : '16/9'} />; if (f.type === 'textarea') return