import { useState, useRef, useCallback } from 'react'; import { Card } from '../components/Card'; import { ProgressBar } from '../components/ProgressBar'; import { Badge } from '../components/Badge'; import { useCompliance } from '../context/ComplianceContext'; import { CHECKLIST_DATA } from '@cra-compliance/shared'; // Checklist item IDs that have matching document templates, keyed to template ID const DOCUMENT_LINKS: Record = { 'vh-1': { templateId: 'vulnerability-disclosure-policy', label: 'Generate VDP' }, 'vh-2': { templateId: 'security-txt', label: 'Generate security.txt' }, 'vh-3': { templateId: 'incident-response-plan', label: 'Generate IRP' }, 'conf-3': { templateId: 'declaration-of-conformity', label: 'Generate Declaration' }, 'doc-2': { templateId: 'sbom', label: 'Generate SBOM' }, }; interface ChecklistProps { onNavigate?: (page: 'documents') => void; } export function Checklist({ onNavigate }: ChecklistProps) { const { progress, loading, loadError, toggleItem, saveNotes, savingItemId, savingNotesId, totalItems, completedItems } = useCompliance(); const [expandedItems, setExpandedItems] = useState>(new Set()); const toggleExpanded = (itemId: string) => { setExpandedItems((prev) => { const next = new Set(prev); if (next.has(itemId)) next.delete(itemId); else next.add(itemId); return next; }); }; if (loading) return
Loading checklist...
; if (loadError) { return (
Could not load checklist progress. {loadError}
If this keeps happening, try deactivating and reactivating the plugin to rebuild the database tables.
); } return (

Compliance Checklist

Work through each item to ensure your WordPress plugin meets EU Cyber Resilience Act requirements.

{completedItems} of {totalItems} items completed

{CHECKLIST_DATA.map((category) => { const catCompleted = category.items.filter((item) => progress[item.id]?.completed).length; return ( {catCompleted}/{category.items.length} } >
{category.items.map((item) => { const isCompleted = progress[item.id]?.completed || false; const isExpanded = expandedItems.has(item.id); const isSaving = savingItemId === item.id; const isSavingNotes = savingNotesId === item.id; const currentNotes = progress[item.id]?.notes || ''; const docLink = DOCUMENT_LINKS[item.id]; return (
{item.automated && Automatable} {item.cra_article} {currentNotes && !isExpanded && ( )}

{item.description}

{isExpanded && (
How to do this:

{item.help_text}

{docLink && onNavigate && ( )}
)}
); })}
); })}
); } interface EvidenceNoteProps { itemId: string; initialValue: string; onSave: (itemId: string, notes: string) => Promise; saving: boolean; } function EvidenceNote({ itemId, initialValue, onSave, saving }: EvidenceNoteProps) { const [value, setValue] = useState(initialValue); const [saved, setSaved] = useState(false); const timerRef = useRef | null>(null); const handleChange = useCallback((e: React.ChangeEvent) => { const next = e.target.value; setValue(next); setSaved(false); if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(async () => { await onSave(itemId, next); setSaved(true); setTimeout(() => setSaved(false), 2000); }, 800); }, [itemId, onSave]); return (