init aegis

This commit is contained in:
maximus
2026-07-22 14:41:21 +02:00
commit 82075f3e6c
33 changed files with 5486 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
import { useState, useEffect } from 'react';
import { pb } from '../../config/pocketbase';
export default function ServicesWidget() {
const [contracts, setContracts] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitSuccess, setSubmitSuccess] = useState(false);
useEffect(() => {
const fetchContracts = async () => {
try {
const records = await pb.collection('aegis_managed_contracts').getFullList({
sort: '-created',
});
setContracts(records);
} catch (error) {
console.error("Erreur lors de la récupération des contrats :", error);
} finally {
setIsLoading(false);
}
};
fetchContracts();
}, []);
// Fonction pour envoyer la demande d'évolution vers PocketBase
const handleEvolutionRequest = async (subject: string, description: string) => {
setIsSubmitting(true);
try {
await pb.collection('aegis_tickets').create({
author: pb.authStore.model?.id,
company: pb.authStore.model?.company,
category: "Évolution",
subject: subject,
description: description,
status: "Ouvert"
});
setSubmitSuccess(true);
// Ferme la modale après 2.5 secondes de message de succès
setTimeout(() => {
setSubmitSuccess(false);
setIsModalOpen(false);
}, 2500);
} catch (error) {
console.error("Erreur lors de la création du ticket d'évolution :", error);
alert("Une erreur est survenue. Veuillez contacter le support par téléphone.");
} finally {
setIsSubmitting(false);
}
};
return (
<>
<div className= "bg-white rounded-xl shadow-sm border border-slate-200 p-6 flex flex-col" >
<div className="mb-4" >
<h2 className="text-lg font-semibold text-slate-900" > Vos services gérés </h2>
< p className = "text-sm text-slate-500" > Catalogue des contrats d'infogérance actifs</p>
</div>
< div className = "flex-1 overflow-y-auto mb-6 space-y-3" >
{
isLoading?(
<p className = "text-sm text-slate-500 animate-pulse" > Chargement de vos contrats sécurisés...</ p >
) : contracts.length === 0 ? (
<p className= "text-sm text-slate-500" > Aucun contrat actif détecté.</p>
) : (
contracts.map((contract) => (
<div key= { contract.id } className = "p-4 bg-slate-50 rounded-lg border border-slate-100 flex justify-between items-center" >
<div>
<h3 className="text-sm font-medium text-slate-900" > { contract.service_name } </h3>
{
contract.description && (
<p className="text-xs text-slate-500 mt-1"> { contract.description } </p>
)}
</div>
< span className = {`text-xs px-2 py-1 rounded-full font-medium ${contract.status === 'Actif' ? 'bg-emerald-50 text-emerald-600' :
contract.status === 'En déploiement' ? 'bg-blue-50 text-blue-600' :
'bg-slate-100 text-slate-600'
}`}>
{ contract.status }
</span>
</div>
))
)}
</div>
< button
onClick = {() => setIsModalOpen(true)}
className = "w-full bg-blue-900 text-white font-medium py-3 rounded-lg hover:bg-blue-800 transition-colors shadow-sm"
>
Demander une évolution du parc
</button>
</div>
{/* MODALE STRATÉGIQUE */ }
{
isModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/50 backdrop-blur-sm p-4" >
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg overflow-hidden animate-in fade-in zoom-in-95 duration-200" >
<div className="p-6" >
<div className="flex justify-between items-center mb-5" >
<h3 className="text-xl font-bold text-slate-900" > Évolution de l'infrastructure</h3>
{
!isSubmitting && !submitSuccess && (
<button onClick={ () => setIsModalOpen(false) } className = "text-slate-400 hover:text-slate-600" >
<svg className="w-6 h-6" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
<path strokeLinecap="round" strokeLinejoin = "round" strokeWidth = { 2} d = "M6 18L18 6M6 6l12 12" />
</svg>
</button>
)
}
</div>
{
submitSuccess ? (
<div className= "text-center py-8" >
<div className="w-16 h-16 bg-emerald-100 text-emerald-600 rounded-full flex items-center justify-center mx-auto mb-4" >
<svg className="w-8 h-8" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
<path strokeLinecap="round" strokeLinejoin = "round" strokeWidth = { 2} d = "M5 13l4 4L19 7" />
</svg>
</div>
< h4 className = "text-lg font-semibold text-slate-900 mb-2" > Demande transmise avec succès </h4>
< p className = "text-sm text-slate-500" > Un expert GISE analyse votre besoin et reviendra vers vous sous 24h.</p>
</div>
) : isSubmitting ? (
<div className= "text-center py-12" >
<p className="text-sm font-medium text-slate-500 animate-pulse" > Création de votre ticket projet sécurisé...</p>
</div>
) : (
<>
<p className= "text-sm text-slate-600 mb-6" >
Quel est votre prochain objectif d'infrastructure ? Sélectionnez une initiative stratégique pour ouvrir un ticket projet avec votre expert.
</p>
< div className = "space-y-3" >
<button
onClick={ () => handleEvolutionRequest("Projet d'Évolution : Cybersécurité", "Le client souhaite renforcer sa sécurité (Audits, Tests d'intrusion, Plan de Reprise d'Activité).") }
className = "w-full text-left p-4 border border-slate-200 rounded-lg hover:border-blue-900 hover:bg-blue-50 transition-colors group"
>
<h4 className="font-medium text-slate-900 group-hover:text-blue-900" > Renforcer la sécurité face aux cybermenaces </h4>
< p className = "text-xs text-slate-500 mt-1" > Audits de sécurité, tests d'intrusion et Plan de Reprise d'Activité(PRA).</p>
</button>
< button
onClick = {() => handleEvolutionRequest("Projet d'Évolution : Architecture Réseau", "Le client souhaite étendre ses capacités (Migration Cloud Privé, nouvelles succursales).")
}
className = "w-full text-left p-4 border border-slate-200 rounded-lg hover:border-blue-900 hover:bg-blue-50 transition-colors group"
>
<h4 className="font-medium text-slate-900 group-hover:text-blue-900" > Étendre les capacités du réseau actuel </h4>
< p className = "text-xs text-slate-500 mt-1" > Migration vers Cloud Privé Sécurisé, ajout de succursales sécurisées.</p>
</button>
< button
onClick = {() => handleEvolutionRequest("Projet d'Évolution : Conformité Légal", "Le client demande un accompagnement vCISO pour la mise en conformité (RGPD, NIS2).")
}
className = "w-full text-left p-4 border border-slate-200 rounded-lg hover:border-blue-900 hover:bg-blue-50 transition-colors group"
>
<h4 className="font-medium text-slate-900 group-hover:text-blue-900" > Mise en conformité légale(RGPD, NIS2) </h4>
< p className = "text-xs text-slate-500 mt-1" > Accompagnement vCISO et mise aux normes de votre système d'information.</p>
</button>
< button
onClick = {() => handleEvolutionRequest("Projet d'Évolution : Outils Souverains", "Le client souhaite déployer de nouveaux outils collaboratifs souverains.")}
className = "w-full text-left p-4 border border-slate-200 rounded-lg hover:border-blue-900 hover:bg-blue-50 transition-colors group"
>
<h4 className="font-medium text-slate-900 group-hover:text-blue-900" > Autre demande stratégique </h4>
< p className = "text-xs text-slate-500 mt-1" > Déploiement d'outils collaboratifs souverains ou besoins spécifiques.</p>
</button>
</div>
</>
)}
</div>
{
!isSubmitting && !submitSuccess && (
<div className="bg-slate-50 px-6 py-4 border-t border-slate-200 flex justify-end" >
<button onClick={ () => setIsModalOpen(false) } className = "text-sm font-medium text-slate-600 hover:text-slate-900" >
Annuler
</button>
</div>
)
}
</div>
</div>
)}
</>
);
}