import { Expense } from "@prisma/client"; import type { ActionFunction, LoaderFunction } from "remix"; import { useActionData, redirect, json, useCatch, Link, Form, useTransition, useLoaderData, } from "remix"; import { db } from "~/utils/db.server"; import { requireUserId, getUser } from "~/utils/session.server"; function validateExpenseDescription(description: string) { if (description.length < 2) { return `That expense's description is too short`; } } type ActionData = { formError?: string; fieldErrors?: { description: string | undefined; amount?: string | undefined; user?: string | undefined; }; fields?: { description: string; amount: number; }; }; type LoaderData = { userId: string; expense: Expense; }; const badRequest = (data: ActionData) => json(data, { status: 400 }); export const loader: LoaderFunction = async ({ request, params }) => { const userId = await requireUserId(request); if (!userId) { throw new Response("Unauthorized", { status: 401 }); } const expense = await db.expense.findUnique({ where: { id: params.expenseId }, }); if (!expense) { throw new Response("What an expense! Not found.", { status: 404 }); } const data: LoaderData = { userId, expense }; return data; }; export const action: ActionFunction = async ({ request }) => { const userId = await requireUserId(request); const user = await getUser(request); const form = await request.formData(); const description = form.get("description"); const amount = parseInt(form.get("amount")?.toString() || "0", 10); if ( typeof description !== "string" || typeof amount !== "number" || user === null ) { return badRequest({ formError: `Form not submitted correctly.`, }); } const fieldErrors = { description: validateExpenseDescription(description), }; const fields = { description, amount }; if (Object.values(fieldErrors).some(Boolean)) { return badRequest({ fieldErrors, fields }); } const expense = await db.expense.create({ data: { ...fields, userId: userId, teamId: user.teamId }, }); if (!expense) { return badRequest({ formError: `Could not create expense.`, }); } return redirect(`/expenses/${expense.id}`); }; export default function EditExpenseRoute() { const data = useLoaderData(); const actionData = useActionData(); const transition = useTransition(); if (transition.submission) { const description = transition.submission.formData.get("description"); const amount = transition.submission.formData.get("content"); if ( typeof description === "string" && typeof amount === "number" && !validateExpenseDescription(description) ) { return (

Description: {description}

Amount: {amount}€

User: {data.userId}

); } } return ( <>

Edit expense

{actionData?.fieldErrors?.description && (
)}
{actionData?.formError && (
)}
); } export function CatchBoundary() { const caught = useCatch(); if (caught.status === 401) { return (

You must be logged in to submit an expense.

Login
); } } export function ErrorBoundary() { return (
Something unexpected went wrong. Sorry about that.
); }