'use server'; import { redirect } from 'next/navigation'; import { revalidatePath } from 'next/cache'; import { sql } from '@vercel/postgres'; import { signIn } from '@/auth'; import { z } from 'zod'; const FormSchema = z.object({ id: z.string(), customerId: z.string({ invalid_type_error: "Please select a customer.", }), amount: z.coerce .number() .gt(0, { message: "Please enter an amoint greater than $0." }), status: z.enum(["pending", "paid"], { invalid_type_error: "Please select an invoice status.", }), date: z.string(), }); const CreateInvoice = FormSchema.omit({ id: true, date: true }); const UpdateInvoice = FormSchema.omit({ date: true }); const DeleteInvoice = FormSchema.pick({ id: true }); export type State = { errors?: { customerId?: string[]; amount?: string[]; status?: string[]; }; message?: string | null; } export async function createInvoice(prevState: State, formData: FormData) { // Validate form using Zod const validatedFields = CreateInvoice.safeParse({ customerId: formData.get("customerId"), amount: formData.get("amount"), status: formData.get("status"), }); // If form validation fails, return errors early. Otherwise, continue. if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors, message: "Missing Fields. Failed to Create Invoice.", }; } // Prepare data for insertion into the database const { customerId, amount, status } = validatedFields.data; const amountInCents = amount * 100; const date = new Date().toISOString().split("T")[0]; // Insert data into the database try { await sql` INSERT INTO invoices (customer_id, amount, status, date) VALUES (${customerId}, ${amountInCents}, ${status}, ${date}) `; } catch (error) { // If a database error occurs, return a more specific error. return { message: "Database Error: Failed to Create Invoice.", }; } // Revalidate the cache for the invoices page and redirect the user. revalidatePath("/dashboard/invoices"); redirect("/dashboard/invoices"); } export async function updateInvoice(prevState: State, formData: FormData) { const validatedFields = UpdateInvoice.safeParse({ id: formData.get("id"), customerId: formData.get("customerId"), amount: formData.get("amount"), status: formData.get("status"), }); if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors, message: "Missing Fields. Failed to Update Invoice.", }; } const { id, customerId, amount, status } = validatedFields.data; const amountInCents = amount * 100; try { await sql` UPDATE invoices SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status} WHERE id = ${id} `; } catch (error) { return { message: "Database Error: Failed to Update Invoice." }; } revalidatePath("/dashboard/invoices"); redirect("/dashboard/invoices"); } export async function deleteInvoice(formData: FormData) { const { id } = DeleteInvoice.parse({ id: formData.get("id"), }); try { await sql`DELETE FROM invoices WHERE id = ${id}`; } catch (error) { return { message: 'Database Error: Failed to Delete Invoice.' } } revalidatePath('/dashboard/invoices'); } export async function authenticate( prevState: string | undefined, formData: FormData ) { // Fixed from suggestions in https://github.com/vercel/next-learn/issues/252 let responseRedirectUrl = null; try { responseRedirectUrl = await signIn("credentials", { ...Object.fromEntries(formData), redirect: false, }); } catch (error) { console.log("error", error); if ((error as Error).message.includes("CredentialsSignin")) { return "CredentialSignin"; } throw error; } finally { if (responseRedirectUrl) { redirect(responseRedirectUrl); } } }