Task #666: Today & Book buttons in room-booking calendar view
Added two quick actions inside the Protocol bookings "Calendar" view's day panel header (artifacts/tx-os/src/pages/protocol.tsx): - "Today" (اليوم): outline button that resets selectedDay to new Date(), so the month picker, day time-grid, and red now-line all snap to today. - "Book" (حجز): primary button gated by the same c?.canRequest capability as the existing toolbar new-booking button. Opens the new-booking dialog pre-filled with the currently selected day. Implementation: - Extended bookingDialog state with an optional `day?: Date`. - BookingDialog now accepts an `initialDay?: Date` prop; for new (non-edit) bookings it pre-fills startsAt=09:00 and endsAt=10:00 on that day via a local dayAt(hour) helper (reuses toLocalInput). Edit flow unchanged. - Added i18n keys protocol.bookings.today / protocol.bookings.book to both ar.json and en.json. Buttons use logical spacing (me-1) so they mirror in Arabic RTL. No backend/API changes. List/Calendar toggle unaffected. tx-os typecheck passes; HMR clean, no console errors. Screenshot tool unavailable (no artifact registered in this repo), verified via typecheck + logs.
This commit is contained in:
@@ -1723,6 +1723,8 @@
|
||||
"endsAt": "إلى",
|
||||
"viewList": "قائمة",
|
||||
"viewCalendar": "تقويم",
|
||||
"today": "اليوم",
|
||||
"book": "حجز",
|
||||
"emptyDay": "لا توجد حجوزات في هذا اليوم.",
|
||||
"shareLinkTitle": "رابط الحجز العام (للمشاركة مع الضيوف)",
|
||||
"copyLink": "نسخ الرابط",
|
||||
|
||||
@@ -1596,6 +1596,8 @@
|
||||
"endsAt": "To",
|
||||
"viewList": "List",
|
||||
"viewCalendar": "Calendar",
|
||||
"today": "Today",
|
||||
"book": "Book",
|
||||
"emptyDay": "No bookings on this day.",
|
||||
"shareLinkTitle": "Public booking link (share with guests)",
|
||||
"copyLink": "Copy link",
|
||||
|
||||
@@ -556,6 +556,7 @@ export default function ProtocolPage() {
|
||||
const [bookingDialog, setBookingDialog] = useState<{
|
||||
open: boolean;
|
||||
edit?: Booking;
|
||||
day?: Date;
|
||||
}>({ open: false });
|
||||
const [rejectTarget, setRejectTarget] = useState<{
|
||||
kind: "booking" | "issue" | "external";
|
||||
@@ -978,13 +979,36 @@ export default function ProtocolPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 rounded-xl border border-slate-200 bg-white p-3">
|
||||
<div className="mb-3 text-sm font-semibold text-slate-600">
|
||||
{selectedDay.toLocaleDateString(isAr ? "ar" : "en", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||
<div className="text-sm font-semibold text-slate-600">
|
||||
{selectedDay.toLocaleDateString(isAr ? "ar" : "en", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setSelectedDay(new Date())}
|
||||
>
|
||||
<CalendarDays size={14} className="me-1" />
|
||||
{t("protocol.bookings.today")}
|
||||
</Button>
|
||||
{c?.canRequest && (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setBookingDialog({ open: true, day: selectedDay })
|
||||
}
|
||||
>
|
||||
<Plus size={14} className="me-1" />
|
||||
{t("protocol.bookings.book")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-h-[70vh] overflow-y-auto">
|
||||
<BookingDayGrid
|
||||
@@ -1364,6 +1388,7 @@ export default function ProtocolPage() {
|
||||
<BookingDialog
|
||||
rooms={rooms.data ?? []}
|
||||
edit={bookingDialog.edit}
|
||||
initialDay={bookingDialog.day}
|
||||
nameOf={nameOf}
|
||||
onClose={() => setBookingDialog({ open: false })}
|
||||
onSaved={() => {
|
||||
@@ -1709,6 +1734,7 @@ function DialogShell({
|
||||
function BookingDialog({
|
||||
rooms,
|
||||
edit,
|
||||
initialDay,
|
||||
nameOf,
|
||||
onClose,
|
||||
onSaved,
|
||||
@@ -1716,21 +1742,30 @@ function BookingDialog({
|
||||
}: {
|
||||
rooms: Room[];
|
||||
edit?: Booking;
|
||||
initialDay?: Date;
|
||||
nameOf: (ar: string, en: string) => string;
|
||||
onClose: () => void;
|
||||
onSaved: () => void;
|
||||
onError: (e: unknown) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const dayAt = (hour: number) => {
|
||||
if (!initialDay) return "";
|
||||
const d = new Date(initialDay);
|
||||
d.setHours(hour, 0, 0, 0);
|
||||
return toLocalInput(d.toISOString());
|
||||
};
|
||||
const [title, setTitle] = useState(edit?.title ?? "");
|
||||
const [roomId, setRoomId] = useState<string>(
|
||||
edit ? String(edit.roomId) : rooms[0] ? String(rooms[0].id) : "",
|
||||
);
|
||||
const [requesterName, setRequesterName] = useState(edit?.requesterName ?? "");
|
||||
const [startsAt, setStartsAt] = useState(
|
||||
edit ? toLocalInput(edit.startsAt) : "",
|
||||
edit ? toLocalInput(edit.startsAt) : dayAt(9),
|
||||
);
|
||||
const [endsAt, setEndsAt] = useState(
|
||||
edit ? toLocalInput(edit.endsAt) : dayAt(10),
|
||||
);
|
||||
const [endsAt, setEndsAt] = useState(edit ? toLocalInput(edit.endsAt) : "");
|
||||
const [notes, setNotes] = useState(edit?.notes ?? "");
|
||||
|
||||
const mut = useMutation({
|
||||
|
||||
Reference in New Issue
Block a user