97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { prisma } from '@/lib/db';
|
|
import { getSessionUser } from '@/lib/auth';
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const body = await request.json();
|
|
const {
|
|
scheduleId,
|
|
passengerName,
|
|
passengerEmail,
|
|
passengerPhone,
|
|
seats, // Array of strings, e.g. ["1", "2"] or ["1A", "1B"]
|
|
} = body;
|
|
|
|
if (!scheduleId || !passengerName || !passengerEmail || !passengerPhone || !seats || !Array.isArray(seats) || seats.length === 0) {
|
|
return NextResponse.json(
|
|
{ error: 'Missing required booking details or seat selections' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Fetch schedule to get the price
|
|
const schedule = await prisma.schedule.findUnique({
|
|
where: { id: Number(scheduleId) },
|
|
});
|
|
|
|
if (!schedule) {
|
|
return NextResponse.json({ error: 'Schedule not found' }, { status: 404 });
|
|
}
|
|
|
|
// Check optional user session link
|
|
const userSession = await getSessionUser();
|
|
const userId = userSession ? userSession.userId : null;
|
|
|
|
// Calculate total price
|
|
const unitPrice = Number(schedule.price);
|
|
const totalPrice = unitPrice * seats.length;
|
|
|
|
// Generate unique booking code
|
|
const bookingCode = 'TRV-' + Math.random().toString(36).substring(2, 8).toUpperCase();
|
|
|
|
// Perform database transaction to ensure atomic operations (no double seat bookings)
|
|
const booking = await prisma.$transaction(async (tx) => {
|
|
// 1. Check if any selected seat is already occupied for this schedule
|
|
const occupied = await tx.bookingSeat.findMany({
|
|
where: {
|
|
scheduleId: Number(scheduleId),
|
|
seatNumber: { in: seats },
|
|
booking: {
|
|
status: { in: ['PENDING', 'PAID'] },
|
|
},
|
|
},
|
|
});
|
|
|
|
if (occupied.length > 0) {
|
|
throw new Error('One or more selected seats have already been booked. Please choose other seats.');
|
|
}
|
|
|
|
// 2. Create the booking record
|
|
const newBooking = await tx.booking.create({
|
|
data: {
|
|
bookingCode,
|
|
scheduleId: Number(scheduleId),
|
|
userId,
|
|
passengerName,
|
|
passengerEmail,
|
|
passengerPhone,
|
|
totalPrice,
|
|
status: 'PENDING',
|
|
},
|
|
});
|
|
|
|
// 3. Create the booking seat records
|
|
const seatRecords = seats.map((seat: string) => ({
|
|
bookingId: newBooking.id,
|
|
scheduleId: Number(scheduleId),
|
|
seatNumber: seat,
|
|
}));
|
|
|
|
await tx.bookingSeat.createMany({
|
|
data: seatRecords,
|
|
});
|
|
|
|
return newBooking;
|
|
});
|
|
|
|
return NextResponse.json({ success: true, bookingCode: booking.bookingCode, bookingId: booking.id });
|
|
} catch (error: any) {
|
|
console.error('Create booking transaction error:', error);
|
|
return NextResponse.json(
|
|
{ error: error.message || 'Failed to process booking. Please try again.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
}
|