Add traslations
This commit is contained in:
@@ -154,7 +154,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
}
|
||||
|
||||
if (!formData.acceptTerms) {
|
||||
newErrors.acceptTerms = t('auth:validation.terms_required', 'Debes aceptar los términos y condiciones');
|
||||
newErrors.acceptTerms = t('auth:validation.terms_required', 'Debes aceptar los términos y condiciones y la política de privacidad');
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
@@ -235,7 +235,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
|
||||
const handlePaymentError = (errorMessage: string) => {
|
||||
showToast.error(errorMessage, {
|
||||
title: 'Error en el pago'
|
||||
title: t('auth:alerts.payment_error', 'Error en el pago')
|
||||
});
|
||||
};
|
||||
|
||||
@@ -252,13 +252,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
// Show 2 steps if plan is pre-selected, 3 steps otherwise
|
||||
const steps = preSelectedPlan
|
||||
? [
|
||||
{ key: 'basic_info', label: 'Información', number: 1, time: '2 min' },
|
||||
{ key: 'payment', label: 'Pago', number: 2, time: '2 min' }
|
||||
{ key: 'basic_info', label: t('auth:steps.info', 'Información'), number: 1, time: '2 min' },
|
||||
{ key: 'payment', label: t('auth:steps.payment', 'Pago'), number: 2, time: '2 min' }
|
||||
]
|
||||
: [
|
||||
{ key: 'basic_info', label: 'Información', number: 1, time: '2 min' },
|
||||
{ key: 'subscription', label: 'Plan', number: 2, time: '1 min' },
|
||||
{ key: 'payment', label: 'Pago', number: 3, time: '2 min' }
|
||||
{ key: 'basic_info', label: t('auth:steps.info', 'Información'), number: 1, time: '2 min' },
|
||||
{ key: 'subscription', label: t('auth:steps.subscription', 'Plan'), number: 2, time: '1 min' },
|
||||
{ key: 'payment', label: t('auth:steps.payment', 'Pago'), number: 3, time: '2 min' }
|
||||
];
|
||||
|
||||
const getStepIndex = (step: RegistrationStep) => {
|
||||
@@ -499,14 +499,8 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<label htmlFor="acceptTerms" className="text-sm text-text-secondary cursor-pointer">
|
||||
Acepto los{' '}
|
||||
<a href="/terms" target="_blank" rel="noopener noreferrer" className="text-color-primary hover:text-color-primary-dark underline">
|
||||
términos y condiciones
|
||||
</a>{' '}
|
||||
y la{' '}
|
||||
<a href="/privacy" target="_blank" rel="noopener noreferrer" className="text-color-primary hover:text-color-primary-dark underline">
|
||||
política de privacidad
|
||||
</a>{' '}
|
||||
{t('auth:register.accept_terms_and_privacy', 'Acepto los términos y condiciones y la política de privacidad')}
|
||||
{' '}
|
||||
<span className="text-color-error">*</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -524,7 +518,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<label htmlFor="marketingConsent" className="text-sm text-text-secondary cursor-pointer">
|
||||
Deseo recibir comunicaciones de marketing y promociones
|
||||
{t('auth:register.marketing_consent', 'Deseo recibir comunicaciones de marketing y promociones')}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -538,7 +532,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<label htmlFor="analyticsConsent" className="text-sm text-text-secondary cursor-pointer">
|
||||
Acepto el uso de cookies analíticas para mejorar la experiencia
|
||||
{t('auth:register.analytics_consent', 'Acepto el uso de cookies analíticas para mejorar la experiencia')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -551,7 +545,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading || !validatePassword(formData.password) || !formData.acceptTerms || passwordMatchStatus !== 'match'}
|
||||
className="w-full sm:w-48"
|
||||
>
|
||||
Siguiente
|
||||
{t('auth:register.next_button', 'Siguiente')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -587,7 +581,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-48 order-2 sm:order-1"
|
||||
>
|
||||
Anterior
|
||||
{t('auth:register.previous_button', 'Anterior')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -596,7 +590,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-48 order-1 sm:order-2"
|
||||
>
|
||||
Siguiente
|
||||
{t('auth:register.next_button', 'Siguiente')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -619,36 +613,36 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
<Card className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 border-2 border-blue-200 dark:border-blue-800">
|
||||
<h3 className="text-lg font-bold text-text-primary mb-4 flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5 text-color-primary" />
|
||||
Resumen de tu Plan
|
||||
{t('auth:payment.payment_summary', 'Resumen de tu Plan')}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-text-secondary">Plan seleccionado:</span>
|
||||
<span className="text-text-secondary">{t('auth:payment.selected_plan', 'Plan seleccionado:')}</span>
|
||||
<span className="font-bold text-color-primary text-lg">{selectedPlanMetadata.name}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-text-secondary">Precio mensual:</span>
|
||||
<span className="text-text-secondary">{t('auth:payment.monthly_price', 'Precio mensual:')}</span>
|
||||
<span className="font-semibold text-text-primary">
|
||||
{subscriptionService.formatPrice(selectedPlanMetadata.monthly_price)}/mes
|
||||
</span>
|
||||
</div>
|
||||
{useTrial && (
|
||||
<div className="flex justify-between items-center pt-3 border-t border-blue-200 dark:border-blue-800">
|
||||
<span className="text-green-700 dark:text-green-400 font-medium">Período de prueba:</span>
|
||||
<span className="text-green-700 dark:text-green-400 font-medium">{t('auth:payment.trial_period', 'Período de prueba:')}</span>
|
||||
<span className="font-bold text-green-700 dark:text-green-400">
|
||||
{isPilot ? `${trialMonths} meses GRATIS` : '14 días gratis'}
|
||||
{isPilot ? t('auth:payment.free_months', {count: trialMonths}) : t('auth:payment.free_days', '14 días gratis')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="pt-3 border-t border-blue-200 dark:border-blue-800">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<span className="text-text-tertiary">Total hoy:</span>
|
||||
<span className="text-text-tertiary">{t('auth:payment.total_today', 'Total hoy:')}</span>
|
||||
<span className="font-bold text-xl text-color-success">€0.00</span>
|
||||
</div>
|
||||
<p className="text-xs text-text-tertiary mt-2 text-center">
|
||||
{useTrial
|
||||
? `Se te cobrará ${subscriptionService.formatPrice(selectedPlanMetadata.monthly_price)} después del período de prueba`
|
||||
: 'Tarjeta requerida para validación'
|
||||
? t('auth:payment.billing_message', {price: subscriptionService.formatPrice(selectedPlanMetadata.monthly_price)})
|
||||
: t('auth:payment.payment_required', 'Tarjeta requerida para validación')
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
@@ -675,7 +669,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-48"
|
||||
>
|
||||
Anterior
|
||||
{t('auth:register.previous_button', 'Anterior')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -701,7 +695,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
{onLoginClick && currentStep === 'basic_info' && (
|
||||
<div className="mt-8 text-center border-t border-border-primary pt-6">
|
||||
<p className="text-text-secondary mb-4">
|
||||
¿Ya tienes una cuenta?
|
||||
{t('auth:register.have_account', '¿Ya tienes una cuenta?')}
|
||||
</p>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -709,7 +703,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
disabled={isLoading}
|
||||
className="text-color-primary hover:text-color-primary-dark"
|
||||
>
|
||||
Iniciar Sesión
|
||||
{t('auth:register.sign_in_link', 'Iniciar Sesión')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -422,8 +422,7 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
throw new Error('Parent tenant not registered');
|
||||
}
|
||||
|
||||
const response = await tenantService.bulkCreateChildTenants({
|
||||
parent_tenant_id: parentTenantId,
|
||||
const response = await tenantService.bulkCreateChildTenants(parentTenantId, {
|
||||
child_tenants: data.childTenants.map((ct: any) => ({
|
||||
name: ct.name,
|
||||
city: ct.city,
|
||||
@@ -435,6 +434,10 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
longitude: ct.longitude,
|
||||
phone: ct.phone,
|
||||
email: ct.email,
|
||||
business_type: ct.business_type,
|
||||
business_model: ct.business_model,
|
||||
timezone: ct.timezone,
|
||||
metadata: ct.metadata,
|
||||
})),
|
||||
auto_configure_distribution: true,
|
||||
});
|
||||
@@ -446,6 +449,11 @@ const OnboardingWizardContent: React.FC = () => {
|
||||
|
||||
if (response.failed_count > 0) {
|
||||
console.warn('⚠️ Some child tenants failed to create:', response.failed_tenants);
|
||||
|
||||
// Show specific errors for each failed tenant
|
||||
response.failed_tenants.forEach(failed => {
|
||||
console.error(`Failed to create tenant ${failed.name} (${failed.location_code}):`, failed.error);
|
||||
});
|
||||
}
|
||||
} catch (childTenantError) {
|
||||
console.error('❌ Failed to create child tenants:', childTenantError);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus, Store, MapPin, Trash2, Edit2, Building2, X } from 'lucide-react';
|
||||
import { Plus, Store, MapPin, Trash2, Edit2, Building2, X, Phone, Mail, Clock, Tag, Globe } from 'lucide-react';
|
||||
import Button from '../../../ui/Button/Button';
|
||||
import Card from '../../../ui/Card/Card';
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter } from '../../../ui/Modal';
|
||||
import { Input } from '../../../ui/Input';
|
||||
import { Select } from '../../../ui/Select';
|
||||
import { AddressAutocomplete } from '../../../ui/AddressAutocomplete';
|
||||
|
||||
export interface ChildTenantSetupStepProps {
|
||||
onUpdate?: (data: { childTenants: ChildTenant[]; canContinue: boolean }) => void;
|
||||
@@ -22,6 +24,14 @@ export interface ChildTenant {
|
||||
address: string;
|
||||
postal_code: string;
|
||||
location_code: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
business_type?: string;
|
||||
business_model?: string;
|
||||
timezone?: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
@@ -42,6 +52,14 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
address: '',
|
||||
postal_code: '',
|
||||
location_code: '',
|
||||
latitude: undefined,
|
||||
longitude: undefined,
|
||||
phone: '',
|
||||
email: '',
|
||||
business_type: 'bakery',
|
||||
business_model: 'retail_bakery',
|
||||
timezone: 'Europe/Madrid',
|
||||
metadata: {},
|
||||
});
|
||||
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
|
||||
|
||||
@@ -67,11 +85,35 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
}
|
||||
if (!formData.postal_code?.trim()) {
|
||||
errors.postal_code = 'El código postal es requerido';
|
||||
} else if (!/^\d{5}$/.test(formData.postal_code)) {
|
||||
errors.postal_code = 'El código postal debe tener exactamente 5 dígitos';
|
||||
}
|
||||
if (!formData.location_code?.trim()) {
|
||||
errors.location_code = 'El código de ubicación es requerido';
|
||||
} else if (formData.location_code.length > 10) {
|
||||
errors.location_code = 'El código no debe exceder 10 caracteres';
|
||||
} else if (!/^[A-Z0-9\-_.]+$/.test(formData.location_code)) {
|
||||
errors.location_code = 'Solo se permiten letras mayúsculas, números y guiones/guiones bajos';
|
||||
}
|
||||
|
||||
// Phone validation
|
||||
if (formData.phone && formData.phone.trim()) {
|
||||
const phone = formData.phone.replace(/[\s\-\(\)]/g, '');
|
||||
const patterns = [
|
||||
/^(\+34|0034|34)?[6789]\d{8}$/, // Mobile
|
||||
/^(\+34|0034|34)?9\d{8}$/ // Landline
|
||||
];
|
||||
if (!patterns.some(pattern => pattern.test(phone))) {
|
||||
errors.phone = 'Introduce un número de teléfono español válido';
|
||||
}
|
||||
}
|
||||
|
||||
// Email validation
|
||||
if (formData.email && formData.email.trim()) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(formData.email)) {
|
||||
errors.email = 'Introduce un correo electrónico válido';
|
||||
}
|
||||
}
|
||||
|
||||
setFormErrors(errors);
|
||||
@@ -122,6 +164,14 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
address: formData.address!,
|
||||
postal_code: formData.postal_code!,
|
||||
location_code: formData.location_code!.toUpperCase(),
|
||||
latitude: formData.latitude,
|
||||
longitude: formData.longitude,
|
||||
phone: formData.phone || undefined,
|
||||
email: formData.email || undefined,
|
||||
business_type: formData.business_type || 'bakery',
|
||||
business_model: formData.business_model || 'retail_bakery',
|
||||
timezone: formData.timezone || 'Europe/Madrid',
|
||||
metadata: formData.metadata || {},
|
||||
};
|
||||
|
||||
if (editingTenant) {
|
||||
@@ -236,9 +286,16 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
<h3 className="font-semibold text-[var(--text-primary)]">
|
||||
{tenant.name}
|
||||
</h3>
|
||||
<span className="text-xs text-[var(--text-tertiary)] font-mono">
|
||||
{tenant.location_code}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<span className="text-xs text-[var(--text-tertiary)] font-mono">
|
||||
{tenant.location_code}
|
||||
</span>
|
||||
{tenant.zone && (
|
||||
<span className="text-xs text-[var(--text-tertiary)]">
|
||||
• {tenant.zone}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
@@ -336,7 +393,142 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* City and Zone */}
|
||||
{/* Business Type */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Tipo de Negocio
|
||||
</label>
|
||||
<Select
|
||||
value={formData.business_type || 'bakery'}
|
||||
onChange={(e) => setFormData({ ...formData, business_type: e.target.value })}
|
||||
>
|
||||
<option value="bakery">Panadería</option>
|
||||
<option value="coffee_shop">Cafetería</option>
|
||||
<option value="pastry_shop">Pastelería</option>
|
||||
<option value="restaurant">Restaurante</option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Business Model */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Modelo de Negocio
|
||||
</label>
|
||||
<Select
|
||||
value={formData.business_model || 'retail_bakery'}
|
||||
onChange={(e) => setFormData({ ...formData, business_model: e.target.value })}
|
||||
>
|
||||
<option value="retail_bakery">Panadería Minorista</option>
|
||||
<option value="central_baker_satellite">Obrador Central + Sucursales</option>
|
||||
<option value="hybrid_bakery">Modelo Híbrido</option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Teléfono
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--text-secondary)]" />
|
||||
<Input
|
||||
value={formData.phone || ''}
|
||||
onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
||||
placeholder="ej. +34 123 456 789"
|
||||
error={formErrors.phone}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Email
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--text-secondary)]" />
|
||||
<Input
|
||||
value={formData.email || ''}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
placeholder="ej. contacto@panaderia.com"
|
||||
error={formErrors.email}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timezone */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Zona Horaria
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Clock className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--text-secondary)]" />
|
||||
<Select
|
||||
value={formData.timezone || 'Europe/Madrid'}
|
||||
onChange={(e) => setFormData({ ...formData, timezone: e.target.value })}
|
||||
className="pl-10"
|
||||
>
|
||||
<option value="Europe/Madrid">Europe/Madrid (UTC+1/UTC+2)</option>
|
||||
<option value="Europe/Paris">Europe/Paris (UTC+1/UTC+2)</option>
|
||||
<option value="Europe/London">Europe/London (UTC+0/UTC+1)</option>
|
||||
<option value="America/New_York">America/New_York (UTC-5/UTC-4)</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Zone */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Zona / Barrio (opcional)
|
||||
</label>
|
||||
<Input
|
||||
value={formData.zone || ''}
|
||||
onChange={(e) => setFormData({ ...formData, zone: e.target.value })}
|
||||
placeholder="ej. Salamanca, Chamberí, Centro"
|
||||
/>
|
||||
<p className="text-xs text-[var(--text-tertiary)] mt-1">
|
||||
Zona o barrio específico dentro de la ciudad
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Address with Autocomplete */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Dirección *
|
||||
</label>
|
||||
<AddressAutocomplete
|
||||
value={formData.address || ''}
|
||||
placeholder="ej. Calle de Serrano, 48"
|
||||
onAddressSelect={(address) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
address: address.display_name,
|
||||
city: address.address.city || address.address.municipality || address.address.suburb || prev.city,
|
||||
postal_code: address.address.postcode || prev.postal_code,
|
||||
latitude: address.lat,
|
||||
longitude: address.lon,
|
||||
}));
|
||||
}}
|
||||
onCoordinatesChange={(lat, lon) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
}));
|
||||
}}
|
||||
countryCode="es"
|
||||
required
|
||||
/>
|
||||
{formErrors.address && (
|
||||
<div className="mt-1 text-sm text-[var(--color-error)]">
|
||||
{formErrors.address}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* City and Postal Code */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
@@ -351,41 +543,17 @@ export const ChildTenantsSetupStep: React.FC<ChildTenantSetupStepProps> = ({
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Zona / Barrio
|
||||
Código Postal *
|
||||
</label>
|
||||
<Input
|
||||
value={formData.zone || ''}
|
||||
onChange={(e) => setFormData({ ...formData, zone: e.target.value })}
|
||||
placeholder="ej. Salamanca"
|
||||
value={formData.postal_code || ''}
|
||||
onChange={(e) => setFormData({ ...formData, postal_code: e.target.value })}
|
||||
placeholder="ej. 28001"
|
||||
error={formErrors.postal_code}
|
||||
maxLength={5}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Address */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Dirección *
|
||||
</label>
|
||||
<Input
|
||||
value={formData.address || ''}
|
||||
onChange={(e) => setFormData({ ...formData, address: e.target.value })}
|
||||
placeholder="ej. Calle de Serrano, 48"
|
||||
error={formErrors.address}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Postal Code */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--text-primary)] mb-1">
|
||||
Código Postal *
|
||||
</label>
|
||||
<Input
|
||||
value={formData.postal_code || ''}
|
||||
onChange={(e) => setFormData({ ...formData, postal_code: e.target.value })}
|
||||
placeholder="ej. 28001"
|
||||
error={formErrors.postal_code}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter justify="end">
|
||||
|
||||
Reference in New Issue
Block a user