import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { db } from '@/lib/db'; import { createClientSchema } from '@smashpoint/shared'; // GET /api/clients - List/search clients export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const { searchParams } = new URL(request.url); const search = searchParams.get('search'); const isActive = searchParams.get('isActive'); const limit = searchParams.get('limit'); const offset = searchParams.get('offset'); // Build the where clause interface ClientWhereClause { organizationId: string; isActive?: boolean; OR?: Array<{ firstName?: { contains: string; mode: 'insensitive' }; lastName?: { contains: string; mode: 'insensitive' }; email?: { contains: string; mode: 'insensitive' }; phone?: { contains: string; mode: 'insensitive' }; }>; } const whereClause: ClientWhereClause = { organizationId: session.user.organizationId, }; // Filter by active status if (isActive !== null) { whereClause.isActive = isActive !== 'false'; } // Search filter for name, email, or phone if (search && search.trim().length > 0) { const searchTerm = search.trim(); whereClause.OR = [ { firstName: { contains: searchTerm, mode: 'insensitive' } }, { lastName: { contains: searchTerm, mode: 'insensitive' } }, { email: { contains: searchTerm, mode: 'insensitive' } }, { phone: { contains: searchTerm, mode: 'insensitive' } }, ]; } // Build pagination options const take = limit ? Math.min(parseInt(limit, 10), 100) : 50; const skip = offset ? parseInt(offset, 10) : 0; // Get total count for pagination const totalCount = await db.client.count({ where: whereClause, }); const clients = await db.client.findMany({ where: whereClause, select: { id: true, firstName: true, lastName: true, email: true, phone: true, level: true, tags: true, isActive: true, createdAt: true, memberships: { where: { status: 'ACTIVE', endDate: { gte: new Date(), }, }, select: { id: true, status: true, remainingHours: true, endDate: true, plan: { select: { id: true, name: true, discountPercent: true, }, }, }, take: 1, }, _count: { select: { bookings: true, }, }, }, orderBy: [ { lastName: 'asc' }, { firstName: 'asc' }, ], take, skip, }); return NextResponse.json({ data: clients, pagination: { total: totalCount, limit: take, offset: skip, hasMore: skip + take < totalCount, }, }); } catch (error) { console.error('Error fetching clients:', error); return NextResponse.json( { error: 'Error fetching clients' }, { status: 500 } ); } } // POST /api/clients - Create a new client export async function POST(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const body = await request.json(); // Validate input with Zod schema const validationResult = createClientSchema.safeParse(body); if (!validationResult.success) { return NextResponse.json( { error: 'Invalid client data', details: validationResult.error.flatten().fieldErrors, }, { status: 400 } ); } const { firstName, lastName, email, phone, dateOfBirth, address, city, postalCode, notes, tags, skillLevel, emergencyContact, } = validationResult.data; // Check if client with same email already exists in organization if (email) { const existingClient = await db.client.findFirst({ where: { organizationId: session.user.organizationId, email, }, }); if (existingClient) { return NextResponse.json( { error: 'A client with this email already exists in your organization' }, { status: 409 } ); } } // Create the client const client = await db.client.create({ data: { organizationId: session.user.organizationId, firstName, lastName, email: email || null, phone: phone || null, dateOfBirth: dateOfBirth || null, address: address || null, notes: notes || null, tags: tags || [], level: skillLevel || null, emergencyContact: emergencyContact?.name || null, emergencyPhone: emergencyContact?.phone || null, isActive: true, }, select: { id: true, firstName: true, lastName: true, email: true, phone: true, level: true, tags: true, isActive: true, createdAt: true, }, }); return NextResponse.json(client, { status: 201 }); } catch (error) { console.error('Error creating client:', error); // Check for unique constraint violation if (error instanceof Error && error.message.includes('Unique constraint')) { return NextResponse.json( { error: 'A client with this email or ID number already exists' }, { status: 409 } ); } return NextResponse.json( { error: 'Error creating client' }, { status: 500 } ); } }