import React, { useState, useEffect } from 'react'; import { Helmet } from 'react-helmet'; import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { motion, AnimatePresence } from 'framer-motion'; import { ChevronDown, ChevronUp, Info, CheckCircle2, AlertCircle, Save, X, Building2, User, MapPin, Wallet, Tag, FileText, Zap, Shield, ArrowLeft, UploadCloud } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { toast } from '@/components/ui/use-toast'; import { Switch } from '@/components/ui/switch'; import { Progress } from '@/components/ui/progress'; import { cn } from '@/lib/utils'; import { mockUsers } from '@/data/mockData'; // --- Schema Validation --- const prospectSchema = z.object({ // 1. General type: z.enum(['entreprise', 'particulier']), name: z.string().min(2, "Le nom est requis"), companyName: z.string().optional(), siret: z.string().optional(), category: z.string().optional(), origin: z.string().optional(), status: z.string().optional(), assignedTo: z.string().optional(), // 2. Coordinates address1: z.string().min(5, "L'adresse est requise"), address2: z.string().optional(), zipCode: z.string().min(4, "Code postal requis"), city: z.string().min(2, "Ville requise"), country: z.string().default('France'), // Contact civility: z.string().optional(), contactName: z.string().min(2, "Nom requis"), contactFirstName: z.string().min(2, "Prénom requis"), position: z.string().optional(), email: z.string().email("Email invalide"), phone: z.string().min(10, "Numéro invalide"), phoneSecondary: z.string().optional(), contactPref: z.enum(['email', 'phone', 'sms', 'whatsapp']).default('email'), // 3. Company Info (Conditional in UI logic, permissive in schema) legalForm: z.string().optional(), employees: z.string().optional(), revenue: z.string().optional(), sector: z.string().optional(), website: z.string().url("URL invalide").optional().or(z.literal('')), linkedin: z.string().url("URL invalide").optional().or(z.literal('')), // 4. Commercial priceFamily: z.string().optional(), accountingCategory: z.string().optional(), accountingAccount: z.string().optional(), paymentMethod: z.string().optional(), paymentTerm: z.string().optional(), billingConditions: z.string().optional(), discountCommercial: z.string().optional(), discountFinancial: z.string().optional(), // 5. Technical needsDescription: z.string().optional(), products: z.array(z.string()).optional(), interestLevel: z.enum(['low', 'medium', 'high']).default('medium'), urgency: z.string().optional(), budget: z.string().optional(), followUpDate: z.string().optional(), tags: z.string().optional(), // Simplification for tags as comma string // 6. Notes notes: z.string().optional(), // 7. Automation autoOpportunity: z.boolean().default(false), autoTask: z.boolean().default(false), sendWelcome: z.boolean().default(false), autoAssign: z.boolean().default(false), // 8. Permissions visibility: z.enum(['me', 'team', 'all']).default('team'), role: z.string().default('commercial'), }).refine((data) => { if (data.type === 'entreprise' && !data.companyName) { return false; } return true; }, { message: "La raison sociale est requise pour une entreprise", path: ["companyName"], }); // --- Components --- const Section = ({ title, icon: Icon, children, defaultOpen = true, progress = 100 }) => { const [isOpen, setIsOpen] = useState(defaultOpen); return (
{isOpen && (
{children}
)}
); }; const InputGroup = ({ label, error, required, tooltip, children, className }) => (
{children} {error ? ( ) : !error && required && children?.props?.value ? ( ) : null}
{error &&

{error.message}

}
); const CreateProspectPage = () => { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [completion, setCompletion] = useState(0); const { register, handleSubmit, watch, control, formState: { errors, isValid, touchedFields }, reset } = useForm({ resolver: zodResolver(prospectSchema), mode: "onChange", defaultValues: { type: 'entreprise', contactPref: 'email', interestLevel: 'medium', country: 'France', autoOpportunity: false, visibility: 'team' } }); const watchAllFields = watch(); const type = watch('type'); // Calculate completion percentage useEffect(() => { const requiredFields = ['name', 'address1', 'zipCode', 'city', 'contactName', 'contactFirstName', 'email', 'phone']; if (type === 'entreprise') requiredFields.push('companyName'); const filled = requiredFields.filter(field => !!watchAllFields[field]); setCompletion(Math.round((filled.length / requiredFields.length) * 100)); }, [watchAllFields, type]); const onSubmit = async (data) => { setIsLoading(true); // Simulate API call await new Promise(resolve => setTimeout(resolve, 1500)); console.log(data); toast({ title: "Prospect créé avec succès", description: `${data.name} a été ajouté à votre base CRM.`, variant: "success" }); setIsLoading(false); navigate('/prospects'); }; const inputClass = (error, success) => cn( "w-full px-3 py-2 bg-white dark:bg-gray-950 border rounded-xl text-sm shadow-sm transition-all focus:outline-none focus:ring-2 pr-10", error ? "border-red-300 focus:border-red-500 focus:ring-red-200" : success ? "border-green-300 focus:border-green-500 focus:ring-green-200" : "border-gray-200 dark:border-gray-800 focus:border-[#007E45] focus:ring-red-100/50" ); return ( <> Créer un prospect - Bijou ERP
{/* Header */}

Nouveau Prospect

Remplissez les informations pour créer une fiche prospect complète.

Complétion de la fiche {completion}%
{/* SECTION 1: INFORMATIONS GÉNÉRALES */}
{['entreprise', 'particulier'].map((val) => ( ))}
{type === 'entreprise' && ( )}
{/* SECTION 2: COORDONNÉES */}

Adresse

Contact Principal

{['email', 'phone', 'sms', 'whatsapp'].map(pref => ( ))}
{/* SECTION 3: COMPANY INFO (Conditional) */} {type === 'entreprise' && (
)} {/* SECTION 4: COMMERCIAL & COMPTA */}