From c59e100e5e4d144dc3f9d28143be32098ca5307d Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 17 Jun 2026 07:40:52 +0000 Subject: [PATCH] style: ui fix in scheduling --- .../new-booking-form/step-scheduling.tsx | 286 +++++++++++------- 1 file changed, 179 insertions(+), 107 deletions(-) diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-scheduling.tsx b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-scheduling.tsx index 181e611be..a17fac477 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-scheduling.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-scheduling.tsx @@ -6,6 +6,8 @@ import { Stack, Text, useMantineTheme, + Modal, + SimpleGrid, } from "@mantine/core"; import { UseFormReturn } from "react-hook-form"; import { BookingFormInputValues, BookingFormValues } from "./schema"; @@ -52,6 +54,7 @@ interface DayData { export function StepScheduling({ form, referenceData }: StepSchedulingProps) { const theme = useMantineTheme(); const [currentDate, setCurrentDate] = useState(new Date()); + const [selectedDayForModal, setSelectedDayForModal] = useState(null); const selectedDate = form.watch("scheduledDate"); const selectedScheduleId = form.watch("trainScheduleId"); @@ -148,10 +151,35 @@ export function StepScheduling({ form, referenceData }: StepSchedulingProps) { const weeksCount = Math.ceil(days.length / 7); + const handleDayClick = (day: DayData) => { + if (day.schedules.length > 1) { + setSelectedDayForModal(day); + } else if (day.schedules.length === 1) { + form.setValue("scheduledDate", day.dateString, { + shouldValidate: true, + }); + form.setValue("trainScheduleId", day.schedules[0].id, { + shouldValidate: true, + }); + } + }; + + const handleSelectScheduleFromModal = (scheduleId: string) => { + if (selectedDayForModal) { + form.setValue("scheduledDate", selectedDayForModal.dateString, { + shouldValidate: true, + }); + form.setValue("trainScheduleId", scheduleId, { + shouldValidate: true, + }); + setSelectedDayForModal(null); + } + }; + return ( - + {/* ── Calendar Card ───────────────────────────────────── */} - + {/* Card header */} { - form.setValue("scheduledDate", dateString, { - shouldValidate: true, - }); - form.setValue("trainScheduleId", scheduleId, { - shouldValidate: true, - }); - }} + onDayClick={handleDayClick} /> ))} @@ -263,7 +283,7 @@ export function StepScheduling({ form, referenceData }: StepSchedulingProps) { {/* ── Side Panel ──────────────────────────────────────── */} - + {/* Booking summary card */} + + {/* ── Schedule Selection Modal ──────────────────────────── */} + setSelectedDayForModal(null)} + title={selectedDayForModal ? format(new Date(selectedDayForModal.dateString + "T00:00:00"), "EEE, MMM d yyyy") : ""} + centered + size="sm" + styles={{ + header: { borderBottom: `1px solid ${theme.colors["edr-border"][0]}` }, + body: { padding: 24 }, + }} + > + + + Choose a departure time + + {selectedDayForModal?.schedules.map((schedule) => ( + + ))} + + ); } interface DayCellProps { day: DayData; - selectedScheduleId: string; - onSelectSchedule: (scheduleId: string, dateString: string) => void; + onDayClick: (day: DayData) => void; } -function DayCell({ day: d, selectedScheduleId, onSelectSchedule }: DayCellProps) { +function DayCell({ day: d, selectedScheduleId, onDayClick, onSelectSchedule }: DayCellProps) { const theme = useMantineTheme(); if (!d.isCurrentMonth) { @@ -400,125 +484,113 @@ function DayCell({ day: d, selectedScheduleId, onSelectSchedule }: DayCellProps) : "transparent"; const cellBorder = d.isSelectedDate - ? `1.5px solid ${theme.colors["edr-green"][5]}` + ? `2px solid ${theme.colors["edr-green"][5]}` : d.hasSchedule - ? "1px solid #E7E8E5" + ? `1px solid ${theme.colors["edr-border"][0]}` : "none"; return ( d.hasSchedule && onDayClick(d)} style={{ height: 92, borderRadius: theme.radius.md, backgroundColor: cellBg, border: cellBorder, overflow: "hidden", - padding: "4px 6px 6px", + padding: "8px 10px 10px", display: "flex", flexDirection: "column", gap: 4, + cursor: d.hasSchedule ? "pointer" : "default", + transition: "all 150ms ease", + boxShadow: d.hasSchedule && !d.isSelectedDate ? "0 1px 3px rgba(0, 0, 0, 0.05)" : "none", + }} + onMouseEnter={(e) => { + if (d.hasSchedule && !d.isSelectedDate) { + e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.1)"; + e.currentTarget.style.borderColor = theme.colors["edr-border"][0]; + } + }} + onMouseLeave={(e) => { + if (d.hasSchedule && !d.isSelectedDate) { + e.currentTarget.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.05)"; + e.currentTarget.style.borderColor = theme.colors["edr-border"][0]; + } }} > {/* Day number + check icon */} - - {d.day} - + + + {d.day} + + {d.isToday && !d.isSelectedDate && ( + + TODAY + + )} + {d.isSelectedDate && ( - + + + )} - {/* Departure chips */} + {/* Schedule times */} {d.hasSchedule && ( - - {d.schedules.slice(0, 2).map((s) => { - const isChipSelected = s.id === selectedScheduleId; - const isFull = s.remainingWagons <= 0; - const canSelect = !isFull; - - return ( + + {d.schedules.slice(0, 2).map((s) => ( + - canSelect && onSelectSchedule(s.id, d.dateString) - } style={{ - display: "flex", - alignItems: "center", - gap: 4, - borderRadius: 7, - padding: "4px 6px", - cursor: canSelect ? "pointer" : "default", - backgroundColor: isChipSelected - ? theme.colors["edr-green"][5] - : isFull - ? theme.colors["edr-red-soft"][0] - : theme.colors["edr-soft"][0], - border: `1px solid ${ - isChipSelected - ? theme.colors["edr-green"][5] - : isFull - ? "#EFCFCA" - : "#BFE3D4" - }`, + width: 4, + height: 4, + borderRadius: "50%", + backgroundColor: theme.colors["edr-green"][5], + flexShrink: 0, }} + /> + - - - {isFull - ? "Full" - : s.trainNumber - ? s.trainNumber - : `${s.remainingWagons} wgn`} - - - - ); - })} + {format(new Date(s.scheduleDate), "HH:mm")} + + + ))} + {d.schedules.length > 2 && ( + + +{d.schedules.length - 2} more + + )} )}