@arquimedes.co/eureka-forms 3.0.71-test → 3.0.72
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.
- package/README +33 -1
- package/dist/@Types/AvailabilityFormStep.d.ts +48 -0
- package/dist/@Types/AvailabilityFormStep.js +24 -0
- package/dist/@Types/BankAccountFormStep.d.ts +67 -0
- package/dist/@Types/BankAccountFormStep.js +14 -0
- package/dist/@Types/BookingFormStep.d.ts +249 -0
- package/dist/@Types/BookingFormStep.js +65 -0
- package/dist/@Types/CalendarFormStep.d.ts +209 -0
- package/dist/@Types/CalendarFormStep.js +55 -0
- package/dist/@Types/CommunicationChannelFormStep.d.ts +51 -0
- package/dist/@Types/CommunicationChannelFormStep.js +15 -0
- package/dist/@Types/ConsentFormStep.d.ts +36 -0
- package/dist/@Types/ConsentFormStep.js +8 -0
- package/dist/@Types/EntityFormFormStep.d.ts +66 -0
- package/dist/@Types/EntityFormFormStep.js +20 -0
- package/dist/@Types/EventFormStep.d.ts +52 -0
- package/dist/@Types/EventFormStep.js +13 -0
- package/dist/@Types/ExternalReferenceFormStep.d.ts +74 -0
- package/dist/@Types/ExternalReferenceFormStep.js +20 -0
- package/dist/@Types/FormStep.d.ts +7 -2
- package/dist/@Types/FormStep.js +2 -1
- package/dist/@Types/LocationFormStep.d.ts +159 -0
- package/dist/@Types/LocationFormStep.js +46 -0
- package/dist/@Types/NumericFormSteps.d.ts +108 -0
- package/dist/@Types/NumericFormSteps.js +20 -0
- package/dist/@Types/PredefinedSelectorFormStep.d.ts +51 -0
- package/dist/@Types/PredefinedSelectorFormStep.js +39 -0
- package/dist/@Types/StepRef.d.ts +135 -0
- package/dist/@Types/StepRef.js +49 -0
- package/dist/App/App.stories.d.ts +10 -0
- package/dist/App/App.stories.js +189 -0
- package/dist/App/App.submitError.test.d.ts +1 -0
- package/dist/App/App.submitError.test.js +53 -0
- package/dist/App/AppHooks.js +8 -0
- package/dist/App/AppHooks.test.js +8 -0
- package/dist/BookingManage/AppointmentText.d.ts +5 -0
- package/dist/BookingManage/AppointmentText.js +20 -0
- package/dist/BookingManage/BookingManagePage.d.ts +17 -0
- package/dist/BookingManage/BookingManagePage.js +177 -0
- package/dist/BookingManage/BookingManagePage.module.css +115 -0
- package/dist/BookingManage/BookingManagePage.test.d.ts +1 -0
- package/dist/BookingManage/BookingManagePage.test.js +149 -0
- package/dist/Form/ConfirmationDialog/ConfirmationDialog.d.ts +14 -5
- package/dist/Form/ConfirmationDialog/ConfirmationDialog.js +8 -1
- package/dist/Form/ConfirmationDialog/ConfirmationDialog.module.css +12 -0
- package/dist/Form/ConfirmationDialog/ConfirmationDialog.test.d.ts +1 -0
- package/dist/Form/ConfirmationDialog/ConfirmationDialog.test.js +21 -0
- package/dist/Form/Form.d.ts +3 -0
- package/dist/Form/Form.js +16 -8
- package/dist/Form/FormFunctions.d.ts +17 -0
- package/dist/Form/FormFunctions.js +22 -0
- package/dist/Form/FormFunctions.test.js +28 -1
- package/dist/Form/FormTypes/ColumnForm/ColumnForm.d.ts +1 -1
- package/dist/Form/FormTypes/ColumnForm/ColumnForm.js +3 -2
- package/dist/Form/FormTypes/StepperForm/StepperForm.d.ts +1 -1
- package/dist/Form/FormTypes/StepperForm/StepperForm.js +3 -2
- package/dist/Form/SubmitError/SubmitError.d.ts +11 -0
- package/dist/Form/SubmitError/SubmitError.js +9 -0
- package/dist/Form/SubmitError/SubmitError.module.css +25 -0
- package/dist/Form/SubmitError/SubmitError.test.d.ts +1 -0
- package/dist/Form/SubmitError/SubmitError.test.js +34 -0
- package/dist/Form/SubmitError/SubmitErrorReason.d.ts +13 -0
- package/dist/Form/SubmitError/SubmitErrorReason.js +20 -0
- package/dist/FormSteps/AvailabilityStep/AvailabilityStep.d.ts +7 -0
- package/dist/FormSteps/AvailabilityStep/AvailabilityStep.js +14 -0
- package/dist/FormSteps/AvailabilityStep/MaterialAvailabilityStep/MaterialAvailabilityStep.d.ts +8 -0
- package/dist/FormSteps/AvailabilityStep/MaterialAvailabilityStep/MaterialAvailabilityStep.js +32 -0
- package/dist/FormSteps/BookingStep/BookingStep.d.ts +17 -0
- package/dist/FormSteps/BookingStep/BookingStep.js +14 -0
- package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.d.ts +21 -0
- package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.js +289 -0
- package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.test.d.ts +1 -0
- package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.test.js +275 -0
- package/dist/FormSteps/CheckBoxStep/CheckBoxStep.mobileLabel.test.js +20 -12
- package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.js +6 -13
- package/dist/FormSteps/CheckBoxStep/MaterialCheckBoxStep/MaterialCheckBoxStep.module.css +2 -13
- package/dist/FormSteps/ClassifierSelectorStep/ClassifierSelectorStep.lateClassifiers.test.d.ts +1 -0
- package/dist/FormSteps/ClassifierSelectorStep/ClassifierSelectorStep.lateClassifiers.test.js +70 -0
- package/dist/FormSteps/ClassifierSelectorStep/MaterialClassifierSelectorStep/MaterialClassifierSelectorStep.js +3 -1
- package/dist/FormSteps/EntityValueStep/MaterialEntityValuePickerStep/MaterialEntityValuePickerStep.js +26 -26
- package/dist/FormSteps/EntityValueStep/entityValueDependencyValue.d.ts +12 -0
- package/dist/FormSteps/EntityValueStep/entityValueDependencyValue.js +35 -0
- package/dist/FormSteps/EntityValueStep/entityValueDependencyValue.test.d.ts +1 -0
- package/dist/FormSteps/EntityValueStep/entityValueDependencyValue.test.js +31 -0
- package/dist/FormSteps/EntityValueStep/entityValuePickerWatchedSteps.d.ts +27 -0
- package/dist/FormSteps/EntityValueStep/entityValuePickerWatchedSteps.js +64 -0
- package/dist/FormSteps/EntityValueStep/entityValuePickerWatchedSteps.test.d.ts +1 -0
- package/dist/FormSteps/EntityValueStep/entityValuePickerWatchedSteps.test.js +95 -0
- package/dist/FormSteps/LocationStep/LocationStep.d.ts +10 -0
- package/dist/FormSteps/LocationStep/LocationStep.js +14 -0
- package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.d.ts +3 -0
- package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.js +54 -0
- package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.module.css +18 -0
- package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.d.ts +3 -0
- package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.js +170 -0
- package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.module.css +96 -0
- package/dist/FormSteps/NumberStep/NumberStep.d.ts +10 -0
- package/dist/FormSteps/NumberStep/NumberStep.js +14 -0
- package/dist/FormSteps/Step.js +16 -0
- package/dist/FormSteps/StepFunctions.js +14 -0
- package/dist/FormSteps/StepFunctions.test.js +54 -0
- package/dist/FormSteps/StepHooks.d.ts +21 -0
- package/dist/FormSteps/StepHooks.js +55 -8
- package/dist/FormSteps/StepHooks.test.d.ts +1 -0
- package/dist/FormSteps/StepHooks.test.js +92 -0
- package/dist/FormSteps/isStepHiddenByCondition.test.d.ts +1 -0
- package/dist/FormSteps/isStepHiddenByCondition.test.js +42 -0
- package/dist/Icons/AddIcon.d.ts +3 -0
- package/dist/Icons/AddIcon.js +7 -0
- package/dist/Icons/EditIcon.d.ts +3 -0
- package/dist/Icons/EditIcon.js +7 -0
- package/dist/Icons/RemoveIcon.d.ts +3 -0
- package/dist/Icons/RemoveIcon.js +7 -0
- package/dist/Icons/ViewDayIcon.d.ts +3 -0
- package/dist/Icons/ViewDayIcon.js +7 -0
- package/dist/Icons/ViewWeekIcon.d.ts +3 -0
- package/dist/Icons/ViewWeekIcon.js +7 -0
- package/dist/Services/BookingService.d.ts +226 -0
- package/dist/Services/BookingService.js +264 -0
- package/dist/Services/BookingService.test.d.ts +1 -0
- package/dist/Services/BookingService.test.js +178 -0
- package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.d.ts +19 -0
- package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.js +80 -0
- package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.module.css +88 -0
- package/dist/Shared/ErkAddressAutocomplete/googlePlaces.d.ts +29 -0
- package/dist/Shared/ErkAddressAutocomplete/googlePlaces.js +139 -0
- package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.d.ts +25 -0
- package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.js +112 -0
- package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.module.css +88 -0
- package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.test.d.ts +1 -0
- package/dist/Shared/ErkAvailabilityPicker/ErkAvailabilityPicker.test.js +73 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.d.ts +105 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.js +399 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.module.css +646 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.d.ts +36 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.js +177 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.test.d.ts +1 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.stories.test.js +47 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.test.d.ts +1 -0
- package/dist/Shared/ErkBookingPicker/ErkBookingPicker.test.js +440 -0
- package/dist/Shared/ErkDatePicker/ErkDatePicker.js +1 -3
- package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.d.ts +2 -3
- package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.js +1 -2
- package/dist/Shared/ErkIconButton/ErkIconButton.d.ts +17 -0
- package/dist/Shared/ErkIconButton/ErkIconButton.js +11 -0
- package/dist/Shared/ErkIconButton/ErkIconButton.module.css +41 -0
- package/dist/Shared/ErkLocationField/ErkLocationField.d.ts +24 -0
- package/dist/Shared/ErkLocationField/ErkLocationField.js +128 -0
- package/dist/Shared/ErkLocationField/ErkLocationField.module.css +164 -0
- package/dist/Shared/ErkLocationField/ErkLocationField.test.d.ts +1 -0
- package/dist/Shared/ErkLocationField/ErkLocationField.test.js +82 -0
- package/dist/Shared/ErkLocationField/addressFields.d.ts +12 -0
- package/dist/Shared/ErkLocationField/addressFields.js +34 -0
- package/dist/Shared/ErkLocationField/locationRestrictions.d.ts +6 -0
- package/dist/Shared/ErkLocationField/locationRestrictions.js +51 -0
- package/dist/Shared/ErkLocationInput/ErkLocationInput.d.ts +15 -0
- package/dist/Shared/ErkLocationInput/ErkLocationInput.js +502 -0
- package/dist/Shared/ErkLocationInput/ErkLocationInput.module.css +128 -0
- package/dist/Shared/ErkNumberField/ErkNumberField.d.ts +42 -0
- package/dist/Shared/ErkNumberField/ErkNumberField.js +169 -0
- package/dist/Utils/AxiosWidget.js +9 -1
- package/dist/constants/FormStepTypes.d.ts +5 -1
- package/dist/constants/FormStepTypes.js +4 -0
- package/dist/index.lib.d.ts +4 -2
- package/dist/index.lib.js +3 -1
- package/dist/main.js +4 -1
- package/package.json +2 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { Dialog } from '@mui/material';
|
|
4
|
+
import { addDays, addMonths, format, isBefore, isSameDay, isSameMonth, startOfDay, startOfMonth, startOfWeek, } from 'date-fns';
|
|
5
|
+
import { es } from 'date-fns/locale';
|
|
6
|
+
import { BookingMode, PersonSelectionMode, } from '../../@Types/BookingFormStep';
|
|
7
|
+
import ErkButton from '../ErkButton/ErkButton';
|
|
8
|
+
import ErkTextField from '../ErkTextField/ErkTextField';
|
|
9
|
+
import { browserTimeZone, clockIn, isValidEmail, nonEmpty, zoneLabel } from '../../Services/BookingService';
|
|
10
|
+
import styles from './ErkBookingPicker.module.css';
|
|
11
|
+
const WEEK_STARTS_ON = 1;
|
|
12
|
+
const DOW = ['L', 'M', 'M', 'J', 'V', 'S', 'D'];
|
|
13
|
+
const MODE_LABEL = {
|
|
14
|
+
[BookingMode.IN_PERSON]: 'Presencial',
|
|
15
|
+
[BookingMode.VIRTUAL]: 'Virtual',
|
|
16
|
+
[BookingMode.HYBRID]: 'Híbrida',
|
|
17
|
+
};
|
|
18
|
+
const STEP_LABEL = {
|
|
19
|
+
type: 'Tipo',
|
|
20
|
+
person: 'Persona',
|
|
21
|
+
day: 'Día',
|
|
22
|
+
hour: 'Hora',
|
|
23
|
+
confirm: 'Reservar',
|
|
24
|
+
};
|
|
25
|
+
const STEP_HINT = {
|
|
26
|
+
type: 'Elige qué necesitas',
|
|
27
|
+
person: 'Elige con quién agendar',
|
|
28
|
+
day: 'Elige un día',
|
|
29
|
+
hour: 'Elige una hora libre',
|
|
30
|
+
confirm: 'Reserva tu horario',
|
|
31
|
+
};
|
|
32
|
+
const GENERIC_SLOTS_ERROR = 'No pudimos cargar los horarios de este día.';
|
|
33
|
+
const GENERIC_DAYS_ERROR = 'No pudimos cargar los días disponibles.';
|
|
34
|
+
const NO_EMAIL_HINT = 'Sin correo no podremos enviarte la confirmación.';
|
|
35
|
+
/** Duración del fade al reestructurarse el layout (entrar/salir del paso final). */
|
|
36
|
+
const XFADE_MS = 160;
|
|
37
|
+
/** El CTA de confirmar usa el acento del form (secondaryContrastColor vía --booking-accent). */
|
|
38
|
+
const ACCENT_SX = {
|
|
39
|
+
backgroundColor: 'var(--booking-accent, #ee6c4d)',
|
|
40
|
+
'&:hover': { backgroundColor: 'var(--booking-accent, #ee6c4d)', filter: 'brightness(0.95)' },
|
|
41
|
+
'&:disabled': { backgroundColor: 'var(--booking-accent, #ee6c4d)', color: '#fff' },
|
|
42
|
+
};
|
|
43
|
+
const BOOKING_COPY = {
|
|
44
|
+
open: 'Agendar cita',
|
|
45
|
+
confirm: 'Reservar',
|
|
46
|
+
confirming: 'Reservando…',
|
|
47
|
+
title: 'Revisa y reserva tu horario',
|
|
48
|
+
note: 'La cita se confirma cuando envíes el formulario.',
|
|
49
|
+
};
|
|
50
|
+
function fmtDate(date) {
|
|
51
|
+
return format(date, 'yyyy-MM-dd');
|
|
52
|
+
}
|
|
53
|
+
function longDate(date) {
|
|
54
|
+
return format(date, 'EEEE d MMM', { locale: es });
|
|
55
|
+
}
|
|
56
|
+
function fullDate(date) {
|
|
57
|
+
return format(date, "EEEE d 'de' MMMM 'de' yyyy", { locale: es });
|
|
58
|
+
}
|
|
59
|
+
function errorText(error, fallback) {
|
|
60
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
61
|
+
}
|
|
62
|
+
/** Mañana hasta el mediodía, tarde desde ahí, en el reloj en que se muestran las horas. */
|
|
63
|
+
function groupSlots(slots) {
|
|
64
|
+
const morning = slots.filter((s) => s.startTime < '12:00');
|
|
65
|
+
const afternoon = slots.filter((s) => s.startTime >= '12:00');
|
|
66
|
+
return [
|
|
67
|
+
{ label: 'Mañana', slots: morning },
|
|
68
|
+
{ label: 'Tarde', slots: afternoon },
|
|
69
|
+
].filter((group) => group.slots.length > 0);
|
|
70
|
+
}
|
|
71
|
+
function initials(name) {
|
|
72
|
+
return name
|
|
73
|
+
.split(' ')
|
|
74
|
+
.map((p) => p[0])
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.slice(0, 2)
|
|
77
|
+
.join('')
|
|
78
|
+
.toUpperCase();
|
|
79
|
+
}
|
|
80
|
+
function capitalize(text) {
|
|
81
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Selector de cita: una tarjeta de entrada ("Agendar cita") abre el wizard en un modal —
|
|
85
|
+
* una decisión por pantalla (persona → día → hora → confirmar), con panel izquierdo de
|
|
86
|
+
* resumen que se va llenando, breadcrumb navegable arriba y barra inferior Atrás / Siguiente.
|
|
87
|
+
* En el paso final el panel derecho desaparece y el resumen se expande en una columna
|
|
88
|
+
* centrada (cross-fade corto). "Reservar" retira el horario de la oferta por unos minutos, cierra el
|
|
89
|
+
* modal y deja el resumen compacto; la cita se confirma cuando se envía el formulario.
|
|
90
|
+
*/
|
|
91
|
+
function ErkBookingPicker({ value, onChange, label, description, durationMinutes, bookingMode, temporaryLockMinutes, personSelectionMode, persons, eventTemplates, timeZone, knownContact, askEmail = false, lapsed, fetchSlots, fetchDays, holdSlot, disabled = false, withoutContact = false, copy: copyOverrides, readOnly = false, error, loadError, onRetryLoad, }) {
|
|
92
|
+
const copy = { ...BOOKING_COPY, ...copyOverrides };
|
|
93
|
+
const needsPerson = !!personSelectionMode && personSelectionMode !== PersonSelectionMode.NONE;
|
|
94
|
+
const personsOptional = personSelectionMode === PersonSelectionMode.OPTIONAL;
|
|
95
|
+
const needsType = (eventTemplates?.length ?? 0) > 1;
|
|
96
|
+
const today = startOfDay(new Date());
|
|
97
|
+
const wizardKeys = useMemo(() => [
|
|
98
|
+
...(needsType ? ['type'] : []),
|
|
99
|
+
...(needsPerson ? ['person'] : []),
|
|
100
|
+
...['day', 'hour', 'confirm'],
|
|
101
|
+
], [needsType, needsPerson]);
|
|
102
|
+
const finalIdx = wizardKeys.length - 1;
|
|
103
|
+
const [open, setOpen] = useState(false);
|
|
104
|
+
const [wizStep, setWizStep] = useState(0);
|
|
105
|
+
const [xfade, setXfade] = useState(false);
|
|
106
|
+
const [eventTemplate, setEventTemplate] = useState(undefined);
|
|
107
|
+
const [person, setPerson] = useState(undefined);
|
|
108
|
+
const [personTouched, setPersonTouched] = useState(false);
|
|
109
|
+
const [date, setDate] = useState(undefined);
|
|
110
|
+
const [viewMonth, setViewMonth] = useState(() => startOfMonth(today));
|
|
111
|
+
const [slot, setSlot] = useState(undefined);
|
|
112
|
+
const [slots, setSlots] = useState([]);
|
|
113
|
+
const [loading, setLoading] = useState(false);
|
|
114
|
+
const [slotsError, setSlotsError] = useState(undefined);
|
|
115
|
+
const [contactName, setContactName] = useState('');
|
|
116
|
+
const [contactEmail, setContactEmail] = useState('');
|
|
117
|
+
const [emailTouched, setEmailTouched] = useState(false);
|
|
118
|
+
/** `undefined` mientras se consultan; sin `fetchDays` nunca se consultan y todo día futuro vale. */
|
|
119
|
+
const [days, setDays] = useState(undefined);
|
|
120
|
+
const [daysError, setDaysError] = useState(undefined);
|
|
121
|
+
const [daysAttempt, setDaysAttempt] = useState(0);
|
|
122
|
+
const [holding, setHolding] = useState(false);
|
|
123
|
+
const [holdError, setHoldError] = useState(undefined);
|
|
124
|
+
/** Cada pedido de horas lleva un número; solo el último puede escribir el estado (A5.a). */
|
|
125
|
+
const slotsRequest = useRef(0);
|
|
126
|
+
// A hold lost after it was made sends the requester back to the hours, with the reason on screen.
|
|
127
|
+
const [seenLapsed, setSeenLapsed] = useState(lapsed);
|
|
128
|
+
if (lapsed !== seenLapsed) {
|
|
129
|
+
setSeenLapsed(lapsed);
|
|
130
|
+
if (lapsed) {
|
|
131
|
+
setSlot(undefined);
|
|
132
|
+
setHoldError(lapsed);
|
|
133
|
+
setWizStep(Math.max(0, wizardKeys.indexOf(date ? 'hour' : 'day')));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const knownName = nonEmpty(knownContact?.name);
|
|
137
|
+
const knownEmail = nonEmpty(knownContact?.email);
|
|
138
|
+
const askName = !withoutContact && !knownName;
|
|
139
|
+
const nameValue = knownName ?? contactName.trim();
|
|
140
|
+
const emailValue = askEmail ? contactEmail.trim() : knownEmail;
|
|
141
|
+
const emailInvalid = !withoutContact && askEmail && !isValidEmail(contactEmail);
|
|
142
|
+
const askContactEmail = !withoutContact && askEmail;
|
|
143
|
+
const localZone = useMemo(() => browserTimeZone(), []);
|
|
144
|
+
const showLocal = !!timeZone && !!localZone && localZone !== timeZone;
|
|
145
|
+
const reduceMotion = useMemo(() => typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches, []);
|
|
146
|
+
const monthCells = useMemo(() => {
|
|
147
|
+
const first = startOfWeek(startOfMonth(viewMonth), { weekStartsOn: WEEK_STARTS_ON });
|
|
148
|
+
return Array.from({ length: 42 }, (_, i) => addDays(first, i));
|
|
149
|
+
}, [viewMonth]);
|
|
150
|
+
const loadSlots = useCallback(async (day, personId, templateId) => {
|
|
151
|
+
if (!fetchSlots)
|
|
152
|
+
return;
|
|
153
|
+
const ticket = ++slotsRequest.current;
|
|
154
|
+
setLoading(true);
|
|
155
|
+
setSlots([]);
|
|
156
|
+
setSlotsError(undefined);
|
|
157
|
+
try {
|
|
158
|
+
const loaded = await fetchSlots({ personId, date: fmtDate(day), eventTemplate: templateId });
|
|
159
|
+
if (ticket === slotsRequest.current)
|
|
160
|
+
setSlots(loaded);
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
if (ticket === slotsRequest.current)
|
|
164
|
+
setSlotsError(errorText(e, GENERIC_SLOTS_ERROR));
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
if (ticket === slotsRequest.current)
|
|
168
|
+
setLoading(false);
|
|
169
|
+
}
|
|
170
|
+
}, [fetchSlots]);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (value || !date)
|
|
173
|
+
return;
|
|
174
|
+
// The picker synchronises with the slots API here, and `loadSlots` raises its own
|
|
175
|
+
// loading flag as it goes out — the very shape the rule asks effects to be used for.
|
|
176
|
+
// oxlint-disable-next-line react/set-state-in-effect
|
|
177
|
+
void loadSlots(date, person?.id, eventTemplate?.id);
|
|
178
|
+
}, [value, date, person, eventTemplate, loadSlots]);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (!fetchDays || !open || value)
|
|
181
|
+
return;
|
|
182
|
+
// The days depend on the type; until one is picked there is nothing to ask for.
|
|
183
|
+
if (needsType && !eventTemplate)
|
|
184
|
+
return;
|
|
185
|
+
let cancelled = false;
|
|
186
|
+
// Same shape as the slots effect: the days come from the host's API and the picker only mirrors them.
|
|
187
|
+
// oxlint-disable-next-line react/set-state-in-effect
|
|
188
|
+
setDays(undefined);
|
|
189
|
+
setDaysError(undefined);
|
|
190
|
+
fetchDays({ personId: person?.id, eventTemplate: eventTemplate?.id })
|
|
191
|
+
.then((available) => {
|
|
192
|
+
if (cancelled)
|
|
193
|
+
return;
|
|
194
|
+
setDays(available);
|
|
195
|
+
// The month on screen may hold nothing; the first day on offer is where the requester needs to be.
|
|
196
|
+
const first = available[0];
|
|
197
|
+
if (!first)
|
|
198
|
+
return;
|
|
199
|
+
setViewMonth((current) => available.some((d) => isSameMonth(new Date(d + 'T00:00:00'), current))
|
|
200
|
+
? current
|
|
201
|
+
: startOfMonth(new Date(first + 'T00:00:00')));
|
|
202
|
+
})
|
|
203
|
+
.catch((e) => {
|
|
204
|
+
if (!cancelled)
|
|
205
|
+
setDaysError(errorText(e, GENERIC_DAYS_ERROR));
|
|
206
|
+
});
|
|
207
|
+
return () => {
|
|
208
|
+
cancelled = true;
|
|
209
|
+
};
|
|
210
|
+
}, [fetchDays, open, value, person, eventTemplate, needsType, daysAttempt]);
|
|
211
|
+
// Al entrar/salir del paso final el layout se reestructura; el reacomodo
|
|
212
|
+
// ocurre mientras la tarjeta está invisible (fade corto en dos fases).
|
|
213
|
+
const goToStep = (target) => {
|
|
214
|
+
const crossesFinal = (wizStep === finalIdx) !== (target === finalIdx);
|
|
215
|
+
if (!crossesFinal || reduceMotion) {
|
|
216
|
+
setWizStep(target);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
setXfade(true);
|
|
220
|
+
window.setTimeout(() => {
|
|
221
|
+
setWizStep(target);
|
|
222
|
+
requestAnimationFrame(() => requestAnimationFrame(() => setXfade(false)));
|
|
223
|
+
}, XFADE_MS);
|
|
224
|
+
};
|
|
225
|
+
const currentKey = wizardKeys[wizStep];
|
|
226
|
+
const canAdvance = (() => {
|
|
227
|
+
switch (currentKey) {
|
|
228
|
+
case 'type':
|
|
229
|
+
return !!eventTemplate;
|
|
230
|
+
case 'person':
|
|
231
|
+
return personsOptional ? personTouched : !!person;
|
|
232
|
+
case 'day':
|
|
233
|
+
return !!date;
|
|
234
|
+
case 'hour':
|
|
235
|
+
return !!slot;
|
|
236
|
+
case 'confirm':
|
|
237
|
+
return withoutContact || (nameValue.length > 0 && !emailInvalid);
|
|
238
|
+
default:
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
})();
|
|
242
|
+
const confirm = async () => {
|
|
243
|
+
if (!slot || !date)
|
|
244
|
+
return;
|
|
245
|
+
const selection = {
|
|
246
|
+
type: 'BOOKING_VALUE',
|
|
247
|
+
slotId: slot.id,
|
|
248
|
+
date: fmtDate(date),
|
|
249
|
+
startTime: slot.startTime,
|
|
250
|
+
endTime: slot.endTime,
|
|
251
|
+
personId: person?.id,
|
|
252
|
+
personName: person?.name,
|
|
253
|
+
contactName: nonEmpty(nameValue),
|
|
254
|
+
contactEmail: nonEmpty(emailValue),
|
|
255
|
+
status: 'PENDING',
|
|
256
|
+
};
|
|
257
|
+
if (!holdSlot) {
|
|
258
|
+
setOpen(false);
|
|
259
|
+
onChange(selection);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
setHolding(true);
|
|
263
|
+
setHoldError(undefined);
|
|
264
|
+
try {
|
|
265
|
+
const held = await holdSlot({
|
|
266
|
+
slot,
|
|
267
|
+
personId: person?.id,
|
|
268
|
+
eventTemplate: eventTemplate?.id,
|
|
269
|
+
contact: { name: nameValue, email: nonEmpty(emailValue) },
|
|
270
|
+
});
|
|
271
|
+
setOpen(false);
|
|
272
|
+
onChange({ ...selection, ...held });
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
// Somebody else took the slot while the form was being finished: back to the hour, with fresh slots.
|
|
276
|
+
setHoldError(e instanceof Error && e.message ? e.message : 'No pudimos reservar ese horario.');
|
|
277
|
+
setSlot(undefined);
|
|
278
|
+
goToStep(wizardKeys.indexOf('hour'));
|
|
279
|
+
void loadSlots(date, person?.id, eventTemplate?.id);
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
setHolding(false);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const reset = () => {
|
|
286
|
+
onChange(null);
|
|
287
|
+
setWizStep(0);
|
|
288
|
+
setEventTemplate(undefined);
|
|
289
|
+
setPerson(undefined);
|
|
290
|
+
setPersonTouched(false);
|
|
291
|
+
setDate(undefined);
|
|
292
|
+
setSlot(undefined);
|
|
293
|
+
setDays(undefined);
|
|
294
|
+
setHoldError(undefined);
|
|
295
|
+
setContactName('');
|
|
296
|
+
setContactEmail('');
|
|
297
|
+
setEmailTouched(false);
|
|
298
|
+
};
|
|
299
|
+
// Another type means another duration: the day and hour picked for the previous one no longer hold.
|
|
300
|
+
const pickType = (template) => {
|
|
301
|
+
if (template.id === eventTemplate?.id)
|
|
302
|
+
return;
|
|
303
|
+
setEventTemplate(template);
|
|
304
|
+
setDate(undefined);
|
|
305
|
+
setSlot(undefined);
|
|
306
|
+
setDays(undefined);
|
|
307
|
+
setHoldError(undefined);
|
|
308
|
+
};
|
|
309
|
+
const pickPerson = (p) => {
|
|
310
|
+
setPerson(p);
|
|
311
|
+
setPersonTouched(true);
|
|
312
|
+
setSlot(undefined);
|
|
313
|
+
};
|
|
314
|
+
// Read-only / already booked → compact summary with the same card aesthetic
|
|
315
|
+
if (readOnly || value) {
|
|
316
|
+
const pending = !!value && value.status === 'PENDING' && !readOnly;
|
|
317
|
+
return (_jsx("div", { className: styles.container, children: _jsxs("div", { className: styles.entry, children: [_jsx("div", { className: styles.bookedIcon, children: "\u2713" }), _jsxs("div", { className: styles.entryInfo, children: [_jsx("span", { className: styles.entryLabel, children: label }), _jsx("span", { className: styles.bookedMain, children: value
|
|
318
|
+
? [
|
|
319
|
+
capitalize(longDate(new Date(value.date + 'T00:00:00'))),
|
|
320
|
+
`${value.startTime}–${value.endTime}`,
|
|
321
|
+
value.personName ? `con ${value.personName}` : undefined,
|
|
322
|
+
]
|
|
323
|
+
.filter(Boolean)
|
|
324
|
+
.join(' · ')
|
|
325
|
+
: 'Sin cita' }), value?.eventTemplate && (_jsxs("span", { className: styles.entrySub, children: [value.eventTemplate.name, " \u00B7 ", value.eventTemplate.durationMinutes, " min"] })), pending && (_jsxs("span", { className: styles.entrySub, children: [value.lockedUntil && timeZone
|
|
326
|
+
? `Reservado hasta las ${clockIn(value.lockedUntil, timeZone)}. `
|
|
327
|
+
: 'Horario reservado. ', "Env\u00EDa el formulario para confirmar la cita."] }))] }), value && !readOnly && _jsx(ErkButton, { text: "Cambiar", variant: "outlined", onClick: reset })] }) }));
|
|
328
|
+
}
|
|
329
|
+
const isFinal = wizStep === finalIdx;
|
|
330
|
+
const duration = eventTemplate?.durationMinutes ?? durationMinutes;
|
|
331
|
+
const mode = eventTemplate?.bookingMode ?? bookingMode;
|
|
332
|
+
const personLabel = person?.name ?? (personTouched && personsOptional ? 'Cualquiera' : undefined);
|
|
333
|
+
const dayText = date ? capitalize(longDate(date)) : undefined;
|
|
334
|
+
const localStart = showLocal && localZone && slot?.start ? clockIn(slot.start, localZone) : undefined;
|
|
335
|
+
const hourText = slot
|
|
336
|
+
? `${slot.startTime} – ${slot.endTime}${localStart ? ` (${localStart} en tu hora)` : ''}`
|
|
337
|
+
: undefined;
|
|
338
|
+
const zoneText = timeZone ? `Horas en ${zoneLabel(timeZone, date ?? new Date())}` : undefined;
|
|
339
|
+
const firstAvailable = slots.find((s) => s.available);
|
|
340
|
+
const summaryRows = [
|
|
341
|
+
...(needsType
|
|
342
|
+
? [{ key: 'type', placeholder: 'Tipo de atención', text: eventTemplate?.name }]
|
|
343
|
+
: []),
|
|
344
|
+
...(needsPerson ? [{ key: 'person', placeholder: 'Persona', text: personLabel }] : []),
|
|
345
|
+
{ key: 'day', placeholder: 'Día', text: dayText },
|
|
346
|
+
{ key: 'hour', placeholder: 'Hora', text: hourText },
|
|
347
|
+
];
|
|
348
|
+
const navParts = [];
|
|
349
|
+
if (eventTemplate)
|
|
350
|
+
navParts.push(_jsx("b", { children: eventTemplate.name }, "t"));
|
|
351
|
+
if (personLabel)
|
|
352
|
+
navParts.push(_jsx("b", { children: personLabel }, "p"));
|
|
353
|
+
if (dayText)
|
|
354
|
+
navParts.push(_jsx("span", { children: dayText }, "d"));
|
|
355
|
+
if (hourText)
|
|
356
|
+
navParts.push(_jsx("span", { className: styles.navHl, children: hourText }, "h"));
|
|
357
|
+
return (_jsxs("div", { className: styles.container, children: [_jsxs("div", { className: styles.entry, children: [_jsx("div", { className: styles.avatar, children: "\uD83D\uDCC5" }), _jsxs("div", { className: styles.entryInfo, children: [_jsx("span", { className: styles.entryLabel, children: label }), _jsx("span", { className: styles.entrySub, children: description ?? 'Agenda tu cita' })] }), _jsx(ErkButton, { text: copy.open, disabled: disabled, onClick: () => setOpen(true) })] }), loadError && (_jsxs("div", { className: styles.retry, role: "alert", children: [_jsx("span", { children: loadError }), onRetryLoad && _jsx(ErkButton, { text: "Reintentar", variant: "outlined", onClick: onRetryLoad })] })), lapsed && !open && (_jsx("div", { className: styles.err, role: "alert", children: lapsed })), _jsx(Dialog, { open: open, onClose: () => setOpen(false), maxWidth: "md", fullWidth: true, slotProps: { paper: { style: { borderRadius: 16, background: 'transparent', boxShadow: 'none' } } }, children: _jsxs("div", { className: `${styles.card}${isFinal ? ` ${styles.cardFinal}` : ''}${xfade ? ` ${styles.xfade}` : ''}`, children: [_jsxs("div", { className: isFinal ? `${styles.book} ${styles.bookFinal}` : styles.book, children: [_jsxs("aside", { className: styles.who, children: [_jsxs("div", { className: styles.brand, children: [_jsx("div", { className: styles.avatar, children: person ? initials(person.name) : '📅' }), _jsxs("div", { children: [_jsx("h2", { className: styles.brandName, children: person?.name ?? label }), _jsx("p", { className: styles.brandRole, children: person?.role ?? description ?? 'Agenda tu cita' })] })] }), _jsxs("div", { className: styles.meta, children: [_jsxs("div", { className: styles.row, children: [_jsx("span", { className: styles.ic, children: "\u25F7" }), duration, " min"] }), mode && (_jsxs("div", { className: styles.row, children: [_jsx("span", { className: styles.ic, children: "\u25F1" }), _jsx("span", { className: styles.pill, children: MODE_LABEL[mode] })] }))] }), _jsx("div", { className: styles.sum, "aria-live": "polite", children: summaryRows.map((r) => (_jsxs("div", { className: r.text ? `${styles.srow} ${styles.srowOn}` : styles.srow, children: [_jsx("span", { className: styles.dot, children: "\u2713" }), _jsx("span", { children: r.text ?? r.placeholder })] }, r.key))) }), isFinal && (askName || askContactEmail) && (_jsxs("div", { className: styles.cfields, children: [askName && (_jsx(ErkTextField, { label: "Nombre", value: contactName, onChange: setContactName, required: true })), askContactEmail && (_jsx(ErkTextField, { label: "Correo", type: "email", value: contactEmail, onChange: setContactEmail, onBlur: () => setEmailTouched(true), required: true, error: emailTouched && emailInvalid, helperText: emailTouched && contactEmail.trim() && emailInvalid
|
|
358
|
+
? 'Escribe un correo válido.'
|
|
359
|
+
: NO_EMAIL_HINT }))] })), isFinal && withoutContact && _jsx("div", { className: styles.lock, children: copy.note }), isFinal && !withoutContact && (_jsxs("div", { className: styles.lock, children: ["\u23F3", ' ', temporaryLockMinutes ? (_jsxs(_Fragment, { children: ["Reservaremos el horario ", _jsxs("b", { children: [temporaryLockMinutes, " min"] }), ".", ' '] })) : ('Reservaremos el horario. '), copy.note] }))] }), _jsxs("div", { className: styles.right, children: [_jsx("nav", { className: styles.stepper, "aria-label": "Progreso", children: wizardKeys.map((key, i) => (_jsxs("span", { className: styles.stepGroup, children: [_jsxs("button", { type: "button", disabled: i >= wizStep, onClick: () => goToStep(i), className: i < wizStep
|
|
360
|
+
? `${styles.stepItem} ${styles.stepDone}`
|
|
361
|
+
: i === wizStep
|
|
362
|
+
? `${styles.stepItem} ${styles.stepOn}`
|
|
363
|
+
: styles.stepItem, children: [_jsx("span", { className: styles.stepNum, children: i < wizStep ? '✓' : i + 1 }), key === 'confirm' ? copy.confirm : STEP_LABEL[key]] }), i < wizardKeys.length - 1 && _jsx("span", { className: styles.stepSep })] }, key))) }), currentKey === 'type' && (_jsxs("section", { className: styles.pane, children: [_jsx("h3", { className: styles.q, children: "\u00BFQu\u00E9 necesitas?" }), _jsx("p", { className: styles.qsub, children: "Elige el tipo de atenci\u00F3n; de \u00E9l dependen la duraci\u00F3n y los horarios." }), _jsx("div", { className: styles.persons, children: (eventTemplates ?? []).map((t) => (_jsxs("button", { type: "button", "aria-pressed": eventTemplate?.id === t.id, className: eventTemplate?.id === t.id
|
|
364
|
+
? `${styles.pcard} ${styles.pcardOn}`
|
|
365
|
+
: styles.pcard, onClick: () => pickType(t), children: [_jsxs("span", { className: styles.pinfo, children: [_jsx("span", { className: styles.pn, children: t.name }), t.description && (_jsx("span", { className: styles.pr, children: t.description }))] }), _jsxs("span", { className: styles.tdur, children: [t.durationMinutes, " min"] }), _jsx("span", { className: styles.chk, children: "\u2713" })] }, t.id))) })] }, "type")), currentKey === 'person' && (_jsxs("section", { className: styles.pane, children: [_jsx("h3", { className: styles.q, children: "\u00BFCon qui\u00E9n quieres agendar?" }), _jsx("p", { className: styles.qsub, children: personsOptional
|
|
366
|
+
? 'Elige una persona o deja que el sistema asigne a quien esté disponible.'
|
|
367
|
+
: 'Elige la persona con la que quieres tu cita.' }), _jsxs("div", { className: styles.persons, children: [(persons ?? []).map((p) => (_jsxs("button", { type: "button", className: person?.id === p.id
|
|
368
|
+
? `${styles.pcard} ${styles.pcardOn}`
|
|
369
|
+
: styles.pcard, onClick: () => pickPerson(p), children: [_jsx("span", { className: styles.pav, children: initials(p.name) }), _jsxs("span", { className: styles.pinfo, children: [_jsx("span", { className: styles.pn, children: p.name }), p.role && _jsx("span", { className: styles.pr, children: p.role })] }), _jsx("span", { className: styles.chk, children: "\u2713" })] }, p.id))), personsOptional && (_jsxs("button", { type: "button", className: personTouched && !person
|
|
370
|
+
? `${styles.pcard} ${styles.pcardAny} ${styles.pcardOn}`
|
|
371
|
+
: `${styles.pcard} ${styles.pcardAny}`, onClick: () => pickPerson(undefined), children: [_jsx("span", { className: styles.pav, children: "\u2726" }), _jsxs("span", { className: styles.pinfo, children: [_jsx("span", { className: styles.pn, children: "Cualquiera" }), _jsx("span", { className: styles.pr, children: "El sistema asigna" })] }), _jsx("span", { className: styles.chk, children: "\u2713" })] }))] })] }, "person")), currentKey === 'day' && (_jsxs("section", { className: styles.pane, children: [_jsx("h3", { className: styles.q, children: "Elige un d\u00EDa" }), _jsx("p", { className: styles.qsub, children: fetchDays
|
|
372
|
+
? 'Solo se pueden elegir los días con horarios disponibles.'
|
|
373
|
+
: 'Solo se muestran fechas a partir de hoy.' }), _jsxs("div", { className: styles.monthWrap, children: [_jsxs("div", { className: styles.monthHead, children: [_jsx("span", { className: styles.monthTitle, children: capitalize(format(viewMonth, 'MMMM yyyy', { locale: es })) }), _jsxs("span", { className: styles.monthNav, children: [_jsx("button", { type: "button", "aria-label": "Mes anterior", onClick: () => setViewMonth((m) => addMonths(m, -1)), children: "\u2039" }), _jsx("button", { type: "button", "aria-label": "Mes siguiente", onClick: () => setViewMonth((m) => addMonths(m, 1)), children: "\u203A" })] })] }), _jsxs("div", { className: styles.mgrid, children: [DOW.map((d, i) => (_jsx("span", { className: styles.mdow, children: d }, i))), monthCells.map((d) => {
|
|
374
|
+
const outside = !isSameMonth(d, viewMonth);
|
|
375
|
+
const past = isBefore(d, today);
|
|
376
|
+
const offered = !daysError && (!days || days.includes(fmtDate(d)));
|
|
377
|
+
const selectable = !outside && !past && offered;
|
|
378
|
+
const cls = date && isSameDay(d, date)
|
|
379
|
+
? `${styles.mday} ${styles.mdaySel}`
|
|
380
|
+
: selectable
|
|
381
|
+
? `${styles.mday} ${styles.mdayFree}`
|
|
382
|
+
: `${styles.mday} ${styles.mdayNone}`;
|
|
383
|
+
return (_jsx("button", { type: "button", disabled: !selectable, "aria-label": fullDate(d), className: cls, onClick: () => {
|
|
384
|
+
setDate(d);
|
|
385
|
+
setSlot(undefined);
|
|
386
|
+
}, children: format(d, 'd') }, d.toISOString()));
|
|
387
|
+
})] })] }), daysError ? (_jsxs("div", { className: styles.retry, role: "alert", children: [_jsx("span", { children: daysError }), _jsx(ErkButton, { text: "Reintentar", variant: "outlined", onClick: () => setDaysAttempt((n) => n + 1) })] })) : fetchDays && days === undefined ? (_jsx("div", { className: styles.empty, children: "Buscando d\u00EDas con horarios\u2026" })) : (days?.length === 0 && (_jsx("div", { className: styles.empty, children: "No hay d\u00EDas con horarios disponibles." })))] }, "day")), currentKey === 'hour' && (_jsxs("section", { className: styles.pane, children: [_jsx("h3", { className: styles.q, children: "Elige una hora" }), _jsxs("p", { className: styles.qsub, children: [dayText, " \u00B7 citas de ", duration, " min"] }), holdError && (_jsx("div", { className: styles.err, role: "alert", children: holdError })), zoneText && _jsx("p", { className: styles.tz, children: zoneText }), loading ? (_jsx("div", { className: styles.empty, children: "Buscando horarios\u2026" })) : slotsError ? (_jsxs("div", { className: styles.retry, role: "alert", children: [_jsx("span", { children: slotsError }), _jsx(ErkButton, { text: "Reintentar", variant: "outlined", onClick: () => date && void loadSlots(date, person?.id, eventTemplate?.id) })] })) : slots.length === 0 ? (_jsx("div", { className: styles.empty, children: "No hay horarios disponibles ese d\u00EDa." })) : (groupSlots(slots).map((group) => (_jsxs("div", { className: styles.slotGroup, children: [_jsx("h4", { className: styles.slotGroupTitle, children: group.label }), _jsx("div", { className: styles.slots, children: group.slots.map((s) => (_jsxs("button", { type: "button", disabled: !s.available, title: `${s.startTime} – ${s.endTime}`, className: slot?.id === s.id
|
|
388
|
+
? `${styles.slot} ${styles.slotSel}`
|
|
389
|
+
: s.available
|
|
390
|
+
? styles.slot
|
|
391
|
+
: `${styles.slot} ${styles.slotTaken}`, onClick: () => s.available && setSlot(s), children: [s.startTime, s === firstAvailable && (_jsx("span", { className: styles.firstTag, children: "Primera disponible" }))] }, s.id))) })] }, group.label)))), localStart && slot && (_jsxs("p", { className: styles.tz, children: [slot.startTime, " es ", localStart, " en tu hora."] }))] }, "hour")), currentKey === 'confirm' && (_jsxs("section", { className: `${styles.pane} ${styles.paneFinal}`, children: [_jsx("h3", { className: styles.q, children: copy.title }), _jsx("p", { className: styles.qsub, children: withoutContact
|
|
392
|
+
? copy.note
|
|
393
|
+
: askName || askEmail
|
|
394
|
+
? 'Completa tus datos y reserva el horario. La cita se confirma al enviar el formulario.'
|
|
395
|
+
: 'Reserva el horario. La cita se confirma al enviar el formulario.' })] }, "confirm"))] })] }), _jsxs("div", { className: styles.navbar, children: [_jsx("div", { className: styles.navSummary, children: isFinal || !canAdvance || navParts.length === 0
|
|
396
|
+
? STEP_HINT[currentKey]
|
|
397
|
+
: navParts.reduce((acc, part, i) => i === 0 ? [part] : [...acc, _jsx("span", { children: " \u00B7 " }, `s${i}`), part], []) }), wizStep > 0 && (_jsx(ErkButton, { text: "Atr\u00E1s", variant: "outlined", onClick: () => goToStep(wizStep - 1) })), isFinal ? (_jsx(ErkButton, { text: holding ? copy.confirming : copy.confirm, sx: ACCENT_SX, disabled: !slot || holding || !canAdvance, onClick: () => void confirm() })) : (_jsx(ErkButton, { text: "Siguiente", disabled: !canAdvance, onClick: () => goToStep(wizStep + 1) }))] })] }) }), error && _jsx("div", { className: styles.err, children: error })] }));
|
|
398
|
+
}
|
|
399
|
+
export default ErkBookingPicker;
|