import { Request, Response } from 'express'; import prisma from '../models/prisma'; import slugify from 'slugify'; import { z } from 'zod'; const portfolioSchema = z.object({ title: z.string().min(1, 'Title is required'), slug: z.string().min(1, 'Slug is required').regex(/^[a-z0-9-]+$/, 'Slug must be lowercase letters, numbers, and hyphens only'), description: z.string().min(1, 'Description is required'), technologies: z.string().nullable().optional(), imageUrl: z.string().nullable().optional(), clientName: z.string().nullable().optional(), industry: z.string().nullable().optional(), testimonials: z.string().nullable().optional(), }); // GET /api/admin/portfolio — List all portfolio items export const getAllPortfolioItems = async (_req: Request, res: Response) => { try { const items = await prisma.portfolioItem.findMany({ orderBy: { createdAt: 'desc' }, include: { author: { select: { name: true, email: true } } }, }); return res.json(items); } catch (error) { console.error('Get portfolio items error:', error); return res.status(500).json({ error: 'Internal server error' }); } }; // GET /api/admin/portfolio/:id — Get single portfolio item export const getPortfolioItem = async (req: Request, res: Response) => { try { const item = await prisma.portfolioItem.findUnique({ where: { id: req.params.id }, include: { author: { select: { name: true, email: true } } }, }); if (!item) return res.status(404).json({ error: 'Not found' }); return res.json(item); } catch (error) { console.error('Get portfolio item error:', error); return res.status(500).json({ error: 'Internal server error' }); } }; // POST /api/admin/portfolio — Create portfolio item export const createPortfolioItem = async (req: Request, res: Response) => { try { const validatedData = portfolioSchema.parse(req.body); const { title, slug, description, technologies, imageUrl, clientName, industry, testimonials } = validatedData; // Check slug uniqueness const existing = await prisma.portfolioItem.findUnique({ where: { slug } }); if (existing) { return res.status(409).json({ error: 'Slug already exists' }); } const item = await prisma.portfolioItem.create({ data: { title, slug, description, technologies: technologies || null, imageUrl: imageUrl || null, clientName: clientName || null, industry: industry || null, testimonials: testimonials || null, }, }); return res.status(201).json(item); } catch (error: any) { if (error instanceof z.ZodError) { return res.status(400).json({ error: error.errors[0].message }); } console.error('Create portfolio item error:', error); return res.status(500).json({ error: 'Internal server error' }); } }; // PATCH /api/admin/portfolio/:id — Update portfolio item export const updatePortfolioItem = async (req: Request, res: Response) => { try { const { id } = req.params; const validatedData = portfolioSchema.partial().parse(req.body); const { title, slug, description, technologies, imageUrl, clientName, industry, testimonials } = validatedData; // Check item exists const existing = await prisma.portfolioItem.findUnique({ where: { id } }); if (!existing) return res.status(404).json({ error: 'Not found' }); // Check slug uniqueness if changed if (slug && slug !== existing.slug) { const slugExists = await prisma.portfolioItem.findUnique({ where: { slug } }); if (slugExists) return res.status(409).json({ error: 'Slug already exists' }); } const item = await prisma.portfolioItem.update({ where: { id }, data: { ...(title !== undefined && { title }), ...(slug !== undefined && { slug }), ...(description !== undefined && { description }), ...(technologies !== undefined && { technologies }), ...(imageUrl !== undefined && { imageUrl }), ...(clientName !== undefined && { clientName }), ...(industry !== undefined && { industry }), ...(testimonials !== undefined && { testimonials }), }, }); return res.json(item); } catch (error: any) { if (error instanceof z.ZodError) { return res.status(400).json({ error: error.errors[0].message }); } console.error('Update portfolio item error:', error); return res.status(500).json({ error: 'Internal server error' }); } }; // DELETE /api/admin/portfolio/:id — Delete portfolio item export const deletePortfolioItem = async (req: Request, res: Response) => { try { const { id } = req.params; const existing = await prisma.portfolioItem.findUnique({ where: { id } }); if (!existing) return res.status(404).json({ error: 'Not found' }); await prisma.portfolioItem.delete({ where: { id } }); return res.json({ message: 'Deleted successfully' }); } catch (error) { console.error('Delete portfolio item error:', error); return res.status(500).json({ error: 'Internal server error' }); } };