diff --git a/frontend/src/components/domain/unified-wizard/wizards/SupplierWizard.tsx b/frontend/src/components/domain/unified-wizard/wizards/SupplierWizard.tsx index 387d03b9..5d050d80 100644 --- a/frontend/src/components/domain/unified-wizard/wizards/SupplierWizard.tsx +++ b/frontend/src/components/domain/unified-wizard/wizards/SupplierWizard.tsx @@ -1,76 +1,122 @@ import React, { useState, useEffect } from 'react'; import { WizardStep, WizardStepProps } from '../../../ui/WizardModal/WizardModal'; -import { Building2, Package, Euro, CheckCircle2, Phone, Mail, Loader2, AlertCircle } from 'lucide-react'; +import { Building2, CheckCircle2, Loader2 } from 'lucide-react'; import { useTenant } from '../../../../stores/tenant.store'; import { suppliersService } from '../../../../api/services/suppliers'; -import { inventoryService } from '../../../../api/services/inventory'; import { showToast } from '../../../../utils/toast'; -import { isPositiveNumber, isInteger, isValidEmail, isValidSpanishPhone } from '../../../../utils/validation'; +import { AdvancedOptionsSection } from '../../../ui/AdvancedOptionsSection'; +import Tooltip from '../../../ui/Tooltip/Tooltip'; interface WizardDataProps extends WizardStepProps { data: Record; onDataChange: (data: Record) => void; } -// Step 1: Supplier Information -const SupplierInfoStep: React.FC = ({ data, onDataChange, onNext }) => { +const SupplierDetailsStep: React.FC = ({ data, onDataChange, onComplete }) => { + const { currentTenant } = useTenant(); const [supplierData, setSupplierData] = useState({ + // Required fields name: data.name || '', + supplierType: data.supplierType || 'ingredients', + status: data.status || 'pending_approval', + paymentTerms: data.paymentTerms || 'net_30', + currency: data.currency || 'EUR', + standardLeadTime: data.standardLeadTime || 3, + + // Basic optional fields contactPerson: data.contactPerson || '', - phone: data.phone || '', email: data.email || '', - address: data.address || '', - paymentTerms: data.paymentTerms || '', - leadTimeDays: data.leadTimeDays || '', + phone: data.phone || '', + + // Advanced optional fields + supplierCode: data.supplierCode || '', + taxId: data.taxId || '', + registrationNumber: data.registrationNumber || '', + mobile: data.mobile || '', + website: data.website || '', + addressLine1: data.addressLine1 || '', + addressLine2: data.addressLine2 || '', + city: data.city || '', + stateProvince: data.stateProvince || '', + postalCode: data.postalCode || '', + country: data.country || '', + creditLimit: data.creditLimit || '', + minimumOrderAmount: data.minimumOrderAmount || '', + deliveryArea: data.deliveryArea || '', + isPreferredSupplier: data.isPreferredSupplier || false, + autoApproveEnabled: data.autoApproveEnabled || false, notes: data.notes || '', + certifications: data.certifications || '', + specializations: data.specializations || '', }); - const [validationErrors, setValidationErrors] = useState>({}); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); - const validateLeadTimeDays = (value: string) => { - if (!value) { - setValidationErrors(prev => ({ ...prev, leadTimeDays: 'Campo requerido' })); - } else if (!isPositiveNumber(value) || !isInteger(value)) { - setValidationErrors(prev => ({ ...prev, leadTimeDays: 'Debe ser un número entero positivo' })); - } else { - setValidationErrors(prev => { - const { leadTimeDays, ...rest } = prev; - return rest; - }); + useEffect(() => { + if (!supplierData.supplierCode && supplierData.name) { + const code = `SUP-${supplierData.name.substring(0, 3).toUpperCase()}-${Date.now().toString().slice(-4)}`; + setSupplierData(prev => ({ ...prev, supplierCode: code })); } - }; + }, [supplierData.name]); - const validatePhone = (phone: string) => { - if (phone && !isValidSpanishPhone(phone)) { - setValidationErrors(prev => ({ ...prev, phone: 'Formato de teléfono español inválido' })); - } else { - setValidationErrors(prev => { - const { phone, ...rest } = prev; - return rest; - }); + useEffect(() => { + onDataChange({ ...data, ...supplierData }); + }, [supplierData]); + + const handleCreateSupplier = async () => { + if (!currentTenant?.id) { + setError('Could not obtain tenant information'); + return; } - }; - const validateEmail = (email: string) => { - if (email && !isValidEmail(email)) { - setValidationErrors(prev => ({ ...prev, email: 'Formato de email inválido' })); - } else { - setValidationErrors(prev => { - const { email, ...rest } = prev; - return rest; - }); - } - }; + setLoading(true); + setError(null); - const handleContinue = () => { - // Validate before continuing - validateLeadTimeDays(supplierData.leadTimeDays); - validatePhone(supplierData.phone); - if (supplierData.email) validateEmail(supplierData.email); + try { + const payload = { + name: supplierData.name, + supplier_type: supplierData.supplierType, + status: supplierData.status, + payment_terms: supplierData.paymentTerms, + currency: supplierData.currency, + standard_lead_time: supplierData.standardLeadTime, + supplier_code: supplierData.supplierCode || undefined, + tax_id: supplierData.taxId || undefined, + registration_number: supplierData.registrationNumber || undefined, + contact_person: supplierData.contactPerson || undefined, + email: supplierData.email || undefined, + phone: supplierData.phone || undefined, + mobile: supplierData.mobile || undefined, + website: supplierData.website || undefined, + address_line1: supplierData.addressLine1 || undefined, + address_line2: supplierData.addressLine2 || undefined, + city: supplierData.city || undefined, + state_province: supplierData.stateProvince || undefined, + postal_code: supplierData.postalCode || undefined, + country: supplierData.country || undefined, + credit_limit: supplierData.creditLimit ? parseFloat(supplierData.creditLimit) : undefined, + minimum_order_amount: supplierData.minimumOrderAmount ? parseFloat(supplierData.minimumOrderAmount) : undefined, + delivery_area: supplierData.deliveryArea || undefined, + is_preferred_supplier: supplierData.isPreferredSupplier, + auto_approve_enabled: supplierData.autoApproveEnabled, + notes: supplierData.notes || undefined, + certifications: supplierData.certifications ? JSON.parse(`{"items": ${JSON.stringify(supplierData.certifications.split(',').map(c => c.trim()))}}`) : undefined, + specializations: supplierData.specializations ? JSON.parse(`{"items": ${JSON.stringify(supplierData.specializations.split(',').map(s => s.trim()))}}`) : undefined, + created_by: currentTenant.id, + updated_by: currentTenant.id, + }; - if (Object.keys(validationErrors).length === 0) { - onDataChange({ ...data, ...supplierData }); - onNext(); + await suppliersService.createSupplier(currentTenant.id, payload); + showToast.success('Supplier created successfully'); + onComplete(); + } catch (err: any) { + console.error('Error creating supplier:', err); + const errorMessage = err.response?.data?.detail || 'Error creating supplier'; + setError(errorMessage); + showToast.error(errorMessage); + } finally { + setLoading(false); } }; @@ -78,384 +124,442 @@ const SupplierInfoStep: React.FC = ({ data, onDataChange, onNex
-

