Files
craft2prints/src/app/admin/gallery/actions.ts
T
AndymickandClaude Haiku 4.5 36cc6dddff Feature: Add WhatsApp notifications for collection-ready orders
- Implement WhatsApp Business API integration via Meta Cloud API
- Add phoneNumber field to Order model for storing customer phone numbers
- Update admin order detail page with phone number input for collection orders
- Send WhatsApp notification when admin marks order as ready for collection
- Add WhatsApp service module with phone number formatting and message sending
- Create database migrations for collection-related fields
- Add WhatsApp credentials to .env configuration

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-19 09:29:09 +01:00

84 lines
2.2 KiB
TypeScript

'use server';
import { redirect } from 'next/navigation';
import { prisma } from '@/lib/prisma';
function slugify(input: string) {
return input
.toUpperCase()
.trim()
.replace(/[^A-Z0-9]+/g, '_')
.replace(/^_+|_+$/g, '');
}
async function uniqueSlug(base: string) {
let slug = base || 'GALLERY';
let n = 2;
while (await prisma.galleryCategory.findUnique({ where: { slug } })) {
slug = `${base}_${n}`;
n += 1;
}
return slug;
}
async function fileToDataUrl(file: File): Promise<string> {
const bytes = Buffer.from(await file.arrayBuffer());
const mimeType = file.type || 'image/png';
return `data:${mimeType};base64,${bytes.toString('base64')}`;
}
export async function uploadGalleryPhoto(formData: FormData) {
const caption = String(formData.get('caption') ?? '').trim();
const categoryId = String(formData.get('categoryId') ?? '').trim() || null;
const file = formData.get('image') as File | null;
if (!file || file.size === 0) {
throw new Error('A photo is required.');
}
const imageDataUrl = await fileToDataUrl(file);
await prisma.galleryPhoto.create({
data: {
imageDataUrl,
caption: caption || null,
categoryId,
},
});
redirect('/admin/gallery');
}
export async function deleteGalleryPhoto(formData: FormData) {
const id = String(formData.get('id') ?? '');
if (!id) return;
await prisma.galleryPhoto.delete({ where: { id } });
redirect('/admin/gallery');
}
export async function createGalleryCategory(formData: FormData) {
const name = String(formData.get('name') ?? '').trim();
const redirectTo = String(formData.get('redirectTo') ?? '/admin/gallery/categories').trim();
if (!name) throw new Error('Category name is required.');
const slug = await uniqueSlug(slugify(name));
await prisma.galleryCategory.create({ data: { name, slug } });
redirect(redirectTo);
}
export async function deleteGalleryCategory(formData: FormData) {
const id = String(formData.get('id') ?? '');
if (!id) return;
const inUse = await prisma.galleryPhoto.count({ where: { categoryId: id } });
if (inUse > 0) {
redirect('/admin/gallery/categories?error=in_use');
}
await prisma.galleryCategory.delete({ where: { id } });
redirect('/admin/gallery/categories');
}