This commit is contained in:
Rio
2026-06-18 12:16:27 +07:00
parent 7de3a3b4b1
commit 5c0ab92401
84 changed files with 12562 additions and 285 deletions
+96
View File
@@ -0,0 +1,96 @@
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 }
);
}
}