From ffd848e5dcda4086b5f85b1fe9d53fdd4f994460 Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 6 Nov 2026 16:40:06 +0100 Subject: [PATCH] feat(mobile): confirm dialogs and Vacay sheets in the common sheet style The shared confirm sheet (44 places) gets the icon tile with the title, a warning icon for destructive questions, and the sheets' 42 px buttons with the danger colour on a destructive confirm. The dashboard's and the collections' own confirm dialogs now use it. Vacay invite, shared calendars and settings get the icon tile header; the settings' rows separated by hairlines become cards on the inner surface, as the trip settings' sections. --- client/scripts/theme-baseline.json | 10 +-- .../screens/collections/MCollections.tsx | 26 +++----- .../mobile/screens/dashboard/MDashboard.tsx | 33 ++-------- .../mobile/screens/settings/MConfirmSheet.tsx | 64 ++++++++++++------- .../screens/trip/sheets/MTripSheetUi.tsx | 3 + .../screens/vacay/MVacayInviteSheet.tsx | 16 ++--- .../screens/vacay/MVacaySettingsSheet.tsx | 43 +++++++++---- .../mobile/screens/vacay/MVacayShareSheet.tsx | 16 ++--- 8 files changed, 108 insertions(+), 103 deletions(-) diff --git a/client/scripts/theme-baseline.json b/client/scripts/theme-baseline.json index a832a9ea6..b8b4cb8fc 100644 --- a/client/scripts/theme-baseline.json +++ b/client/scripts/theme-baseline.json @@ -181,9 +181,9 @@ "mobile/screens/collections/MCollShareSheet.tsx": 14, "mobile/screens/collections/MCollSheetKit.tsx": 5, "mobile/screens/collections/MCollTripPickerSheet.tsx": 4, - "mobile/screens/collections/MCollections.tsx": 21, + "mobile/screens/collections/MCollections.tsx": 18, "mobile/screens/dashboard/MDashWidgets.tsx": 22, - "mobile/screens/dashboard/MDashboard.tsx": 36, + "mobile/screens/dashboard/MDashboard.tsx": 31, "mobile/screens/dashboard/MNewTripSheet.tsx": 12, "mobile/screens/dashboard/MUserMenu.tsx": 7, "mobile/screens/journey/MJourney.tsx": 10, @@ -279,10 +279,10 @@ "mobile/screens/trip/tabs/filesModel.ts": 5, "mobile/screens/trip/tabs/tabChrome.tsx": 12, "mobile/screens/vacay/MVacay.tsx": 17, - "mobile/screens/vacay/MVacayInviteSheet.tsx": 9, + "mobile/screens/vacay/MVacayInviteSheet.tsx": 8, "mobile/screens/vacay/MVacayMonth.tsx": 3, - "mobile/screens/vacay/MVacaySettingsSheet.tsx": 35, - "mobile/screens/vacay/MVacayShareSheet.tsx": 11, + "mobile/screens/vacay/MVacaySettingsSheet.tsx": 34, + "mobile/screens/vacay/MVacayShareSheet.tsx": 10, "mobile/screens/vacay/vacayDayModel.ts": 1, "pages/AtlasPage.tsx": 32, "pages/DashboardPage.tsx": 1, diff --git a/client/src/mobile/screens/collections/MCollections.tsx b/client/src/mobile/screens/collections/MCollections.tsx index 2a37f8941..9214753f9 100644 --- a/client/src/mobile/screens/collections/MCollections.tsx +++ b/client/src/mobile/screens/collections/MCollections.tsx @@ -12,7 +12,6 @@ import { STATUS_ORDER } from '../../../pages/collections/collectionsModel' import type { StatusFilter } from '../../../store/collectionStore' import { ALL_SAVED } from '../../../store/collectionStore' import CollectionMap from '../../../components/Collections/CollectionMap' -import MSheet from '../../components/MSheet' import MCollPlaceRow from './MCollPlaceRow' import MCollPlaceSheet from './MCollPlaceSheet' import MCollAddSheet from './MCollAddSheet' @@ -24,7 +23,7 @@ import MCollListPickerSheet from './MCollListPickerSheet' import MCollTours from './MCollTours' import MCollInvites from './MCollInvites' import { STATUS_SPEC } from './collectionsMobileModel' -import { CancelPill, PrimaryPill } from './MCollSheetKit' +import MConfirmSheet from '../settings/MConfirmSheet' const HEADER_CIRCLE = 'flex h-[38px] w-[38px] flex-none items-center justify-center rounded-full border border-[color:var(--m-gbr)] bg-[color:var(--m-sheet)] text-m-ink shadow-[0_5px_12px_-8px_rgba(0,0,0,.18)]' @@ -546,23 +545,16 @@ export default function MCollections() { /> {/* Delete-list confirm */} - c.setConfirmDeleteList(null)} - material="opaque" - ariaLabel={t('collections.deleteList')} - > -
-
{t('collections.deleteList')}
-
{t('collections.deleteListConfirm')}
-
- c.setConfirmDeleteList(null)}>{t('common.cancel')} - - {t('common.delete')} - -
-
-
+ title={t('collections.deleteList')} + message={t('collections.deleteListConfirm')} + confirmLabel={t('common.delete')} + cancelLabel={t('common.cancel')} + danger + onConfirm={c.handleDeleteList} + /> ) } diff --git a/client/src/mobile/screens/dashboard/MDashboard.tsx b/client/src/mobile/screens/dashboard/MDashboard.tsx index 5763bf069..8f1eabbd5 100644 --- a/client/src/mobile/screens/dashboard/MDashboard.tsx +++ b/client/src/mobile/screens/dashboard/MDashboard.tsx @@ -21,13 +21,13 @@ import { entityGradient } from '../../../utils/gradients' import MGlassBar from '../../components/MGlassBar' import MIconBtn from '../../components/MIconBtn' import MSegmented from '../../components/MSegmented' -import MSheet from '../../components/MSheet' import MUserMenu from './MUserMenu' import { useMobileDashOrder, useMobileDashVisibility, MobileDashWidget } from './MDashWidgets' import MNewTripSheet from './MNewTripSheet' import type { MobileDashToken } from '@trek/shared' import { usePullToRefresh } from '../../usePullToRefresh' import MPullIndicator from '../../components/MPullIndicator' +import SharedConfirmSheet from '../settings/MConfirmSheet' type TripFilter = 'planned' | 'archive' | 'completed' @@ -581,36 +581,11 @@ function ListStat({ value, label }: { value: number; label: string }): React.Rea ) } -// ── Confirm sheet (delete / copy) ──────────────────────────────────────────── -function MConfirmSheet({ open, title, message, confirmLabel, danger = false, onConfirm, onClose }: { +// ── Confirm sheet (delete / copy): the shared one, with the common cancel ───── +function MConfirmSheet(props: { open: boolean; title: string; message: string; confirmLabel: string danger?: boolean; onConfirm: () => void; onClose: () => void }): React.ReactElement { const { t } = useTranslation() - return ( - -
-
{title}
-
{message}
-
- - -
-
-
- ) + return } diff --git a/client/src/mobile/screens/settings/MConfirmSheet.tsx b/client/src/mobile/screens/settings/MConfirmSheet.tsx index 2e536ad8b..0b2149849 100644 --- a/client/src/mobile/screens/settings/MConfirmSheet.tsx +++ b/client/src/mobile/screens/settings/MConfirmSheet.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react' +import { AlertTriangle, CircleHelp, Info } from 'lucide-react' import MSheet from '../../components/MSheet' -import { MSetButton } from './MSettingsUi' +import { SHEET_DANGER, SHEET_PRIMARY, SHEET_SECONDARY } from '../trip/sheets/MTripSheetUi' interface MConfirmSheetProps { open: boolean @@ -17,9 +18,11 @@ interface MConfirmSheetProps { } /** - * Small confirm dialog as a centred floating card. Without onConfirm it is a - * plain notice. The card stops at the screen height: a long message or list - * scrolls between the title and the buttons, which always stay in reach. + * Small confirm dialog as a centred floating card, in the shape of every phone + * sheet: the icon tile and title, the message, the sheets' 42 px buttons. + * Without onConfirm it is a plain notice. The card stops at the screen height: + * a long message or list scrolls between the title and the buttons, which + * always stay in reach. */ export default function MConfirmSheet({ open, @@ -33,25 +36,42 @@ export default function MConfirmSheet({ onConfirm, children, }: MConfirmSheetProps) { + const Icon = danger ? AlertTriangle : onConfirm ? CircleHelp : Info return ( - -
-
{title}
-
-

{message}

- {children} -
- {/* A long label moves its button to a row of its own instead of wrapping inside it. */} -
- - {cancelLabel} - - {onConfirm && confirmLabel && ( - - {confirmLabel} - - )} -
+ +
+ + + +
{title}
+
+
+

{message}

+ {children} +
+ {/* A long label moves its button to a row of its own instead of wrapping inside it. */} +
+ + {onConfirm && confirmLabel && ( + + )}
) diff --git a/client/src/mobile/screens/trip/sheets/MTripSheetUi.tsx b/client/src/mobile/screens/trip/sheets/MTripSheetUi.tsx index 269822ff1..06851860a 100644 --- a/client/src/mobile/screens/trip/sheets/MTripSheetUi.tsx +++ b/client/src/mobile/screens/trip/sheets/MTripSheetUi.tsx @@ -29,6 +29,9 @@ export const SHEET_ROUND_PRIMARY = /** The quiet action with a label: add, invite, end. Full width in a section. */ export const SHEET_SECONDARY = 'flex h-[42px] items-center justify-center gap-1.5 rounded-full border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] px-4 text-[length:calc(13px*var(--fs-scale-body,1))] font-semibold text-m-ink disabled:opacity-40' +/** A destructive action with a label: the quiet button's shape, the danger colour on its text. */ +export const SHEET_DANGER = + 'flex h-[42px] items-center justify-center gap-1.5 rounded-full border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] px-4 text-[length:calc(13px*var(--fs-scale-body,1))] font-semibold text-[color:var(--m-st-danger)] disabled:opacity-40' /** The quiet round icon action of a row: remove, rename, make owner. */ export const SHEET_ROUND = 'flex h-[42px] w-[42px] flex-none items-center justify-center rounded-full bg-[color:var(--m-ic)] text-m-muted disabled:opacity-40' diff --git a/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx b/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx index 501e78baf..f8f7a294a 100644 --- a/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx +++ b/client/src/mobile/screens/vacay/MVacayInviteSheet.tsx @@ -1,11 +1,11 @@ -import { ChevronDown, Clock, Loader2, X } from 'lucide-react' +import { ChevronDown, Clock, Loader2, UserPlus } from 'lucide-react' import MSheet from '../../components/MSheet' -import MIconBtn from '../../components/MIconBtn' import MUserPickerList from '../../components/MUserPickerList' import { useVacayStore } from '../../../store/vacayStore' import { useTranslation } from '../../../i18n' import { type VacayUser } from '../../../types' import { useVacayUserPicker } from '../../../components/Vacay/useVacayUserPicker' +import { TileHeader } from '../trip/sheets/MTripSheetUi' interface MVacayInviteSheetProps { open: boolean @@ -33,12 +33,12 @@ export default function MVacayInviteSheet({ open, onClose }: MVacayInviteSheetPr return (
-
-
{t('vacay.inviteUser')}
- - - -
+ } + title={t('vacay.inviteUser')} + onClose={onClose} + closeLabel={t('common.close')} + />
{t('vacay.inviteHint')}
diff --git a/client/src/mobile/screens/vacay/MVacaySettingsSheet.tsx b/client/src/mobile/screens/vacay/MVacaySettingsSheet.tsx index dd0097eea..e063a3b1f 100644 --- a/client/src/mobile/screens/vacay/MVacaySettingsSheet.tsx +++ b/client/src/mobile/screens/vacay/MVacaySettingsSheet.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react' -import { ArrowRightLeft, Building2, Calendar, CalendarRange, CalendarX, ChevronDown, Globe, GraduationCap, Loader2, Plus, Trash2, Unlink, X } from 'lucide-react' +import { ArrowRightLeft, Building2, Calendar, CalendarRange, CalendarX, ChevronDown, Globe, GraduationCap, Loader2, Plus, Trash2, Unlink, X, Settings2 } from 'lucide-react' import MSheet from '../../components/MSheet' import MIconBtn from '../../components/MIconBtn' import MToggle from '../../components/MToggle' @@ -13,6 +13,7 @@ import { } from '../../../components/Vacay/useVacaySettings' import { FALLBACK_PERSON_COLOR } from './vacayDayModel' import type { VacayHolidayCalendar } from '../../../types' +import { INNER_CLS, TileHeader } from '../trip/sheets/MTripSheetUi' const CALENDAR_COLORS = ['#fecaca', '#fed7aa', '#fde68a', '#bbf7d0', '#a5f3fc', '#c7d2fe', '#e9d5ff', '#fda4af'] @@ -28,6 +29,9 @@ interface MVacaySettingsSheetProps { * start, carry-over, company holidays, public-holiday calendars (nager.at — * label/color/country/region, several) and dissolving the Fusion. */ +/** Each setting is a card on the inner surface, as the trip settings' sections. */ +const CARD = `flex-none rounded-[18px] px-3 pb-1 ${INNER_CLS}` + export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsSheetProps) { const { t, language } = useTranslation() const { plan, updatePlan, addHolidayCalendar, updateHolidayCalendar, deleteHolidayCalendar, isFused, users } = useVacayStore() @@ -52,15 +56,18 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe ] return ( - -
-
{t('vacay.settings')}
- - - + +
+ } + title={t('vacay.settings')} + onClose={onClose} + closeLabel={t('common.close')} + />
-
+
+
{/* Block weekends */}
@@ -94,7 +101,8 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe
)} -
+
+
{/* Week start */}
@@ -121,7 +129,8 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe })}
-
+
+
{/* Carry over */}
@@ -132,11 +141,13 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe
updatePlan({ carry_over_enabled: !plan.carry_over_enabled })} ariaLabel={t('vacay.carryOver')} />
-
+ +
{/* Vacation year (#737) — personal, unlike the plan settings around it */} -
+
+
{/* Company holidays */}
@@ -147,7 +158,8 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe
updatePlan({ company_holidays_enabled: !plan.company_holidays_enabled })} ariaLabel={t('vacay.companyHolidays')} />
-
+ +
{/* Public holidays + calendar editor */}
@@ -189,7 +201,8 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe )} )} -
+
+
{/* School holidays + calendar editor */}
@@ -233,6 +246,8 @@ export default function MVacaySettingsSheet({ open, onClose }: MVacaySettingsShe )} +
+ {/* Dissolve Fusion */} {isFused && (
diff --git a/client/src/mobile/screens/vacay/MVacayShareSheet.tsx b/client/src/mobile/screens/vacay/MVacayShareSheet.tsx index b31f9238b..88d2d4f50 100644 --- a/client/src/mobile/screens/vacay/MVacayShareSheet.tsx +++ b/client/src/mobile/screens/vacay/MVacayShareSheet.tsx @@ -1,11 +1,11 @@ -import { ChevronDown, Eye, EyeOff, Loader2, Share2, X } from 'lucide-react' +import { ChevronDown, Eye, EyeOff, Loader2, Share2 } from 'lucide-react' import MSheet from '../../components/MSheet' -import MIconBtn from '../../components/MIconBtn' import MUserPickerList from '../../components/MUserPickerList' import { useVacayStore } from '../../../store/vacayStore' import { useTranslation } from '../../../i18n' import { useVacayShareActions } from '../../../components/Vacay/useVacayShareActions' import { useVacayUserPicker } from '../../../components/Vacay/useVacayUserPicker' +import { TileHeader } from '../trip/sheets/MTripSheetUi' interface MVacayShareSheetProps { open: boolean @@ -34,12 +34,12 @@ export default function MVacayShareSheet({ open, onClose }: MVacayShareSheetProp return (
-
-
{t('vacay.sharedCalendars')}
- - - -
+ } + title={t('vacay.sharedCalendars')} + onClose={onClose} + closeLabel={t('common.close')} + />
{t('vacay.shareCalendarHint')}