From 13a4b3cd8a3912e7cef1fd2121f92d166910e908 Mon Sep 17 00:00:00 2001 From: mickael Date: Tue, 20 Jan 2026 11:06:26 +0300 Subject: [PATCH] add pages --- src/pages/DashboardPage.tsx | 442 +++++++ src/pages/DocumentsPage.jsx | 25 + src/pages/SageBuilderPage.tsx | 91 ++ src/pages/UIKitPage.jsx | 154 +++ src/pages/UserProfilePage.jsx | 112 ++ src/pages/admin/ActivityLogPage.jsx | 22 + src/pages/admin/RolesPage.jsx | 22 + src/pages/admin/SettingsPage.jsx | 22 + src/pages/admin/UsersPage.jsx | 38 + src/pages/auth/Login.tsx | 272 ++++ src/pages/auth/forgot.tsx | 203 +++ src/pages/auth/reset.tsx | 319 +++++ src/pages/crm/ActivitiesPage.jsx | 46 + src/pages/crm/ArticlesPage.jsx | 25 + src/pages/crm/ClientDetailPage.tsx | 585 +++++++++ src/pages/crm/ClientsPage.tsx | 613 +++++++++ src/pages/crm/CreateClientPage.tsx | 1001 +++++++++++++++ src/pages/crm/CreateProspectPage.jsx | 652 ++++++++++ src/pages/crm/OpportunitiesPage.jsx | 115 ++ src/pages/crm/OpportunitiesPipelinePage.jsx | 133 ++ src/pages/crm/OpportunityDetailPage.jsx | 213 ++++ src/pages/crm/PipelinePage.jsx | 81 ++ src/pages/crm/ProspectDetailPage.jsx | 274 ++++ src/pages/crm/ProspectsPage.jsx | 156 +++ src/pages/crm/SuppliersDetailPage.tsx | 535 ++++++++ src/pages/crm/SuppliersPage.tsx | 294 +++++ src/pages/crm/TasksPage.jsx | 43 + .../purchases/PurchaseInvoiceDetailPage.jsx | 74 ++ src/pages/purchases/PurchaseInvoicesPage.jsx | 134 ++ .../purchases/PurchaseOrderDetailPage.jsx | 140 +++ src/pages/purchases/PurchaseOrdersPage.jsx | 154 +++ .../purchases/ReceptionNoteDetailPage.jsx | 38 + src/pages/purchases/ReceptionNotesPage.jsx | 129 ++ src/pages/sales/CreditNotesDetailPage.tsx | 829 +++++++++++++ src/pages/sales/CreditNotesPage.tsx | 548 ++++++++ src/pages/sales/DeliveryNotesDetailPage.tsx | 1103 +++++++++++++++++ src/pages/sales/DeliveryNotesPage.tsx | 736 +++++++++++ src/pages/sales/InvoiceCreatePage.tsx | 774 ++++++++++++ src/pages/sales/InvoiceDetailPage.tsx | 530 ++++++++ src/pages/sales/InvoicesPage.tsx | 954 ++++++++++++++ src/pages/sales/OrderDetailPage.tsx | 875 +++++++++++++ src/pages/sales/OrdersPage.tsx | 557 +++++++++ src/pages/sales/PaymentDetailPage.jsx | 514 ++++++++ src/pages/sales/PaymentsPage.tsx | 575 +++++++++ src/pages/sales/QuoteCreate.tsx | 773 ++++++++++++ src/pages/sales/QuoteDetailPage.tsx | 732 +++++++++++ src/pages/sales/QuotesPage.tsx | 698 +++++++++++ .../signature/SignatureCreditPurchase.jsx | 169 +++ src/pages/signature/SignatureDashboard.jsx | 109 ++ src/pages/signature/SignatureSettings.jsx | 122 ++ src/pages/signature/SignatureTracking.tsx | 490 ++++++++ src/pages/support/SupportDashboardPage.jsx | 69 ++ src/pages/support/TicketDetailPage.jsx | 198 +++ src/pages/support/TicketsPage.jsx | 86 ++ src/pages/tiers/ArticleDetailPage.tsx | 465 +++++++ src/pages/tiers/ArticlesPage.tsx | 315 +++++ src/pages/tiers/CommercialPage.tsx | 379 ++++++ src/pages/tiers/ProductFamiliesPage.tsx | 228 ++++ src/pages/tiers/ProductFamilyDetailPage.tsx | 195 +++ 59 files changed, 20180 insertions(+) create mode 100644 src/pages/DashboardPage.tsx create mode 100644 src/pages/DocumentsPage.jsx create mode 100644 src/pages/SageBuilderPage.tsx create mode 100644 src/pages/UIKitPage.jsx create mode 100644 src/pages/UserProfilePage.jsx create mode 100644 src/pages/admin/ActivityLogPage.jsx create mode 100644 src/pages/admin/RolesPage.jsx create mode 100644 src/pages/admin/SettingsPage.jsx create mode 100644 src/pages/admin/UsersPage.jsx create mode 100644 src/pages/auth/Login.tsx create mode 100644 src/pages/auth/forgot.tsx create mode 100644 src/pages/auth/reset.tsx create mode 100644 src/pages/crm/ActivitiesPage.jsx create mode 100644 src/pages/crm/ArticlesPage.jsx create mode 100644 src/pages/crm/ClientDetailPage.tsx create mode 100644 src/pages/crm/ClientsPage.tsx create mode 100644 src/pages/crm/CreateClientPage.tsx create mode 100644 src/pages/crm/CreateProspectPage.jsx create mode 100644 src/pages/crm/OpportunitiesPage.jsx create mode 100644 src/pages/crm/OpportunitiesPipelinePage.jsx create mode 100644 src/pages/crm/OpportunityDetailPage.jsx create mode 100644 src/pages/crm/PipelinePage.jsx create mode 100644 src/pages/crm/ProspectDetailPage.jsx create mode 100644 src/pages/crm/ProspectsPage.jsx create mode 100644 src/pages/crm/SuppliersDetailPage.tsx create mode 100644 src/pages/crm/SuppliersPage.tsx create mode 100644 src/pages/crm/TasksPage.jsx create mode 100644 src/pages/purchases/PurchaseInvoiceDetailPage.jsx create mode 100644 src/pages/purchases/PurchaseInvoicesPage.jsx create mode 100644 src/pages/purchases/PurchaseOrderDetailPage.jsx create mode 100644 src/pages/purchases/PurchaseOrdersPage.jsx create mode 100644 src/pages/purchases/ReceptionNoteDetailPage.jsx create mode 100644 src/pages/purchases/ReceptionNotesPage.jsx create mode 100644 src/pages/sales/CreditNotesDetailPage.tsx create mode 100644 src/pages/sales/CreditNotesPage.tsx create mode 100644 src/pages/sales/DeliveryNotesDetailPage.tsx create mode 100644 src/pages/sales/DeliveryNotesPage.tsx create mode 100644 src/pages/sales/InvoiceCreatePage.tsx create mode 100644 src/pages/sales/InvoiceDetailPage.tsx create mode 100644 src/pages/sales/InvoicesPage.tsx create mode 100644 src/pages/sales/OrderDetailPage.tsx create mode 100644 src/pages/sales/OrdersPage.tsx create mode 100644 src/pages/sales/PaymentDetailPage.jsx create mode 100644 src/pages/sales/PaymentsPage.tsx create mode 100644 src/pages/sales/QuoteCreate.tsx create mode 100644 src/pages/sales/QuoteDetailPage.tsx create mode 100644 src/pages/sales/QuotesPage.tsx create mode 100644 src/pages/signature/SignatureCreditPurchase.jsx create mode 100644 src/pages/signature/SignatureDashboard.jsx create mode 100644 src/pages/signature/SignatureSettings.jsx create mode 100644 src/pages/signature/SignatureTracking.tsx create mode 100644 src/pages/support/SupportDashboardPage.jsx create mode 100644 src/pages/support/TicketDetailPage.jsx create mode 100644 src/pages/support/TicketsPage.jsx create mode 100644 src/pages/tiers/ArticleDetailPage.tsx create mode 100644 src/pages/tiers/ArticlesPage.tsx create mode 100644 src/pages/tiers/CommercialPage.tsx create mode 100644 src/pages/tiers/ProductFamiliesPage.tsx create mode 100644 src/pages/tiers/ProductFamilyDetailPage.tsx diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx new file mode 100644 index 0000000..ef64497 --- /dev/null +++ b/src/pages/DashboardPage.tsx @@ -0,0 +1,442 @@ + +import React, { useState, useMemo, useEffect } from 'react'; +import { Helmet } from 'react-helmet'; +import { motion } from 'framer-motion'; +import { useNavigate } from 'react-router-dom'; +import { TrendingUp, TrendingDown, Euro, FileText, ShoppingCart, Receipt, Target, Activity, Users, CreditCard, Clock, RotateCcw } from 'lucide-react'; +import SegmentedControl from '@/components/SegmentedControl'; +import KPIBar, { PeriodType } from '@/components/KPIBar'; +import ChartCard from '@/components/ChartCard'; +import { mockStats, mockQuotes, mockInvoices, mockClients, calculateKPIs, CompanyInfo } from '@/data/mockData'; +import { useAppDispatch, useAppSelector } from '@/store/hooks'; +import { factureStatus, getAllfacture } from '@/store/features/factures/selectors'; +import { Facture } from '@/types/factureType'; +import { getFactures } from '@/store/features/factures/thunk'; +import { filterItemByPeriod, getPreviousPeriodItems } from '@/components/filter/ItemsFilter'; +import { devisStatus, getAllDevis } from '@/store/features/devis/selectors'; +import { DevisListItem } from '@/types/devisType'; +import { getDevisList } from '@/store/features/devis/thunk'; +import { clientStatus, getAllClients } from '@/store/features/client/selectors'; +import { Client } from '@/types/clientType'; +import { commandeStatus, getAllcommandes } from '@/store/features/commande/selectors'; +import { Commande } from '@/types/commandeTypes'; +import { getClients } from '@/store/features/client/thunk'; +import { getCommandes } from '@/store/features/commande/thunk'; +import { ChartByStatusChart, ChartDataven, TopClientsChart } from '@/components/chart/Chart'; +import PeriodSelector from '@/components/common/PeriodSelector'; +import { getArticles } from '@/store/features/article/thunk'; +import { articleStatus } from '@/store/features/article/selectors'; +import { avoirStatus, getAllavoir } from '@/store/features/avoir/selectors'; +import { Avoir } from '@/types/avoirType'; +import { getAvoirs } from '@/store/features/avoir/thunk'; +import { gatewaysStatus } from '@/store/features/gateways/selectors'; +import { getGateways } from '@/store/features/gateways/thunk'; +import { commercialsStatus } from '@/store/features/commercial/selectors'; +import { getCommercials } from '@/store/features/commercial/thunk'; +import { universignStatus } from '@/store/features/universign/selectors'; +import { getUniversigns } from '@/store/features/universign/thunk'; +import { useDashboardData } from '@/store/hooks/useAppData'; +import { getuserConnected } from '@/store/features/user/selectors'; + +const DashboardPage = () => { + const [activeSegment, setActiveSegment] = useState('ventes'); + const [period, setPeriod] = useState('all'); + const navigate = useNavigate(); + const dispatch = useAppDispatch() + + const segments = [ + { id: 'ventes', label: 'Ventes & CRM' }, + // { id: 'achats', label: 'Achats' }, + // { id: 'sav', label: 'SAV & Qualité' }, + ]; + + // ✅ Récupération des données + + const statusCommercial = useAppSelector(commercialsStatus) ; + + const statusUniversign = useAppSelector(universignStatus) ; + + const factures = useAppSelector(getAllfacture) as Facture[]; + const statusFacture = useAppSelector(factureStatus); + + const devis = useAppSelector(getAllDevis) as DevisListItem[]; + const statusDevis = useAppSelector(devisStatus); + + const clients = useAppSelector(getAllClients) as Client[]; + const statusClient = useAppSelector(clientStatus); + + const commandes = useAppSelector(getAllcommandes) as Commande[]; + const statusCommande = useAppSelector(commandeStatus); + + const avoirs = useAppSelector(getAllavoir) as Avoir[]; + const statusAvoir = useAppSelector(avoirStatus); + + const statusArticle = useAppSelector(articleStatus) + + const userConnected = useAppSelector(getuserConnected); + + const { refresh } = useDashboardData(); + + + + // ✅ Charger les données au montage + useEffect(() => { + const load = async () => { + if (statusDevis === "idle") await dispatch(getDevisList()).unwrap(); + if (statusCommercial === "idle") await dispatch(getCommercials()).unwrap(); + if (statusUniversign === "idle") await dispatch(getUniversigns()).unwrap(); + if (statusArticle === "idle") await dispatch(getArticles()).unwrap(); + if (statusFacture === "idle") await dispatch(getFactures()).unwrap(); + if (statusClient === "idle") await dispatch(getClients()).unwrap(); + if (statusCommande === "idle") await dispatch(getCommandes()).unwrap(); + if (statusAvoir === "idle") await dispatch(getAvoirs()).unwrap(); + }; + load(); + }, [statusFacture, statusDevis, statusArticle, statusAvoir, statusCommercial, statusUniversign, dispatch]); + + // ✅ Filtrer par période + const filteredFactures = useMemo(() => { + return filterItemByPeriod(factures, period); + }, [factures, period]); + + const filteredDevis = useMemo(() => { + return filterItemByPeriod(devis, period); + }, [devis, period]); + + const filteredCommandes = useMemo(() => { + return filterItemByPeriod(commandes, period); + }, [commandes, period]); + + const filteredAvoir = useMemo(() => { + return filterItemByPeriod(avoirs, period); + }, [avoirs, period]); + + + + const isLoadingFacture = useMemo(() => { + return statusFacture === 'loading' || statusFacture === 'idle' + }, [statusFacture]); + + const isLoadingDevis = useMemo(() => { + return statusDevis === 'loading' || statusDevis === 'idle' + }, [statusDevis]); + + const isLoadingClient = useMemo(() => { + return statusClient === 'loading' || statusClient === 'idle' + }, [statusClient]); + + const isLoadingCommande = useMemo(() => { + return statusCommande === 'loading' || statusCommande === 'idle' + }, [statusCommande]); + + const isLoadingAvoir = useMemo(() => { + return statusAvoir === 'loading' || statusAvoir === 'idle' + }, [statusAvoir]); + + // ✅ Calculer les KPIs + const kpis = useMemo(() => { + if (activeSegment === 'ventes') { + const caFacture = Math.round( + filteredFactures + .filter(f => f.statut === 2) + .reduce((sum, f) => sum + f.total_ht, 0) + ); + + + const previousFactures = getPreviousPeriodItems(factures, period); + const previousCAFacture = previousFactures + .filter(f => f.statut === 2) + .reduce((sum, f) => sum + f.total_ht, 0); + + const caChange = previousCAFacture > 0 + ? ((caFacture - previousCAFacture) / previousCAFacture * 100).toFixed(1) + : '0'; + const caTrend = caFacture >= previousCAFacture ? 'up' : 'down'; + + // ✅ PIPELINE (Devis en attente ou acceptés) + const pipeline = Math.round( + filteredDevis + .filter(d => d.statut === 1 || d.statut === 2) // En attente ou Acceptés + .reduce((sum, d) => sum + d.total_ht, 0)) + + const previousDevis = getPreviousPeriodItems(devis, period); + const previousPipeline = previousDevis + .filter(d => d.statut === 1 || d.statut === 2) + .reduce((sum, d) => sum + d.total_ht, 0); + + const pipelineChange = previousPipeline > 0 + ? ((pipeline - previousPipeline) / previousPipeline * 100).toFixed(1) + : '0'; + const pipelineTrend = pipeline >= previousPipeline ? 'up' : 'down'; + + const nouveauxClients = clients.length; + const clientsChange = '+0'; + const clientsTrend = 'neutral'; + + const nbCommandes = filteredCommandes.filter(c => c.statut === 2).length; + const commandesChange = '+0'; + const commandesTrend = 'neutral'; + + const nbAvoirs = filteredAvoir.filter(c => c.statut === 2).length; + + return [ + { + title: 'CA Facturé', + value: isLoadingFacture ? '...' : `${caFacture.toLocaleString('fr-FR')}€`, + change: isLoadingFacture ? '' : `${caTrend === 'up' ? '+' : ''}${caChange}%`, + trend: caTrend, + icon: Euro, + onClick: () => navigate('/home/factures'), + loading: isLoadingFacture, + tooltip: { + content: "Montant total des factures validées sur la période.", + calculation: "Σ(Factures HT) - Σ(Avoirs HT)", + source: "Module Ventes > Factures" + } + }, + { + title: 'Pipeline', + value: isLoadingDevis ? '...' : `${pipeline.toLocaleString('fr-FR')}€`, + change: isLoadingDevis ? '' : `${pipelineTrend === 'up' ? '+' : ''}${pipelineChange}%`, + trend: pipelineTrend, + icon: Target, + onClick: () => navigate('/home/devis'), + loading: isLoadingDevis, + tooltip: { + content: "Montant total des devis en attente et acceptés", + calculation: "Σ(Montant HT des devis en attente ou acceptés)", + source: "CRM > Devis" + } + }, + { + title: 'Clients', + value: isLoadingClient ? '...' : nouveauxClients, + change: isLoadingClient ? '' : clientsChange, + trend: clientsTrend, + icon: Users, + onClick: () => navigate('/home/clients'), + loading: isLoadingClient, + tooltip: { + content: "Nombre de nouveaux comptes clients créés.", + source: "CRM > Clients" + } + }, + { + title: 'Commandes', + value: isLoadingCommande ? '...' : nbCommandes, + change: isLoadingCommande ? '' : commandesChange, + trend: commandesTrend, + icon: ShoppingCart, + onClick: () => navigate('/home/commandes'), + loading: isLoadingCommande, + tooltip: { + content: "Nombre de commandes validées non livrées.", + source: "Ventes > Commandes" + } + }, + { + title: 'Avoir', + value: isLoadingAvoir ? '...' : nbAvoirs, + change: isLoadingAvoir ? '' : commandesChange, + trend: commandesTrend, + icon: RotateCcw, + onClick: () => navigate('/home/avoirs'), + loading: isLoadingAvoir, + tooltip: { + content: "Nombre d'avoir validées non validés.", + source: "Ventes > Avoirs" + } + }, + ]; + } else if (activeSegment === 'achats') { + // TODO: Implémenter les KPIs achats + return [ + { + title: 'Dépenses', + value: '...', + change: '', + trend: 'neutral', + icon: Euro, + loading: true, + }, + { + title: 'Commandes Four.', + value: '...', + change: '', + trend: 'neutral', + icon: ShoppingCart, + loading: true, + }, + { + title: 'Factures à payer', + value: '...', + change: '', + trend: 'neutral', + icon: Receipt, + loading: true, + }, + ]; + } else { + // TODO: Implémenter les KPIs support + return [ + { + title: 'Tickets Ouverts', + value: '...', + change: '', + trend: 'neutral', + icon: Activity, + onClick: () => navigate('/home/tickets'), + loading: true, + }, + { + title: 'SLA Global', + value: '...', + change: '', + trend: 'neutral', + icon: TrendingUp, + loading: true, + }, + { + title: 'Satisfaction', + value: '...', + change: '', + trend: 'neutral', + icon: Users, + loading: true, + }, + { + title: 'Temps Moyen', + value: '...', + change: '', + trend: 'neutral', + icon: Clock, + loading: true, + }, + ]; + } + }, [ + activeSegment, + period, + navigate, + isLoadingCommande, + isLoadingClient, + isLoadingDevis, + isLoadingFacture, + filteredFactures, + filteredDevis, + factures, + devis, + ]); + + return ( + <> + + Tableau de bord - {CompanyInfo.name} + + + + {/*
+
+

Vue d'ensemble

+

Bienvenue, Jean Dupont

+
+ +
*/} +
+
+

Vue d'ensemble

+

Bienvenue, {userConnected?.nom} {userConnected?.prenom}

+
+
+ + +
+
+ + + + + {activeSegment === 'ventes' && ( +
+ + + + {/* */} + {/* */} +
+ )} + + {activeSegment === 'achats' && ( +
+ {/* + */} +
+ )} + + {activeSegment === 'sav' && ( +
+ {/* + */} +
+ )} +
+
+ + ); +}; + +export default DashboardPage; diff --git a/src/pages/DocumentsPage.jsx b/src/pages/DocumentsPage.jsx new file mode 100644 index 0000000..8bbbce5 --- /dev/null +++ b/src/pages/DocumentsPage.jsx @@ -0,0 +1,25 @@ + +import React from 'react'; +import { Helmet } from 'react-helmet'; + + +const DocumentsPage = () => { + return ( + <> + + Documents - Bijou ERP + + +
+
+

GED - Documents

+
+
+

Page Documents - Contenu à venir

+
+
+ + ); +}; + +export default DocumentsPage; diff --git a/src/pages/SageBuilderPage.tsx b/src/pages/SageBuilderPage.tsx new file mode 100644 index 0000000..f9dd9c1 --- /dev/null +++ b/src/pages/SageBuilderPage.tsx @@ -0,0 +1,91 @@ +import { ModalLoading } from '@/components/modal/ModalLoading'; +import { getSageBuilderHtmlContent, sageBuilderError, sageBuilderStatus } from '@/store/features/sage-builder/selectors'; +import { getSageBuilderDashboard } from '@/store/features/sage-builder/thunk'; +import { useAppDispatch, useAppSelector } from '@/store/hooks'; +import React, { useEffect, useRef } from 'react'; + +const SageBuilderPage = () => { + const dispatch = useAppDispatch(); + const iframeRef = useRef(null); + + const htmlContent = useAppSelector(getSageBuilderHtmlContent); + const status = useAppSelector(sageBuilderStatus); + const error = useAppSelector(sageBuilderError); + + useEffect(() => { + // Charger le dashboard au montage du composant + if (status === 'idle') { + dispatch(getSageBuilderDashboard()); + } + }, [dispatch, status]); + + useEffect(() => { + // Injecter le HTML dans l'iframe quand il est disponible + if (htmlContent && iframeRef.current && status === 'succeeded') { + const iframeDoc = iframeRef.current.contentDocument || iframeRef.current.contentWindow?.document; + if (iframeDoc) { + iframeDoc.open(); + iframeDoc.write(htmlContent); + iframeDoc.close(); + } + } + }, [htmlContent, status]); + + const handleRetry = () => { + dispatch(getSageBuilderDashboard()); + }; + + return ( +
+ {/* Loading Overlay */} + {status === 'loading' && } + + {/* Error State */} + {status === 'failed' && error && ( +
+
+
+ + + +
+

+ Erreur de chargement +

+

+ {error} +

+ +
+
+ )} + + {/* Iframe */} +