- Información del Proveedor -

+

Supplier Details

+

Essential supplier information

+ {error && ( +
+ {error} +
+ )} + + {/* Required Fields */}
setSupplierData({ ...supplierData, name: e.target.value })} - placeholder="Ej: Harinas Premium S.L." + placeholder="e.g., Premium Flour Suppliers Ltd." className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" />
-
- setSupplierData({ ...supplierData, phone: e.target.value })} - placeholder="+34 123 456 789" + setSupplierData({ ...supplierData, email: e.target.value })} - placeholder="contacto@proveedor.com" - className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" - /> -
- -
- - setSupplierData({ ...supplierData, address: e.target.value })} - placeholder="Calle, Ciudad, País" - className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" - /> + > + + + + + +
- setSupplierData({ ...supplierData, leadTimeDays: e.target.value })} - onBlur={(e) => validateLeadTimeDays(e.target.value)} - placeholder="Ej: 7" - min="0" - className={`w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 bg-[var(--bg-primary)] text-[var(--text-primary)] ${ - validationErrors.leadTimeDays - ? 'border-red-500 focus:ring-red-500' - : 'border-[var(--border-secondary)] focus:ring-[var(--color-primary)]' - }`} - /> - {validationErrors.leadTimeDays && ( -

- - {validationErrors.leadTimeDays} -

- )} -
- -
-
-
+
-