import { useState } from "react"; import { Form, Link, redirect, useActionData } from "react-router"; import type { ActionFunctionArgs } from "react-router"; import { createArtist, getIpAddress } from "~/lib/db.server"; export async function action({ request }: ActionFunctionArgs) { const fd = await request.formData(); const name = (fd.get("name") as string).trim(); const slug = (fd.get("slug") as string).trim(); const message = (fd.get("message") as string).trim(); const links: { label: string; url: string }[] = JSON.parse( (fd.get("links") as string) || "[]" ); const errors: Record = {}; if (!name) errors.name = "必須です"; if (!slug) errors.slug = "必須です"; if (!message) errors.message = "必須です"; if (Object.keys(errors).length > 0) return { errors }; const id = crypto.randomUUID(); try { createArtist({ id, slug, name, links, message, ip_address: getIpAddress(request) }); } catch (e) { if (e instanceof Error && e.message.includes("UNIQUE constraint failed: artists.slug")) { return { errors: { slug: "このslugは既に使用されています" } }; } throw e; } return redirect(`/artists/of/${id}`); } function toSlug(s: string) { return s.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^\w぀-ヿ一-鿿＀-￯-]/g, "").replace(/^-+|-+$/g, ""); } export default function ArtistNew() { const actionData = useActionData(); const errors = actionData?.errors ?? {}; const [name, setName] = useState(""); const [slug, setSlug] = useState(""); const [slugManual, setSlugManual] = useState(false); const [links, setLinks] = useState<{ label: string; url: string }[]>([]); return (

New Artist

{ setName(e.target.value); if (!slugManual) setSlug(toSlug(e.target.value)); }} className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-gray-100 focus:outline-none focus:border-blue-500" /> {errors.name &&

{errors.name}

}
{ setSlugManual(true); setSlug(e.target.value); }} className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-gray-100 focus:outline-none focus:border-blue-500 font-mono text-sm" /> {errors.slug &&

{errors.slug}

}
{links.map((link, i) => (
setLinks(links.map((l, idx) => idx === i ? { ...l, label: e.target.value } : l))} placeholder="ラベル (例: X)" className="w-28 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-gray-100 focus:outline-none focus:border-blue-500 text-sm" /> setLinks(links.map((l, idx) => idx === i ? { ...l, url: e.target.value } : l))} placeholder="https://..." className="flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-2 text-gray-100 focus:outline-none focus:border-blue-500 text-sm" />
))}
{errors.message &&

{errors.message}

}
キャンセル
); }