@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
package/dist/App/App.stories.js
CHANGED
|
@@ -296,3 +296,192 @@ export const Postview = {
|
|
|
296
296
|
editable: false,
|
|
297
297
|
},
|
|
298
298
|
};
|
|
299
|
+
// ---------------------------------------------------------------------------------------------
|
|
300
|
+
// Checkboxes: a full form exercising every checkbox variant side by side (sizes, required,
|
|
301
|
+
// description, list mode, long label, nested steps, uncheckedSteps and a checkbox inside a
|
|
302
|
+
// collapsible). Resize the preview to see the collapsed (100% width, 1-line clamp) label mode.
|
|
303
|
+
// ---------------------------------------------------------------------------------------------
|
|
304
|
+
const cbTextInput = (id, label, size = 2) => ({
|
|
305
|
+
id,
|
|
306
|
+
type: 'TEXTINPUT',
|
|
307
|
+
label,
|
|
308
|
+
description: '',
|
|
309
|
+
required: false,
|
|
310
|
+
size,
|
|
311
|
+
isSubject: false,
|
|
312
|
+
});
|
|
313
|
+
const checkboxesForm = {
|
|
314
|
+
steps: {
|
|
315
|
+
'CHECKBOX-size-1': {
|
|
316
|
+
id: 'CHECKBOX-size-1',
|
|
317
|
+
type: 'CHECKBOX',
|
|
318
|
+
label: 'Tamaño 1',
|
|
319
|
+
description: null,
|
|
320
|
+
required: false,
|
|
321
|
+
size: 1,
|
|
322
|
+
},
|
|
323
|
+
'CHECKBOX-size-2': {
|
|
324
|
+
id: 'CHECKBOX-size-2',
|
|
325
|
+
type: 'CHECKBOX',
|
|
326
|
+
label: 'Tamaño 2 con descripción',
|
|
327
|
+
description: 'Texto de ayuda debajo del checkbox.',
|
|
328
|
+
required: false,
|
|
329
|
+
size: 2,
|
|
330
|
+
},
|
|
331
|
+
'CHECKBOX-size-1-required': {
|
|
332
|
+
id: 'CHECKBOX-size-1-required',
|
|
333
|
+
type: 'CHECKBOX',
|
|
334
|
+
label: 'Obligatorio',
|
|
335
|
+
description: null,
|
|
336
|
+
required: true,
|
|
337
|
+
size: 1,
|
|
338
|
+
},
|
|
339
|
+
'CHECKBOX-size-3': {
|
|
340
|
+
id: 'CHECKBOX-size-3',
|
|
341
|
+
type: 'CHECKBOX',
|
|
342
|
+
label: 'Tamaño 3 con un label bastante largo que en escritorio se corta con puntos suspensivos',
|
|
343
|
+
description: null,
|
|
344
|
+
required: false,
|
|
345
|
+
size: 3,
|
|
346
|
+
},
|
|
347
|
+
'CHECKBOX-size-1-checked': {
|
|
348
|
+
id: 'CHECKBOX-size-1-checked',
|
|
349
|
+
type: 'CHECKBOX',
|
|
350
|
+
label: 'Marcado',
|
|
351
|
+
description: null,
|
|
352
|
+
required: false,
|
|
353
|
+
size: 1,
|
|
354
|
+
defaultValue: true,
|
|
355
|
+
},
|
|
356
|
+
'CHECKBOX-size-4': {
|
|
357
|
+
id: 'CHECKBOX-size-4',
|
|
358
|
+
type: 'CHECKBOX',
|
|
359
|
+
label: 'Tamaño 4 (ancho completo): confirmo que la información suministrada es veraz, que he leído la ' +
|
|
360
|
+
'política de tratamiento de datos personales y autorizo el uso de mis datos para los fines ' +
|
|
361
|
+
'descritos en el presente formulario',
|
|
362
|
+
description: null,
|
|
363
|
+
required: true,
|
|
364
|
+
size: 4,
|
|
365
|
+
},
|
|
366
|
+
'CHECKBOX-list': {
|
|
367
|
+
id: 'CHECKBOX-list',
|
|
368
|
+
type: 'CHECKBOX',
|
|
369
|
+
label: 'Modo lista (isList): el checkbox va antes del label y sin dos puntos',
|
|
370
|
+
description: null,
|
|
371
|
+
required: false,
|
|
372
|
+
size: 4,
|
|
373
|
+
isList: true,
|
|
374
|
+
},
|
|
375
|
+
'CHECKBOX-nested': {
|
|
376
|
+
id: 'CHECKBOX-nested',
|
|
377
|
+
type: 'CHECKBOX',
|
|
378
|
+
label: '¿Desea agregar una dirección de envío distinta?',
|
|
379
|
+
description: 'Al marcarlo aparecen los campos de la dirección.',
|
|
380
|
+
required: false,
|
|
381
|
+
size: 4,
|
|
382
|
+
steps: ['TEXTINPUT-shipping-address', 'TEXTINPUT-shipping-city'],
|
|
383
|
+
},
|
|
384
|
+
'TEXTINPUT-shipping-address': cbTextInput('TEXTINPUT-shipping-address', 'Dirección de envío', 2),
|
|
385
|
+
'TEXTINPUT-shipping-city': cbTextInput('TEXTINPUT-shipping-city', 'Ciudad', 2),
|
|
386
|
+
'CHECKBOX-unchecked-steps': {
|
|
387
|
+
id: 'CHECKBOX-unchecked-steps',
|
|
388
|
+
type: 'CHECKBOX',
|
|
389
|
+
label: 'Usar los datos de contacto registrados',
|
|
390
|
+
description: 'Al desmarcarlo aparecen los campos de contacto (uncheckedSteps).',
|
|
391
|
+
required: false,
|
|
392
|
+
size: 4,
|
|
393
|
+
defaultValue: true,
|
|
394
|
+
uncheckedSteps: ['TEXTINPUT-contact-phone', 'TEXTINPUT-contact-email'],
|
|
395
|
+
},
|
|
396
|
+
'TEXTINPUT-contact-phone': cbTextInput('TEXTINPUT-contact-phone', 'Teléfono', 2),
|
|
397
|
+
'TEXTINPUT-contact-email': cbTextInput('TEXTINPUT-contact-email', 'Correo', 2),
|
|
398
|
+
'COLLAPSIBLE-checkbox': {
|
|
399
|
+
id: 'COLLAPSIBLE-checkbox',
|
|
400
|
+
type: 'COLLAPSIBLE',
|
|
401
|
+
label: 'Colapsable con checkbox anidado',
|
|
402
|
+
defaultValue: true,
|
|
403
|
+
steps: ['CHECKBOX-in-collapsible'],
|
|
404
|
+
},
|
|
405
|
+
'CHECKBOX-in-collapsible': {
|
|
406
|
+
id: 'CHECKBOX-in-collapsible',
|
|
407
|
+
type: 'CHECKBOX',
|
|
408
|
+
label: '¿Desea agregar información adicional?',
|
|
409
|
+
description: null,
|
|
410
|
+
required: false,
|
|
411
|
+
size: 4,
|
|
412
|
+
steps: ['TEXTINPUT-extra-1', 'TEXTINPUT-extra-2'],
|
|
413
|
+
},
|
|
414
|
+
'TEXTINPUT-extra-1': cbTextInput('TEXTINPUT-extra-1', 'Información adicional 1', 2),
|
|
415
|
+
'TEXTINPUT-extra-2': cbTextInput('TEXTINPUT-extra-2', 'Información adicional 2', 2),
|
|
416
|
+
},
|
|
417
|
+
size: { blockNum: 4, blockSize: 210, spacingSize: 20 },
|
|
418
|
+
rootSteps: [
|
|
419
|
+
'CHECKBOX-size-1',
|
|
420
|
+
'CHECKBOX-size-2',
|
|
421
|
+
'CHECKBOX-size-1-required',
|
|
422
|
+
'CHECKBOX-size-3',
|
|
423
|
+
'CHECKBOX-size-1-checked',
|
|
424
|
+
'CHECKBOX-size-4',
|
|
425
|
+
'CHECKBOX-list',
|
|
426
|
+
'CHECKBOX-nested',
|
|
427
|
+
'CHECKBOX-unchecked-steps',
|
|
428
|
+
'COLLAPSIBLE-checkbox',
|
|
429
|
+
],
|
|
430
|
+
label: 'Enviar',
|
|
431
|
+
firstSection: 'FIRST',
|
|
432
|
+
sections: {
|
|
433
|
+
FIRST: {
|
|
434
|
+
id: 'FIRST',
|
|
435
|
+
steps: [
|
|
436
|
+
'CHECKBOX-size-1',
|
|
437
|
+
'CHECKBOX-size-2',
|
|
438
|
+
'CHECKBOX-size-1-required',
|
|
439
|
+
'CHECKBOX-size-3',
|
|
440
|
+
'CHECKBOX-size-1-checked',
|
|
441
|
+
'CHECKBOX-size-4',
|
|
442
|
+
'CHECKBOX-list',
|
|
443
|
+
'CHECKBOX-nested',
|
|
444
|
+
'CHECKBOX-unchecked-steps',
|
|
445
|
+
'COLLAPSIBLE-checkbox',
|
|
446
|
+
],
|
|
447
|
+
name: '',
|
|
448
|
+
nextSection: null,
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
/**
|
|
453
|
+
* Every checkbox variant in one form: sizes 1–4, required, description, list mode, long
|
|
454
|
+
* labels, nested `steps` / `uncheckedSteps`, and a checkbox inside a collapsible. Resize the
|
|
455
|
+
* preview below ~900px to see the collapsed label mode (100% width, 1-line clamp, "ver más").
|
|
456
|
+
* Inside the collapsible, close/reopen it and then check the checkbox to reproduce the clipped
|
|
457
|
+
* nested steps.
|
|
458
|
+
*/
|
|
459
|
+
export const Checkboxes = {
|
|
460
|
+
args: {
|
|
461
|
+
formData: checkboxesForm,
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
/** Same checkboxes form in postview, read-only. */
|
|
465
|
+
export const CheckboxesPostview = {
|
|
466
|
+
args: {
|
|
467
|
+
formData: checkboxesForm,
|
|
468
|
+
valuesData: {
|
|
469
|
+
'CHECKBOX-size-1': true,
|
|
470
|
+
'CHECKBOX-size-2': false,
|
|
471
|
+
'CHECKBOX-size-1-required': true,
|
|
472
|
+
'CHECKBOX-size-3': true,
|
|
473
|
+
'CHECKBOX-size-1-checked': true,
|
|
474
|
+
'CHECKBOX-size-4': true,
|
|
475
|
+
'CHECKBOX-list': true,
|
|
476
|
+
'CHECKBOX-nested': true,
|
|
477
|
+
'TEXTINPUT-shipping-address': 'Calle 123 # 45-67',
|
|
478
|
+
'TEXTINPUT-shipping-city': 'Bogotá',
|
|
479
|
+
'CHECKBOX-unchecked-steps': true,
|
|
480
|
+
'COLLAPSIBLE-checkbox': true,
|
|
481
|
+
'CHECKBOX-in-collapsible': false,
|
|
482
|
+
},
|
|
483
|
+
postview: true,
|
|
484
|
+
internal: true,
|
|
485
|
+
editable: false,
|
|
486
|
+
},
|
|
487
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import '@testing-library/jest-dom';
|
|
6
|
+
import App from './App';
|
|
7
|
+
import FormStepTypes from '../constants/FormStepTypes';
|
|
8
|
+
import { SUBMIT_ERROR_MESSAGE } from '../Form/SubmitError/SubmitErrorReason';
|
|
9
|
+
const { post } = vi.hoisted(() => ({ post: vi.fn() }));
|
|
10
|
+
vi.mock('../Utils/AxiosWidget', async (importOriginal) => {
|
|
11
|
+
const original = await importOriginal();
|
|
12
|
+
return { ...original, default: Object.assign(Object.create(original.default), { post }) };
|
|
13
|
+
});
|
|
14
|
+
const FORM = {
|
|
15
|
+
name: 'Contacto',
|
|
16
|
+
apiKey: 'form-key',
|
|
17
|
+
firstSection: 'FIRST',
|
|
18
|
+
sections: { FIRST: { id: 'FIRST', name: '', nextSection: null, steps: ['comment'] } },
|
|
19
|
+
steps: {
|
|
20
|
+
comment: {
|
|
21
|
+
id: 'comment',
|
|
22
|
+
idSection: 'FIRST',
|
|
23
|
+
stepPath: ['comment'],
|
|
24
|
+
type: FormStepTypes.TEXTINPUT,
|
|
25
|
+
label: 'Comentario',
|
|
26
|
+
description: null,
|
|
27
|
+
required: false,
|
|
28
|
+
size: 4,
|
|
29
|
+
editable: true,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
size: { blockNum: 4, blockSize: 210, spacingSize: 20 },
|
|
33
|
+
};
|
|
34
|
+
describe('Form submission that fails', () => {
|
|
35
|
+
it('tells the requester, keeps what they filled in and sends it again on retry', async () => {
|
|
36
|
+
const user = userEvent.setup();
|
|
37
|
+
post.mockRejectedValueOnce({
|
|
38
|
+
response: { status: 409, data: 'La reserva expiró; elige un horario nuevamente' },
|
|
39
|
+
}).mockResolvedValueOnce({ data: { case: 'ABC123', url: 'https://example.com', idTicket: 't1' } });
|
|
40
|
+
render(_jsx(App, { formData: FORM, idOrganization: "acme", internal: true, isWidget: true, editable: true }));
|
|
41
|
+
const comment = await screen.findByLabelText(/Comentario/);
|
|
42
|
+
await user.type(comment, 'Necesito ayuda');
|
|
43
|
+
await user.click(screen.getByTestId('form__submit'));
|
|
44
|
+
const alert = await screen.findByRole('alert');
|
|
45
|
+
expect(alert).toHaveTextContent(SUBMIT_ERROR_MESSAGE);
|
|
46
|
+
expect(alert).toHaveTextContent('La reserva expiró; elige un horario nuevamente');
|
|
47
|
+
expect(screen.getByLabelText(/Comentario/)).toHaveValue('Necesito ayuda');
|
|
48
|
+
await user.click(screen.getByRole('button', { name: 'Reintentar' }));
|
|
49
|
+
await waitFor(() => expect(screen.queryByText(SUBMIT_ERROR_MESSAGE)).not.toBeInTheDocument());
|
|
50
|
+
expect(post).toHaveBeenCalledTimes(2);
|
|
51
|
+
expect(post.mock.calls[1][1]).toEqual(expect.objectContaining({ comment: 'Necesito ayuda' }));
|
|
52
|
+
});
|
|
53
|
+
});
|
package/dist/App/AppHooks.js
CHANGED
|
@@ -158,6 +158,14 @@ export const useSetupApp = (isEmbedded, props) => {
|
|
|
158
158
|
idCurrentAgent,
|
|
159
159
|
handleConfirmed,
|
|
160
160
|
]);
|
|
161
|
+
// The host may resolve the classifiers asynchronously (the admin fetches them while the form is
|
|
162
|
+
// already mounted). `loadData` only copies the prop it saw when it ran, so patch the form whenever a
|
|
163
|
+
// new dictionary arrives — without re-running the load, which would reset what the user typed.
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!classifiers || !form || form.classifiers === classifiers)
|
|
166
|
+
return;
|
|
167
|
+
setForm({ ...form, classifiers });
|
|
168
|
+
}, [classifiers, form]);
|
|
161
169
|
const reload = useCallback(() => {
|
|
162
170
|
if (!form)
|
|
163
171
|
return;
|
|
@@ -81,6 +81,14 @@ describe('AppHooks', function () {
|
|
|
81
81
|
expect(result.current.organization).toBeNull();
|
|
82
82
|
expect(result.current.branding).toBeUndefined();
|
|
83
83
|
});
|
|
84
|
+
test('picks up classifiers that arrive after the first render', async () => {
|
|
85
|
+
const classifier = { _id: 'tipo', name: 'Tipo de solicitud', children: [] };
|
|
86
|
+
const { result, rerender } = renderHookWithProviders((props) => useSetupApp(false, { formData: BaseForm, internal: true, classifiers: props.classifiers }), { initialProps: { classifiers: {} } });
|
|
87
|
+
await waitFor(() => expect(result.current.form).toBeTruthy());
|
|
88
|
+
expect(result.current.form?.classifiers).toEqual({});
|
|
89
|
+
rerender({ classifiers: { tipo: classifier } });
|
|
90
|
+
await waitFor(() => expect(result.current.form?.classifiers?.tipo).toEqual(classifier));
|
|
91
|
+
});
|
|
84
92
|
test('Apikey Default', async () => {
|
|
85
93
|
mocks.get.mockImplementation((a) => {
|
|
86
94
|
if (a === '/organization') {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BookingAppointment } from '../Services/BookingService';
|
|
2
|
+
/** "Martes 14 de octubre", en la zona de la organización. */
|
|
3
|
+
export declare function appointmentDay(iso: string, timeZone: string): string;
|
|
4
|
+
/** "Martes 14 de octubre, 09:00–09:30". */
|
|
5
|
+
export declare function appointmentWhen(appointment: Pick<BookingAppointment, 'start' | 'end' | 'timeZone'>): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { clockIn } from '../Services/BookingService';
|
|
2
|
+
/** "Martes 14 de octubre", en la zona de la organización. */
|
|
3
|
+
export function appointmentDay(iso, timeZone) {
|
|
4
|
+
const parts = {};
|
|
5
|
+
for (const part of new Intl.DateTimeFormat('es-CO', {
|
|
6
|
+
weekday: 'long',
|
|
7
|
+
day: 'numeric',
|
|
8
|
+
month: 'long',
|
|
9
|
+
timeZone,
|
|
10
|
+
}).formatToParts(new Date(iso))) {
|
|
11
|
+
parts[part.type] = part.value;
|
|
12
|
+
}
|
|
13
|
+
const text = `${parts.weekday} ${parts.day} de ${parts.month}`;
|
|
14
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
15
|
+
}
|
|
16
|
+
/** "Martes 14 de octubre, 09:00–09:30". */
|
|
17
|
+
export function appointmentWhen(appointment) {
|
|
18
|
+
const { start, end, timeZone } = appointment;
|
|
19
|
+
return `${appointmentDay(start, timeZone)}, ${clockIn(start, timeZone)}–${clockIn(end, timeZone)}`;
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface BookingManagePageProps {
|
|
2
|
+
/** El handle del enlace del correo (`/booking/<token>`). */
|
|
3
|
+
token: string;
|
|
4
|
+
/** La organización, cuando la página no se sirve desde su subdominio de formularios. */
|
|
5
|
+
idOrganization?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* La página del enlace "Cancela o cambia la hora de tu cita" de los correos de agendamiento.
|
|
9
|
+
*
|
|
10
|
+
* Muestra la cita en el reloj de la organización y deja cancelarla (con confirmación) o moverla a otra
|
|
11
|
+
* hora con la misma persona. Las horas salen de la oferta del mismo paso del formulario, igual que la
|
|
12
|
+
* primera vez, con el mismo `ErkBookingPicker`; elegir una mueve la cita en el momento, sin formulario
|
|
13
|
+
* que enviar. Cada respuesta trae el handle vigente de la cita (cambia al moverla), y es el que se usa
|
|
14
|
+
* en el siguiente pedido.
|
|
15
|
+
*/
|
|
16
|
+
declare function BookingManagePage({ token, idOrganization }: BookingManagePageProps): JSX.Element;
|
|
17
|
+
export default BookingManagePage;
|
|
@@ -0,0 +1,177 @@
|
|
|
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 { BookingMode, PersonSelectionMode } from '../@Types/BookingFormStep';
|
|
5
|
+
import ErkBookingPicker from '../Shared/ErkBookingPicker/ErkBookingPicker';
|
|
6
|
+
import ErkButton from '../Shared/ErkButton/ErkButton';
|
|
7
|
+
import Loader from '../Shared/Loader/Loader';
|
|
8
|
+
import MaterialProviders from '../Utils/MaterialProviders';
|
|
9
|
+
import InternalFormStyle from '../constants/InternalFormStyle';
|
|
10
|
+
import { BookingErrorCodes, cancelBookingAppointment, clockIn, fetchBookingAppointment, fetchBookingOffer, fetchBookingSlots, moveBookingAppointment, toBookingError, toPickerSlots, zoneLabel, } from '../Services/BookingService';
|
|
11
|
+
import { appointmentDay, appointmentWhen } from './AppointmentText';
|
|
12
|
+
import styles from './BookingManagePage.module.css';
|
|
13
|
+
const MODE_LABEL = {
|
|
14
|
+
[BookingMode.IN_PERSON]: 'Presencial',
|
|
15
|
+
[BookingMode.VIRTUAL]: 'Virtual',
|
|
16
|
+
[BookingMode.HYBRID]: 'Presencial o virtual',
|
|
17
|
+
};
|
|
18
|
+
const MOVE_COPY = {
|
|
19
|
+
open: 'Elegir otra hora',
|
|
20
|
+
confirm: 'Cambiar la hora',
|
|
21
|
+
confirming: 'Cambiando…',
|
|
22
|
+
title: 'Revisa la nueva hora',
|
|
23
|
+
note: 'Moveremos tu cita a esta hora y te enviaremos la confirmación por correo.',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* La página del enlace "Cancela o cambia la hora de tu cita" de los correos de agendamiento.
|
|
27
|
+
*
|
|
28
|
+
* Muestra la cita en el reloj de la organización y deja cancelarla (con confirmación) o moverla a otra
|
|
29
|
+
* hora con la misma persona. Las horas salen de la oferta del mismo paso del formulario, igual que la
|
|
30
|
+
* primera vez, con el mismo `ErkBookingPicker`; elegir una mueve la cita en el momento, sin formulario
|
|
31
|
+
* que enviar. Cada respuesta trae el handle vigente de la cita (cambia al moverla), y es el que se usa
|
|
32
|
+
* en el siguiente pedido.
|
|
33
|
+
*/
|
|
34
|
+
function BookingManagePage({ token, idOrganization }) {
|
|
35
|
+
const [appointment, setAppointment] = useState(undefined);
|
|
36
|
+
const [loadError, setLoadError] = useState(undefined);
|
|
37
|
+
const [notice, setNotice] = useState(undefined);
|
|
38
|
+
const [confirmCancel, setConfirmCancel] = useState(false);
|
|
39
|
+
const [cancelling, setCancelling] = useState(false);
|
|
40
|
+
const [cancelError, setCancelError] = useState(undefined);
|
|
41
|
+
/** The appointment the callbacks read; the state copy only drives rendering. */
|
|
42
|
+
const current = useRef(undefined);
|
|
43
|
+
const adopt = useCallback((loaded) => {
|
|
44
|
+
current.current = loaded;
|
|
45
|
+
setAppointment(loaded);
|
|
46
|
+
}, []);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
fetchBookingAppointment(token, idOrganization)
|
|
50
|
+
.then((loaded) => {
|
|
51
|
+
if (!cancelled)
|
|
52
|
+
adopt(loaded);
|
|
53
|
+
})
|
|
54
|
+
.catch((e) => {
|
|
55
|
+
if (!cancelled)
|
|
56
|
+
setLoadError(toBookingError(e, 'No pudimos cargar tu cita.').message);
|
|
57
|
+
});
|
|
58
|
+
return () => {
|
|
59
|
+
cancelled = true;
|
|
60
|
+
};
|
|
61
|
+
}, [token, idOrganization, adopt]);
|
|
62
|
+
const reschedule = appointment?.reschedule;
|
|
63
|
+
const route = useMemo(() => (reschedule ? { apiKey: reschedule.apiKey, idStep: reschedule.idStep, idOrganization } : undefined), [reschedule, idOrganization]);
|
|
64
|
+
/**
|
|
65
|
+
* Reads the step's offer with the appointment's person and, when it has one, its event template, so
|
|
66
|
+
* the new hour keeps the type and its duration. A person handle lives two hours; a stale one
|
|
67
|
+
* (403 `STALE_OFFER`) reloads the appointment, which hands out a fresh one, and the read is retried once.
|
|
68
|
+
*/
|
|
69
|
+
const withPerson = useCallback(async (call, fallback) => {
|
|
70
|
+
const person = current.current?.reschedule?.person;
|
|
71
|
+
if (!person)
|
|
72
|
+
throw new Error(fallback);
|
|
73
|
+
try {
|
|
74
|
+
return await call(person);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
const refusal = toBookingError(e, fallback);
|
|
78
|
+
if (refusal.code !== BookingErrorCodes.STALE_OFFER || !current.current)
|
|
79
|
+
throw refusal;
|
|
80
|
+
try {
|
|
81
|
+
const reloaded = await fetchBookingAppointment(current.current.token, idOrganization);
|
|
82
|
+
adopt(reloaded);
|
|
83
|
+
if (!reloaded.reschedule)
|
|
84
|
+
throw refusal;
|
|
85
|
+
return await call(reloaded.reschedule.person);
|
|
86
|
+
}
|
|
87
|
+
catch (retryError) {
|
|
88
|
+
throw toBookingError(retryError, fallback);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, [idOrganization, adopt]);
|
|
92
|
+
const fetchDays = useCallback(async () => {
|
|
93
|
+
if (!route)
|
|
94
|
+
return [];
|
|
95
|
+
return withPerson(async (person) => (await fetchBookingOffer(route, { person, eventTemplate: current.current?.reschedule?.eventTemplate }))
|
|
96
|
+
.days, 'No pudimos cargar los días disponibles.');
|
|
97
|
+
}, [route, withPerson]);
|
|
98
|
+
const fetchSlots = useCallback(async ({ date }) => {
|
|
99
|
+
if (!route)
|
|
100
|
+
return [];
|
|
101
|
+
return withPerson(async (person) => toPickerSlots(await fetchBookingSlots(route, date, {
|
|
102
|
+
person,
|
|
103
|
+
eventTemplate: current.current?.reschedule?.eventTemplate,
|
|
104
|
+
}), current.current?.timeZone ?? 'UTC'), 'No pudimos cargar los horarios de este día.');
|
|
105
|
+
}, [route, withPerson]);
|
|
106
|
+
const moveTo = useCallback(async ({ slot }) => {
|
|
107
|
+
const from = current.current;
|
|
108
|
+
if (!from)
|
|
109
|
+
throw new Error('No pudimos cambiar la hora de tu cita.');
|
|
110
|
+
try {
|
|
111
|
+
const moved = await moveBookingAppointment(from.token, slot.id, idOrganization);
|
|
112
|
+
adopt(moved);
|
|
113
|
+
setNotice(`Listo. Tu cita quedó para el ${appointmentWhen(moved)}. Te enviamos la confirmación por correo.`);
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
throw toBookingError(e, 'No pudimos cambiar la hora de tu cita. Intenta de nuevo.');
|
|
118
|
+
}
|
|
119
|
+
}, [idOrganization, adopt]);
|
|
120
|
+
const cancel = async () => {
|
|
121
|
+
const from = current.current;
|
|
122
|
+
if (!from)
|
|
123
|
+
return;
|
|
124
|
+
setCancelling(true);
|
|
125
|
+
setCancelError(undefined);
|
|
126
|
+
try {
|
|
127
|
+
adopt(await cancelBookingAppointment(from.token, idOrganization));
|
|
128
|
+
setConfirmCancel(false);
|
|
129
|
+
setNotice('Tu cita quedó cancelada. Te enviamos la confirmación por correo.');
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
setCancelError(toBookingError(e, 'No pudimos cancelar tu cita. Intenta de nuevo.').message);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
setCancelling(false);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const renderBody = () => {
|
|
139
|
+
if (loadError) {
|
|
140
|
+
return (_jsx("p", { className: styles.error, role: "alert", children: loadError }));
|
|
141
|
+
}
|
|
142
|
+
if (!appointment) {
|
|
143
|
+
return (_jsx("div", { className: styles.loading, children: _jsx(Loader, { size: 32 }) }));
|
|
144
|
+
}
|
|
145
|
+
const confirmed = appointment.status === 'CONFIRMED';
|
|
146
|
+
const rows = [
|
|
147
|
+
{ label: 'Fecha', value: appointmentDay(appointment.start, appointment.timeZone) },
|
|
148
|
+
{
|
|
149
|
+
label: 'Hora',
|
|
150
|
+
value: `${clockIn(appointment.start, appointment.timeZone)}–${clockIn(appointment.end, appointment.timeZone)} (${zoneLabel(appointment.timeZone, new Date(appointment.start))})`,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
if (appointment.person) {
|
|
154
|
+
const role = appointment.person.role ? `, ${appointment.person.role}` : '';
|
|
155
|
+
rows.push({ label: 'Con', value: `${appointment.person.name}${role}` });
|
|
156
|
+
}
|
|
157
|
+
const templateName = appointment.eventTemplate?.name ?? appointment.eventTemplateName;
|
|
158
|
+
if (templateName) {
|
|
159
|
+
const templateDuration = appointment.eventTemplate
|
|
160
|
+
? ` (${appointment.eventTemplate.durationMinutes} min)`
|
|
161
|
+
: '';
|
|
162
|
+
rows.push({ label: 'Tipo de atención', value: `${templateName}${templateDuration}` });
|
|
163
|
+
}
|
|
164
|
+
if (appointment.bookingMode && MODE_LABEL[appointment.bookingMode]) {
|
|
165
|
+
rows.push({ label: 'Modalidad', value: MODE_LABEL[appointment.bookingMode] });
|
|
166
|
+
}
|
|
167
|
+
if (appointment.joinUrl) {
|
|
168
|
+
rows.push({ label: 'Reunión', value: 'Unirse a la reunión', href: appointment.joinUrl });
|
|
169
|
+
}
|
|
170
|
+
return (_jsxs(_Fragment, { children: [!confirmed && (_jsx("p", { className: styles.cancelled, role: "status", children: "Esta cita est\u00E1 cancelada." })), _jsx("dl", { className: confirmed ? styles.details : `${styles.details} ${styles.detailsCancelled}`, children: rows.map((row) => (_jsxs("div", { className: styles.row, children: [_jsx("dt", { children: row.label }), _jsx("dd", { children: row.href ? (_jsx("a", { href: row.href, target: "_blank", rel: "noreferrer", children: row.value })) : (row.value) })] }, row.label))) }), notice && (_jsx("p", { className: styles.notice, role: "status", children: notice })), confirmed && (_jsxs("div", { className: styles.actions, children: [reschedule && (_jsx(ErkBookingPicker, { value: null, onChange: () => undefined, label: "\u00BFNecesitas otra hora?", description: `Elige otro día u hora con ${appointment.person?.name ?? 'la misma persona'}.`, durationMinutes: reschedule.durationMinutes, bookingMode: appointment.bookingMode, personSelectionMode: PersonSelectionMode.NONE, timeZone: appointment.timeZone, fetchDays: fetchDays, fetchSlots: fetchSlots, holdSlot: moveTo, withoutContact: true, copy: MOVE_COPY }, appointment.token)), _jsxs("div", { className: styles.cancelRow, children: [_jsx("span", { children: "\u00BFNo puedes asistir?" }), _jsx(ErkButton, { text: "Cancelar cita", variant: "outlined", color: "error", onClick: () => {
|
|
171
|
+
setCancelError(undefined);
|
|
172
|
+
setConfirmCancel(true);
|
|
173
|
+
} })] })] }))] }));
|
|
174
|
+
};
|
|
175
|
+
return (_jsx(MaterialProviders, { formStyle: InternalFormStyle, children: _jsxs("main", { className: styles.page, children: [_jsxs("section", { className: styles.card, children: [_jsx("h1", { className: styles.title, children: "Tu cita" }), renderBody()] }), _jsx(Dialog, { open: confirmCancel, onClose: () => !cancelling && setConfirmCancel(false), maxWidth: "xs", children: _jsxs("div", { className: styles.dialog, children: [_jsx("h2", { className: styles.dialogTitle, children: "\u00BFCancelar tu cita?" }), _jsxs("p", { children: [appointment ? `${appointmentWhen(appointment)}. ` : '', "Liberaremos este horario y avisaremos a", ' ', appointment?.person?.name ?? 'la persona que te iba a atender', ". No se puede deshacer."] }), cancelError && (_jsx("p", { className: styles.error, role: "alert", children: cancelError })), _jsxs("div", { className: styles.dialogActions, children: [_jsx(ErkButton, { text: "Volver", variant: "outlined", disabled: cancelling, onClick: () => setConfirmCancel(false) }), _jsx(ErkButton, { text: "S\u00ED, cancelar", color: "error", loading: cancelling, onClick: () => void cancel() })] })] }) })] }) }));
|
|
176
|
+
}
|
|
177
|
+
export default BookingManagePage;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
.page {
|
|
2
|
+
min-height: 100vh;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
padding: 32px 16px;
|
|
5
|
+
background: #f5f7fa;
|
|
6
|
+
color: #293241;
|
|
7
|
+
font-family: inherit;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.card {
|
|
11
|
+
max-width: 560px;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: 24px;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
border: 1px solid #d9dee6;
|
|
16
|
+
border-radius: 16px;
|
|
17
|
+
background: #fff;
|
|
18
|
+
box-shadow: 0 12px 34px rgba(43, 65, 93, 0.08);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.title {
|
|
22
|
+
margin: 0 0 16px;
|
|
23
|
+
font-size: 1.5rem;
|
|
24
|
+
font-weight: 600;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.loading {
|
|
28
|
+
display: flex;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
padding: 24px 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.details {
|
|
34
|
+
margin: 0 0 16px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.detailsCancelled {
|
|
38
|
+
opacity: 0.6;
|
|
39
|
+
text-decoration: line-through;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.row {
|
|
43
|
+
display: grid;
|
|
44
|
+
grid-template-columns: 140px 1fr;
|
|
45
|
+
gap: 8px;
|
|
46
|
+
padding: 6px 0;
|
|
47
|
+
border-bottom: 1px solid #eef1f5;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.row dt {
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.row dd {
|
|
55
|
+
margin: 0;
|
|
56
|
+
overflow-wrap: anywhere;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.cancelled {
|
|
60
|
+
margin: 0 0 12px;
|
|
61
|
+
padding: 10px 12px;
|
|
62
|
+
border-radius: 10px;
|
|
63
|
+
background: #fdecee;
|
|
64
|
+
color: #cc2936;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.notice {
|
|
68
|
+
margin: 0 0 16px;
|
|
69
|
+
padding: 10px 12px;
|
|
70
|
+
border-radius: 10px;
|
|
71
|
+
background: #e8f4ec;
|
|
72
|
+
color: #1e6b3a;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.error {
|
|
76
|
+
margin: 0 0 12px;
|
|
77
|
+
color: #cc2936;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.actions {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
gap: 16px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.cancelRow {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: space-between;
|
|
90
|
+
gap: 12px;
|
|
91
|
+
flex-wrap: wrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.dialog {
|
|
95
|
+
padding: 20px 24px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.dialogTitle {
|
|
99
|
+
margin: 0 0 8px;
|
|
100
|
+
font-size: 1.25rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.dialogActions {
|
|
104
|
+
display: flex;
|
|
105
|
+
justify-content: flex-end;
|
|
106
|
+
gap: 8px;
|
|
107
|
+
margin-top: 16px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (max-width: 480px) {
|
|
111
|
+
.row {
|
|
112
|
+
grid-template-columns: 1fr;
|
|
113
|
+
gap: 2px;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|