Files
boka-gasol247/src/app/[locale]/admin/pickup-slots/actions.ts
2026-05-22 10:50:48 +02:00

102 lines
3.1 KiB
TypeScript

'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { prisma } from '@/lib/prisma';
import { requireAdmin } from '@/lib/requireAdmin';
// datetime-local inputs return "YYYY-MM-DDTHH:mm" in the browser's local
// timezone. We treat them as local time and convert via Date constructor
// (which interprets the string in the server's timezone — for a single-host
// deploy that's fine; document if you ever shard across timezones).
const slotSchema = z
.object({
labelSv: z.string().trim().min(1).max(200),
labelEn: z.string().trim().min(1).max(200),
startsAt: z.string().trim().min(1),
endsAt: z.string().trim().min(1),
capacity: z.coerce.number().int().min(0).max(10000),
active: z.preprocess(
(v) => v === 'on' || v === 'true' || v === true,
z.boolean(),
),
})
.refine((d) => new Date(d.startsAt) < new Date(d.endsAt), {
path: ['endsAt'],
message: 'invalidTime',
});
type Parsed = z.infer<typeof slotSchema>;
function toDbFields(d: Parsed) {
return {
labelSv: d.labelSv,
labelEn: d.labelEn,
startsAt: new Date(d.startsAt),
endsAt: new Date(d.endsAt),
capacity: d.capacity,
active: d.active,
};
}
function fromFormData(fd: FormData): Parsed {
return slotSchema.parse({
labelSv: fd.get('labelSv') ?? '',
labelEn: fd.get('labelEn') ?? '',
startsAt: fd.get('startsAt') ?? '',
endsAt: fd.get('endsAt') ?? '',
capacity: fd.get('capacity') ?? 0,
active: fd.get('active') ?? false,
});
}
export async function createPickupSlot(formData: FormData) {
await requireAdmin();
const data = fromFormData(formData);
await prisma.pickupSlot.create({ data: toDbFields(data) });
revalidatePath('/admin/pickup-slots');
revalidatePath('/');
redirect('/admin/pickup-slots');
}
export async function updatePickupSlot(formData: FormData) {
await requireAdmin();
const id = String(formData.get('id') ?? '');
if (!id) return;
const data = fromFormData(formData);
await prisma.pickupSlot.update({ where: { id }, data: toDbFields(data) });
revalidatePath('/admin/pickup-slots');
revalidatePath(`/admin/pickup-slots/${id}`);
revalidatePath('/');
redirect('/admin/pickup-slots');
}
export async function togglePickupSlotActive(formData: FormData) {
await requireAdmin();
const id = String(formData.get('id') ?? '');
if (!id) return;
const cur = await prisma.pickupSlot.findUnique({ where: { id } });
if (!cur) return;
await prisma.pickupSlot.update({
where: { id },
data: { active: !cur.active },
});
revalidatePath('/admin/pickup-slots');
revalidatePath('/');
}
export async function deletePickupSlot(formData: FormData) {
await requireAdmin();
const id = String(formData.get('id') ?? '');
if (!id) return;
const inUse = await prisma.booking.count({ where: { pickupSlotId: id } });
if (inUse > 0) {
redirect(`/admin/pickup-slots/${id}?error=in-use&count=${inUse}`);
}
await prisma.pickupSlot.delete({ where: { id } });
revalidatePath('/admin/pickup-slots');
revalidatePath('/');
redirect('/admin/pickup-slots');
}