@medalsocial/meda 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -83,10 +83,36 @@ export interface ConfirmationProps {
|
|
|
83
83
|
/** The visit's total in minor units. */
|
|
84
84
|
totalOre: number;
|
|
85
85
|
address?: string | null;
|
|
86
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* The calendar link: a calendar file the site serves (`text/calendar`, which
|
|
88
|
+
* iOS and macOS open straight into «Add to Calendar»), a calendar's own
|
|
89
|
+
* add-event page (Google Calendar), or a `data:text/calendar` URL. `null`
|
|
90
|
+
* draws no link.
|
|
91
|
+
*/
|
|
87
92
|
calendarHref?: string | null;
|
|
88
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* `download` filename for the calendar link. Only a `data:` or `blob:` URL
|
|
95
|
+
* gets it: browsers refuse to navigate to one, so the attribute is what makes
|
|
96
|
+
* it do anything. On a real URL it would force a download, and an iPhone
|
|
97
|
+
* would save the file to Files instead of opening Calendar.
|
|
98
|
+
*/
|
|
89
99
|
calendarFileName?: string;
|
|
100
|
+
/**
|
|
101
|
+
* The calendar link is another site's page (Google Calendar's add-event
|
|
102
|
+
* form): it opens in a new tab. A calendar file — relative or absolute —
|
|
103
|
+
* opens in place, which is what lets iOS show «Add to Calendar».
|
|
104
|
+
*/
|
|
105
|
+
calendarNewTab?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* A second way into a calendar, drawn as a small link: Google Calendar beside
|
|
108
|
+
* a calendar file, or the file beside Google Calendar. `newTab` as for
|
|
109
|
+
* `calendarNewTab`. `null` draws none.
|
|
110
|
+
*/
|
|
111
|
+
calendarAlt?: {
|
|
112
|
+
href: string;
|
|
113
|
+
label: string;
|
|
114
|
+
newTab?: boolean;
|
|
115
|
+
} | null;
|
|
90
116
|
/** Where all of the visitor's bookings are listed. `null` draws no link. */
|
|
91
117
|
portalHref?: string | null;
|
|
92
118
|
/** A way back to an empty wizard. Absent, the button is not drawn. */
|
|
@@ -109,4 +135,4 @@ export interface ConfirmationProps {
|
|
|
109
135
|
classNames?: SlotClassNames<ConfirmationSlot>;
|
|
110
136
|
components?: ConfirmationComponents;
|
|
111
137
|
}
|
|
112
|
-
export declare function Confirmation({ lines: rawLines, startTs, totalOre, address, calendarHref, calendarFileName, portalHref, onStartOver, now, layout, directionsHref, freeChangeUntilTs, format, labels, classNames, components, }: ConfirmationProps): import("react").JSX.Element;
|
|
138
|
+
export declare function Confirmation({ lines: rawLines, startTs, totalOre, address, calendarHref, calendarFileName, calendarAlt, calendarNewTab, portalHref, onStartOver, now, layout, directionsHref, freeChangeUntilTs, format, labels, classNames, components, }: ConfirmationProps): import("react").JSX.Element;
|
|
@@ -68,10 +68,22 @@ export function DefaultPartyLine({ line, text, manageLabel, manageAriaLabel, lin
|
|
|
68
68
|
return (_jsxs("li", { className: "flex flex-wrap items-baseline gap-x-3", children: [_jsx("span", { className: "tabular-nums", children: text }), line.manageHref && (_jsx("a", { href: line.manageHref, "aria-label": manageAriaLabel, className: linkClassName ?? 'text-sm font-semibold text-primary underline underline-offset-4', children: manageLabel }))] }));
|
|
69
69
|
}
|
|
70
70
|
const SEPARATOR = ' · ';
|
|
71
|
-
|
|
71
|
+
/** `download` only where it is needed (see `calendarFileName`). */
|
|
72
|
+
function downloadFor(href, fileName) {
|
|
73
|
+
return /^(data|blob):/i.test(href) ? fileName : undefined;
|
|
74
|
+
}
|
|
75
|
+
/** A calendar link: the file in place, another calendar's page in a new tab. */
|
|
76
|
+
function calendarLinkProps(href, fileName, newTab = false) {
|
|
77
|
+
return {
|
|
78
|
+
href,
|
|
79
|
+
download: downloadFor(href, fileName),
|
|
80
|
+
...(newTab ? { target: '_blank', rel: 'noopener' } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function Confirmation({ lines: rawLines, startTs, totalOre, address = null, calendarHref = null, calendarFileName = 'booking.ics', calendarAlt = null, calendarNewTab = false, portalHref = null, onStartOver, now = Date.now(), layout = 'card', directionsHref = null, freeChangeUntilTs = null, format, labels, classNames, components, }) {
|
|
72
84
|
const { clock } = format;
|
|
73
85
|
if (layout === 'hero') {
|
|
74
|
-
return (_jsx(HeroConfirmation, { lines: rawLines, startTs: startTs, totalOre: totalOre, address: address, calendarHref: calendarHref, calendarFileName: calendarFileName, portalHref: portalHref, onStartOver: onStartOver, now: now, directionsHref: directionsHref, freeChangeUntilTs: freeChangeUntilTs, format: format, labels: labels, classNames: classNames, components: components }));
|
|
86
|
+
return (_jsx(HeroConfirmation, { lines: rawLines, startTs: startTs, totalOre: totalOre, address: address, calendarHref: calendarHref, calendarFileName: calendarFileName, calendarAlt: calendarAlt, calendarNewTab: calendarNewTab, portalHref: portalHref, onStartOver: onStartOver, now: now, directionsHref: directionsHref, freeChangeUntilTs: freeChangeUntilTs, format: format, labels: labels, classNames: classNames, components: components }));
|
|
75
87
|
}
|
|
76
88
|
// Cleaned here as well as upstream: a restored confirmation carries whatever
|
|
77
89
|
// name was resolved when it was stored.
|
|
@@ -96,7 +108,7 @@ export function Confirmation({ lines: rawLines, startTs, totalOre, address = nul
|
|
|
96
108
|
// This line's own price, which for one person is the whole total.
|
|
97
109
|
format.price(first.priceOre),
|
|
98
110
|
], SEPARATOR) })) : null;
|
|
99
|
-
return (_jsxs("section", { "aria-labelledby": "booking-confirmed-heading", className: slotClass(classNames, 'root', 'space-y-6'), children: [_jsx("h2", { id: "booking-confirmed-heading", tabIndex: -1, className: slotClass(classNames, 'heading', 'font-sans text-2xl font-bold outline-none md:text-3xl'), children: labels['confirmation.heading'] }), lines.length > 1 ? (_jsx(PartyCard, { lines: lines, totalOre: totalOre, now: now, format: format, labels: labels, classNames: classNames, PartyLine: components?.PartyLine ?? DefaultPartyLine })) : (singleCard), address && (_jsx("p", { className: slotClass(classNames, 'address', 'text-sm text-muted-foreground'), children: address })), _jsxs("div", { className: "flex flex-wrap gap-4", children: [calendarHref && (_jsx("a", {
|
|
111
|
+
return (_jsxs("section", { "aria-labelledby": "booking-confirmed-heading", className: slotClass(classNames, 'root', 'space-y-6'), children: [_jsx("h2", { id: "booking-confirmed-heading", tabIndex: -1, className: slotClass(classNames, 'heading', 'font-sans text-2xl font-bold outline-none md:text-3xl'), children: labels['confirmation.heading'] }), lines.length > 1 ? (_jsx(PartyCard, { lines: lines, totalOre: totalOre, now: now, format: format, labels: labels, classNames: classNames, PartyLine: components?.PartyLine ?? DefaultPartyLine })) : (singleCard), address && (_jsx("p", { className: slotClass(classNames, 'address', 'text-sm text-muted-foreground'), children: address })), _jsxs("div", { className: "flex flex-wrap gap-4", children: [calendarHref && (_jsx("a", { ...calendarLinkProps(calendarHref, calendarFileName, calendarNewTab), className: linkClass, children: labels['confirmation.calendar'] })), calendarAlt && (_jsx("a", { ...calendarLinkProps(calendarAlt.href, calendarFileName, calendarAlt.newTab), className: linkClass, children: calendarAlt.label })), lines.length === 1 && first?.manageHref && (_jsx("a", { href: first.manageHref, className: linkClass, children: labels['confirmation.manage'] }))] }), portalHref && (_jsx("p", { className: "text-sm", children: _jsx("a", { href: portalHref, className: linkClass, children: labels['confirmation.portal'] }) })), onStartOver && (_jsx("div", { children: _jsx(BookingButton, { variant: "outline", className: slotClass(classNames, 'startOver'), onClick: onStartOver, children: labels['confirmation.startOver'] }) }))] }));
|
|
100
112
|
}
|
|
101
113
|
/**
|
|
102
114
|
* The family card: one line per person in the order they sit down, each with
|
|
@@ -139,7 +151,7 @@ function visitMinutes(item) {
|
|
|
139
151
|
* booked, and the two things a parent does next — put it in the calendar and
|
|
140
152
|
* find the way — as buttons rather than links.
|
|
141
153
|
*/
|
|
142
|
-
function HeroConfirmation({ lines: rawLines, startTs, totalOre, address, calendarHref, calendarFileName, portalHref, onStartOver, now, directionsHref, freeChangeUntilTs, format, labels, classNames, components, }) {
|
|
154
|
+
function HeroConfirmation({ lines: rawLines, startTs, totalOre, address, calendarHref, calendarFileName, calendarAlt, calendarNewTab, portalHref, onStartOver, now, directionsHref, freeChangeUntilTs, format, labels, classNames, components, }) {
|
|
143
155
|
const { clock } = format;
|
|
144
156
|
const lines = rawLines.map((line) => ({
|
|
145
157
|
...line,
|
|
@@ -201,11 +213,11 @@ function HeroConfirmation({ lines: rawLines, startTs, totalOre, address, calenda
|
|
|
201
213
|
? fillLabel(label('confirmation.leafTime'), {
|
|
202
214
|
time: clock.formatTime(startTs),
|
|
203
215
|
})
|
|
204
|
-
: clock.formatTime(startTs) })] }), _jsx("h2", { id: "booking-confirmed-heading", tabIndex: -1, className: slotClass(classNames, 'heading', 'font-sans text-2xl font-bold outline-none md:text-3xl'), children: labels['confirmation.heading'] }), _jsx("p", { className: "sr-only", children: when }), label('confirmation.sent') && (_jsx("p", { className: "text-sm text-muted-foreground", children: renderLabel(label('confirmation.sent')) }))] }), plainLine, lines.length > 1 && (_jsx(PartyCard, { lines: lines, totalOre: totalOre, now: now, format: format, labels: labels, classNames: classNames, PartyLine: components?.PartyLine ?? DefaultPartyLine })), rows.length > 0 && (_jsx("dl", { className: slotClass(classNames, 'card', 'divide-y divide-border rounded-lg border border-border bg-card px-5'), children: rows.map((row) => (_jsxs("div", { className: "flex items-center gap-4 py-3 text-sm", children: [_jsx("dt", { className: "w-16 shrink-0 text-muted-foreground", children: renderLabel(row.name) }), _jsx("dd", { className: "min-w-0 font-medium", children: row.value })] }, labelText(row.name)))) })), (calendarHref || (directionsHref && label('confirmation.directions'))) && (_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [calendarHref && (_jsxs("a", {
|
|
216
|
+
: clock.formatTime(startTs) })] }), _jsx("h2", { id: "booking-confirmed-heading", tabIndex: -1, className: slotClass(classNames, 'heading', 'font-sans text-2xl font-bold outline-none md:text-3xl'), children: labels['confirmation.heading'] }), _jsx("p", { className: "sr-only", children: when }), label('confirmation.sent') && (_jsx("p", { className: "text-sm text-muted-foreground", children: renderLabel(label('confirmation.sent')) }))] }), plainLine, lines.length > 1 && (_jsx(PartyCard, { lines: lines, totalOre: totalOre, now: now, format: format, labels: labels, classNames: classNames, PartyLine: components?.PartyLine ?? DefaultPartyLine })), rows.length > 0 && (_jsx("dl", { className: slotClass(classNames, 'card', 'divide-y divide-border rounded-lg border border-border bg-card px-5'), children: rows.map((row) => (_jsxs("div", { className: "flex items-center gap-4 py-3 text-sm", children: [_jsx("dt", { className: "w-16 shrink-0 text-muted-foreground", children: renderLabel(row.name) }), _jsx("dd", { className: "min-w-0 font-medium", children: row.value })] }, labelText(row.name)))) })), (calendarHref || (directionsHref && label('confirmation.directions'))) && (_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [calendarHref && (_jsxs("a", { ...calendarLinkProps(calendarHref, calendarFileName, calendarNewTab), className: bookingButtonClass({ size: 'lg', className: 'gap-2' }), children: [_jsx(CalendarPlus, { "aria-hidden": "true", className: "size-4" }), labels['confirmation.calendar']] })), directionsHref && label('confirmation.directions') && (_jsxs("a", { href: directionsHref, target: "_blank", rel: "noopener", className: bookingButtonClass({
|
|
205
217
|
variant: 'outline',
|
|
206
218
|
size: 'lg',
|
|
207
219
|
className: cn('gap-2', !calendarHref && 'col-span-2'),
|
|
208
|
-
}), children: [_jsx(MapPin, { "aria-hidden": "true", className: "size-4" }), renderLabel(label('confirmation.directions'))] }))] })), label('confirmation.tip.heading') && label('confirmation.tip.body') && (_jsxs("div", { className: "rounded-lg bg-muted px-5 py-4 text-sm", children: [_jsx("p", { className: "font-semibold", children: renderLabel(label('confirmation.tip.heading')) }), _jsx("p", { className: "text-muted-foreground", children: renderLabel(label('confirmation.tip.body')) })] })), lines.length === 1 && first?.manageHref && (_jsxs("p", { className: "text-center text-sm", children: [_jsx("a", { href: first.manageHref, className: linkClass, children: labels['confirmation.manage'] }), manageUntil && freeChangeUntilTs !== null && freeChangeUntilTs !== undefined && (_jsxs("span", { className: "text-muted-foreground", children: [SEPARATOR, fillLabel(manageUntil, {
|
|
220
|
+
}), children: [_jsx(MapPin, { "aria-hidden": "true", className: "size-4" }), renderLabel(label('confirmation.directions'))] }))] })), calendarAlt && (_jsx("p", { className: "-mt-2 text-center text-sm", children: _jsx("a", { ...calendarLinkProps(calendarAlt.href, calendarFileName, calendarAlt.newTab), className: linkClass, children: calendarAlt.label }) })), label('confirmation.tip.heading') && label('confirmation.tip.body') && (_jsxs("div", { className: "rounded-lg bg-muted px-5 py-4 text-sm", children: [_jsx("p", { className: "font-semibold", children: renderLabel(label('confirmation.tip.heading')) }), _jsx("p", { className: "text-muted-foreground", children: renderLabel(label('confirmation.tip.body')) })] })), lines.length === 1 && first?.manageHref && (_jsxs("p", { className: "text-center text-sm", children: [_jsx("a", { href: first.manageHref, className: linkClass, children: labels['confirmation.manage'] }), manageUntil && freeChangeUntilTs !== null && freeChangeUntilTs !== undefined && (_jsxs("span", { className: "text-muted-foreground", children: [SEPARATOR, fillLabel(manageUntil, {
|
|
209
221
|
day: clock.dayLabel(freeChangeUntilTs, now),
|
|
210
222
|
time: clock.formatTime(freeChangeUntilTs),
|
|
211
223
|
})] }))] })), portalHref && (_jsx("p", { className: "text-center text-sm", children: _jsx("a", { href: portalHref, className: linkClass, children: labels['confirmation.portal'] }) })), onStartOver && (_jsx("div", { className: "flex justify-center", children: _jsx(BookingButton, { variant: "outline", className: slotClass(classNames, 'startOver'), onClick: onStartOver, children: labels['confirmation.startOver'] }) }))] }));
|