pelagia-portal/App/app/(portal)/admin/users/actions.ts
2026-05-18 23:18:58 +05:30

133 lines
4.5 KiB
TypeScript

"use server";
import { auth } from "@/auth";
import { db } from "@/lib/db";
import { hasPermission } from "@/lib/permissions";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { revalidatePath } from "next/cache";
import type { Role } from "@prisma/client";
type ActionResult = { ok: true } | { error: string };
const userSchema = z.object({
employeeId: z.string().min(1, "Employee ID is required"),
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email"),
role: z.enum(["TECHNICAL", "MANNING", "ACCOUNTS", "MANAGER", "SUPERUSER", "AUDITOR", "ADMIN"]),
password: z.string().min(8, "Password must be at least 8 characters").optional(),
});
export async function createUser(formData: FormData): Promise<ActionResult> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_users")) {
return { error: "Unauthorized" };
}
const parsed = userSchema.safeParse({
employeeId: formData.get("employeeId"),
name: formData.get("name"),
email: formData.get("email"),
role: formData.get("role"),
password: formData.get("password") || undefined,
});
if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
const data = parsed.data;
if (!data.password) return { error: "Password is required for new users" };
const exists = await db.user.findFirst({
where: { OR: [{ email: data.email }, { employeeId: data.employeeId }] },
});
if (exists) return { error: "A user with that email or employee ID already exists" };
const passwordHash = await bcrypt.hash(data.password, 12);
await db.user.create({
data: {
employeeId: data.employeeId,
name: data.name,
email: data.email,
role: data.role as Role,
passwordHash,
},
});
revalidatePath("/admin/users");
return { ok: true };
}
export async function updateUser(formData: FormData): Promise<ActionResult> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_users")) {
return { error: "Unauthorized" };
}
const id = formData.get("id") as string;
if (!id) return { error: "User ID is required" };
const parsed = userSchema.safeParse({
employeeId: formData.get("employeeId"),
name: formData.get("name"),
email: formData.get("email"),
role: formData.get("role"),
password: formData.get("password") || undefined,
});
if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
const data = parsed.data;
const conflict = await db.user.findFirst({
where: {
AND: [
{ id: { not: id } },
{ OR: [{ email: data.email }, { employeeId: data.employeeId }] },
],
},
});
if (conflict) return { error: "Another user already has that email or employee ID" };
const updateData: Parameters<typeof db.user.update>[0]["data"] = {
employeeId: data.employeeId,
name: data.name,
email: data.email,
role: data.role as Role,
};
if (data.password) {
updateData.passwordHash = await bcrypt.hash(data.password, 12);
}
await db.user.update({ where: { id }, data: updateData });
revalidatePath("/admin/users");
return { ok: true };
}
export async function deleteUser(id: string): Promise<ActionResult> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_users")) return { error: "Unauthorized" };
if (id === session.user.id) return { error: "Cannot delete your own account." };
const inUse = await db.purchaseOrder.findFirst({ where: { submitterId: id } });
if (inUse) return { error: "Cannot delete: user has submitted purchase orders. Deactivate them instead." };
await db.$transaction(async (tx) => {
await tx.notification.deleteMany({ where: { userId: id } });
await tx.user.delete({ where: { id } });
});
revalidatePath("/admin/users");
return { ok: true };
}
export async function toggleUserActive(userId: string): Promise<ActionResult> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_users")) {
return { error: "Unauthorized" };
}
if (userId === session.user.id) return { error: "You cannot deactivate your own account" };
const user = await db.user.findUnique({ where: { id: userId }, select: { isActive: true } });
if (!user) return { error: "User not found" };
await db.user.update({ where: { id: userId }, data: { isActive: !user.isActive } });
revalidatePath("/admin/users");
return { ok: true };
}