Files
SkinbaseNova/resources/js/Pages/Admin/Academy/CrudForm.jsx

105 lines
3.9 KiB
JavaScript

import React from 'react'
import { Head, Link, router, useForm } from '@inertiajs/react'
import AdminLayout from '../../../Layouts/AdminLayout'
import NovaSelect from '../../../components/ui/NovaSelect'
function normalizePayload(fields, data) {
const payload = { ...data }
fields.forEach((field) => {
if (field.type === 'csv') {
payload[field.name] = String(payload[field.name] || '').split(',').map((item) => item.trim()).filter(Boolean)
}
if (field.type === 'json') {
try {
payload[field.name] = payload[field.name] ? JSON.parse(payload[field.name]) : {}
} catch {
payload[field.name] = {}
}
}
})
return payload
}
function Field({ field, form }) {
const value = form.data[field.name]
if (field.type === 'checkbox') {
return (
<label className="flex items-center gap-3 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-slate-200">
<input type="checkbox" checked={Boolean(value)} onChange={(event) => form.setData(field.name, event.target.checked)} />
{field.label}
</label>
)
}
if (field.type === 'textarea') {
return <textarea value={value || ''} onChange={(event) => form.setData(field.name, event.target.value)} rows={6} className="mt-2 w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none" />
}
if (field.type === 'select') {
return (
<NovaSelect
value={value ?? ''}
onChange={(nextValue) => form.setData(field.name, nextValue ?? '')}
options={field.options || []}
searchable={false}
className="mt-2 rounded-2xl bg-black/20"
/>
)
}
if (field.type === 'multiselect') {
return (
<NovaSelect
multi
value={value || []}
onChange={(nextValue) => form.setData(field.name, Array.isArray(nextValue) ? nextValue : [])}
options={field.options || []}
className="mt-2 rounded-2xl bg-black/20"
/>
)
}
return <input type={field.type || 'text'} value={value ?? ''} onChange={(event) => form.setData(field.name, event.target.value)} className="mt-2 w-full rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-sm text-white outline-none" />
}
export default function AcademyCrudForm({ title, subtitle, fields, record, submitUrl, indexUrl, destroyUrl, method }) {
const form = useForm(record)
const submit = (event) => {
event.preventDefault()
const payload = normalizePayload(fields, form.data)
if (method === 'patch') {
form.transform(() => payload).patch(submitUrl)
return
}
form.transform(() => payload).post(submitUrl)
}
return (
<AdminLayout title={title} subtitle={subtitle}>
<Head title={`Admin · ${title}`} />
<form onSubmit={submit} className="space-y-5 rounded-[30px] border border-white/[0.08] bg-white/[0.03] p-6">
{fields.map((field) => (
<div key={field.name}>
{field.type !== 'checkbox' ? <label className="text-sm font-semibold text-white">{field.label}</label> : null}
<Field field={field} form={form} />
{form.errors[field.name] ? <p className="mt-2 text-sm text-rose-300">{form.errors[field.name]}</p> : null}
</div>
))}
<div className="flex flex-wrap gap-3">
<button type="submit" disabled={form.processing} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100">{form.processing ? 'Saving...' : 'Save'}</button>
<Link href={indexUrl} className="rounded-full border border-white/10 bg-white/[0.04] px-5 py-3 text-sm font-semibold text-white">Back</Link>
{destroyUrl ? <button type="button" onClick={() => { if (!window.confirm('Delete this record?')) return; router.delete(destroyUrl) }} className="rounded-full border border-rose-300/20 bg-rose-300/10 px-5 py-3 text-sm font-semibold text-rose-100">Delete</button> : null}
</div>
</form>
</AdminLayout>
)
}