Replace window.confirm() with themed ConfirmModal across dashboard

This commit is contained in:
root
2026-04-20 21:54:37 +00:00
parent 0cdaecf890
commit aa3ce3bae9
8 changed files with 510 additions and 187 deletions

View File

@@ -8,6 +8,7 @@ import AuditLog from './components/AuditLog';
import NvdSyncModal from './components/NvdSyncModal';
import NavDrawer from './components/NavDrawer';
import CalendarWidget from './components/CalendarWidget';
import ConfirmModal from './components/ConfirmModal';
import VulnerabilityTriagePage from './components/pages/ReportingPage';
import KnowledgeBasePage from './components/pages/KnowledgeBasePage';
import ExportsPage from './components/pages/ExportsPage';
@@ -241,6 +242,9 @@ export default function App() {
const [archiveList, setArchiveList] = useState([]);
const [archiveListLoading, setArchiveListLoading] = useState(false);
// Confirmation modal state — replaces window.confirm()
const [pendingConfirm, setPendingConfirm] = useState(null);
const toggleCVEExpand = (cveId) => {
setExpandedCVEs(prev => ({ ...prev, [cveId]: !prev[cveId] }));
};
@@ -532,26 +536,30 @@ export default function App() {
};
const handleDeleteDocument = async (docId, cveId, vendor) => {
if (!window.confirm('Are you sure you want to delete this document?')) {
return;
}
setPendingConfirm({
title: 'Delete Document',
message: 'Are you sure you want to delete this document?',
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const response = await fetch(`${API_BASE}/documents/${docId}`, {
method: 'DELETE',
credentials: 'include'
});
try {
const response = await fetch(`${API_BASE}/documents/${docId}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to delete document');
if (!response.ok) throw new Error('Failed to delete document');
alert('Document deleted successfully!');
const key = `${cveId}-${vendor}`;
delete cveDocuments[key];
await fetchDocuments(cveId, vendor);
fetchCVEs();
} catch (err) {
alert(`Error: ${err.message}`);
}
alert('Document deleted successfully!');
const key = `${cveId}-${vendor}`;
delete cveDocuments[key];
await fetchDocuments(cveId, vendor);
fetchCVEs();
} catch (err) {
alert(`Error: ${err.message}`);
}
},
});
};
const handleEditCVE = (cve) => {
@@ -644,65 +652,73 @@ export default function App() {
};
const handleDeleteCVEEntry = async (cve) => {
if (!window.confirm(`Are you sure you want to delete the "${cve.vendor}" entry for ${cve.cve_id}? This will also delete all associated documents.`)) {
return;
}
setPendingConfirm({
title: 'Delete Vendor Entry',
message: `Are you sure you want to delete the "${cve.vendor}" entry for ${cve.cve_id}? This will also delete all associated documents.`,
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const url = `${API_BASE}/cves/${cve.id}`;
console.log('DELETE request to:', url);
const response = await fetch(url, {
method: 'DELETE',
credentials: 'include'
});
try {
const url = `${API_BASE}/cves/${cve.id}`;
console.log('DELETE request to:', url);
const response = await fetch(url, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
const data = await response.json();
throw new Error(data.error || 'Failed to delete CVE entry');
} else {
throw new Error(`Server returned ${response.status} ${response.statusText}. Check API_BASE configuration.`);
}
}
if (!response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
const data = await response.json();
throw new Error(data.error || 'Failed to delete CVE entry');
} else {
throw new Error(`Server returned ${response.status} ${response.statusText}. Check API_BASE configuration.`);
alert(`Deleted ${cve.vendor} entry for ${cve.cve_id}`);
fetchCVEs();
fetchVendors();
} catch (err) {
alert(`Error: ${err.message}`);
}
}
alert(`Deleted ${cve.vendor} entry for ${cve.cve_id}`);
fetchCVEs();
fetchVendors();
} catch (err) {
alert(`Error: ${err.message}`);
}
},
});
};
const handleDeleteEntireCVE = async (cveId, vendorCount) => {
if (!window.confirm(`Are you sure you want to delete ALL ${vendorCount} vendor entries for ${cveId}? This will permanently remove all associated documents and files.`)) {
return;
}
setPendingConfirm({
title: 'Delete Entire CVE',
message: `Are you sure you want to delete ALL ${vendorCount} vendor entries for ${cveId}? This will permanently remove all associated documents and files.`,
confirmText: 'Delete All',
onConfirm: async () => {
setPendingConfirm(null);
try {
const url = `${API_BASE}/cves/by-cve-id/${encodeURIComponent(cveId)}`;
console.log('DELETE request to:', url);
const response = await fetch(url, {
method: 'DELETE',
credentials: 'include'
});
try {
const url = `${API_BASE}/cves/by-cve-id/${encodeURIComponent(cveId)}`;
console.log('DELETE request to:', url);
const response = await fetch(url, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
const data = await response.json();
throw new Error(data.error || 'Failed to delete CVE');
} else {
throw new Error(`Server returned ${response.status} ${response.statusText}. Check API_BASE configuration.`);
}
}
if (!response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
const data = await response.json();
throw new Error(data.error || 'Failed to delete CVE');
} else {
throw new Error(`Server returned ${response.status} ${response.statusText}. Check API_BASE configuration.`);
alert(`Deleted all entries for ${cveId}`);
fetchCVEs();
fetchVendors();
} catch (err) {
alert(`Error: ${err.message}`);
}
}
alert(`Deleted all entries for ${cveId}`);
fetchCVEs();
fetchVendors();
} catch (err) {
alert(`Error: ${err.message}`);
}
},
});
};
const handleAddTicket = async (e) => {
@@ -770,18 +786,25 @@ export default function App() {
};
const handleDeleteTicket = async (ticket) => {
if (!window.confirm(`Delete ticket ${ticket.ticket_key}?`)) return;
try {
const response = await fetch(`${API_BASE}/jira-tickets/${ticket.id}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to delete ticket');
alert('Ticket deleted');
fetchJiraTickets();
} catch (err) {
alert(`Error: ${err.message}`);
}
setPendingConfirm({
title: 'Delete Ticket',
message: `Delete ticket ${ticket.ticket_key}?`,
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const response = await fetch(`${API_BASE}/jira-tickets/${ticket.id}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to delete ticket');
alert('Ticket deleted');
fetchJiraTickets();
} catch (err) {
alert(`Error: ${err.message}`);
}
},
});
};
const openAddTicketForCVE = (cve_id, vendor) => {
@@ -855,18 +878,25 @@ export default function App() {
};
const handleDeleteArcherTicket = async (ticket) => {
if (!window.confirm(`Delete Archer ticket ${ticket.exc_number}?`)) return;
try {
const response = await fetch(`${API_BASE}/archer-tickets/${ticket.id}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to delete Archer ticket');
alert('Archer ticket deleted');
fetchArcherTickets();
} catch (err) {
alert(`Error: ${err.message}`);
}
setPendingConfirm({
title: 'Delete Archer Ticket',
message: `Delete Archer ticket ${ticket.exc_number}?`,
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const response = await fetch(`${API_BASE}/archer-tickets/${ticket.id}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to delete Archer ticket');
alert('Archer ticket deleted');
fetchArcherTickets();
} catch (err) {
alert(`Error: ${err.message}`);
}
},
});
};
const openAddArcherTicketForCVE = (cve_id, vendor) => {
@@ -2420,6 +2450,17 @@ export default function App() {
</div>}
{/* End Three Column Layout */}
{/* Confirmation Modal */}
<ConfirmModal
open={!!pendingConfirm}
title={pendingConfirm?.title}
message={pendingConfirm?.message}
confirmText={pendingConfirm?.confirmText}
variant={pendingConfirm?.variant || 'danger'}
onConfirm={pendingConfirm?.onConfirm}
onCancel={() => setPendingConfirm(null)}
/>
</div>
</div>
);