added error handling for form actions

This commit is contained in:
Joonas 2023-01-12 19:26:01 +02:00
parent e23bd2a068
commit fb6d36d429
4 changed files with 58 additions and 33 deletions

View File

@ -2,7 +2,7 @@ import Header from "./Header";
export default function Overlay({ children }) {
return (
<div className="container max-w-4xl mx-auto">
<div className="container mx-auto">
<Header/>
{children}
</div>

View File

@ -1,9 +1,10 @@
import { useLoaderData, Form } from "@remix-run/react";
import { useLoaderData, useActionData, Form } from "@remix-run/react";
import { unstable_createFileUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
import prisma from "~/utils/db.server";
import { redirect } from "@remix-run/node";
import { Link } from "@remix-run/react";
import Overlay from "~/components/Overlay";
import { json } from "@remix-run/node";
export async function action({ request }) {
const clonedData = request.clone();
@ -17,8 +18,25 @@ export async function action({ request }) {
file: ({ filename }) => filename,
});
const multiPartformdata = await unstable_parseMultipartFormData(request, fileUploadHandler);
const imageName = multiPartformdata.get("image").name;
const errors = {};
let multiPartformdata;
let imageName;
try {
multiPartformdata = await unstable_parseMultipartFormData(request, fileUploadHandler);
imageName = multiPartformdata.get("image").name;
} catch (err) {
errors.image = "Image size too big or something elase happened";
}
if (typeof title !== "string" || title.length > 50) {
errors.title = "Title too long or not a string";
};
if (typeof post !== "string" || post.length > 50) {
errors.post = "Post too long or not a string";
};
if (Object.keys(errors).length) {
return json(errors, { status: 422 });
}
const newThread = await prisma.thread.create({
data: {
@ -43,18 +61,27 @@ export async function loader() {
export default function Index() {
const data = useLoaderData();
console.log(data)
const errors = useActionData();
return (
<Overlay>
<div className="bg-slate-100 border p-4 space-y-4">
<h1 className="text-center font-semibold text-2xl text-gray-800">Create a thread</h1>
<Form className="flex flex-col text-center space-y-1 justify-center items-center " method="post" encType="multipart/form-data">
<label htmlFor="title-input">Title: <input required type="text" name="title" placeholder="Lain" /></label>
<Form className="rounded flex flex-col text-center space-y-1 justify-center items-center " method="post" encType="multipart/form-data">
<label htmlFor="title-input">Title:
<input required minLength={3} type="text" name="title" placeholder="Lain" />
<p>{errors ? errors.title : ''}</p>
</label>
<br />
<label htmlFor="post-input">Post: <input required type="text" name="post" placeholder="Iwakura" /></label>
<label htmlFor="post-input">Post:
<input required minLength={3} type="text" name="post" placeholder="Iwakura" />
<p>{errors ? errors.post : ''}</p>
</label>
<br />
<label htmlFor="image-upload">Image: (500kb max) <br /> <input required accept="image/*" type="file" id="image-upload" name="image" /></label>
<label htmlFor="image-upload">Image: (500kb max) <br />
<input required accept="image/*" type="file" id="image-upload" name="image" />
<p>{errors ? errors.image : ''}</p>
</label>
<br />
<button className="bg-white py-2 px-4 rounded-full" type="submit">Submit</button>
</Form>
@ -63,8 +90,10 @@ export default function Index() {
<ul className="flex flex-wrap justify-around items-center p-8">
{data.map(thread =>
<li className="shadow rounded-xl p-4 m-4 hover:shadow-xl scale-100 hover:scale-110 duration-500" key={thread.id}>
<Link className="space-y-4" to={`threads/${thread.id}`}>
<img className="w-48 rounded" src={`/uploads/${thread.imageName}`} alt="" />
<Link className="space-y-4 flex flex-col" to={`threads/${thread.id}`}>
<div className="flex items-center justify-center">
<img className="w-48 rounded " src={`/uploads/${thread.imageName}`} alt="" />
</div>
<div>
<h2 className="text-2xl text-center tracking-tighter text-gray-700">{thread.title}</h2>
<p className="text-gray-400 text-center">{thread.post}</p>

View File

@ -1,5 +1,5 @@
import { redirect, unstable_createFileUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
import { useLoaderData, Form } from "@remix-run/react";
import { redirect, unstable_createFileUploadHandler, unstable_parseMultipartFormData, json } from "@remix-run/node";
import { useLoaderData, Form, useActionData } from "@remix-run/react";
import Overlay from "~/components/Overlay";
import prisma from "~/utils/db.server";
@ -20,6 +20,14 @@ export async function action({ request, params }) {
let imageName;
multiPartformdata.get("image") !== null ? imageName = multiPartformdata.get("image").name : imageName = null;
const errors = {};
if (typeof post !== "string" || post.length > 50) {
errors.post = "Post too long or not a string";
};
if (Object.keys(errors).length) {
return json(errors, { status: 422 });
}
const createPost = await prisma.post.create({
data: {
comment: post,
@ -47,10 +55,11 @@ export async function loader({ params }) {
export default function Thread() {
const data = useLoaderData();
const actionData = useActionData();
return (
<Overlay>
<div className="flex flex-col ">
<div className="flex flex-col p-4">
<div className="">
<span><strong>{data.title}</strong> Post id: <strong>{data.id}</strong> Created at: <strong>{data.createdAt}</strong> Reply count: <strong>{data.posts.length}</strong></span>
<br />
@ -69,9 +78,10 @@ export default function Thread() {
)}
</ul>
</div>
<Form className="bg-slate-100 p-2 space-y-2 shadow flex flex-col justify-center items-center" method="post" encType="multipart/form-data">
<Form className="bg-slate-100 p-4 space-y-2 shadow flex flex-col justify-center items-center" method="post" encType="multipart/form-data">
<label htmlFor="text-input">
Post: <input required minLength={3} name="post" type="text" />
{actionData ? <p>{actionData.post}</p> : ''}
</label>
<label htmlFor="image-upload">
Image: <input accept="image/*" type="file" name="image" id="image-upload" />

View File

@ -572,10 +572,6 @@ video {
width: 12rem;
}
.max-w-4xl {
max-width: 56rem;
}
.scale-100 {
--tw-scale-x: 1;
--tw-scale-y: 1;
@ -626,6 +622,10 @@ video {
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
}
.rounded {
border-radius: 0.25rem;
}
.rounded-full {
border-radius: 9999px;
}
@ -634,10 +634,6 @@ video {
border-radius: 0.75rem;
}
.rounded {
border-radius: 0.25rem;
}
.border {
border-width: 1px;
}
@ -730,16 +726,6 @@ video {
color: rgb(156 163 175 / var(--tw-text-opacity));
}
.text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
.text-gray-200 {
--tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));