import React, { useState, useMemo, useEffect } from 'react'; import { Helmet } from 'react-helmet'; import { useNavigate } from 'react-router-dom'; import { Plus, Eye, Mail, Users, Building2, UserCheck, UserX, X } from 'lucide-react'; import DataTable from '@/components/DataTable'; import KPIBar, { PeriodType } from '@/components/KPIBar'; import { toast } from '@/components/ui/use-toast'; import { CompanyInfo } from '@/data/mockData'; import { useAppDispatch, useAppSelector } from '@/store/hooks'; import { getClients } from '@/store/features/client/thunk'; import { clientStatus, getAllClients } from '@/store/features/client/selectors'; import { Client, Contacts } from '@/types/clientType'; import { selectClient } from '@/store/features/client/slice'; import PrimaryButton_v2 from '@/components/PrimaryButton_v2'; import StatusBadgetLettre from '@/components/StatusBadgetLettre'; import ColumnSelector, { ColumnConfig } from '@/components/common/ColumnSelector'; import { Commercial } from '@/types/commercialType'; import { cn } from '@/lib/utils'; import { useDashboardData } from '@/store/hooks/useAppData'; // ============================================ // TYPES // ============================================ type FilterType = 'all' | 'active' | 'with_contacts' | 'inactive'; interface KPIConfig { id: FilterType; title: string; icon: React.ElementType; color: string; getValue: (clients: Client[]) => number; getSubtitle: (clients: Client[], value: number) => string; getChange: (clients: Client[], value: number) => string; getTrend: (value: number) => 'up' | 'down' | 'neutral'; filter: (clients: Client[]) => Client[]; } // ============================================ // CONFIGURATION DES COLONNES // ============================================ const DEFAULT_COLUMNS: ColumnConfig[] = [ { key: 'numero', label: 'Numéro', visible: true, locked: true }, { key: 'intitule', label: 'Nom', visible: true }, { key: 'email', label: 'Email', visible: true }, { key: 'telephone', label: 'Téléphone', visible: true }, { key: 'contacts', label: 'Contacts', visible: true }, { key: 'commercial', label: 'Commercial', visible: true }, { key: 'est_actif', label: 'Statut', visible: true }, ]; // ============================================ // CONFIGURATION DES KPIs // ============================================ const KPI_CONFIG: KPIConfig[] = [ { id: 'all', title: 'Total Clients', icon: Users, color: 'blue', getValue: (clients) => clients.length, getSubtitle: (clients) => { const active = clients.filter(c => c.est_actif !== false).length; return `${active} actif${active > 1 ? 's' : ''}`; }, getChange: () => '', getTrend: () => 'neutral', filter: (clients) => clients, }, { id: 'active', title: 'Clients Actifs', icon: UserCheck, color: 'green', getValue: (clients) => clients.filter(c => c.est_actif !== false).length, getSubtitle: () => 'du portefeuille', getChange: (clients, value) => { const total = clients.length; return total > 0 ? `${((value / total) * 100).toFixed(1)}%` : '0%'; }, getTrend: () => 'up', filter: (clients) => clients.filter(c => c.est_actif !== false), }, { id: 'with_contacts', title: 'Avec Contacts', icon: Building2, color: 'orange', getValue: (clients) => clients.filter(c => c.contacts && c.contacts.length > 0).length, getSubtitle: () => 'ont des contacts', getChange: (clients, value) => { const total = clients.length; return total > 0 ? `${((value / total) * 100).toFixed(0)}%` : '0%'; }, getTrend: () => 'neutral', filter: (clients) => clients.filter(c => c.contacts && c.contacts.length > 0), }, { id: 'inactive', title: 'Inactifs', icon: UserX, color: 'red', getValue: (clients) => clients.filter(c => c.est_actif === false).length, getSubtitle: (clients, value) => { const total = clients.length; return `${total > 0 ? ((value / total) * 100).toFixed(0) : 0}% du total`; }, getChange: () => '', getTrend: (value) => (value > 0 ? 'down' : 'up'), filter: (clients) => clients.filter(c => c.est_actif === false), }, ]; // ============================================ // COMPOSANT PRINCIPAL // ============================================ const ClientsPage = () => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const clients = useAppSelector(getAllClients) as Client[]; const statusClient = useAppSelector(clientStatus); const [period, setPeriod] = useState('all'); const { refresh } = useDashboardData(); // État du filtre actif const [activeFilter, setActiveFilter] = useState('all'); // État des colonnes visibles const [columnConfig, setColumnConfig] = useState(DEFAULT_COLUMNS); const isLoading = statusClient === 'loading' && clients.length === 0; useEffect(() => { const load = async () => { if (statusClient === 'idle' || statusClient === 'failed') { await dispatch(getClients()).unwrap(); } }; load(); }, [statusClient, dispatch]); // Générer les KPIs avec onClick const kpis = useMemo(() => { return KPI_CONFIG.map(config => { const value = config.getValue(clients); return { id: config.id, title: config.title, value, change: config.getChange(clients, value), trend: config.getTrend(value), icon: config.icon, subtitle: config.getSubtitle(clients, value), color: config.color, isActive: activeFilter === config.id, onClick: () => { // Toggle: si déjà actif, revenir à "all" setActiveFilter(prev => (prev === config.id ? 'all' : config.id)); }, }; }); }, [clients, activeFilter]); // Filtrer les clients selon le filtre actif const filteredClients = useMemo(() => { const config = KPI_CONFIG.find(k => k.id === activeFilter); if (!config) return clients; return config.filter(clients); }, [clients, activeFilter]); // Tri par intitulé (alphabétique) const sortedClients = useMemo(() => { return [...filteredClients].sort((a, b) => (a.intitule || '').localeCompare(b.intitule || '') ); }, [filteredClients]); // Label du filtre actif const activeFilterLabel = useMemo(() => { const config = KPI_CONFIG.find(k => k.id === activeFilter); return config?.title || 'Tous'; }, [activeFilter]); const handleCreate = () => { navigate('/home/clients/create'); }; const handleView = (row: Client) => { dispatch(selectClient(row)); navigate(`/home/clients/${row.numero}`); }; // ============================================ // COLONNES DYNAMIQUES // ============================================ const allColumnsDefinition = useMemo(() => { return { numero: { key: 'numero', label: 'Numéro', sortable: true }, intitule: { key: 'intitule', label: 'Nom', sortable: true }, email: { key: 'email', label: 'Email', sortable: true }, telephone: { key: 'telephone', label: 'Téléphone', sortable: true, render: (value: any) => {value}, }, contacts: { key: 'contacts', label: 'Contacts', sortable: false, render: (row: Contacts[]) => { const count = row?.length || 0; const defaultContact = row?.find(c => c.est_defaut); return (
{count} contact{count !== 1 ? 's' : ''} {defaultContact && ( {defaultContact.nom} {defaultContact.prenom} (défaut) )}
); }, }, est_actif: { key: 'est_actif', label: 'Statut', sortable: true, render: (isActive: any) => ( ), }, commercial: { key: 'commercial', label: 'Commercial', sortable: true, render: (value?: Commercial) => ( {value?.prenom && value?.nom ? `${value.prenom} ${value.nom}` : '_'} ), }, }; }, []); const visibleColumns = useMemo(() => { return columnConfig .filter(col => col.visible) .map(col => allColumnsDefinition[col.key as keyof typeof allColumnsDefinition]) .filter(Boolean); }, [columnConfig, allColumnsDefinition]); const actions = (row: Client) => ( <> ); return ( <> Clients - {CompanyInfo.name}

Clients

{/* Badge filtre actif */} {activeFilter !== 'all' && ( )}

{activeFilter === 'all' ? ( <> {clients.filter(c => c.est_actif !== false).length} clients actifs sur{' '} {clients.length} ) : ( <> {filteredClients.length} client{filteredClients.length > 1 ? 's' : ''}{' '} ({activeFilterLabel.toLowerCase()}) )}

Nouveau client
handleView(row)} actions={actions} status={isLoading} />
); }; export default ClientsPage; // import React, { useState, useMemo, useEffect } from 'react'; // import { Helmet } from 'react-helmet'; // import { useNavigate } from 'react-router-dom'; // import { Plus, Eye, Mail, Users, Building2, UserCheck, UserX, UserPlus } from 'lucide-react'; // import DataTable from '@/components/DataTable'; // import KPIBar, { PeriodType } from '@/components/KPIBar'; // import { toast } from '@/components/ui/use-toast'; // import { CompanyInfo } from '@/data/mockData'; // import { useAppDispatch, useAppSelector } from '@/store/hooks'; // import { getClients } from '@/store/features/client/thunk'; // import { clientStatus, getAllClients } from '@/store/features/client/selectors'; // import { Client, Contacts } from '@/types/clientType'; // import { selectClient } from '@/store/features/client/slice'; // import PrimaryButton_v2 from '@/components/PrimaryButton_v2'; // import StatusBadgetLettre from '@/components/StatusBadgetLettre'; // import ColumnSelector, { ColumnConfig } from '@/components/common/ColumnSelector'; // import { Commercial } from '@/types/commercialType'; // // ============================================ // // CONFIGURATION DES COLONNES // // ============================================ // const DEFAULT_COLUMNS: ColumnConfig[] = [ // { key: 'numero', label: 'Numéro', visible: true, locked: true }, // { key: 'intitule', label: 'Nom', visible: true }, // { key: 'email', label: 'Email', visible: true }, // { key: 'telephone', label: 'Téléphone', visible: true }, // { key: 'contacts', label: 'Contacts', visible: true }, // { key: 'commercial', label: 'Commercial', visible: true }, // { key: 'est_actif', label: 'Statut', visible: true }, // ]; // const ClientsPage = () => { // const navigate = useNavigate(); // const dispatch = useAppDispatch(); // const clients = useAppSelector(getAllClients) as Client[]; // const statusClient = useAppSelector(clientStatus); // const [period, setPeriod] = useState('all'); // // État des colonnes visibles // const [columnConfig, setColumnConfig] = useState(DEFAULT_COLUMNS); // const handleRefresh = async () => { // await dispatch(getClients()).unwrap(); // }; // const isLoading = statusClient === 'loading' && clients.length === 0; // useEffect(() => { // const load = async () => { // if (statusClient === 'idle' || statusClient === 'failed') { // await dispatch(getClients()).unwrap(); // } // }; // load(); // }, [statusClient, dispatch]); // // Tri par intitulé (alphabétique) // const sortedClients = useMemo(() => { // return [...clients].sort((a, b) => (a.intitule || '').localeCompare(b.intitule || '')); // }, [clients]); // const kpis = useMemo(() => { // const totalClients = clients.length; // const activeClients = clients.filter(c => c.est_actif !== false); // const inactiveClients = clients.filter(c => c.est_actif === false); // const prospects = clients.filter(c => c.est_prospect === true); // const withContacts = clients.filter(c => c.contacts && c.contacts.length > 0); // const activityRate = totalClients > 0 ? ((activeClients.length / totalClients) * 100).toFixed(1) : '0'; // const contactRate = totalClients > 0 ? ((withContacts.length / totalClients) * 100).toFixed(0) : '0'; // return [ // { // title: 'Total Clients', // value: totalClients, // change: '', // trend: 'neutral', // icon: Users, // subtitle: `${activeClients.length} actif${activeClients.length > 1 ? 's' : ''}`, // color: 'blue', // }, // { // title: 'Clients Actifs', // value: activeClients.length, // change: `${activityRate}%`, // trend: 'up', // icon: UserCheck, // subtitle: 'du portefeuille', // color: 'green', // }, // { // title: 'Avec Contacts', // value: withContacts.length, // change: `${contactRate}%`, // trend: 'neutral', // icon: Building2, // subtitle: 'ont des contacts', // color: 'orange', // }, // { // title: 'Inactifs', // value: inactiveClients.length, // change: '', // trend: inactiveClients.length > 0 ? 'down' : 'up', // icon: UserX, // subtitle: `${totalClients > 0 ? ((inactiveClients.length / totalClients) * 100).toFixed(0) : 0}% du total`, // color: 'red', // }, // ]; // }, [clients]); // const handleCreate = () => { // navigate('/home/clients/create'); // }; // const handleView = (row: Client) => { // dispatch(selectClient(row)); // navigate(`/home/clients/${row.numero}`); // }; // // ============================================ // // COLONNES DYNAMIQUES // // ============================================ // const allColumnsDefinition = useMemo(() => { // return { // numero: { key: 'numero', label: 'Numéro', sortable: true }, // intitule: { key: 'intitule', label: 'Nom', sortable: true }, // email: { key: 'email', label: 'Email', sortable: true }, // telephone: { key: 'telephone', label: 'Téléphone', sortable: true, render: (value: any) => + {value} , }, // contacts: { // key: 'contacts', // label: 'Contacts', // sortable: false, // render: (row: Contacts[]) => { // const count = row?.length || 0; // const defaultContact = row.find(c => c.est_defaut); // return ( //
// // {count} contact{count !== 1 ? 's' : ''} // // {defaultContact && ( // // {defaultContact.nom} {defaultContact.prenom} (défaut) // // )} //
// ); // }, // }, // est_actif: { // key: 'est_actif', // label: 'Statut', // sortable: true, // render: (isActive: any) => , // }, // commercial: { // key: 'commercial', // label: 'Commercial', // sortable: true, // render: (value?: Commercial) => ( // {value?.prenom && value?.nom ? `${value.prenom} ${value.nom}` : '_'} // ) // }, // }; // }, []); // const visibleColumns = useMemo(() => { // return columnConfig // .filter(col => col.visible) // .map(col => allColumnsDefinition[col.key as keyof typeof allColumnsDefinition]) // .filter(Boolean); // }, [columnConfig, allColumnsDefinition]); // const actions = (row: Client) => ( // <> // // // // ); // return ( // <> // // Clients - {CompanyInfo.name} // // //
// //
//
//

Clients

//

// {clients.filter(c => c.est_actif !== false).length} clients actifs sur {clients.length} //

//
//
// // // Nouveau client // //
//
// handleView(row)} // actions={actions} // status={isLoading} // /> //
// // ); // }; // export default ClientsPage;