1701 lines
72 KiB
TypeScript
1701 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 'primereact/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 { 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" as any}
|
|
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" as any}
|
|
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" as any}
|
|
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" as any}
|
|
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' : 'info'
|
|
}
|
|
/>
|
|
</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 checked={false} onChange={() => {}} 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"
|
|
/>
|
|
|
|
<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);
|
|
}
|
|
|
|
|
|
|