Imporve onboarding UI

This commit is contained in:
Urtzi Alfaro
2025-12-19 13:10:24 +01:00
parent 71ee2976a2
commit bfa5ff0637
39 changed files with 1016 additions and 483 deletions

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Button, Input } from '../../../ui';
import { AddressAutocomplete } from '../../../ui/AddressAutocomplete';
import { useRegisterBakery } from '../../../../api/hooks/tenant';
import { BakeryRegistration } from '../../../../api/types/tenant';
import { useRegisterBakery, useTenant, useUpdateTenant } from '../../../../api/hooks/tenant';
import { BakeryRegistration, TenantUpdate } from '../../../../api/types/tenant';
import { AddressResult } from '../../../../services/api/geocodingApi';
import { useWizardContext } from '../context';
import { poiContextApi } from '../../../../services/api/poiContextApi';
@@ -34,6 +34,7 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
isFirstStep
}) => {
const wizardContext = useWizardContext();
const tenantId = wizardContext.state.tenantId;
// Check if user is enterprise tier for conditional labels
const subscriptionTier = localStorage.getItem('subscription_tier');
@@ -52,8 +53,31 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
business_model: businessModel
});
// Fetch existing tenant data if we have a tenantId (persistence)
const { data: existingTenant, isLoading: isLoadingTenant } = useTenant(tenantId || '');
// Update formData when existing tenant data is fetched
useEffect(() => {
if (existingTenant) {
console.log('🔄 Populating RegisterTenantStep with existing data:', existingTenant);
setFormData({
name: existingTenant.name,
address: existingTenant.address,
postal_code: existingTenant.postal_code,
phone: existingTenant.phone || '',
city: existingTenant.city,
business_type: existingTenant.business_type,
business_model: existingTenant.business_model || businessModel
});
// Update location in context if available from tenant
// Note: Backend might not store lat/lon directly in Tenant table in all versions,
// but if we had them or if we want to re-trigger geocoding, we'd handle it here.
}
}, [existingTenant, businessModel]);
// Update business_model when bakeryType changes in context
React.useEffect(() => {
useEffect(() => {
const newBusinessModel = getBakeryBusinessModel(wizardContext.state.bakeryType);
if (newBusinessModel !== formData.business_model) {
setFormData(prev => ({
@@ -65,6 +89,7 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
const [errors, setErrors] = useState<Record<string, string>>({});
const registerBakery = useRegisterBakery();
const updateTenant = useUpdateTenant();
const handleInputChange = (field: keyof BakeryRegistration, value: string) => {
setFormData(prev => ({
@@ -143,14 +168,31 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
return;
}
console.log('📝 Registering tenant with data:', {
console.log('📝 Submitting tenant data:', {
isUpdate: !!tenantId,
bakeryType: wizardContext.state.bakeryType,
business_model: formData.business_model,
formData
});
try {
const tenant = await registerBakery.mutateAsync(formData);
let tenant;
if (tenantId) {
// Update existing tenant
const updateData: TenantUpdate = {
name: formData.name,
address: formData.address,
phone: formData.phone,
business_type: formData.business_type,
business_model: formData.business_model
};
tenant = await updateTenant.mutateAsync({ tenantId, updateData });
console.log('✅ Tenant updated successfully:', tenant.id);
} else {
// Create new tenant
tenant = await registerBakery.mutateAsync(formData);
console.log('✅ Tenant registered successfully:', tenant.id);
}
// Trigger POI detection in the background (non-blocking)
// This replaces the removed POI Detection step
@@ -203,29 +245,51 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
};
return (
<div className="space-y-4 md:space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6">
<Input
label={isEnterprise ? "Nombre del Obrador Central" : "Nombre de la Panadería"}
placeholder={isEnterprise ? "Ingresa el nombre de tu obrador central" : "Ingresa el nombre de tu panadería"}
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
error={errors.name}
isRequired
/>
<div className="space-y-6 md:space-y-8">
{/* Informational header */}
<div className="bg-gradient-to-r from-[var(--color-primary)]/10 to-[var(--color-primary)]/5 border-l-4 border-[var(--color-primary)] rounded-lg p-4 md:p-5">
<div className="flex items-start gap-3">
<div className="text-2xl flex-shrink-0">{isEnterprise ? '🏭' : '🏪'}</div>
<div>
<h3 className="font-semibold text-[var(--text-primary)] mb-1">
{isEnterprise ? 'Registra tu Obrador Central' : 'Registra tu Panadería'}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
{isEnterprise
? 'Ingresa los datos de tu obrador principal. Después podrás agregar las sucursales.'
: 'Completa la información básica de tu panadería para comenzar.'}
</p>
</div>
</div>
</div>
<Input
label="Teléfono"
type="tel"
placeholder="+34 123 456 789"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
error={errors.phone}
isRequired
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 md:gap-6">
<div className="transform transition-all duration-200 hover:scale-[1.01]">
<Input
label={isEnterprise ? "Nombre del Obrador Central" : "Nombre de la Panadería"}
placeholder={isEnterprise ? "Ingresa el nombre de tu obrador central" : "Ingresa el nombre de tu panadería"}
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
error={errors.name}
isRequired
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-1">
<div className="transform transition-all duration-200 hover:scale-[1.01]">
<Input
label="Teléfono"
type="tel"
placeholder="+34 123 456 789"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
error={errors.phone}
isRequired
/>
</div>
<div className="md:col-span-2 transform transition-all duration-200 hover:scale-[1.01] relative z-20">
<label className="block text-sm font-semibold text-[var(--text-primary)] mb-2 flex items-center gap-2">
<span className="text-lg">📍</span>
Dirección <span className="text-red-500">*</span>
</label>
<AddressAutocomplete
@@ -240,45 +304,60 @@ export const RegisterTenantStep: React.FC<RegisterTenantStepProps> = ({
required
/>
{errors.address && (
<div className="mt-1 text-sm text-red-600">
<div className="mt-2 text-sm text-red-600 flex items-center gap-1.5 animate-shake">
<span></span>
{errors.address}
</div>
)}
</div>
<Input
label="Código Postal"
placeholder="28001"
value={formData.postal_code}
onChange={(e) => handleInputChange('postal_code', e.target.value)}
error={errors.postal_code}
isRequired
maxLength={5}
/>
<div className="transform transition-all duration-200 hover:scale-[1.01]">
<Input
label="Código Postal"
placeholder="28001"
value={formData.postal_code}
onChange={(e) => handleInputChange('postal_code', e.target.value)}
error={errors.postal_code}
isRequired
maxLength={5}
/>
</div>
<Input
label="Ciudad (Opcional)"
placeholder="Madrid"
value={formData.city}
onChange={(e) => handleInputChange('city', e.target.value)}
error={errors.city}
/>
<div className="transform transition-all duration-200 hover:scale-[1.01]">
<Input
label="Ciudad (Opcional)"
placeholder="Madrid"
value={formData.city}
onChange={(e) => handleInputChange('city', e.target.value)}
error={errors.city}
/>
</div>
</div>
{errors.submit && (
<div className="text-[var(--color-error)] text-sm bg-[var(--color-error)]/10 p-3 rounded-lg">
{errors.submit}
<div className="bg-gradient-to-r from-[var(--color-error)]/10 to-[var(--color-error)]/5 border-l-4 border-[var(--color-error)] rounded-lg p-4 animate-shake">
<div className="flex items-start gap-3">
<span className="text-2xl flex-shrink-0"></span>
<div>
<h4 className="font-semibold text-[var(--color-error)] mb-1">Error al registrar</h4>
<p className="text-sm text-[var(--text-secondary)]">{errors.submit}</p>
</div>
</div>
</div>
)}
<div className="flex justify-end">
<div className="flex justify-end pt-4 border-t-2 border-[var(--border-color)]/50">
<Button
onClick={handleSubmit}
isLoading={registerBakery.isPending}
loadingText={isEnterprise ? "Registrando obrador..." : "Registrando..."}
isLoading={registerBakery.isPending || updateTenant.isPending}
loadingText={tenantId ? "Actualizando obrador..." : (isEnterprise ? "Registrando obrador..." : "Registrando...")}
size="lg"
className="w-full sm:w-auto sm:min-w-[280px] text-base font-semibold transform transition-all duration-300 hover:scale-105 shadow-lg"
>
{isEnterprise ? "Crear Obrador Central y Continuar" : "Crear Panadería y Continuar"}
{tenantId
? (isEnterprise ? "Actualizar Obrador Central y Continuar →" : "Actualizar Panadería y Continuar →")
: (isEnterprise ? "Crear Obrador Central y Continuar →" : "Crear Panadería y Continuar →")
}
</Button>
</div>
</div>