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:
2026-05-04 11:59:41 +08:00
parent 3f7025c8e4
commit 30753fdc61
6 changed files with 256 additions and 2 deletions

View File

@@ -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()