1699 lines
72 KiB
TypeScript
1699 lines
72 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState, useRef, useEffect } from 'react';
|
||
import { useAuth } from '../../../../contexts/AuthContext';
|
||
import ProtectedRoute from '../../../../components/auth/ProtectedRoute';
|
||
import { Card } from 'primereact/card';
|
||
import { Button } from 'primereact/button';
|
||
import { InputText } from 'primereact/inputtext';
|
||
import { InputTextarea } from 'primereact/inputtextarea';
|
||
import { InputNumber } from 'primereact/inputnumber';
|
||
import { InputSwitch } from 'primereact/inputswitch';
|
||
import { Dropdown } from 'primereact/dropdown';
|
||
import { Toast } from 'primereact/toast';
|
||
import { TabView, TabPanel } from 'primereact/tabview';
|
||
import { FileUpload } from 'primereact/fileupload';
|
||
import { Divider } from 'primereact/divider';
|
||
import { ColorPicker } from 'primereact/colorpicker';
|
||
import { SelectButton } from 'primereact/selectbutton';
|
||
import { Calendar } from 'primereact/calendar';
|
||
import { Chip } from 'primereact/chip';
|
||
import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog';
|
||
import { Toolbar } from 'primereact/toolbar';
|
||
import { SpeedDial } from 'primereact/speeddial';
|
||
import { Panel } from 'primereact/panel';
|
||
import { Accordion, AccordionTab } from 'primereact/accordion';
|
||
import { Badge } from 'primereact/badge';
|
||
import { Tag } from 'primereact/tag';
|
||
import { Avatar } from 'primereact/avatar';
|
||
import { AvatarGroup } from 'primereact/avatargroup';
|
||
import { Knob } from 'primereact/knob';
|
||
import { ProgressBar } from 'primereact/progressbar';
|
||
import { Skeleton } from 'primereact/skeleton';
|
||
import { Message } from 'primereact/message';
|
||
import { Messages } from 'primereact/messages';
|
||
import { Fieldset } from 'primereact/fieldset';
|
||
import { ScrollPanel } from 'primereact/scrollpanel';
|
||
import { Splitter, SplitterPanel } from 'primereact/splitter';
|
||
import { DataView } from 'primereact/dataview';
|
||
import { Timeline } from 'primereact/timeline';
|
||
import { Steps } from 'primereact/steps';
|
||
import { MenuItem } from 'primereact/menuitem';
|
||
import { Menu } from 'primereact/menu';
|
||
import { TieredMenu } from 'primereact/tieredmenu';
|
||
import { ContextMenu } from 'primereact/contextmenu';
|
||
import { Sidebar } from 'primereact/sidebar';
|
||
import { OverlayPanel } from 'primereact/overlaypanel';
|
||
import { Dialog } from 'primereact/dialog';
|
||
import { Rating } from 'primereact/rating';
|
||
import { Slider } from 'primereact/slider';
|
||
import { ToggleButton } from 'primereact/togglebutton';
|
||
import { MultiSelect } from 'primereact/multiselect';
|
||
import { Checkbox } from 'primereact/checkbox';
|
||
import { RadioButton } from 'primereact/radiobutton';
|
||
import { ListBox } from 'primereact/listbox';
|
||
import { InputMask } from 'primereact/inputmask';
|
||
import { AutoComplete } from 'primereact/autocomplete';
|
||
import { Password } from 'primereact/password';
|
||
import { BlockUI } from 'primereact/blockui';
|
||
import { PickList } from 'primereact/picklist';
|
||
import { OrderList } from 'primereact/orderlist';
|
||
import { DataScroller } from 'primereact/datascroller';
|
||
import { VirtualScroller } from 'primереact/virtualscroller';
|
||
import { Galleria } from 'primereact/galleria';
|
||
import { Image } from 'primereact/image';
|
||
import { Carousel } from 'primereact/carousel';
|
||
import { Chart } from 'primereact/chart';
|
||
import { Terminal } from 'primereact/terminal';
|
||
import { DeferredContent } from 'primereact/deferredcontent';
|
||
import { InlineMessage } from 'primereact/inlinemessage';
|
||
import { ScrollTop } from 'primereact/scrolltop';
|
||
import { CascadeSelect } from 'primereact/cascadeselect';
|
||
import { TreeSelect } from 'primereact/treeselect';
|
||
import { Mention } from 'primereact/mention';
|
||
import { Editor } from 'primereact/editor';
|
||
import { Tree } from 'primereact/tree';
|
||
import { TreeTable } from 'primereact/treetable';
|
||
import { Column } from 'primereact/column';
|
||
|
||
interface CompanySettings {
|
||
nom: string;
|
||
adresse: string;
|
||
telephone: string;
|
||
email: string;
|
||
siteWeb: string;
|
||
numeroTVA: string;
|
||
numeroSiret: string;
|
||
capitalSocial: number;
|
||
formeJuridique: string;
|
||
logo: string;
|
||
}
|
||
|
||
interface InvoiceSettings {
|
||
prefixFacture: string;
|
||
prefixDevis: string;
|
||
prochainNumeroFacture: number;
|
||
prochainNumeroDevis: number;
|
||
tauxTVADefaut: number;
|
||
conditionsPaiement: string;
|
||
mentionsLegales: string;
|
||
delaiPaiementDefaut: number;
|
||
penalitesRetard: number;
|
||
}
|
||
|
||
interface EmailSettings {
|
||
serveurSMTP: string;
|
||
portSMTP: number;
|
||
utilisateurSMTP: string;
|
||
motDePasseSMTP: string;
|
||
expediteurDefaut: string;
|
||
useTLS: boolean;
|
||
emailsNotification: string[];
|
||
}
|
||
|
||
interface SystemSettings {
|
||
langue: string;
|
||
fuseau: string;
|
||
deviseDefaut: string;
|
||
formatDate: string;
|
||
formatHeure: string;
|
||
theme: string;
|
||
couleurPrimaire: string;
|
||
activerNotifications: boolean;
|
||
activerRappels: boolean;
|
||
delaiRappelFacture: number;
|
||
delaiRappelDevis: number;
|
||
}
|
||
|
||
interface BackupSettings {
|
||
sauvegardeAuto: boolean;
|
||
frequenceSauvegarde: string;
|
||
heureSauvegarde: Date;
|
||
retention: number;
|
||
destination: string;
|
||
chiffrementActif: boolean;
|
||
}
|
||
|
||
const ParametresPage = () => {
|
||
const [activeIndex, setActiveIndex] = useState(0);
|
||
const [loading, setLoading] = useState(false);
|
||
const [blocked, setBlocked] = useState(false);
|
||
const [darkMode, setDarkMode] = useState(false);
|
||
const [compactMode, setCompactMode] = useState(false);
|
||
const [expertMode, setExpertMode] = useState(false);
|
||
const [autoSave, setAutoSave] = useState(true);
|
||
const [settingsHistory, setSettingsHistory] = useState([]);
|
||
const [lastSaved, setLastSaved] = useState<Date | null>(null);
|
||
const [unsavedChanges, setUnsavedChanges] = useState(false);
|
||
const [configProfile, setConfigProfile] = useState('standard');
|
||
const [performanceLevel, setPerformanceLevel] = useState(75);
|
||
const [securityScore, setSecurityScore] = useState(85);
|
||
const [systemHealth, setSystemHealth] = useState(92);
|
||
const [backupProgress, setBackupProgress] = useState(0);
|
||
const [exportProgress, setExportProgress] = useState(0);
|
||
const [sidebarVisible, setSidebarVisible] = useState(false);
|
||
const [helpVisible, setHelpVisible] = useState(false);
|
||
const [historyVisible, setHistoryVisible] = useState(false);
|
||
const [configWizard, setConfigWizard] = useState(false);
|
||
const [wizardStep, setWizardStep] = useState(0);
|
||
const [autoRefresh, setAutoRefresh] = useState(true);
|
||
const [refreshInterval, setRefreshInterval] = useState(30);
|
||
const [notifications, setNotifications] = useState([]);
|
||
const [searchTerm, setSearchTerm] = useState('');
|
||
const [filteredSettings, setFilteredSettings] = useState([]);
|
||
const [settingsComparison, setSettingsComparison] = useState(false);
|
||
const [recommendedSettings, setRecommendedSettings] = useState([]);
|
||
const [quickActions, setQuickActions] = useState([]);
|
||
|
||
const toast = useRef<Toast>(null);
|
||
const messages = useRef<Messages>(null);
|
||
const overlayPanel = useRef<OverlayPanel>(null);
|
||
const contextMenu = useRef<ContextMenu>(null);
|
||
const tieredMenu = useRef<TieredMenu>(null);
|
||
const terminal = useRef<Terminal>(null);
|
||
|
||
// TODO: Charger les paramètres de l'entreprise depuis l'API
|
||
// Exemple: useEffect(() => { fetch('/api/admin/company-settings').then(res => res.json()).then(setCompanySettings); }, []);
|
||
const [companySettings, setCompanySettings] = useState<CompanySettings>({
|
||
nom: '',
|
||
adresse: '',
|
||
telephone: '',
|
||
email: '',
|
||
siteWeb: '',
|
||
numeroTVA: '',
|
||
numeroSiret: '',
|
||
capitalSocial: 0,
|
||
formeJuridique: '',
|
||
logo: ''
|
||
});
|
||
|
||
// TODO: Charger les paramètres de facturation depuis l'API
|
||
const [invoiceSettings, setInvoiceSettings] = useState<InvoiceSettings>({
|
||
prefixFacture: '',
|
||
prefixDevis: '',
|
||
prochainNumeroFacture: 0,
|
||
prochainNumeroDevis: 0,
|
||
tauxTVADefaut: 0,
|
||
conditionsPaiement: '',
|
||
mentionsLegales: '',
|
||
delaiPaiementDefaut: 0,
|
||
penalitesRetard: 0
|
||
});
|
||
|
||
// TODO: Charger les paramètres email depuis l'API
|
||
const [emailSettings, setEmailSettings] = useState<EmailSettings>({
|
||
serveurSMTP: '',
|
||
portSMTP: 0,
|
||
utilisateurSMTP: '',
|
||
motDePasseSMTP: '',
|
||
expediteurDefaut: '',
|
||
useTLS: false,
|
||
emailsNotification: []
|
||
});
|
||
|
||
// TODO: Charger les paramètres système depuis l'API
|
||
const [systemSettings, setSystemSettings] = useState<SystemSettings>({
|
||
langue: '',
|
||
fuseau: '',
|
||
deviseDefaut: '',
|
||
formatDate: '',
|
||
formatHeure: '',
|
||
theme: '',
|
||
couleurPrimaire: '',
|
||
activerNotifications: false,
|
||
activerRappels: false,
|
||
delaiRappelFacture: 0,
|
||
delaiRappelDevis: 0
|
||
});
|
||
|
||
// TODO: Charger les paramètres de sauvegarde depuis l'API
|
||
const [backupSettings, setBackupSettings] = useState<BackupSettings>({
|
||
sauvegardeAuto: false,
|
||
frequenceSauvegarde: '',
|
||
heureSauvegarde: new Date(),
|
||
retention: 0,
|
||
destination: '',
|
||
chiffrementActif: false
|
||
});
|
||
|
||
const formesJuridiques = [
|
||
{ label: 'SARL', value: 'SARL' },
|
||
{ label: 'SAS', value: 'SAS' },
|
||
{ label: 'SA', value: 'SA' },
|
||
{ label: 'EURL', value: 'EURL' },
|
||
{ label: 'Auto-entrepreneur', value: 'AUTO' },
|
||
{ label: 'Autre', value: 'AUTRE' }
|
||
];
|
||
|
||
const langues = [
|
||
{ label: 'Français', value: 'fr' },
|
||
{ label: 'English', value: 'en' },
|
||
{ label: 'Español', value: 'es' },
|
||
{ label: 'Deutsch', value: 'de' }
|
||
];
|
||
|
||
const fuseaux = [
|
||
{ label: 'Europe/Paris', value: 'Europe/Paris' },
|
||
{ label: 'Europe/London', value: 'Europe/London' },
|
||
{ label: 'America/New_York', value: 'America/New_York' },
|
||
{ label: 'Asia/Tokyo', value: 'Asia/Tokyo' }
|
||
];
|
||
|
||
const devises = [
|
||
{ label: 'Euro (EUR)', value: 'EUR' },
|
||
{ label: 'Dollar US (USD)', value: 'USD' },
|
||
{ label: 'Livre Sterling (GBP)', value: 'GBP' },
|
||
{ label: 'Franc CFA (XOF)', value: 'XOF' }
|
||
];
|
||
|
||
const themes = [
|
||
{ label: 'Clair', value: 'light', icon: 'sun' },
|
||
{ label: 'Sombre', value: 'dark', icon: 'moon' },
|
||
{ label: 'Auto', value: 'auto', icon: 'adjust' },
|
||
{ label: 'Contraste élevé', value: 'contrast', icon: 'eye' },
|
||
{ label: 'Mode nuit', value: 'night', icon: 'moon' }
|
||
];
|
||
|
||
const configProfiles = [
|
||
{ label: 'Basique', value: 'basic', description: 'Configuration simplifiée' },
|
||
{ label: 'Standard', value: 'standard', description: 'Configuration recommandée' },
|
||
{ label: 'Avancé', value: 'advanced', description: 'Toutes les options' },
|
||
{ label: 'Expert', value: 'expert', description: 'Configuration complète' },
|
||
{ label: 'Personnalisé', value: 'custom', description: 'Configuration sur mesure' }
|
||
];
|
||
|
||
const wizardSteps = [
|
||
{ label: 'Entreprise', icon: 'pi pi-building' },
|
||
{ label: 'Utilisateurs', icon: 'pi pi-users' },
|
||
{ label: 'Sécurité', icon: 'pi pi-shield' },
|
||
{ label: 'Intégrations', icon: 'pi pi-link' },
|
||
{ label: 'Finalisation', icon: 'pi pi-check' }
|
||
];
|
||
|
||
const quickActionsData = [
|
||
{ label: 'Sauvegarde rapide', icon: 'pi pi-save', command: () => quickBackup() },
|
||
{ label: 'Mode maintenance', icon: 'pi pi-wrench', command: () => toggleMaintenance() },
|
||
{ label: 'Réinitialiser cache', icon: 'pi pi-refresh', command: () => clearCache() },
|
||
{ label: 'Export complet', icon: 'pi pi-download', command: () => exportAll() },
|
||
{ label: 'Test connectivité', icon: 'pi pi-wifi', command: () => testConnections() },
|
||
{ label: 'Optimiser DB', icon: 'pi pi-database', command: () => optimizeDatabase() }
|
||
];
|
||
|
||
const performanceMetrics = [
|
||
{ label: 'CPU', value: 45, color: '#10B981' },
|
||
{ label: 'Mémoire', value: 67, color: '#F59E0B' },
|
||
{ label: 'Disque', value: 23, color: '#3B82F6' },
|
||
{ label: 'Réseau', value: 89, color: '#8B5CF6' }
|
||
];
|
||
|
||
const securityAudits = [
|
||
{ date: new Date(), action: 'Connexion admin', status: 'success', ip: '192.168.1.100' },
|
||
{ date: new Date(), action: 'Modification paramètres', status: 'success', ip: '192.168.1.100' },
|
||
{ date: new Date(), action: 'Tentative accès', status: 'warning', ip: '192.168.1.205' },
|
||
{ date: new Date(), action: 'Sauvegarde auto', status: 'success', ip: 'system' }
|
||
];
|
||
|
||
const integrationsList = [
|
||
{ name: 'QuickBooks', status: 'connected', icon: 'pi pi-calculator' },
|
||
{ name: 'Slack', status: 'connected', icon: 'pi pi-slack' },
|
||
{ name: 'Google Drive', status: 'disconnected', icon: 'pi pi-google' },
|
||
{ name: 'Dropbox', status: 'error', icon: 'pi pi-cloud' },
|
||
{ name: 'Microsoft 365', status: 'connected', icon: 'pi pi-microsoft' },
|
||
{ name: 'Zapier', status: 'pending', icon: 'pi pi-bolt' }
|
||
];
|
||
|
||
const frequenceOptions = [
|
||
{ label: 'Horaire', value: 'horaire' },
|
||
{ label: 'Quotidien', value: 'quotidien' },
|
||
{ label: 'Hebdomadaire', value: 'hebdomadaire' },
|
||
{ label: 'Mensuel', value: 'mensuel' }
|
||
];
|
||
|
||
const destinationOptions = [
|
||
{ label: 'Stockage local', value: 'local' },
|
||
{ label: 'Cloud (Google Drive)', value: 'google' },
|
||
{ label: 'Cloud (Dropbox)', value: 'dropbox' },
|
||
{ label: 'Serveur FTP', value: 'ftp' }
|
||
];
|
||
|
||
const saveSettings = (section: string) => {
|
||
setLoading(true);
|
||
|
||
// Simuler la sauvegarde
|
||
setTimeout(() => {
|
||
setLoading(false);
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Succès',
|
||
detail: `Les paramètres ${section} ont été sauvegardés`,
|
||
life: 3000
|
||
});
|
||
}, 1000);
|
||
};
|
||
|
||
const resetSettings = (section: string) => {
|
||
confirmDialog({
|
||
message: `Êtes-vous sûr de vouloir réinitialiser les paramètres ${section} ?`,
|
||
header: 'Confirmation',
|
||
icon: 'pi pi-exclamation-triangle',
|
||
acceptLabel: 'Oui',
|
||
rejectLabel: 'Non',
|
||
accept: () => {
|
||
toast.current?.show({
|
||
severity: 'info',
|
||
summary: 'Réinitialisation',
|
||
detail: `Les paramètres ${section} ont été réinitialisés`,
|
||
life: 3000
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
const testEmailConnection = () => {
|
||
setLoading(true);
|
||
|
||
setTimeout(() => {
|
||
setLoading(false);
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Test réussi',
|
||
detail: 'La connexion au serveur SMTP a été établie avec succès',
|
||
life: 3000
|
||
});
|
||
}, 2000);
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (autoRefresh) {
|
||
const interval = setInterval(() => {
|
||
updateSystemMetrics();
|
||
}, refreshInterval * 1000);
|
||
return () => clearInterval(interval);
|
||
}
|
||
}, [autoRefresh, refreshInterval]);
|
||
|
||
const updateSystemMetrics = () => {
|
||
setPerformanceLevel(Math.floor(Math.random() * 20) + 70);
|
||
setSecurityScore(Math.floor(Math.random() * 15) + 80);
|
||
setSystemHealth(Math.floor(Math.random() * 10) + 85);
|
||
};
|
||
|
||
const quickBackup = () => {
|
||
setBackupProgress(0);
|
||
const interval = setInterval(() => {
|
||
setBackupProgress(prev => {
|
||
if (prev >= 100) {
|
||
clearInterval(interval);
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Sauvegarde terminée',
|
||
detail: 'Sauvegarde rapide effectuée avec succès',
|
||
life: 3000
|
||
});
|
||
return 100;
|
||
}
|
||
return prev + 10;
|
||
});
|
||
}, 200);
|
||
};
|
||
|
||
const toggleMaintenance = () => {
|
||
confirmDialog({
|
||
message: 'Activer le mode maintenance ?',
|
||
header: 'Mode Maintenance',
|
||
icon: 'pi pi-wrench',
|
||
acceptLabel: 'Activer',
|
||
rejectLabel: 'Annuler',
|
||
accept: () => {
|
||
toast.current?.show({
|
||
severity: 'info',
|
||
summary: 'Mode maintenance',
|
||
detail: 'Le mode maintenance a été activé',
|
||
life: 3000
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
const clearCache = () => {
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Cache vidé',
|
||
detail: 'Le cache système a été réinitialisé',
|
||
life: 3000
|
||
});
|
||
};
|
||
|
||
const exportAll = () => {
|
||
setExportProgress(0);
|
||
const interval = setInterval(() => {
|
||
setExportProgress(prev => {
|
||
if (prev >= 100) {
|
||
clearInterval(interval);
|
||
exportSettings();
|
||
return 100;
|
||
}
|
||
return prev + 5;
|
||
});
|
||
}, 100);
|
||
};
|
||
|
||
const testConnections = () => {
|
||
toast.current?.show({
|
||
severity: 'info',
|
||
summary: 'Test en cours',
|
||
detail: 'Test de connectivité en cours...',
|
||
life: 3000
|
||
});
|
||
|
||
setTimeout(() => {
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Connectivité OK',
|
||
detail: 'Toutes les connexions sont opérationnelles',
|
||
life: 3000
|
||
});
|
||
}, 2000);
|
||
};
|
||
|
||
const optimizeDatabase = () => {
|
||
toast.current?.show({
|
||
severity: 'info',
|
||
summary: 'Optimisation DB',
|
||
detail: 'Optimisation de la base de données en cours...',
|
||
life: 5000
|
||
});
|
||
};
|
||
|
||
const exportSettings = () => {
|
||
const allSettings = {
|
||
company: companySettings,
|
||
invoice: invoiceSettings,
|
||
email: emailSettings,
|
||
system: systemSettings,
|
||
backup: backupSettings,
|
||
metadata: {
|
||
exportDate: new Date().toISOString(),
|
||
version: '2.1.0',
|
||
profile: configProfile
|
||
}
|
||
};
|
||
|
||
const blob = new Blob([JSON.stringify(allSettings, null, 2)], { type: 'application/json' });
|
||
const link = document.createElement('a');
|
||
link.href = URL.createObjectURL(blob);
|
||
link.download = `btpxpress-settings-${new Date().toISOString().split('T')[0]}.json`;
|
||
link.click();
|
||
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Export réussi',
|
||
detail: 'Les paramètres ont été exportés',
|
||
life: 3000
|
||
});
|
||
|
||
setLastSaved(new Date());
|
||
setUnsavedChanges(false);
|
||
};
|
||
|
||
const renderCompanySettings = () => (
|
||
<Card>
|
||
<div className="formgrid grid">
|
||
<div className="field col-12">
|
||
<h5>Informations de l'entreprise</h5>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-6">
|
||
<label htmlFor="companyName">Nom de l'entreprise</label>
|
||
<InputText
|
||
id="companyName"
|
||
value={companySettings.nom}
|
||
onChange={(e) => setCompanySettings({...companySettings, nom: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-6">
|
||
<label htmlFor="formeJuridique">Forme juridique</label>
|
||
<Dropdown
|
||
id="formeJuridique"
|
||
value={companySettings.formeJuridique}
|
||
options={formesJuridiques}
|
||
onChange={(e) => setCompanySettings({...companySettings, formeJuridique: e.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<label htmlFor="address">Adresse</label>
|
||
<InputTextarea
|
||
id="address"
|
||
value={companySettings.adresse}
|
||
onChange={(e) => setCompanySettings({...companySettings, adresse: e.target.value})}
|
||
rows={3}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="phone">Téléphone</label>
|
||
<InputText
|
||
id="phone"
|
||
value={companySettings.telephone}
|
||
onChange={(e) => setCompanySettings({...companySettings, telephone: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="email">Email</label>
|
||
<InputText
|
||
id="email"
|
||
value={companySettings.email}
|
||
onChange={(e) => setCompanySettings({...companySettings, email: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="website">Site web</label>
|
||
<InputText
|
||
id="website"
|
||
value={companySettings.siteWeb}
|
||
onChange={(e) => setCompanySettings({...companySettings, siteWeb: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="tva">Numéro TVA</label>
|
||
<InputText
|
||
id="tva"
|
||
value={companySettings.numeroTVA}
|
||
onChange={(e) => setCompanySettings({...companySettings, numeroTVA: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="siret">Numéro SIRET</label>
|
||
<InputText
|
||
id="siret"
|
||
value={companySettings.numeroSiret}
|
||
onChange={(e) => setCompanySettings({...companySettings, numeroSiret: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="capital">Capital social (€)</label>
|
||
<InputNumber
|
||
id="capital"
|
||
value={companySettings.capitalSocial}
|
||
onValueChange={(e) => setCompanySettings({...companySettings, capitalSocial: e.value || 0})}
|
||
mode="currency"
|
||
currency="EUR"
|
||
locale="fr-FR"
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<label htmlFor="logo">Logo de l'entreprise</label>
|
||
<FileUpload
|
||
name="logo"
|
||
accept="image/*"
|
||
maxFileSize={1000000}
|
||
emptyTemplate={
|
||
<p className="m-0">Glissez et déposez le logo ici ou cliquez pour sélectionner.</p>
|
||
}
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<div className="flex gap-2">
|
||
<Button
|
||
label="Sauvegarder"
|
||
icon="pi pi-save"
|
||
onClick={() => saveSettings('entreprise')}
|
||
loading={loading}
|
||
/>
|
||
<Button
|
||
label="Réinitialiser"
|
||
icon="pi pi-refresh"
|
||
severity="secondary"
|
||
onClick={() => resetSettings('entreprise')}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
);
|
||
|
||
const renderInvoiceSettings = () => (
|
||
<Card>
|
||
<div className="formgrid grid">
|
||
<div className="field col-12">
|
||
<h5>Paramètres de facturation</h5>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="prefixFacture">Préfixe factures</label>
|
||
<InputText
|
||
id="prefixFacture"
|
||
value={invoiceSettings.prefixFacture}
|
||
onChange={(e) => setInvoiceSettings({...invoiceSettings, prefixFacture: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="nextFacture">Prochain n° facture</label>
|
||
<InputNumber
|
||
id="nextFacture"
|
||
value={invoiceSettings.prochainNumeroFacture}
|
||
onValueChange={(e) => setInvoiceSettings({...invoiceSettings, prochainNumeroFacture: e.value || 1})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="prefixDevis">Préfixe devis</label>
|
||
<InputText
|
||
id="prefixDevis"
|
||
value={invoiceSettings.prefixDevis}
|
||
onChange={(e) => setInvoiceSettings({...invoiceSettings, prefixDevis: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="nextDevis">Prochain n° devis</label>
|
||
<InputNumber
|
||
id="nextDevis"
|
||
value={invoiceSettings.prochainNumeroDevis}
|
||
onValueChange={(e) => setInvoiceSettings({...invoiceSettings, prochainNumeroDevis: e.value || 1})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="tauxTVA">Taux TVA par défaut (%)</label>
|
||
<InputNumber
|
||
id="tauxTVA"
|
||
value={invoiceSettings.tauxTVADefaut}
|
||
onValueChange={(e) => setInvoiceSettings({...invoiceSettings, tauxTVADefaut: e.value || 0})}
|
||
suffix="%"
|
||
min={0}
|
||
max={100}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="delaiPaiement">Délai de paiement (jours)</label>
|
||
<InputNumber
|
||
id="delaiPaiement"
|
||
value={invoiceSettings.delaiPaiementDefaut}
|
||
onValueChange={(e) => setInvoiceSettings({...invoiceSettings, delaiPaiementDefaut: e.value || 30})}
|
||
suffix=" jours"
|
||
min={0}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="penalites">Pénalités de retard (%)</label>
|
||
<InputNumber
|
||
id="penalites"
|
||
value={invoiceSettings.penalitesRetard}
|
||
onValueChange={(e) => setInvoiceSettings({...invoiceSettings, penalitesRetard: e.value || 0})}
|
||
suffix="%"
|
||
min={0}
|
||
max={100}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<label htmlFor="conditionsPaiement">Conditions de paiement</label>
|
||
<InputTextarea
|
||
id="conditionsPaiement"
|
||
value={invoiceSettings.conditionsPaiement}
|
||
onChange={(e) => setInvoiceSettings({...invoiceSettings, conditionsPaiement: e.target.value})}
|
||
rows={3}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<label htmlFor="mentionsLegales">Mentions légales</label>
|
||
<InputTextarea
|
||
id="mentionsLegales"
|
||
value={invoiceSettings.mentionsLegales}
|
||
onChange={(e) => setInvoiceSettings({...invoiceSettings, mentionsLegales: e.target.value})}
|
||
rows={4}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<div className="flex gap-2">
|
||
<Button
|
||
label="Sauvegarder"
|
||
icon="pi pi-save"
|
||
onClick={() => saveSettings('facturation')}
|
||
loading={loading}
|
||
/>
|
||
<Button
|
||
label="Réinitialiser"
|
||
icon="pi pi-refresh"
|
||
severity="secondary"
|
||
onClick={() => resetSettings('facturation')}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
);
|
||
|
||
const renderEmailSettings = () => (
|
||
<Card>
|
||
<div className="formgrid grid">
|
||
<div className="field col-12">
|
||
<h5>Configuration email</h5>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-8">
|
||
<label htmlFor="smtpServer">Serveur SMTP</label>
|
||
<InputText
|
||
id="smtpServer"
|
||
value={emailSettings.serveurSMTP}
|
||
onChange={(e) => setEmailSettings({...emailSettings, serveurSMTP: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="smtpPort">Port SMTP</label>
|
||
<InputNumber
|
||
id="smtpPort"
|
||
value={emailSettings.portSMTP}
|
||
onValueChange={(e) => setEmailSettings({...emailSettings, portSMTP: e.value || 587})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-6">
|
||
<label htmlFor="smtpUser">Utilisateur SMTP</label>
|
||
<InputText
|
||
id="smtpUser"
|
||
value={emailSettings.utilisateurSMTP}
|
||
onChange={(e) => setEmailSettings({...emailSettings, utilisateurSMTP: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-6">
|
||
<label htmlFor="smtpPassword">Mot de passe SMTP</label>
|
||
<InputText
|
||
id="smtpPassword"
|
||
type="password"
|
||
value={emailSettings.motDePasseSMTP}
|
||
onChange={(e) => setEmailSettings({...emailSettings, motDePasseSMTP: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-8">
|
||
<label htmlFor="fromEmail">Email expéditeur par défaut</label>
|
||
<InputText
|
||
id="fromEmail"
|
||
value={emailSettings.expediteurDefaut}
|
||
onChange={(e) => setEmailSettings({...emailSettings, expediteurDefaut: e.target.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-4">
|
||
<label htmlFor="useTLS">Utiliser TLS/SSL</label>
|
||
<div className="flex align-items-center h-3rem">
|
||
<InputSwitch
|
||
id="useTLS"
|
||
checked={emailSettings.useTLS}
|
||
onChange={(e) => setEmailSettings({...emailSettings, useTLS: e.value})}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<label>Emails de notification</label>
|
||
<div className="flex flex-wrap gap-2 mb-2">
|
||
{emailSettings.emailsNotification.map((email, index) => (
|
||
<Chip
|
||
key={index}
|
||
label={email}
|
||
removable
|
||
onRemove={() => {
|
||
const newEmails = [...emailSettings.emailsNotification];
|
||
newEmails.splice(index, 1);
|
||
setEmailSettings({...emailSettings, emailsNotification: newEmails});
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
<div className="p-inputgroup">
|
||
<InputText
|
||
placeholder="Ajouter un email de notification"
|
||
onKeyPress={(e) => {
|
||
if (e.key === 'Enter') {
|
||
const input = e.currentTarget;
|
||
if (input.value && input.value.includes('@')) {
|
||
setEmailSettings({
|
||
...emailSettings,
|
||
emailsNotification: [...emailSettings.emailsNotification, input.value]
|
||
});
|
||
input.value = '';
|
||
}
|
||
}
|
||
}}
|
||
/>
|
||
<Button icon="pi pi-plus" />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<div className="flex gap-2">
|
||
<Button
|
||
label="Tester la connexion"
|
||
icon="pi pi-wifi"
|
||
severity="info"
|
||
onClick={testEmailConnection}
|
||
loading={loading}
|
||
/>
|
||
<Button
|
||
label="Sauvegarder"
|
||
icon="pi pi-save"
|
||
onClick={() => saveSettings('email')}
|
||
loading={loading}
|
||
/>
|
||
<Button
|
||
label="Réinitialiser"
|
||
icon="pi pi-refresh"
|
||
severity="secondary"
|
||
onClick={() => resetSettings('email')}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
);
|
||
|
||
const renderSystemSettings = () => (
|
||
<Card>
|
||
<div className="formgrid grid">
|
||
<div className="field col-12">
|
||
<h5>Paramètres système</h5>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="langue">Langue</label>
|
||
<Dropdown
|
||
id="langue"
|
||
value={systemSettings.langue}
|
||
options={langues}
|
||
onChange={(e) => setSystemSettings({...systemSettings, langue: e.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="fuseau">Fuseau horaire</label>
|
||
<Dropdown
|
||
id="fuseau"
|
||
value={systemSettings.fuseau}
|
||
options={fuseaux}
|
||
onChange={(e) => setSystemSettings({...systemSettings, fuseau: e.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="devise">Devise par défaut</label>
|
||
<Dropdown
|
||
id="devise"
|
||
value={systemSettings.deviseDefaut}
|
||
options={devises}
|
||
onChange={(e) => setSystemSettings({...systemSettings, deviseDefaut: e.value})}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="theme">Thème</label>
|
||
<SelectButton
|
||
value={systemSettings.theme}
|
||
options={themes}
|
||
onChange={(e) => setSystemSettings({...systemSettings, theme: e.value})}
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="couleurPrimaire">Couleur primaire</label>
|
||
<div className="flex align-items-center gap-2">
|
||
<ColorPicker
|
||
value={systemSettings.couleurPrimaire}
|
||
onChange={(e) => setSystemSettings({...systemSettings, couleurPrimaire: `#${e.value}`})}
|
||
/>
|
||
<InputText
|
||
value={systemSettings.couleurPrimaire}
|
||
onChange={(e) => setSystemSettings({...systemSettings, couleurPrimaire: e.target.value})}
|
||
className="flex-1"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<Divider />
|
||
|
||
<div className="field col-12">
|
||
<h6>Notifications et rappels</h6>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="notifications">Activer les notifications</label>
|
||
<div className="flex align-items-center h-3rem">
|
||
<InputSwitch
|
||
id="notifications"
|
||
checked={systemSettings.activerNotifications}
|
||
onChange={(e) => setSystemSettings({...systemSettings, activerNotifications: e.value})}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="rappels">Activer les rappels</label>
|
||
<div className="flex align-items-center h-3rem">
|
||
<InputSwitch
|
||
id="rappels"
|
||
checked={systemSettings.activerRappels}
|
||
onChange={(e) => setSystemSettings({...systemSettings, activerRappels: e.value})}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="rappelFacture">Rappel factures (jours)</label>
|
||
<InputNumber
|
||
id="rappelFacture"
|
||
value={systemSettings.delaiRappelFacture}
|
||
onValueChange={(e) => setSystemSettings({...systemSettings, delaiRappelFacture: e.value || 7})}
|
||
suffix=" jours"
|
||
min={1}
|
||
className="w-full"
|
||
disabled={!systemSettings.activerRappels}
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12 md:col-3">
|
||
<label htmlFor="rappelDevis">Rappel devis (jours)</label>
|
||
<InputNumber
|
||
id="rappelDevis"
|
||
value={systemSettings.delaiRappelDevis}
|
||
onValueChange={(e) => setSystemSettings({...systemSettings, delaiRappelDevis: e.value || 3})}
|
||
suffix=" jours"
|
||
min={1}
|
||
className="w-full"
|
||
disabled={!systemSettings.activerRappels}
|
||
/>
|
||
</div>
|
||
|
||
<div className="field col-12">
|
||
<div className="flex gap-2">
|
||
<Button
|
||
label="Sauvegarder"
|
||
icon="pi pi-save"
|
||
onClick={() => saveSettings('système')}
|
||
loading={loading}
|
||
/>
|
||
<Button
|
||
label="Réinitialiser"
|
||
icon="pi pi-refresh"
|
||
severity="secondary"
|
||
onClick={() => resetSettings('système')}
|
||
/>
|
||
<Button
|
||
label="Exporter tous les paramètres"
|
||
icon="pi pi-download"
|
||
severity="help"
|
||
onClick={exportSettings}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
);
|
||
|
||
const renderSystemOverview = () => (
|
||
<div className="grid">
|
||
<div className="col-12 md:col-4">
|
||
<Card className="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
|
||
<div className="text-center">
|
||
<Knob
|
||
value={performanceLevel}
|
||
readOnly
|
||
size={80}
|
||
strokeWidth={8}
|
||
valueColor="white"
|
||
rangeColor="rgba(255,255,255,0.3)"
|
||
/>
|
||
<h6 className="mt-3 mb-0">Performance Système</h6>
|
||
<p className="text-sm opacity-90">Optimal</p>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
|
||
<div className="col-12 md:col-4">
|
||
<Card className="bg-gradient-to-r from-green-500 to-teal-600 text-white">
|
||
<div className="text-center">
|
||
<Knob
|
||
value={securityScore}
|
||
readOnly
|
||
size={80}
|
||
strokeWidth={8}
|
||
valueColor="white"
|
||
rangeColor="rgba(255,255,255,0.3)"
|
||
/>
|
||
<h6 className="mt-3 mb-0">Score Sécurité</h6>
|
||
<p className="text-sm opacity-90">Excellent</p>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
|
||
<div className="col-12 md:col-4">
|
||
<Card className="bg-gradient-to-r from-orange-500 to-pink-600 text-white">
|
||
<div className="text-center">
|
||
<Knob
|
||
value={systemHealth}
|
||
readOnly
|
||
size={80}
|
||
strokeWidth={8}
|
||
valueColor="white"
|
||
rangeColor="rgba(255,255,255,0.3)"
|
||
/>
|
||
<h6 className="mt-3 mb-0">Santé Système</h6>
|
||
<p className="text-sm opacity-90">Très bon</p>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
|
||
<div className="col-12">
|
||
<Card title="Métriques en temps réel">
|
||
<div className="mb-4">
|
||
{performanceMetrics.map((metric, index) => (
|
||
<div key={index} className="mb-3">
|
||
<div className="flex justify-content-between mb-2">
|
||
<span>{metric.label}</span>
|
||
<span>{metric.value}%</span>
|
||
</div>
|
||
<ProgressBar value={metric.value} color={metric.color} />
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="grid">
|
||
<div className="col-12 md:col-3">
|
||
<div className="text-center p-3 border-round bg-blue-50">
|
||
<i className="pi pi-server text-blue-500 text-2xl mb-2" />
|
||
<div className="text-lg font-bold text-blue-700">24h</div>
|
||
<div className="text-sm text-600">Uptime</div>
|
||
</div>
|
||
</div>
|
||
<div className="col-12 md:col-3">
|
||
<div className="text-center p-3 border-round bg-green-50">
|
||
<i className="pi pi-users text-green-500 text-2xl mb-2" />
|
||
<div className="text-lg font-bold text-green-700">1,247</div>
|
||
<div className="text-sm text-600">Utilisateurs actifs</div>
|
||
</div>
|
||
</div>
|
||
<div className="col-12 md:col-3">
|
||
<div className="text-center p-3 border-round bg-purple-50">
|
||
<i className="pi pi-database text-purple-500 text-2xl mb-2" />
|
||
<div className="text-lg font-bold text-purple-700">2.4GB</div>
|
||
<div className="text-sm text-600">Données traitées</div>
|
||
</div>
|
||
</div>
|
||
<div className="col-12 md:col-3">
|
||
<div className="text-center p-3 border-round bg-orange-50">
|
||
<i className="pi pi-bolt text-orange-500 text-2xl mb-2" />
|
||
<div className="text-lg font-bold text-orange-700">156ms</div>
|
||
<div className="text-sm text-600">Temps réponse</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
const renderIntegrationsTab = () => (
|
||
<div className="grid">
|
||
<div className="col-12">
|
||
<Card title="Intégrations tierces">
|
||
<DataView
|
||
value={integrationsList}
|
||
itemTemplate={(item) => (
|
||
<div className="col-12 md:col-6 lg:col-4 p-2">
|
||
<Card className="h-full">
|
||
<div className="flex align-items-center justify-content-between mb-3">
|
||
<div className="flex align-items-center">
|
||
<Avatar
|
||
icon={item.icon}
|
||
className="mr-2"
|
||
style={{ backgroundColor: '#DEE2E6', color: '#495057' }}
|
||
/>
|
||
<span className="font-bold">{item.name}</span>
|
||
</div>
|
||
<Tag
|
||
value={item.status}
|
||
severity={
|
||
item.status === 'connected' ? 'success' :
|
||
item.status === 'error' ? 'danger' :
|
||
item.status === 'pending' ? 'warning' : 'secondary'
|
||
}
|
||
/>
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<Button
|
||
label="Configure"
|
||
size="small"
|
||
className="flex-1"
|
||
/>
|
||
<Button
|
||
icon="pi pi-cog"
|
||
size="small"
|
||
outlined
|
||
/>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
)}
|
||
layout="grid"
|
||
/>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
const renderSecurityTab = () => (
|
||
<div className="grid">
|
||
<div className="col-12 md:col-6">
|
||
<Card title="Audit de sécurité">
|
||
<Timeline
|
||
value={securityAudits}
|
||
content={(item) => (
|
||
<div className="flex align-items-center">
|
||
<Badge
|
||
value={item.status}
|
||
severity={
|
||
item.status === 'success' ? 'success' :
|
||
item.status === 'warning' ? 'warning' : 'danger'
|
||
}
|
||
className="mr-2"
|
||
/>
|
||
<div>
|
||
<div className="font-bold">{item.action}</div>
|
||
<div className="text-sm text-600">{item.ip} - {item.date.toLocaleTimeString()}</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
className="w-full"
|
||
/>
|
||
</Card>
|
||
</div>
|
||
|
||
<div className="col-12 md:col-6">
|
||
<Card title="Paramètres de sécurité avancés">
|
||
<Accordion multiple>
|
||
<AccordionTab header="Authentification">
|
||
<div className="field">
|
||
<label>Authentification à deux facteurs</label>
|
||
<InputSwitch className="ml-2" />
|
||
</div>
|
||
<div className="field">
|
||
<label>Durée de session (minutes)</label>
|
||
<Slider value={30} min={5} max={480} step={5} className="w-full mt-2" />
|
||
</div>
|
||
</AccordionTab>
|
||
|
||
<AccordionTab header="Mots de passe">
|
||
<div className="field">
|
||
<label>Longueur minimale</label>
|
||
<InputNumber value={8} min={6} max={32} className="w-full" />
|
||
</div>
|
||
<div className="field">
|
||
<label>Complexité requise</label>
|
||
<div className="flex flex-column gap-2 mt-2">
|
||
<div className="flex align-items-center">
|
||
<Checkbox checked className="mr-2" />
|
||
<label>Majuscules</label>
|
||
</div>
|
||
<div className="flex align-items-center">
|
||
<Checkbox checked className="mr-2" />
|
||
<label>Caractères spéciaux</label>
|
||
</div>
|
||
<div className="flex align-items-center">
|
||
<Checkbox checked className="mr-2" />
|
||
<label>Chiffres</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</AccordionTab>
|
||
|
||
<AccordionTab header="Chiffrement">
|
||
<div className="field">
|
||
<label>Algorithme de chiffrement</label>
|
||
<Dropdown
|
||
value="AES-256"
|
||
options={[
|
||
{ label: 'AES-128', value: 'AES-128' },
|
||
{ label: 'AES-256', value: 'AES-256' },
|
||
{ label: 'RSA-2048', value: 'RSA-2048' }
|
||
]}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
</AccordionTab>
|
||
</Accordion>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
const renderAdvancedTab = () => (
|
||
<Splitter style={{ height: '600px' }}>
|
||
<SplitterPanel size={30}>
|
||
<ScrollPanel style={{ width: '100%', height: '100%' }}>
|
||
<div className="p-3">
|
||
<h6>Navigation rapide</h6>
|
||
<Menu
|
||
model={[
|
||
{ label: 'API & Webhooks', icon: 'pi pi-link' },
|
||
{ label: 'Variables d\'environnement', icon: 'pi pi-code' },
|
||
{ label: 'Logs système', icon: 'pi pi-file' },
|
||
{ label: 'Performance', icon: 'pi pi-chart-line' },
|
||
{ label: 'Base de données', icon: 'pi pi-database' },
|
||
{ label: 'Terminal', icon: 'pi pi-desktop' }
|
||
]}
|
||
/>
|
||
</div>
|
||
</ScrollPanel>
|
||
</SplitterPanel>
|
||
|
||
<SplitterPanel size={70}>
|
||
<ScrollPanel style={{ width: '100%', height: '100%' }}>
|
||
<div className="p-3">
|
||
<Card title="Configuration avancée">
|
||
<Fieldset legend="API Configuration" toggleable>
|
||
<div className="grid">
|
||
<div className="col-12 md:col-6">
|
||
<div className="field">
|
||
<label>Rate Limiting (req/min)</label>
|
||
<InputNumber value={1000} className="w-full" />
|
||
</div>
|
||
</div>
|
||
<div className="col-12 md:col-6">
|
||
<div className="field">
|
||
<label>Timeout (ms)</label>
|
||
<InputNumber value={30000} className="w-full" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Fieldset>
|
||
|
||
<Fieldset legend="Terminal système" toggleable className="mt-3">
|
||
<Terminal
|
||
ref={terminal}
|
||
welcomeMessage="Bienvenue dans le terminal BTpXpress. Tapez 'help' pour les commandes disponibles."
|
||
prompt="btpxpress $"
|
||
className="bg-gray-900 text-white"
|
||
/>
|
||
</Fieldset>
|
||
|
||
<Fieldset legend="Monitoring avancé" toggleable className="mt-3">
|
||
<Chart
|
||
type="line"
|
||
data={{
|
||
labels: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00'],
|
||
datasets: [{
|
||
label: 'CPU Usage',
|
||
data: [20, 35, 60, 45, 70, 30],
|
||
borderColor: '#42A5F5',
|
||
backgroundColor: 'rgba(66, 165, 245, 0.1)'
|
||
}]
|
||
}}
|
||
options={{
|
||
responsive: true,
|
||
plugins: {
|
||
legend: { position: 'top' }
|
||
}
|
||
}}
|
||
/>
|
||
</Fieldset>
|
||
</Card>
|
||
</div>
|
||
</ScrollPanel>
|
||
</SplitterPanel>
|
||
</Splitter>
|
||
);
|
||
|
||
const leftToolbarTemplate = () => (
|
||
<div className="flex align-items-center gap-2">
|
||
<Avatar
|
||
icon="pi pi-cog"
|
||
className="mr-2"
|
||
style={{ backgroundColor: '#3B82F6', color: 'white' }}
|
||
/>
|
||
<div>
|
||
<div className="font-bold text-lg">Paramètres Système</div>
|
||
<div className="text-sm text-600">
|
||
{lastSaved ? `Dernière sauvegarde: ${lastSaved.toLocaleString()}` : 'Aucune sauvegarde'}
|
||
{unsavedChanges && <Badge value="Non sauvé" severity="warning" className="ml-2" />}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
const rightToolbarTemplate = () => (
|
||
<div className="flex align-items-center gap-2">
|
||
<ToggleButton
|
||
checked={autoRefresh}
|
||
onChange={(e) => setAutoRefresh(e.value)}
|
||
onIcon="pi pi-pause"
|
||
offIcon="pi pi-play"
|
||
onLabel="Auto-refresh"
|
||
offLabel="Manual"
|
||
size="small"
|
||
/>
|
||
|
||
<Button
|
||
icon="pi pi-question-circle"
|
||
rounded
|
||
text
|
||
onClick={() => setHelpVisible(true)}
|
||
tooltip="Aide"
|
||
/>
|
||
|
||
<Button
|
||
icon="pi pi-history"
|
||
rounded
|
||
text
|
||
onClick={() => setHistoryVisible(true)}
|
||
tooltip="Historique"
|
||
/>
|
||
|
||
<Button
|
||
icon="pi pi-bars"
|
||
rounded
|
||
text
|
||
onClick={() => setSidebarVisible(true)}
|
||
tooltip="Menu"
|
||
/>
|
||
</div>
|
||
);
|
||
|
||
return (
|
||
<div className="grid">
|
||
<div className="col-12">
|
||
<BlockUI blocked={blocked}>
|
||
<Toast ref={toast} />
|
||
<Messages ref={messages} />
|
||
<ConfirmDialog />
|
||
|
||
<Toolbar
|
||
left={leftToolbarTemplate}
|
||
right={rightToolbarTemplate}
|
||
className="mb-4 bg-gradient-to-r from-blue-500 to-purple-600 text-white border-round"
|
||
/>
|
||
|
||
{exportProgress > 0 && exportProgress < 100 && (
|
||
<Card className="mb-4">
|
||
<div className="flex align-items-center gap-3">
|
||
<i className="pi pi-spin pi-spinner text-blue-500" />
|
||
<div className="flex-1">
|
||
<p className="m-0 font-bold">Export en cours...</p>
|
||
<ProgressBar value={exportProgress} className="mt-2" />
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{backupProgress > 0 && backupProgress < 100 && (
|
||
<Card className="mb-4">
|
||
<div className="flex align-items-center gap-3">
|
||
<i className="pi pi-spin pi-save text-green-500" />
|
||
<div className="flex-1">
|
||
<p className="m-0 font-bold">Sauvegarde en cours...</p>
|
||
<ProgressBar value={backupProgress} className="mt-2" />
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
<TabView
|
||
activeIndex={activeIndex}
|
||
onTabChange={(e) => setActiveIndex(e.index)}
|
||
className="tabview-custom"
|
||
>
|
||
<TabPanel header="Vue d'ensemble" leftIcon="pi pi-home mr-2">
|
||
{renderSystemOverview()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Entreprise" leftIcon="pi pi-building mr-2">
|
||
{renderCompanySettings()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Facturation" leftIcon="pi pi-file-edit mr-2">
|
||
{renderInvoiceSettings()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Email" leftIcon="pi pi-envelope mr-2">
|
||
{renderEmailSettings()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Système" leftIcon="pi pi-cog mr-2">
|
||
{renderSystemSettings()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Sécurité" leftIcon="pi pi-shield mr-2">
|
||
{renderSecurityTab()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Intégrations" leftIcon="pi pi-link mr-2">
|
||
{renderIntegrationsTab()}
|
||
</TabPanel>
|
||
|
||
<TabPanel header="Avancé" leftIcon="pi pi-code mr-2">
|
||
{renderAdvancedTab()}
|
||
</TabPanel>
|
||
</TabView>
|
||
|
||
<SpeedDial
|
||
model={quickActionsData}
|
||
radius={80}
|
||
type="semi-circle"
|
||
direction="up-left"
|
||
style={{ left: 'calc(50% - 2rem)', bottom: 0 }}
|
||
buttonClassName="p-button-help"
|
||
maskClassName="bg-black-alpha-30"
|
||
/>
|
||
|
||
<Sidebar
|
||
visible={sidebarVisible}
|
||
onHide={() => setSidebarVisible(false)}
|
||
position="right"
|
||
className="w-20rem"
|
||
>
|
||
<h3>Actions rapides</h3>
|
||
<div className="flex flex-column gap-2">
|
||
{quickActionsData.map((action, index) => (
|
||
<Button
|
||
key={index}
|
||
label={action.label}
|
||
icon={action.icon}
|
||
outlined
|
||
className="justify-content-start"
|
||
onClick={action.command}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
<Divider />
|
||
|
||
<h4>Profil de configuration</h4>
|
||
<Dropdown
|
||
value={configProfile}
|
||
options={configProfiles}
|
||
onChange={(e) => setConfigProfile(e.value)}
|
||
optionLabel="label"
|
||
optionValue="value"
|
||
className="w-full"
|
||
/>
|
||
|
||
<div className="mt-3">
|
||
<h5>Options d'affichage</h5>
|
||
<div className="flex align-items-center gap-2 mb-2">
|
||
<InputSwitch
|
||
checked={darkMode}
|
||
onChange={(e) => setDarkMode(e.value)}
|
||
/>
|
||
<label>Mode sombre</label>
|
||
</div>
|
||
<div className="flex align-items-center gap-2 mb-2">
|
||
<InputSwitch
|
||
checked={compactMode}
|
||
onChange={(e) => setCompactMode(e.value)}
|
||
/>
|
||
<label>Mode compact</label>
|
||
</div>
|
||
<div className="flex align-items-center gap-2">
|
||
<InputSwitch
|
||
checked={expertMode}
|
||
onChange={(e) => setExpertMode(e.value)}
|
||
/>
|
||
<label>Mode expert</label>
|
||
</div>
|
||
</div>
|
||
</Sidebar>
|
||
|
||
<Dialog
|
||
header="Assistant de configuration"
|
||
visible={configWizard}
|
||
onHide={() => setConfigWizard(false)}
|
||
style={{ width: '50vw' }}
|
||
modal
|
||
>
|
||
<Steps
|
||
model={wizardSteps}
|
||
activeIndex={wizardStep}
|
||
onSelect={(e) => setWizardStep(e.index)}
|
||
readOnly={false}
|
||
className="mb-4"
|
||
/>
|
||
|
||
<div className="wizard-content">
|
||
{wizardStep === 0 && (
|
||
<div>
|
||
<h4>Configuration de l'entreprise</h4>
|
||
<p>Configurons d'abord les informations de base de votre entreprise.</p>
|
||
{renderCompanySettings()}
|
||
</div>
|
||
)}
|
||
{wizardStep === 1 && (
|
||
<div>
|
||
<h4>Gestion des utilisateurs</h4>
|
||
<p>Définissez les paramètres d'accès et de sécurité.</p>
|
||
<Message
|
||
severity="info"
|
||
text="Configuration des rôles et permissions utilisateurs"
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
)}
|
||
{wizardStep === 4 && (
|
||
<div className="text-center">
|
||
<i className="pi pi-check-circle text-green-500 text-6xl mb-3" />
|
||
<h4>Configuration terminée !</h4>
|
||
<p>Votre système est maintenant configuré et prêt à l'emploi.</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex justify-content-between mt-4">
|
||
<Button
|
||
label="Précédent"
|
||
icon="pi pi-chevron-left"
|
||
outlined
|
||
disabled={wizardStep === 0}
|
||
onClick={() => setWizardStep(wizardStep - 1)}
|
||
/>
|
||
<Button
|
||
label={wizardStep === wizardSteps.length - 1 ? 'Terminer' : 'Suivant'}
|
||
icon={wizardStep === wizardSteps.length - 1 ? 'pi pi-check' : 'pi pi-chevron-right'}
|
||
iconPos="right"
|
||
onClick={() => {
|
||
if (wizardStep === wizardSteps.length - 1) {
|
||
setConfigWizard(false);
|
||
toast.current?.show({
|
||
severity: 'success',
|
||
summary: 'Configuration terminée',
|
||
detail: 'Votre système a été configuré avec succès',
|
||
life: 5000
|
||
});
|
||
} else {
|
||
setWizardStep(wizardStep + 1);
|
||
}
|
||
}}
|
||
/>
|
||
</div>
|
||
</Dialog>
|
||
|
||
<ScrollTop threshold={600} className="w-3rem h-3rem border-round bg-primary" />
|
||
|
||
</BlockUI>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const ProtectedParametresPage = () => {
|
||
return (
|
||
<ProtectedRoute requiredRoles={['SUPER_ADMIN', 'ADMIN']}>
|
||
<ParametresPage />
|
||
</ProtectedRoute>
|
||
);
|
||
};
|
||
|
||
export default ProtectedParametresPage;
|
||
|
||
// Style CSS personnalisé pour améliorer l'apparence
|
||
const customStyles = `
|
||
.tabview-custom .p-tabview-nav {
|
||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 10px 10px 0 0;
|
||
}
|
||
|
||
.tabview-custom .p-tabview-nav li .p-tabview-nav-link {
|
||
color: white;
|
||
border: none;
|
||
}
|
||
|
||
.tabview-custom .p-tabview-nav li.p-highlight .p-tabview-nav-link {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 8px;
|
||
margin: 4px;
|
||
}
|
||
|
||
.bg-gradient-to-r {
|
||
background: linear-gradient(90deg, var(--tw-gradient-stops));
|
||
}
|
||
|
||
.from-blue-500 {
|
||
--tw-gradient-from: #3b82f6;
|
||
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(59, 130, 246, 0));
|
||
}
|
||
|
||
.to-purple-600 {
|
||
--tw-gradient-to: #9333ea;
|
||
}
|
||
|
||
.wizard-content {
|
||
min-height: 300px;
|
||
}
|
||
`;
|
||
|
||
// Injection du style dans le document
|
||
if (typeof document !== 'undefined') {
|
||
const style = document.createElement('style');
|
||
style.textContent = customStyles;
|
||
document.head.appendChild(style);
|
||
} |