borders/app/routes/notes/new.tsx

117 lines
3.1 KiB
TypeScript

import * as React from "react";
import { Form, json, redirect, useActionData } from "remix";
import type { ActionFunction } from "remix";
import Alert from "@reach/alert";
import { createNote } from "~/models/note.server";
import { requireUserId } from "~/session.server";
type ActionData = {
errors?: {
title?: string;
body?: string;
};
};
export const action: ActionFunction = async ({ request }) => {
const userId = await requireUserId(request);
const formData = await request.formData();
const title = formData.get("title");
const body = formData.get("body");
if (typeof title !== "string" || title.length === 0) {
return json<ActionData>(
{ errors: { title: "Title is required" } },
{ status: 400 }
);
}
if (typeof body !== "string" || body.length === 0) {
return json<ActionData>(
{ errors: { body: "Body is required" } },
{ status: 400 }
);
}
const note = await createNote({ title, body, userId });
return redirect(`/notes/${note.id}`);
};
export default function NewNotePage() {
const actionData = useActionData() as ActionData;
const titleRef = React.useRef<HTMLInputElement>(null);
const bodyRef = React.useRef<HTMLTextAreaElement>(null);
React.useEffect(() => {
if (actionData?.errors?.title) {
titleRef.current?.focus();
} else if (actionData?.errors?.body) {
bodyRef.current?.focus();
}
}, [actionData]);
return (
<Form
method="post"
style={{
display: "flex",
flexDirection: "column",
gap: 8,
width: "100%",
}}
>
<div>
<label className="flex w-full flex-col gap-1">
<span>Title: </span>
<input
ref={titleRef}
name="title"
className="flex-1 rounded-md border-2 border-blue-500 px-3 text-lg leading-loose"
aria-invalid={actionData?.errors?.title ? true : undefined}
aria-errormessage={
actionData?.errors?.title ? "title-error" : undefined
}
/>
</label>
{actionData?.errors?.title && (
<Alert className="pt-1 text-red-700" id="title=error">
{actionData.errors.title}
</Alert>
)}
</div>
<div>
<label className="flex w-full flex-col gap-1">
<span>Body: </span>
<textarea
ref={bodyRef}
name="body"
rows={8}
className="w-full flex-1 rounded-md border-2 border-blue-500 py-2 px-3 text-lg leading-6"
aria-invalid={actionData?.errors?.body ? true : undefined}
aria-errormessage={
actionData?.errors?.body ? "body-error" : undefined
}
/>
</label>
{actionData?.errors?.body && (
<Alert className="pt-1 text-red-700" id="body=error">
{actionData.errors.body}
</Alert>
)}
</div>
<div className="text-right">
<button
type="submit"
className="rounded bg-blue-500 py-2 px-4 text-white hover:bg-blue-600 focus:bg-blue-400"
>
Save
</button>
</div>
</Form>
);
}