carconnect-gatherleads-ui-lib 2.4.1 → 2.4.5

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.md CHANGED
@@ -870,66 +870,80 @@ interface GatherTextareaFieldConfig extends BaseFieldConfig {
870
870
  }
871
871
  ```
872
872
 
873
- ### 6. **GatherDateFieldConfig** - Campos de Fecha
873
+ #### **Características Destacadas**
874
874
 
875
- ```tsx
876
- interface GatherDateFieldConfig extends BaseFieldConfig {
877
- type: 'date' | 'daterange';
878
- maxDate?: Date;
879
- minDate?: Date;
880
- singleDate?: boolean; // Solo para daterange
881
- displayFormat?: string; // Formato de visualización
882
- size?: 'sm' | 'md' | 'lg';
883
- }
884
- ```
875
+ ✨ **Nuevas Funcionalidades:**
885
876
 
886
- **Ejemplos:**
877
+ 1. **Scroll en selector de años** - El selector ahora tiene altura máxima y scroll automático
878
+ 2. **Control de años personalizables** - `pastYearsCount={10}` muestra año actual + últimos 10 años
879
+ 3. **Textos personalizables** - Botones Apply/Cancel en cualquier idioma
880
+ 4. **Callbacks para eventos** - `onApply`, `onCancel`, `onOpenChange` para lógica personalizada
881
+ 5. **Formatos de fecha flexibles** - Personaliza cómo se muestran las fechas
882
+ 6. **Auto-cierre inteligente** - Cierre automático tras selección para UX más rápida
883
+ 7. **Modo sin botones** - `hideActionButtons={true}` para aplicar cambios inmediatamente
884
+
885
+ #### **Ejemplos de Uso Avanzado**
887
886
 
888
887
  ```tsx
889
- // FECHA SIMPLE
890
- {
891
- name: "birthDate",
892
- label: "Fecha de nacimiento",
893
- type: "date",
894
- maxDate: new Date(), // No futuro
895
- minDate: new Date("1900-01-01"), // Desde 1900
896
- displayFormat: "DD/MM/YYYY", // Formato día/mes/año
897
- required: true,
898
- size: "md"
899
- }
888
+ // EJEMPLO 1: Date Picker rápido sin confirmación
889
+ <GatherDatePicker
890
+ mode="single"
891
+ selected={date}
892
+ onSelect={setDate}
893
+ autoClose={true}
894
+ hideActionButtons={true}
895
+ placeholder="Selección rápida"
896
+ />
900
897
 
901
- // RANGO DE FECHAS
902
- {
903
- name: "vacationPeriod",
904
- label: "Período de vacaciones",
905
- type: "daterange",
906
- singleData: false,
907
- minDate: new Date(), // Desde hoy
908
- maxDate: new Date("2025-12-31"), // Hasta fin de año
909
- displayFormat: "DD/MM/YYYY",
910
- helperText: "Seleccione fecha de inicio y fin"
911
- }
898
+ // EJEMPLO 2: Con validación y callbacks
899
+ <GatherDatePicker
900
+ mode="single"
901
+ selected={date}
902
+ onSelect={setDate}
903
+ applyButtonText="Guardar Fecha"
904
+ cancelButtonText="Descartar"
905
+ onApply={(selectedDate) => {
906
+ // Validar y guardar
907
+ if (selectedDate) {
908
+ saveToAPI(selectedDate);
909
+ toast.success('Fecha guardada');
910
+ }
911
+ }}
912
+ onCancel={() => {
913
+ toast.info('Cambios descartados');
914
+ }}
915
+ />
912
916
 
913
- // FECHA CON HORA
914
- {
915
- name: "appointmentDateTime",
916
- label: "Fecha y hora de la cita",
917
- type: "date",
918
- minDate: new Date(),
919
- displayFormat: "DD/MM/YYYY HH:mm", // Con hora
920
- size: "lg"
921
- }
917
+ // EJEMPLO 3: Rango con formato personalizado y años limitados
918
+ <GatherDatePicker
919
+ mode="range"
920
+ selected={dateRange}
921
+ onSelect={setDateRange}
922
+ rangeFromFormat="dd/MM/yy"
923
+ rangeToFormat="dd/MM/yy"
924
+ pastYearsCount={5} // Solo últimos 5 años
925
+ placeholder="Del - Al"
926
+ />
922
927
 
