feat(bookings): add internal remark field to bookings
Add a remark column to the bookings table for management-only notes. Include UI to view and edit remarks directly from the bookings list. Create API endpoint and database queries to support remark updates.
This commit is contained in:
@@ -47,6 +47,7 @@ type DbBookingRow = {
|
||||
person_in_charge_id: string
|
||||
person_in_charge_name: string | null
|
||||
person_in_charge_phone_number: string | null
|
||||
remark?: string | null
|
||||
status: BookingStatus | string
|
||||
status_label: string | null
|
||||
created_at: Date | string
|
||||
@@ -139,6 +140,7 @@ function bookingSelectColumns(sql: any) {
|
||||
bookings.person_in_charge_id,
|
||||
coalesce(users.full_name, bookings.person_in_charge_name) as person_in_charge_name,
|
||||
coalesce(users.phone_number, bookings.person_in_charge_phone_number) as person_in_charge_phone_number,
|
||||
bookings.remark,
|
||||
bookings.status,
|
||||
booking_statuses.label as status_label,
|
||||
bookings.created_at,
|
||||
@@ -231,6 +233,7 @@ function mapBooking(row: DbBookingRow): PublicBooking {
|
||||
personInChargeId: row.person_in_charge_id,
|
||||
personInChargeName: row.person_in_charge_name || '',
|
||||
personInChargePhoneNumber: row.person_in_charge_phone_number || '',
|
||||
remark: row.remark || null,
|
||||
status,
|
||||
statusLabel: row.status_label || getBookingStatusLabel(status),
|
||||
createdAt: toIsoString(row.created_at) ?? new Date().toISOString(),
|
||||
@@ -493,6 +496,7 @@ export async function createBooking(input: {
|
||||
unit_price,
|
||||
total_price,
|
||||
person_in_charge_id,
|
||||
remark,
|
||||
status
|
||||
)
|
||||
values (
|
||||
@@ -511,6 +515,7 @@ export async function createBooking(input: {
|
||||
${input.unitPrice},
|
||||
${input.totalPrice},
|
||||
${input.personInChargeId},
|
||||
null,
|
||||
'pending'
|
||||
)
|
||||
returning *
|
||||
@@ -588,6 +593,50 @@ export async function listBookings(options?: {
|
||||
return rows.map(mapBooking)
|
||||
}
|
||||
|
||||
export async function updateBookingRemark(input: {
|
||||
bookingId: string
|
||||
personInChargeId?: string
|
||||
remark: string | null
|
||||
}): Promise<PublicBooking | null> {
|
||||
await ensureDatabaseReady()
|
||||
const sql = getSqlClient()
|
||||
|
||||
const rows = input.personInChargeId
|
||||
? await sql<DbBookingRow[]>`
|
||||
with updated_booking as (
|
||||
update bookings
|
||||
set
|
||||
remark = ${input.remark},
|
||||
updated_at = now()
|
||||
where id = ${input.bookingId}
|
||||
and person_in_charge_id = ${input.personInChargeId}
|
||||
returning *
|
||||
)
|
||||
select ${bookingSelectColumns(sql)}
|
||||
from updated_booking as bookings
|
||||
${bookingJoins(sql)}
|
||||
where dinner_events.is_active = true
|
||||
limit 1
|
||||
`
|
||||
: await sql<DbBookingRow[]>`
|
||||
with updated_booking as (
|
||||
update bookings
|
||||
set
|
||||
remark = ${input.remark},
|
||||
updated_at = now()
|
||||
where id = ${input.bookingId}
|
||||
returning *
|
||||
)
|
||||
select ${bookingSelectColumns(sql)}
|
||||
from updated_booking as bookings
|
||||
${bookingJoins(sql)}
|
||||
where dinner_events.is_active = true
|
||||
limit 1
|
||||
`
|
||||
|
||||
return rows[0] ? mapBooking(rows[0]) : null
|
||||
}
|
||||
|
||||
export async function listBookingSeats(bookingId: string): Promise<PublicBookingSeat[]> {
|
||||
await ensureDatabaseReady()
|
||||
const sql = getSqlClient()
|
||||
|
||||
@@ -39,6 +39,18 @@ export function parseCreateBookingInput(body: {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseBookingRemarkInput(body: {
|
||||
remark?: string | null
|
||||
}) {
|
||||
const remark = typeof body.remark === 'string' ? body.remark.trim() : ''
|
||||
|
||||
assertBadRequest(remark.length <= 1000, 'Remark must be 1,000 characters or fewer')
|
||||
|
||||
return {
|
||||
remark: remark || null
|
||||
}
|
||||
}
|
||||
|
||||
export function buildBookingMessage(booking: PublicBooking, confirmationUrl: string) {
|
||||
return [
|
||||
`I'd like to book tickets for the ${booking.event.title}.`,
|
||||
|
||||
@@ -260,6 +260,7 @@ async function initializeDatabase() {
|
||||
person_in_charge_id text not null references users(id) on delete restrict,
|
||||
person_in_charge_name text not null,
|
||||
person_in_charge_phone_number text not null,
|
||||
remark text,
|
||||
status text not null default 'pending',
|
||||
confirmed_at timestamptz,
|
||||
created_at timestamptz not null default now(),
|
||||
@@ -287,6 +288,11 @@ async function initializeDatabase() {
|
||||
add column if not exists ticket_type_id text
|
||||
`
|
||||
|
||||
await sql`
|
||||
alter table bookings
|
||||
add column if not exists remark text
|
||||
`
|
||||
|
||||
await sql`
|
||||
create unique index if not exists bookings_receipt_token_idx
|
||||
on bookings (receipt_token)
|
||||
|
||||
Reference in New Issue
Block a user