import { useState } from "react"; import { data, Form, Link, redirect, useActionData, useLoaderData } from "react-router"; import type { ActionFunctionArgs, LoaderFunctionArgs } from "react-router"; import { getBandListById, getListEntries, getIpAddress, updateBandList, type ListEntryInput, } from "~/lib/db.server"; export async function loader({ params }: LoaderFunctionArgs) { const list = getBandListById(params.uuid!); if (!list) throw data("Not found", { status: 404 }); const entries = getListEntries(list.id); return { list, entries }; } export async function action({ params, request }: ActionFunctionArgs) { const list = getBandListById(params.uuid!); if (!list) throw data("Not found", { status: 404 }); const fd = await request.formData(); const title = (fd.get("title") as string).trim(); const slug = (fd.get("slug") as string).trim(); const description = (fd.get("description") as string).trim(); const message = (fd.get("message") as string).trim(); const entries: ListEntryInput[] = JSON.parse((fd.get("entries") as string) || "[]"); const errors: Record = {}; if (!title) errors.title = "必須です"; if (!slug) errors.slug = "必須です"; if (!message) errors.message = "必須です"; if (Object.keys(errors).length > 0) return { errors }; try { updateBandList(list.id, { slug, title, description, entries, message, ip_address: getIpAddress(request) }); } catch (e) { if (e instanceof Error && e.message.includes("UNIQUE constraint failed: lists.slug")) { return { errors: { slug: "このslugは既に使用されています" } }; } throw e; } return redirect(`/lists/of/${list.id}`); } type EntryRow = { key: string; band_name: string; note: string }; export default function ListEdit() { const { list, entries: initEntries } = useLoaderData(); const actionData = useActionData(); const errors = actionData?.errors ?? {}; const [title, setTitle] = useState(list.title); const [slug, setSlug] = useState(list.slug); const [description, setDescription] = useState(list.description); const [entries, setEntries] = useState( initEntries.map((e) => ({ key: crypto.randomUUID(), band_name: e.band_name, note: e.note })) ); function addEntry() { setEntries((prev) => [...prev, { key: crypto.randomUUID(), band_name: "", note: "" }]); } function removeEntry(key: string) { setEntries((prev) => prev.filter((e) => e.key !== key)); } function updateEntry(key: string, field: "band_name" | "note", value: string) { setEntries((prev) => prev.map((e) => e.key === key ? { ...e, [field]: value } : e)); } return (

{list.title} — 編集

({ band_name, note })))} />
setTitle(e.target.value)} /> {errors.title &&

{errors.title}

}
setSlug(e.target.value)} className="mono" /> {errors.slug &&

{errors.slug}

}
setDescription(e.target.value)} />
{entries.map((entry) => (
updateEntry(entry.key, "band_name", e.target.value)} placeholder="バンド名" /> updateEntry(entry.key, "note", e.target.value)} placeholder="メモ" />
))}
{errors.message &&

{errors.message}

}
キャンセル
); }