#324 Make the Add/Edit Meeting dialog fit on a phone screen
After #322 trimmed the dialog, the Save/Cancel footer slid below the visible 90vh sheet on phones because the whole DialogContent was a single overflow-y-auto block. Changes in artifacts/tx-os/src/pages/executive-meetings.tsx: MeetingFormDialog - Rebuilt DialogContent as a flex column: shrink-0 header, flex-1 min-h-0 overflow-y-auto middle (the only scroller), shrink-0 footer. Save/Cancel now stay pinned at the bottom even after many attendees are added. - Reduced mobile padding/gaps (`p-4 sm:p-6`, `gap-3 sm:gap-4`, grid `gap-2 sm:gap-3`). Desktop spacing unchanged. - Switched the field grid from `grid-cols-1 sm:grid-cols-2` to a flat `grid-cols-2` so the two short fields (التاريخ, الرقم اليومي) share a row even on mobile. Title spans both columns; time pickers span both on mobile and one each on sm+ via the new mobileFull variant. - Inner scroll wrapper uses negative margins + matching padding so scroll content can extend edge-to-edge without losing the dialog's gutter. FormRow helper - Broadened `full` from `sm:col-span-2` to `col-span-2` (works in both 1-col and 2-col grids; only callers using `full` are in this file). - Added `mobileFull` prop → `col-span-2 sm:col-span-1` for the time pickers, so AM/PM controls don't get squeezed on phones. Out of scope (untouched): validation, save logic, attendee model, removed-field defaults from #322, duplicate dialog, audit, schedule view, translations.
This commit is contained in:
@@ -5399,20 +5399,25 @@ function MeetingFormDialog({
|
||||
|
||||
return (
|
||||
<Dialog open={true} onOpenChange={(o) => !o && onClose()}>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
{/* #324: rebuild DialogContent as a flex column so the header,
|
||||
a single scrollable middle, and the footer stack predictably
|
||||
on mobile. Without this the whole sheet scrolled and the
|
||||
Save/Cancel buttons slid below the visible 90vh viewport on
|
||||
phones. Reduced mobile padding/gaps; desktop unchanged. */}
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] p-4 sm:p-6 flex flex-col gap-3 sm:gap-4 overflow-hidden">
|
||||
<DialogHeader className="shrink-0">
|
||||
<DialogTitle>
|
||||
{state.id == null
|
||||
? t("executiveMeetings.manage.addMeeting")
|
||||
: t("executiveMeetings.manage.editMeeting")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
{/* #322 polish: arrange the trimmed form into a tidy grid that
|
||||
avoids dangling cells. Title spans full width on every
|
||||
breakpoint so it gets enough room. Date + daily number sit
|
||||
on one row on tablet/desktop, then start + end times share
|
||||
the next row. On mobile each field stacks. */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div className="flex-1 min-h-0 overflow-y-auto -mx-4 px-4 sm:-mx-6 sm:px-6 space-y-3 sm:space-y-4">
|
||||
{/* #322 polish (grid): title spans full width. Date + daily
|
||||
number always share one row (both are short). Time pickers
|
||||
are full-width on mobile so the AM/PM controls don't get
|
||||
cramped, but share a row on tablet/desktop. */}
|
||||
<div className="grid grid-cols-2 gap-2 sm:gap-3">
|
||||
<FormRow
|
||||
label={t("executiveMeetings.manage.field.titleAr")}
|
||||
full
|
||||
@@ -5441,7 +5446,10 @@ function MeetingFormDialog({
|
||||
onChange={(e) => update("dailyNumber", e.target.value)}
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow label={t("executiveMeetings.manage.field.startTime")}>
|
||||
<FormRow
|
||||
label={t("executiveMeetings.manage.field.startTime")}
|
||||
mobileFull
|
||||
>
|
||||
<TimePicker12h
|
||||
ref={startPickerRef}
|
||||
value={state.startTime}
|
||||
@@ -5457,7 +5465,10 @@ function MeetingFormDialog({
|
||||
pmTestId="em-form-startTime-pm"
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow label={t("executiveMeetings.manage.field.endTime")}>
|
||||
<FormRow
|
||||
label={t("executiveMeetings.manage.field.endTime")}
|
||||
mobileFull
|
||||
>
|
||||
<TimePicker12h
|
||||
ref={endPickerRef}
|
||||
value={state.endTime}
|
||||
@@ -5480,7 +5491,7 @@ function MeetingFormDialog({
|
||||
preserved for meetings being edited. */}
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pt-3 mt-1">
|
||||
<div className="border-t border-gray-200 pt-3">
|
||||
<div className="flex items-center justify-between mb-2 gap-2 flex-wrap">
|
||||
<h4 className="font-semibold text-sm text-[#0B1E3F]">
|
||||
{t("executiveMeetings.manage.attendees.label")} (
|
||||
@@ -5569,8 +5580,12 @@ function MeetingFormDialog({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
{/* #324: footer sits outside the scroll area so it stays
|
||||
pinned to the bottom of the dialog even after many
|
||||
attendees are added. */}
|
||||
<DialogFooter className="shrink-0">
|
||||
<Button variant="ghost" onClick={onClose} disabled={saving}>
|
||||
{t("executiveMeetings.common.cancel")}
|
||||
</Button>
|
||||
@@ -5592,15 +5607,26 @@ function FormRow({
|
||||
label,
|
||||
children,
|
||||
full,
|
||||
mobileFull,
|
||||
hint,
|
||||
}: {
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
/** Always span both columns of a 2-col grid. Safe in single-column
|
||||
* grids too (col-span-2 just clamps to the available width). */
|
||||
full?: boolean;
|
||||
/** Span both columns on mobile but a single column from sm+. Used
|
||||
* by #324 so the time pickers don't get squeezed on phones. */
|
||||
mobileFull?: boolean;
|
||||
hint?: string;
|
||||
}) {
|
||||
const span = full
|
||||
? "col-span-2"
|
||||
: mobileFull
|
||||
? "col-span-2 sm:col-span-1"
|
||||
: "";
|
||||
return (
|
||||
<div className={full ? "sm:col-span-2" : ""}>
|
||||
<div className={span}>
|
||||
<Label className="text-xs text-gray-700">{label}</Label>
|
||||
<div className="mt-1">{children}</div>
|
||||
{hint && <p className="text-[11px] text-gray-500 mt-0.5">{hint}</p>}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
Reference in New Issue
Block a user