added error handling for form actions
This commit is contained in:
parent
e23bd2a068
commit
fb6d36d429
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue