105 lines
3.9 KiB
JavaScript
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>
|
|
)
|
|
} |