Add attendee count field to booking requests and display it
Updates API schemas and database to include attendee count for bookings, modifies the public request form to accept this new optional field, and displays the attendee count on the booking details. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 9c655ed7-866c-454d-b646-e458df854c65 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/OsTuUKE Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -161,6 +161,7 @@ const bookingCreateSchema = z
|
||||
roomId: z.number().int().positive(),
|
||||
title: z.string().trim().min(1).max(500),
|
||||
requesterName: z.string().trim().max(300).nullable().optional(),
|
||||
attendeeCount: z.number().int().positive().max(100000).nullable().optional(),
|
||||
startsAt: isoDateTime,
|
||||
endsAt: isoDateTime,
|
||||
notes: z.string().trim().max(5000).nullable().optional(),
|
||||
@@ -175,6 +176,7 @@ const bookingPatchSchema = z
|
||||
roomId: z.number().int().positive().optional(),
|
||||
title: z.string().trim().min(1).max(500).optional(),
|
||||
requesterName: z.string().trim().max(300).nullable().optional(),
|
||||
attendeeCount: z.number().int().positive().max(100000).nullable().optional(),
|
||||
startsAt: isoDateTime.optional(),
|
||||
endsAt: isoDateTime.optional(),
|
||||
notes: z.string().trim().max(5000).nullable().optional(),
|
||||
@@ -191,6 +193,7 @@ const publicBookingRequestSchema = z
|
||||
requesterName: z.string().trim().min(1).max(300),
|
||||
requesterPhone: z.string().trim().min(3).max(40),
|
||||
requesterOrg: z.string().trim().max(300).nullable().optional(),
|
||||
attendeeCount: z.number().int().positive().max(100000).nullable().optional(),
|
||||
startsAt: isoDateTime,
|
||||
endsAt: isoDateTime,
|
||||
notes: z.string().trim().max(5000).nullable().optional(),
|
||||
@@ -458,6 +461,7 @@ router.post(
|
||||
requesterName: body.requesterName,
|
||||
requesterPhone: body.requesterPhone,
|
||||
requesterOrg: body.requesterOrg ?? null,
|
||||
attendeeCount: body.attendeeCount ?? null,
|
||||
startsAt,
|
||||
endsAt,
|
||||
status: "pending",
|
||||
@@ -687,6 +691,7 @@ router.post(
|
||||
roomId: body.roomId,
|
||||
title: body.title,
|
||||
requesterName: body.requesterName ?? null,
|
||||
attendeeCount: body.attendeeCount ?? null,
|
||||
startsAt,
|
||||
endsAt,
|
||||
status: canApprove ? "approved" : "pending",
|
||||
@@ -773,6 +778,9 @@ router.patch(
|
||||
...(body.requesterName !== undefined
|
||||
? { requesterName: body.requesterName }
|
||||
: {}),
|
||||
...(body.attendeeCount !== undefined
|
||||
? { attendeeCount: body.attendeeCount }
|
||||
: {}),
|
||||
startsAt,
|
||||
endsAt,
|
||||
...(body.notes !== undefined ? { notes: body.notes } : {}),
|
||||
|
||||
@@ -1714,6 +1714,7 @@
|
||||
"requester": "مقدّم الطلب",
|
||||
"org": "الجهة",
|
||||
"phone": "الهاتف",
|
||||
"attendeeCount": "عدد الحضور",
|
||||
"publicRequest": "طلب عام",
|
||||
"roomLabel": "القاعة",
|
||||
"titleLabel": "عنوان الحجز",
|
||||
|
||||
@@ -1587,6 +1587,7 @@
|
||||
"requester": "Requester",
|
||||
"org": "Organization",
|
||||
"phone": "Phone",
|
||||
"attendeeCount": "Attendees",
|
||||
"publicRequest": "Public request",
|
||||
"roomLabel": "Room",
|
||||
"titleLabel": "Booking title",
|
||||
|
||||
@@ -22,6 +22,7 @@ type FormState = {
|
||||
title: string;
|
||||
requesterPhone: string;
|
||||
requesterOrg: string;
|
||||
attendeeCount: string;
|
||||
roomId: string;
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
@@ -34,6 +35,7 @@ const EMPTY: FormState = {
|
||||
title: "",
|
||||
requesterPhone: "",
|
||||
requesterOrg: "",
|
||||
attendeeCount: "",
|
||||
roomId: "",
|
||||
startsAt: "",
|
||||
endsAt: "",
|
||||
@@ -105,6 +107,10 @@ export default function ProtocolRequestPage() {
|
||||
requesterName: form.requesterName.trim(),
|
||||
requesterPhone: form.requesterPhone.trim(),
|
||||
requesterOrg: form.requesterOrg.trim() || null,
|
||||
attendeeCount:
|
||||
form.attendeeCount.trim() === ""
|
||||
? null
|
||||
: Number(form.attendeeCount),
|
||||
startsAt: localToIso(form.startsAt),
|
||||
endsAt: localToIso(form.endsAt),
|
||||
notes: form.notes.trim() || null,
|
||||
@@ -249,6 +255,19 @@ export default function ProtocolRequestPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="attendeeCount">عدد الحضور</Label>
|
||||
<Input
|
||||
id="attendeeCount"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
min={1}
|
||||
value={form.attendeeCount}
|
||||
onChange={(e) => set("attendeeCount", e.target.value)}
|
||||
placeholder="العدد المتوقع للحضور (اختياري)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="room">
|
||||
القاعة <span className="text-rose-500">*</span>
|
||||
|
||||
@@ -86,6 +86,7 @@ type Booking = {
|
||||
requesterName: string | null;
|
||||
requesterPhone: string | null;
|
||||
requesterOrg: string | null;
|
||||
attendeeCount: number | null;
|
||||
source: "internal" | "public";
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
@@ -497,6 +498,11 @@ export default function ProtocolPage() {
|
||||
<span dir="ltr">{b.requesterPhone}</span>
|
||||
</div>
|
||||
)}
|
||||
{b.attendeeCount != null && (
|
||||
<div className="text-xs text-slate-400">
|
||||
{t("protocol.bookings.attendeeCount")}: {b.attendeeCount}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-1 shrink-0">
|
||||
{b.source === "public" && (
|
||||
|
||||
@@ -94,6 +94,9 @@ export const protocolRoomBookingsTable = pgTable(
|
||||
// The requester's organization / entity (الجهة). Optional even for
|
||||
// public requests; always null for internal bookings unless set.
|
||||
requesterOrg: varchar("requester_org", { length: 300 }),
|
||||
// Expected number of attendees (عدد الحضور). Optional; supplied by the
|
||||
// guest on a public request or by staff on an internal booking.
|
||||
attendeeCount: integer("attendee_count"),
|
||||
// Provenance marker. "internal" for bookings created by authenticated
|
||||
// protocol staff through the app; "public" for guest requests coming in
|
||||
// via the no-login booking-request link. Defaults to "internal" so all
|
||||
|
||||
Reference in New Issue
Block a user