webdev-service-backend / src /controllers /portfolio.controller.ts
underrate's picture
Initial commit
d53e5b4 verified
Raw
History Blame Contribute Delete
5.71 kB
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' });
}
};