923
- // FECHA ÚNICA EN RANGO
924
- {
925
- name: "singleDateInRange",
926
- label: "Seleccione una fecha",
927
- type: "daterange",
928
- singleDate: true, // Solo una fecha aunque sea daterange
929
- displayFormat: "MMMM D, YYYY" // "Enero 15, 2025"
930
- }
928
+ // EJEMPLO 4: Input de formulario completo
929
+ <GatherDatePickerInput
930
+ label="Fecha de Registro"
931
+ description="Seleccione la fecha de registro del cliente"
932
+ placeholder="dd/mm/aaaa"
933
+ mode="single"
934
+ selected={date}
935
+ onSelect={setDate}
936
+ dateFormat="dd/MM/yyyy"
937
+ showSelectedDate={true}
938
+ selectedDateLabel="Registrado el:"
939
+ autoClose={true}
940
+ minDate={subDays(new Date(), 365)}
941
+ maxDate={new Date()}
942
+ />
931
943
  ```
932
944
 
945
+ ---
946
+
933
947
  ### 7. **GatherCheckboxGroupFieldConfig** - Grupo de Checkboxes
934
948
 
935
949
  ```tsx
@@ -2614,3 +2628,563 @@ const apiData = await fetch('/api/users').then((r) => r.json());
2614
2628
  ```
2615
2629
 
2616
2630
  Esta guía completa cubre todos los aspectos de GatherTable, desde uso básico hasta implementaciones avanzadas con ordenamiento, filtrado y renderizado personalizado.
2631
+
2632
+ # 🪟 Guía Completa de GatherModal
2633
+
2634
+ ## 🎯 Introducción
2635
+
2636
+ `GatherModal` es un componente modal genérico y altamente personalizable que proporciona diálogos flexibles con soporte completo para diferentes posiciones, tamaños, animaciones y accesibilidad avanzada.
2637
+
2638
+ ## 🏗️ Características Principales
2639
+
2640
+ - **Posiciones**: center, left, right (ideal para paneles laterales)
2641
+ - **Tamaños**: sm, md, lg, xl, full
2642
+ - **Header personalizable**: Título simple o contenido completamente custom
2643
+ - **Footer flexible**: Botones por defecto o contenido personalizado
2644
+ - **Accesibilidad avanzada**: Focus management, keyboard navigation
2645
+ - **Animaciones**: Transiciones suaves configurables
2646
+ - **Portal rendering**: Se renderiza fuera del árbol DOM
2647
+
2648
+ ## 📋 Uso Básico
2649
+
2650
+ ### Modal Simple de Confirmación
2651
+
2652
+ ```tsx
2653
+ import { GatherModal } from '@/base/modal';
2654
+ import { useState } from 'react';
2655
+
2656
+ const BasicModalExample = () => {
2657
+ const [isOpen, setIsOpen] = useState(false);
2658
+
2659
+ return (
2660
+ <>
2661
+ <button onClick={() => setIsOpen(true)}>Abrir Modal</button>
2662
+
2663
+ <GatherModal
2664
+ open={isOpen}
2665
+ onClose={() => setIsOpen(false)}
2666
+ header={{
2667
+ title: 'Confirmar Acción',
2668
+ subtitle: '¿Estás seguro de continuar?',
2669
+ }}
2670
+ footer={{
2671
+ acceptText: 'Confirmar',
2672
+ cancelText: 'Cancelar',
2673
+ acceptVariant: 'gather-primary',
2674
+ cancelVariant: 'gather-outline',
2675
+ onAccept: () => {
2676
+ console.log('Confirmado');
2677
+ setIsOpen(false);
2678
+ },
2679
+ onCancel: () => setIsOpen(false),
2680
+ }}
2681
+ >
2682
+ <p>Esta acción no se puede deshacer.</p>
2683
+ </GatherModal>
2684
+ </>
2685
+ );
2686
+ };
2687
+ ```
2688
+
2689
+ ### Modal de Formulario
2690
+
2691
+ ```tsx
2692
+ const FormModalExample = () => {
2693
+ const [isOpen, setIsOpen] = useState(false);
2694
+ const [loading, setLoading] = useState(false);
2695
+
2696
+ const handleSubmit = async () => {
2697
+ setLoading(true);
2698
+ try {
2699
+ // Simular API call
2700
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2701
+ setIsOpen(false);
2702
+ } finally {
2703
+ setLoading(false);
2704
+ }
2705
+ };
2706
+
2707
+ return (
2708
+ <GatherModal
2709
+ open={isOpen}
2710
+ onClose={() => setIsOpen(false)}
2711
+ size='lg'
2712
+ header={{
2713
+ title: 'Nuevo Usuario',
2714
+ subtitle: 'Complete los datos del usuario',
2715
+ }}
2716
+ footer={{
2717
+ acceptText: 'Guardar',
2718
+ cancelText: 'Cancelar',
2719
+ acceptLoading: loading,
2720
+ acceptDisabled: loading,
2721
+ onAccept: handleSubmit,
2722
+ onCancel: () => setIsOpen(false),
2723
+ }}
2724
+ >
2725
+ <div className='space-y-4'>
2726
+ <div>
2727
+ <label className='mb-1 block text-sm font-medium'>
2728
+ Nombre completo
2729
+ </label>
2730
+ <input
2731
+ type='text'
2732
+ className='w-full rounded border p-2'
2733
+ placeholder='Juan Pérez'
2734
+ />
2735
+ </div>
2736
+ <div>
2737
+ <label className='mb-1 block text-sm font-medium'>Email</label>
2738
+ <input
2739
+ type='email'
2740
+ className='w-full rounded border p-2'
2741
+ placeholder='juan@ejemplo.com'
2742
+ />
2743
+ </div>
2744
+ </div>
2745
+ </GatherModal>
2746
+ );
2747
+ };
2748
+ ```
2749
+
2750
+ ## 🎨 Posiciones y Tamaños
2751
+
2752
+ ### Modal Centrado (Por Defecto)
2753
+
2754
+ ```tsx
2755
+ <GatherModal
2756
+ open={isOpen}
2757
+ position='center' // Por defecto
2758
+ size='md' // Tamaño mediano
2759
+ onClose={() => setIsOpen(false)}
2760
+ >
2761
+ <p>Modal centrado en la pantalla</p>
2762
+ </GatherModal>
2763
+ ```
2764
+
2765
+ ### Panel Lateral Derecho
2766
+
2767
+ ```tsx
2768
+ <GatherModal
2769
+ open={isOpen}
2770
+ position='right'
2771
+ size='lg'
2772
+ onClose={() => setIsOpen(false)}
2773
+ header={{
2774
+ title: 'Panel de Configuración',
2775
+ }}
2776
+ >
2777
+ <div className='space-y-4'>
2778
+ <h3>Configuraciones</h3>
2779
+ <div>Contenido del panel lateral...</div>
2780
+ </div>
2781
+ </GatherModal>
2782
+ ```
2783
+
2784
+ ### Modal de Pantalla Completa
2785
+
2786
+ ```tsx
2787
+ <GatherModal
2788
+ open={isOpen}
2789
+ size='full'
2790
+ onClose={() => setIsOpen(false)}
2791
+ header={{
2792
+ title: 'Vista Completa',
2793
+ }}
2794
+ scrollBehavior={{
2795
+ bodyScrollable: true,
2796
+ bodyMaxHeight: '80vh',
2797
+ }}
2798
+ >
2799
+ <div>Contenido que ocupa toda la pantalla...</div>
2800
+ </GatherModal>
2801
+ ```
2802
+
2803
+ ## 🔧 Configuración Avanzada
2804
+
2805
+ ### Modal con Contenido Personalizado
2806
+
2807
+ ```tsx
2808
+ <GatherModal
2809
+ open={isOpen}
2810
+ onClose={() => setIsOpen(false)}
2811
+ header={{
2812
+ children: (
2813
+ <div className='flex items-center gap-3'>
2814
+ <div className='flex h-8 w-8 items-center justify-center rounded-full bg-blue-500'>
2815
+ <span className='text-sm text-white'>!</span>
2816
+ </div>
2817
+ <div>
2818
+ <h2 className='text-lg font-semibold'>Advertencia</h2>
2819
+ <p className='text-sm text-gray-600'>Acción requerida</p>
2820
+ </div>
2821
+ </div>
2822
+ ),
2823
+ showCloseButton: true,
2824
+ }}
2825
+ footer={{
2826
+ children: (
2827
+ <div className='flex w-full justify-between'>
2828
+ <span className='text-sm text-gray-500'>
2829
+ Última actualización: hace 5 min
2830
+ </span>
2831
+ <div className='flex gap-2'>
2832
+ <button className='rounded px-4 py-2 text-gray-600 hover:bg-gray-100'>
2833
+ Ignorar
2834
+ </button>
2835
+ <button className='rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600'>
2836
+ Resolver
2837
+ </button>
2838
+ </div>
2839
+ </div>
2840
+ ),
2841
+ }}
2842
+ >
2843
+ <p>Contenido del modal con header y footer personalizados.</p>
2844
+ </GatherModal>
2845
+ ```
2846
+
2847
+ ### Modal con Scroll Personalizado
2848
+
2849
+ ```tsx
2850
+ <GatherModal
2851
+ open={isOpen}
2852
+ onClose={() => setIsOpen(false)}
2853
+ header={{
2854
+ title: 'Contenido Extenso',
2855
+ scrollable: true,
2856
+ maxHeight: '150px',
2857
+ }}
2858
+ scrollBehavior={{
2859
+ bodyScrollable: true,
2860
+ bodyMaxHeight: '400px',
2861
+ modalScrollable: false,
2862
+ }}
2863
+ footer={{
2864
+ scrollable: true,
2865
+ maxHeight: '100px',
2866
+ }}
2867
+ >
2868
+ <div className='space-y-4'>
2869
+ {Array.from({ length: 20 }, (_, i) => (
2870
+ <p key={i}>
2871
+ Párrafo {i + 1}: Lorem ipsum dolor sit amet consectetur adipisicing
2872
+ elit...
2873
+ </p>
2874
+ ))}
2875
+ </div>
2876
+ </GatherModal>
2877
+ ```
2878
+
2879
+ ## ⚙️ Props Principales
2880
+
2881
+ ### Props del Modal
2882
+
2883
+ ```tsx
2884
+ interface GatherModalProps {
2885
+ // ESTADO
2886
+ open: boolean; // Si el modal está abierto
2887
+ onClose?: () => void; // Función de cierre
2888
+
2889
+ // DISEÑO
2890
+ position?: 'center' | 'left' | 'right'; // Posición
2891
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; // Tamaño
2892
+
2893
+ // COMPORTAMIENTO
2894
+ closeOnOverlayClick?: boolean; // Cerrar al hacer clic fuera
2895
+ closeOnEscape?: boolean; // Cerrar con tecla Escape
2896
+ preventBodyScroll?: boolean; // Prevenir scroll del body
2897
+ autoFocus?: boolean; // Enfocar automáticamente
2898
+
2899
+ // CONTENIDO
2900
+ header?: GatherModalHeaderProps; // Configuración del header
2901
+ footer?: GatherModalFooterProps; // Configuración del footer
2902
+ children: React.ReactNode; // Contenido principal
2903
+
2904
+ // ACCESIBILIDAD Y ANIMACIÓN
2905
+ animationDuration?: number; // Duración de animaciones (ms)
2906
+ zIndex?: number; // Z-index base
2907
+ scrollBehavior?: ScrollConfig; // Configuración de scroll
2908
+ }
2909
+ ```
2910
+
2911
+ ### Props del Header
2912
+
2913
+ ```tsx
2914
+ interface GatherModalHeaderProps {
2915
+ title?: string; // Título del modal
2916
+ subtitle?: string; // Subtítulo
2917
+ showCloseButton?: boolean; // Mostrar botón X
2918
+ onClose?: () => void; // Función del botón cerrar
2919
+ closeIcon?: React.ReactNode; // Ícono personalizado
2920
+ scrollable?: boolean; // Si el header hace scroll
2921
+ children?: React.ReactNode; // Contenido personalizado
2922
+ }
2923
+ ```
2924
+
2925
+ ### Props del Footer
2926
+
2927
+ ```tsx
2928
+ interface GatherModalFooterProps {
2929
+ // BOTONES POR DEFECTO
2930
+ acceptText?: string; // Texto botón aceptar
2931
+ cancelText?: string; // Texto botón cancelar
2932
+ onAccept?: () => void; // Acción de aceptar
2933
+ onCancel?: () => void; // Acción de cancelar
2934
+
2935
+ // ESTADOS DE LOS BOTONES
2936
+ acceptLoading?: boolean; // Loading en botón aceptar
2937
+ acceptDisabled?: boolean; // Deshabilitar aceptar
2938
+
2939
+ // VARIANTES Y DISEÑO
2940
+ acceptVariant?: ButtonVariant; // Estilo del botón aceptar
2941
+ cancelVariant?: ButtonVariant; // Estilo del botón cancelar
2942
+ buttonsOrder?: 'normal' | 'reverse'; // Orden de botones
2943
+ buttonsAlignment?: 'start' | 'center' | 'end' | 'between';
2944
+
2945
+ // PERSONALIZACIÓN
2946
+ children?: React.ReactNode; // Footer completamente personalizado
2947
+ scrollable?: boolean; // Si el footer hace scroll
2948
+ }
2949
+ ```
2950
+
2951
+ ## 🎯 Casos de Uso Comunes
2952
+
2953
+ ### Modal de Confirmación de Eliminación
2954
+
2955
+ ```tsx
2956
+ const DeleteConfirmModal = ({ item, onConfirm, onCancel }) => (
2957
+ <GatherModal
2958
+ open={!!item}
2959
+ onClose={onCancel}
2960
+ size='sm'
2961
+ header={{
2962
+ title: 'Confirmar Eliminación',
2963
+ subtitle: '¿Estás seguro de eliminar este elemento?',
2964
+ }}
2965
+ footer={{
2966
+ acceptText: 'Eliminar',
2967
+ cancelText: 'Cancelar',
2968
+ acceptVariant: 'gather-error',
2969
+ onAccept: () => onConfirm(item),
2970
+ onCancel: onCancel,
2971
+ }}
2972
+ >
2973
+ <p>Esta acción no se puede deshacer.</p>
2974
+ {item && (
2975
+ <div className='mt-3 rounded bg-gray-50 p-3'>
2976
+ <strong>{item.name}</strong>
2977
+ </div>
2978
+ )}
2979
+ </GatherModal>
2980
+ );
2981
+ ```
2982
+
2983
+ ### Panel de Detalles Lateral
2984
+
2985
+ ```tsx
2986
+ const DetailsSidebar = ({ user, isOpen, onClose }) => (
2987
+ <GatherModal
2988
+ open={isOpen}
2989
+ onClose={onClose}
2990
+ position='right'
2991
+ size='lg'
2992
+ header={{
2993
+ title: 'Detalles del Usuario',
2994
+ subtitle: user?.name,
2995
+ }}
2996
+ preventBodyScroll={true}
2997
+ >
2998
+ {user && (
2999
+ <div className='space-y-6'>
3000
+ <div className='text-center'>
3001
+ <img
3002
+ src={user.avatar}
3003
+ alt={user.name}
3004
+ className='mx-auto mb-4 h-20 w-20 rounded-full'
3005
+ />
3006
+ <h3 className='text-xl font-semibold'>{user.name}</h3>
3007
+ <p className='text-gray-600'>{user.email}</p>
3008
+ </div>
3009
+
3010
+ <div className='space-y-4'>
3011
+ <div>
3012
+ <label className='font-medium'>Departamento</label>
3013
+ <p>{user.department}</p>
3014
+ </div>
3015
+ <div>
3016
+ <label className='font-medium'>Rol</label>
3017
+ <p>{user.role}</p>
3018
+ </div>
3019
+ </div>
3020
+ </div>
3021
+ )}
3022
+ </GatherModal>
3023
+ );
3024
+ ```
3025
+
3026
+ Esta guía cubre los aspectos principales de GatherModal, proporcionando ejemplos prácticos para implementaciones comunes desde modales simples hasta paneles laterales complejos.
3027
+
3028
+ # 📅 Guía Completa de GatherDatePicker
3029
+
3030
+ ## 🎯 Introducción
3031
+
3032
+ `GatherDatePicker` es un componente de selección de fechas flexible y robusto, basado en `react-day-picker`. Ofrece dos variantes principales: un botón que abre un popover (`GatherDatePicker`) y un input de texto (`GatherDatePickerInput`). Soporta selección de fechas individuales y rangos, zonas horarias, y validaciones.
3033
+
3034
+ ## 📦 Uso Básico
3035
+
3036
+ ### Selector de Fecha Simple (Botón)
3037
+
3038
+ ```tsx
3039
+ import { GatherDatePicker } from '@/base/date-picker/gather-date-picker';
3040
+
3041
+ const SimpleDatePicker = () => {
3042
+ const [date, setDate] = React.useState<Date | undefined>(new Date());
3043
+
3044
+ return (
3045
+ <GatherDatePicker
3046
+ mode='single'
3047
+ selected={date}
3048
+ onSelect={setDate}
3049
+ placeholder='Seleccionar fecha'
3050
+ />
3051
+ );
3052
+ };
3053
+ ```
3054
+
3055
+ ### Input de Fecha
3056
+
3057
+ ```tsx
3058
+ import { GatherDatePickerInput } from '@/base/date-picker/gather-date-picker-input';
3059
+
3060
+ const InputDatePicker = () => {
3061
+ const [date, setDate] = React.useState<Date | undefined>();
3062
+
3063
+ return (
3064
+ <GatherDatePickerInput
3065
+ mode='single'
3066
+ selected={date}
3067
+ onSelect={setDate}
3068
+ label='Fecha de nacimiento'
3069
+ placeholder='Seleccione su fecha de nacimiento'
3070
+ />
3071
+ );
3072
+ };
3073
+ ```
3074
+
3075
+ ## ⚙️ Props Principales
3076
+
3077
+ ### GatherDatePickerProps & GatherDatePickerInputProps
3078
+
3079
+ Ambos componentes comparten la mayoría de las props, heredando de una base común:
3080
+
3081
+ ```tsx
3082
+ interface GatherDatePickerProps {
3083
+ // CONFIGURACIÓN
3084
+ mode?: 'single' | 'range'; // Modo de selección
3085
+ selected?: Date | DateRange; // Valor seleccionado
3086
+ onSelect?: (date: any) => void; // Callback de selección
3087
+ timeZone?: string; // Zona horaria (default: 'America/Guayaquil')
3088
+
3089
+ // VISUAL
3090
+ placeholder?: string; // Texto placeholder
3091
+ disabled?: boolean; // Estado deshabilitado
3092
+ className?: string; // Clases adicionales
3093
+
3094
+ // RANGO Y RESTRICCIONES
3095
+ minDate?: Date; // Fecha mínima
3096
+ maxDate?: Date; // Fecha máxima
3097
+ disabledDates?: Date[]; // Fechas deshabilitadas específicas
3098
+ pastYearsCount?: number; // Años pasados a mostrar en selector
3099
+
3100
+ // FORMATO
3101
+ dateFormat?: string; // Formato de fecha (ej: 'dd MMM yyyy')
3102
+ rangeFromFormat?: string; // Formato inicio rango
3103
+ rangeToFormat?: string; // Formato fin rango
3104
+
3105
+ // COMPORTAMIENTO
3106
+ autoClose?: boolean; // Cerrar al seleccionar (single mode)
3107
+
3108
+ // VALIDACIÓN (NUEVO)
3109
+ error?: boolean; // Estado de error (borde rojo)
3110
+ helperText?: string; // Texto de ayuda/error
3111
+ required?: boolean; // Marca de campo requerido (*)
3112
+ }
3113
+ ```
3114
+
3115
+ ### Props Específicas de GatherDatePicker (Botón)
3116
+
3117
+ ```tsx
3118
+ interface GatherDatePickerProps {
3119
+ // ... props comunes
3120
+ variant?: 'default' | 'outline' | 'ghost' | 'secondary'; // Variante del botón
3121
+ hideActionButtons?: boolean; // Ocultar botones Aplicar/Cancelar
3122
+ applyButtonText?: string; // Texto botón Aplicar
3123
+ cancelButtonText?: string; // Texto botón Cancelar
3124
+ showPresets?: boolean; // Mostrar panel de presets (ayer, hoy, etc.)
3125
+ presets?: DatePreset[]; // Presets personalizados
3126
+ }
3127
+ ```
3128
+
3129
+ ### Props Específicas de GatherDatePickerInput (Input)
3130
+
3131
+ ```tsx
3132
+ interface GatherDatePickerInputProps {
3133
+ // ... props comunes
3134
+ label?: string; // Etiqueta del input
3135
+ description?: string; // Descripción bajo el label
3136
+ showSelectedDate?: boolean; // Mostrar texto de fecha seleccionada
3137
+ selectedDateLabel?: string; // Prefijo para fecha seleccionada
3138
+ }
3139
+ ```
3140
+
3141
+ ## 🚨 Manejo de Errores y Validaciones
3142
+
3143
+ Los componentes ahora soportan estados visuales de error y campos requeridos para integrarse mejor en formularios.
3144
+
3145
+ ### Input con Error y Mensaje
3146
+
3147
+ ```tsx
3148
+ <GatherDatePickerInput
3149
+ mode='single'
3150
+ label='Fecha de Facturación'
3151
+ required={true} // Muestra asterisco rojo *
3152
+ error={true} // Borde rojo en el input
3153
+ helperText='La fecha es obligatoria' // Muestra mensaje en rojo si hay error
3154
+ onSelect={setDate}
3155
+ selected={date}
3156
+ />
3157
+ ```
3158
+
3159
+ ### Botón con Estado de Error
3160
+
3161
+ ```tsx
3162
+ <GatherDatePicker
3163
+ mode='single'
3164
+ selected={date}
3165
+ onSelect={setDate}
3166
+ error={true} // El botón tendrá un borde rojo
3167
+ className='w-full'
3168
+ />;
3169
+ {
3170
+ /* Puedes renderizar el texto de error manualmente si usas la versión de botón */
3171
+ }
3172
+ {
3173
+ hasError && (
3174
+ <p className='mt-1 text-xs text-red-500'>Seleccione una fecha válida</p>
3175
+ );
3176
+ }
3177
+ ```
3178
+
3179
+ ## 🌍 Zonas Horarias y Formatos
3180
+
3181
+ El componente maneja zonas horarias utilizando `date-fns-tz`. Por defecto usa `America/Guayaquil`.
3182
+
3183
+ ```tsx
3184
+ <GatherDatePicker
3185
+ timeZone='Europe/Madrid' // Configurar zona horaria
3186
+ dateFormat='dd/MM/yyyy' // Configurar formato visual
3187
+ selected={date}
3188
+ onSelect={setDate}
3189
+ />
3190
+ ```