From 62f0e75abd9c103aafd539dfc169d1b3d40242d9 Mon Sep 17 00:00:00 2001 From: andrzej Date: Thu, 19 Sep 2024 11:37:01 +0200 Subject: [PATCH] fix: create server actions --- prisma/dev.db | Bin 69632 -> 69632 bytes src/app/lib/create.ts | 90 ++++++++++++++++++++------------ src/app/login/page.tsx | 2 +- src/app/publication/columns.tsx | 8 +-- src/app/story/columns.tsx | 6 +-- src/app/ui/forms/pub.tsx | 40 +++++++------- src/app/ui/forms/schemas.ts | 46 ++++++++++++++++ src/app/ui/forms/story.tsx | 3 +- src/app/ui/forms/sub.tsx | 74 ++++++-------------------- 9 files changed, 146 insertions(+), 123 deletions(-) diff --git a/prisma/dev.db b/prisma/dev.db index 761320fe59a40421387616d5b8485af098227177..c1f3f25d4e2dd31fcc0bb07069b4f07b8e7a0eef 100644 GIT binary patch delta 325 zcmZozz|ydQWr8$g(nJ|&#-xo2js9Fl?7Ymvi~*%d0+V0+OG2JNotWoNM>%T+~$ArYzk7`d}{^xyZBe|XYj4%yU6dz&(D9D?kBcjs9Hv?7Ymvi~*%de3M`MOG>cwU1#Dy$#rMBJo GG5`SBXIhc~ diff --git a/src/app/lib/create.ts b/src/app/lib/create.ts index 31850d6..80af2c2 100644 --- a/src/app/lib/create.ts +++ b/src/app/lib/create.ts @@ -1,14 +1,16 @@ "use server" -import { Genre, Story } from "@prisma/client" +import { Genre, Pub, Story, Sub } from "@prisma/client" import prisma from "./db" import { revalidatePath } from "next/cache" import { redirect } from "next/navigation" import { z } from "zod" import { storySchema } from "app/ui/forms/schemas" +import { pubSchema } from "app/ui/forms/schemas" +import { subSchema } from "app/ui/forms/schemas" //TODO - data validation, error handling, unauthorized access handling -export async function createStory(data: Story & { genres: number[] }): Promise | undefined { +export async function createStory(data: Story & { genres: number[] }): Promise { // will return undefined if middleware authorization fails "use server" //Prepare data @@ -21,11 +23,12 @@ export async function createStory(data: Story & { genres: number[] }): Promise { "use server" + //prepare data + const pubData = { + title: data.title, + link: data.link, + query_after_days: data.query_after_days + } const genresArray = data.genres.map(e => { return { id: e } }) - const res = await prisma.pub.create({ - data: { - title: data.title, - link: data.link, - query_after_days: data.query_after_days - } - }) - console.log(res) - const genresRes = await prisma.pub.update({ - where: { id: res.id }, - data: - { genres: { set: genresArray } } - }) - console.log(genresRes) - revalidatePath("/publication") - redirect("/publication") + + //prepare schemas + const schema = pubSchema.omit({ genres: true }) + const genreSchema = z.object({ id: z.number() }) + const genresSchema = z.array(genreSchema) + + try { + + //validate + schema.parse(pubData) + genresSchema.safeParse(genresArray) + + //submit + const res = await prisma.pub.create({ + data: pubData + }) + const genresRes = await prisma.pub.update({ + where: { id: res.id }, + data: + { genres: { set: genresArray } } + }) + revalidatePath("/publication") + return res + } catch (error) { + console.error(error) + return undefined + } + } -export async function createSub(data) { +export async function createSub(data: Sub): Promise { "use server" - const res = await prisma.sub.create({ data }) - console.log(res) - revalidatePath("/submission") - redirect("/submission") - + try { + subSchema.parse(data) + const res = await prisma.sub.create({ data }) + revalidatePath("/submission") + return res + } catch (error) { + console.error(error) + return undefined + } } diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 93bf5d0..74eaece 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -17,7 +17,7 @@ import Link from "next/link"; export default function LoginForm() { const router = useRouter() const searchParams = useSearchParams() - const redirect = searchParams.get("from") + const redirect = searchParams.get("from") ?? "submission" const form = useForm>({ resolver: zodResolver(loginSchema), }) diff --git a/src/app/publication/columns.tsx b/src/app/publication/columns.tsx index 2962191..df3c487 100644 --- a/src/app/publication/columns.tsx +++ b/src/app/publication/columns.tsx @@ -7,7 +7,7 @@ import { PubsWithGenres } from "./page" import { TextInputCell } from "app/ui/tables/inputs/textInput" import { selectCol } from "app/ui/tables/selectColumn" import NumberInputCell from "app/ui/tables/inputs/numberInput" -import { formSchema } from "app/ui/forms/pub" +import { pubSchema } from "app/ui/forms/schemas" import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput" @@ -29,14 +29,14 @@ export const columns: ColumnDef[] = [ ) }, cell: TextInputCell, - meta: { formSchema } + meta: { formSchema: pubSchema } }, { accessorKey: "link", header: "Link", cell: TextInputCell, - meta: { formSchema } + meta: { formSchema: pubSchema } }, columnHelper.accessor("genres", { @@ -51,7 +51,7 @@ export const columns: ColumnDef[] = [ cell: NumberInputCell, meta: { step: 10, - formSchema + formSchema: pubSchema }, }, diff --git a/src/app/story/columns.tsx b/src/app/story/columns.tsx index a7fbf28..cc189b2 100644 --- a/src/app/story/columns.tsx +++ b/src/app/story/columns.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button" import GenreBadges from "app/ui/genreBadges" import { selectCol } from "app/ui/tables/selectColumn" import NumberInputCell from "app/ui/tables/inputs/numberInput" -import { formSchema } from "app/ui/forms/story" +import { storySchema } from "app/ui/forms/schemas" import { TextInputCell } from "app/ui/tables/inputs/textInput" import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput" const columnHelper = createColumnHelper() @@ -27,7 +27,7 @@ export const columns: ColumnDef[] = [ ) }, cell: TextInputCell, - meta: { formSchema } + meta: { formSchema: storySchema } }, { @@ -47,7 +47,7 @@ export const columns: ColumnDef[] = [ cell: NumberInputCell, meta: { step: 50, - formSchema + formSchema: storySchema } }, columnHelper.accessor("genres", { diff --git a/src/app/ui/forms/pub.tsx b/src/app/ui/forms/pub.tsx index 43bd326..149db14 100644 --- a/src/app/ui/forms/pub.tsx +++ b/src/app/ui/forms/pub.tsx @@ -19,17 +19,11 @@ import { randomPublicationTitle } from "app/lib/shortStoryTitleGenerator" import { ComponentProps } from "react" import { Genre } from "@prisma/client" import GenrePicker from "./genrePicker" - -export const formSchema = z.object({ - title: z.string().min(2).max(50), - link: z.string(), - query_after_days: z.coerce.number().min(30), - genres: z.array(z.number()), -}) +import { pubSchema } from "./schemas" export default function PubForm({ genres, createPub, className }: ComponentProps<"div"> & { genres: Array, createPub: (data: any) => void }) { - const form = useForm>({ - resolver: zodResolver(formSchema), + const form = useForm>({ + resolver: zodResolver(pubSchema), defaultValues: { title: "", link: "", @@ -38,19 +32,21 @@ export default function PubForm({ genres, createPub, className }: ComponentProps }, }) - function onSubmit(values: z.infer) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - toast({ - title: "You submitted the following values:", - description: ( -
-					{JSON.stringify(values, null, 2)}
-				
- ), - }) - createPub(values) - console.log(values) + async function onSubmit(values: z.infer) { + try { + const res = await createPub(values) + if (res === undefined) throw new Error("something went wrong") + toast({ + title: "Successfuly submitted:", + description: values.title + }) + } catch (error) { + toast({ + title: "UH-OH", + description: error.message + }) + } + window.location.reload() } function onErrors(errors) { diff --git a/src/app/ui/forms/schemas.ts b/src/app/ui/forms/schemas.ts index 753b98c..08580c8 100644 --- a/src/app/ui/forms/schemas.ts +++ b/src/app/ui/forms/schemas.ts @@ -5,3 +5,49 @@ export const storySchema = z.object({ word_count: z.number(), genres: z.object({ id: z.number(), name: z.string() }).array() }) + +export const pubSchema = z.object({ + title: z.string().min(2).max(50), + link: z.string(), + query_after_days: z.coerce.number().min(30), + genres: z.array(z.number()), +}) + +export const subSchema = z.object({ + id: z.number().optional(), + storyId: z.coerce.number(), + pubId: z.coerce.number(), + submitted: z.coerce.date().transform((date) => date.toString()), + responded: z.coerce.date().transform((date) => { + if (date.toString() !== new Date(null).toString()) { + return date.toString() + } + return null + }).optional(), + responseId: z.coerce.number() +}) + .refine(object => { + const submitted = new Date(object.submitted) + const responded = object.responded ? new Date(object.responded) : null + return responded >= submitted || responded === null + }, + { + path: ["responded"], + message: "'Responded' must be a later date than 'submitted'" + }) + .refine(object => { + if (object.responded) { + //there is a 'responded' date and the response is not 'pending' + return object.responseId !== 7 + } + if (!object.responded) { + //there is not a 'responded' date and the response is pending + return object.responseId === 7 + } + }, + { + path: ["responseId"], + message: "A pending response cannot have a date, and a non-pending response must have a date" + } + ) + diff --git a/src/app/ui/forms/story.tsx b/src/app/ui/forms/story.tsx index 233e8d6..a689a9f 100644 --- a/src/app/ui/forms/story.tsx +++ b/src/app/ui/forms/story.tsx @@ -49,14 +49,13 @@ export default function StoryForm({ genres, createStory, className, existingData title: "Successfully submitted:", description: values.title, }) - //TODO refresh data without reloading page? } catch (error) { toast({ title: "UH-OH", description: error.message }) - console.error(error) } + window.location.reload() } diff --git a/src/app/ui/forms/sub.tsx b/src/app/ui/forms/sub.tsx index 444a980..c81ea00 100644 --- a/src/app/ui/forms/sub.tsx +++ b/src/app/ui/forms/sub.tsx @@ -32,54 +32,15 @@ import { SelectValue, } from "@/components/ui/select" import { useState } from "react" -import { editSubmission } from "app/lib/edit" import { createSub } from "app/lib/create" +import { subSchema } from "./schemas" -export const formSchema = z.object({ - id: z.number().optional(), - storyId: z.coerce.number(), - pubId: z.coerce.number(), - submitted: z.coerce.date().transform((date) => date.toString()), - responded: z.coerce.date().transform((date) => { - - if (date.toString() !== new Date(null).toString()) { - return date.toString() - } - return null - }).optional(), - responseId: z.coerce.number() -}) - .refine(object => { - const submitted = new Date(object.submitted) - const responded = object.responded ? new Date(object.responded) : null - return responded >= submitted || responded === null - }, - { - path: ["responded"], - message: "'Responded' must be a later date than 'submitted'" - }) - .refine(object => { - if (object.responded) { - //there is a 'responded' date and the response is not 'pending' - return object.responseId !== 7 - } - if (!object.responded) { - //there is not a 'responded' date and the response is pending - return object.responseId === 7 - } - }, - { - path: ["responseId"], - message: "A pending response cannot have a date, and a non-pending response must have a date" - } - ) - -export type SubForm = z.infer +export type SubForm = z.infer export default function SubmissionForm({ stories, pubs, responses, defaults }) { - const form = useForm>({ - resolver: zodResolver(formSchema), + const form = useForm>({ + resolver: zodResolver(subSchema), defaultValues: { responseId: responses[0].id, ...defaults @@ -106,23 +67,18 @@ export default function SubmissionForm({ stories, pubs, responses, defaults }) { // 2. Define a submit handler. - function onSubmit(values: z.infer) { - // Do something with the form values. - // ✅ This will be type-safe and validated. - toast({ - title: "You submitted the following values:", - description: ( -
-					{JSON.stringify(values, null, 2)}
-				
- ), - }) - if (values.id) { - editSubmission(values) - } else { - createSub(values) + async function onSubmit(values: z.infer) { + try { + const res = await createSub(values) + if (res === undefined) throw new Error("something went wrong") + toast({ title: "Successfully created new submission!" }) + } catch (error) { + toast({ + title: "UH-OH", + description: error.message + }) } - console.log(values) + window.location.reload() } function onErrors(errors) {