import React, { useMemo, useState } from "react"; import { motion } from "framer-motion"; import { Bar, BarChart, CartesianGrid, PolarAngleAxis, PolarGrid, PolarRadiusAxis, Radar, RadarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { CalendarDays, ClipboardCheck, Clock3, Eye, FileText, Gauge, KanbanSquare, Layers3, Plus, ShieldAlert, Sparkles, Target, } from "lucide-react"; import { useSoc2Registry } from "./features/hooks/useSoc2Registry.js"; import { OFFICIAL_SCHEMA } from "./model/schema.js"; import { INITIAL_DB } from "./model/seed.js"; import { buildRegistryManifests, validateRegistry } from "./lib/validation.js"; import { buildBarData, buildBlockers, buildDataHealth, buildEvidenceMap, buildPackReadiness, buildRadarData, buildScoredPillars, computeGlobalScore, filterEvidence, getOverallStatus, getPackTone, getOwners, } from "./lib/selectors.js"; import { daysUntil } from "./lib/utils.js"; import { PILLAR_ICONS } from "./lib/view.js"; import PillarOverviewCard from "./components/PillarOverviewCard.jsx"; import MilestoneCard from "./components/MilestoneCard.jsx"; import EvidenceLibrary from "./components/EvidenceLibrary.jsx"; import EvidenceEditor from "./components/EvidenceEditor.jsx"; import DataTab from "./components/DataTab.jsx"; import { ProgressBar, ScorePill, SectionTitle, Tag } from "./components/ui/primitives.jsx"; function DashboardPanel({ scoredPillars, targetReadiness, overall, selectedPillar, setSelectedPillar }) { const radarData = buildRadarData(scoredPillars, targetReadiness); return ( <>
Logique de score
Absent = 0, Conçu = 0,35, En opération = 0,7, Prouvé = 1. Les preuves bonifient la crédibilité du score.
Règle de plancher
Tant que Security reste sous 60%, l’état global doit être considéré fragile.
État actuel
{overall.label}
{scoredPillars.map((pillar) => ( ))}
); } export default function App() { const registry = useSoc2Registry(); const { db, lastSavedAt, importError, syncMode, syncStatus, updateMilestoneStage, updateEvidence, toggleEvidenceMilestoneLink, createEvidence, deleteEvidence, exportDb, copyDb, importDb, resetDemo } = registry; const [activeTab, setActiveTab] = useState("dashboard"); const [selectedPillar, setSelectedPillar] = useState("security"); const [selectedEvidenceId, setSelectedEvidenceId] = useState(db.evidence[0]?.id || ""); const [search, setSearch] = useState(""); const [evidenceFilter, setEvidenceFilter] = useState("all"); const [ownerFilter, setOwnerFilter] = useState("all"); const [auditorMode, setAuditorMode] = useState(false); const [importText, setImportText] = useState(""); const evidenceMap = useMemo(() => buildEvidenceMap(db), [db]); const scoredPillars = useMemo(() => buildScoredPillars(db, evidenceMap), [db, evidenceMap]); const selected = scoredPillars.find((pillar) => pillar.id === selectedPillar) || scoredPillars[0]; const selectedEvidence = db.evidence.find((item) => item.id === selectedEvidenceId) || db.evidence[0] || null; const owners = useMemo(() => getOwners(db), [db]); const globalScore = useMemo(() => computeGlobalScore(db.pillars, evidenceMap), [db, evidenceMap]); const overall = getOverallStatus(globalScore); const auditWindowDays = daysUntil(db.appContext.observationWindow.end); const blockers = useMemo(() => buildBlockers(db, evidenceMap), [db, evidenceMap]); const packReadiness = useMemo(() => buildPackReadiness(db), [db]); const dataHealth = useMemo(() => buildDataHealth(db), [db]); const validation = useMemo(() => validateRegistry(db), [db]); const manifests = useMemo(() => buildRegistryManifests(db), [db]); const filteredEvidence = useMemo( () => filterEvidence(db, { query: search, pillarId: evidenceFilter, owner: ownerFilter, auditorMode }), [db, search, evidenceFilter, ownerFilter, auditorMode] ); const barData = useMemo(() => buildBarData(selected, evidenceMap), [selected, evidenceMap]); const readyEvidenceCount = db.evidence.filter((item) => item.auditorReady).length; const readinessRate = db.evidence.length ? Math.round((readyEvidenceCount / db.evidence.length) * 1000) / 10 : 0; const blocker = scoredPillars.find((pillar) => pillar.id === "security" && pillar.score < 60); const handleCreateEvidence = () => { const id = createEvidence(selectedPillar); setSelectedEvidenceId(id); setActiveTab("evidence"); }; const handleDeleteEvidence = (evidenceId) => { deleteEvidence(evidenceId); const fallback = db.evidence.find((item) => item.id !== evidenceId); setSelectedEvidenceId(fallback?.id || ""); }; const handleImport = () => { const result = importDb(importText); if (result.ok) { setSelectedPillar(result.data.pillars[0]?.id || "security"); setSelectedEvidenceId(result.data.evidence[0]?.id || ""); setImportText(""); } }; const handleReset = () => { resetDemo(); setSelectedPillar("security"); setSelectedEvidenceId(INITIAL_DB.evidence[0]?.id || ""); }; return (
{db.appContext.orgName} • {db.appContext.programName} {overall.label} {blocker ? Blocage: Security < 60 : null}

Assistant web de conformité SOC 2 orienté preuve, remédiation et exposition auditeur

Architecture modulaire, source de vérité versionnable, backend API optionnel, radar des 5 piliers, bibliothèque de preuves et salle auditeur.

{["dashboard", "controls", "execution", "evidence", "auditor", "data"].map((tab) => ( ))}
{activeTab === "dashboard" && ( )} {activeTab === "controls" && (
{selected.milestones.map((milestone) => ( milestone.evidenceRefs.includes(item.id))} onStageChange={updateMilestoneStage} /> ))}
)} {activeTab === "execution" && (
{blockers.map((item) => (
{item.pillarName}
{item.title}
{item.dueInDays < 0 ? `Retard ${Math.abs(item.dueInDays)} j` : `${item.dueInDays} j`}
{item.nextAction}
))}
{db.pillars.flatMap((pillar) => pillar.milestones.map((milestone) => (
{pillar.name}{milestone.stage}
{milestone.title}
{milestone.owner} • {milestone.cadence}
Échéance: {milestone.dueAt}
)) )}
)} {activeTab === "evidence" && (
)} {activeTab === "auditor" && (
{packReadiness.map((pack) => (
{pack.title}
{pack.description}
{pack.status}
Degré de préparation{pack.readiness}%
{pack.linked.map((item) => {item.title})}
))}
Mode {auditorMode ? "auditeur" : "interne"}
{auditorMode ? "Affichage réduit aux preuves auditables." : "Affichage complet incluant brouillons et remédiation."}
Description du système
Service in-scope, architecture, dépendances, responsabilités, flux de données, outils de surveillance, changements et posture de reprise.
Chaîne de preuve
Chaque jalon significatif doit être relié à des éléments probants datés, intelligibles et contextualisés, avec propriétaire et fraîcheur connue.
Écarts et remédiation
Les trous de conformité ne sont pas cachés. Ils sont nommés, qualifiés, suivis et accompagnés d’une action corrective explicite.
Narratif recommandé
Présenter d’abord le périmètre, puis les contrôles communs, ensuite les preuves les plus parlantes, et finir par les exceptions connues avec leurs remédiations.
)} {activeTab === "data" && ( )}
); }