From 5b87201b28cfc2b4f3d4312d2ade3d31741aad01 Mon Sep 17 00:00:00 2001 From: Chandler Copeland Date: Thu, 19 Mar 2026 16:38:49 -0600 Subject: [PATCH] feat(03-02): add createClient, updateClient, deleteClient server actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 'use server' file with Zod validation (name min 1 char, valid email) - createClient: validate, insert, revalidatePath /portal/clients - updateClient: bind pattern (id, prevState, formData), revalidates client list + profile - deleteClient: auth check, delete by id, revalidatePath /portal/clients - Fixed Zod v4 .issues access (not .errors — v4 API change) --- .../src/lib/actions/clients.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 teressa-copeland-homes/src/lib/actions/clients.ts diff --git a/teressa-copeland-homes/src/lib/actions/clients.ts b/teressa-copeland-homes/src/lib/actions/clients.ts new file mode 100644 index 0000000..d503360 --- /dev/null +++ b/teressa-copeland-homes/src/lib/actions/clients.ts @@ -0,0 +1,83 @@ +"use server"; + +import { z } from "zod"; +import { db } from "@/lib/db"; +import { clients } from "@/lib/db/schema"; +import { auth } from "@/lib/auth"; +import { revalidatePath } from "next/cache"; +import { eq } from "drizzle-orm"; + +const clientSchema = z.object({ + name: z.string().min(1, "Name is required"), + email: z.string().email("Valid email address required"), +}); + +export async function createClient( + _prevState: { error?: string; success?: boolean } | null, + formData: FormData +): Promise<{ error?: string; success?: boolean }> { + const session = await auth(); + if (!session) { + return { error: "Unauthorized" }; + } + + const parsed = clientSchema.safeParse({ + name: formData.get("name"), + email: formData.get("email"), + }); + + if (!parsed.success) { + return { error: parsed.error.issues[0].message }; + } + + await db.insert(clients).values({ + name: parsed.data.name, + email: parsed.data.email, + }); + + revalidatePath("/portal/clients"); + return { success: true }; +} + +export async function updateClient( + id: string, + _prevState: { error?: string; success?: boolean } | null, + formData: FormData +): Promise<{ error?: string; success?: boolean }> { + const session = await auth(); + if (!session) { + return { error: "Unauthorized" }; + } + + const parsed = clientSchema.safeParse({ + name: formData.get("name"), + email: formData.get("email"), + }); + + if (!parsed.success) { + return { error: parsed.error.issues[0].message }; + } + + await db + .update(clients) + .set({ name: parsed.data.name, email: parsed.data.email, updatedAt: new Date() }) + .where(eq(clients.id, id)); + + revalidatePath("/portal/clients"); + revalidatePath("/portal/clients/" + id); + return { success: true }; +} + +export async function deleteClient( + id: string +): Promise<{ error?: string; success?: boolean }> { + const session = await auth(); + if (!session) { + return { error: "Unauthorized" }; + } + + await db.delete(clients).where(eq(clients.id, id)); + + revalidatePath("/portal/clients"); + return { success: true }; +}