feat(bookings): allow editing and soft-deleting bookings
Add edit modal to update guest details, ticket selection, and quantity Implement soft delete functionality to archive bookings
This commit is contained in:
31
server/api/bookings/[id].delete.ts
Normal file
31
server/api/bookings/[id].delete.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { DeleteBookingResponse } from '~~/shared/booking'
|
||||
|
||||
import { requireAuth } from '../../utils/auth'
|
||||
import { getBookingById, softDeleteBooking } from '../../utils/booking-repository'
|
||||
import { getRequiredRouteParam, httpError } from '../../utils/http'
|
||||
|
||||
export default defineEventHandler(async (event): Promise<DeleteBookingResponse> => {
|
||||
const auth = await requireAuth(event)
|
||||
const bookingId = getRequiredRouteParam(event, 'id', 'Booking ID')
|
||||
|
||||
const existingBooking = await getBookingById(bookingId, auth.user.role === 'super_admin'
|
||||
? undefined
|
||||
: { personInChargeId: auth.user.id })
|
||||
|
||||
if (!existingBooking) {
|
||||
httpError(404, 'Booking not found')
|
||||
}
|
||||
|
||||
const booking = await softDeleteBooking({
|
||||
bookingId,
|
||||
personInChargeId: auth.user.role === 'super_admin' ? undefined : auth.user.id
|
||||
})
|
||||
|
||||
if (!booking) {
|
||||
httpError(404, 'Booking not found')
|
||||
}
|
||||
|
||||
return {
|
||||
booking
|
||||
}
|
||||
})
|
||||
88
server/api/bookings/[id].patch.ts
Normal file
88
server/api/bookings/[id].patch.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { UpdateBookingDetailsResponse } from '~~/shared/booking'
|
||||
|
||||
import { getSeatCount } from '~~/shared/booking'
|
||||
import { requireAuth } from '../../utils/auth'
|
||||
import {
|
||||
getBookingById,
|
||||
getBookingInventorySummary,
|
||||
getActiveBookingModeOptionByCode,
|
||||
getActiveTicketCatalogItemByCode,
|
||||
updateBookingDetails
|
||||
} from '../../utils/booking-repository'
|
||||
import { parseUpdateBookingDetailsInput } from '../../utils/bookings'
|
||||
import { getRequiredRouteParam, httpError } from '../../utils/http'
|
||||
|
||||
export default defineEventHandler(async (event): Promise<UpdateBookingDetailsResponse> => {
|
||||
const auth = await requireAuth(event)
|
||||
const bookingId = getRequiredRouteParam(event, 'id', 'Booking ID')
|
||||
const body = await readBody<{
|
||||
customerName?: string
|
||||
customerPhone?: string
|
||||
bookingMode?: string | null
|
||||
quantity?: number
|
||||
ticketType?: string
|
||||
remark?: string | null
|
||||
}>(event)
|
||||
|
||||
const existingBooking = await getBookingById(bookingId, auth.user.role === 'super_admin'
|
||||
? undefined
|
||||
: { personInChargeId: auth.user.id })
|
||||
|
||||
if (!existingBooking) {
|
||||
httpError(404, 'Booking not found')
|
||||
}
|
||||
|
||||
const input = parseUpdateBookingDetailsInput(body)
|
||||
const [bookingMode, ticket] = await Promise.all([
|
||||
getActiveBookingModeOptionByCode(input.bookingMode),
|
||||
getActiveTicketCatalogItemByCode(input.ticketType)
|
||||
])
|
||||
|
||||
if (!bookingMode) {
|
||||
httpError(400, 'Booking mode is invalid')
|
||||
}
|
||||
|
||||
if (!ticket) {
|
||||
httpError(400, 'Ticket category is invalid')
|
||||
}
|
||||
|
||||
if (bookingMode.eventId !== ticket.eventId || bookingMode.eventId !== existingBooking.event.id) {
|
||||
httpError(400, 'Booking mode and ticket category must belong to the same event')
|
||||
}
|
||||
|
||||
const seatCount = getSeatCount(bookingMode, input.quantity)
|
||||
const totalPrice = seatCount * ticket.price
|
||||
const seatIncrease = Math.max(seatCount - existingBooking.seatCount, 0)
|
||||
|
||||
if (existingBooking.status === 'confirmed' && seatIncrease > 0) {
|
||||
const summary = await getBookingInventorySummary()
|
||||
|
||||
if (summary.leftSeats !== null && seatIncrease > summary.leftSeats) {
|
||||
httpError(409, `Total seats cannot exceed the remaining capacity by ${seatIncrease - summary.leftSeats} seats`)
|
||||
}
|
||||
}
|
||||
|
||||
const booking = await updateBookingDetails({
|
||||
bookingId,
|
||||
customerName: input.customerName,
|
||||
customerPhone: input.customerPhone,
|
||||
bookingModeId: bookingMode.id,
|
||||
bookingMode: bookingMode.value,
|
||||
quantity: input.quantity,
|
||||
seatCount,
|
||||
ticketTypeId: ticket.id,
|
||||
ticketType: ticket.value,
|
||||
unitPrice: ticket.price,
|
||||
totalPrice,
|
||||
remark: input.remark,
|
||||
personInChargeId: auth.user.role === 'super_admin' ? undefined : auth.user.id
|
||||
})
|
||||
|
||||
if (!booking) {
|
||||
httpError(404, 'Booking not found')
|
||||
}
|
||||
|
||||
return {
|
||||
booking
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user