feat(03-02): add createClient, updateClient, deleteClient server actions

- '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)
This commit is contained in:
Chandler Copeland
2026-03-19 16:38:49 -06:00
parent 28d42f5d9b
commit 5b87201b28

View File

@@ -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 };
}