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

@@ -1,8 +1,8 @@
import React, { useState, useEffect, useCallback } from 'react';
import { Shield, Clock, Activity, Plus, Edit2, Trash2, Loader, AlertCircle, CheckCircle, X, ChevronLeft, ChevronRight, Search, Users, FileText } from 'lucide-react';
import { useAuth } from '../../contexts/AuthContext';
import ConfirmModal from '../ConfirmModal';
// ⚠️ CONVENTION: Use relative API path, not absolute URL. Should be: process.env.REACT_APP_API_BASE || ''
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
const TABS = [
@@ -115,6 +115,7 @@ function UserManagementPanel() {
const [formData, setFormData] = useState({ username: '', email: '', password: '', group: 'Read_Only' });
const [formError, setFormError] = useState('');
const [successMessage, setSuccessMessage] = useState('');
const [pendingConfirm, setPendingConfirm] = useState(null);
const fetchUsers = useCallback(async () => {
setLoading(true);
@@ -200,19 +201,26 @@ function UserManagementPanel() {
};
const handleDelete = async (userId) => {
if (!window.confirm('Are you sure you want to delete this user?')) return;
try {
const res = await fetch(`${API_BASE}/users/${userId}`, {
method: 'DELETE',
credentials: 'include',
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Delete failed');
setSuccessMessage('User deleted');
fetchUsers();
} catch (err) {
alert(err.message);
}
setPendingConfirm({
title: 'Delete User',
message: 'Are you sure you want to delete this user?',
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const res = await fetch(`${API_BASE}/users/${userId}`, {
method: 'DELETE',
credentials: 'include',
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Delete failed');
setSuccessMessage('User deleted');
fetchUsers();
} catch (err) {
alert(err.message);
}
},
});
};
const handleToggleActive = async (user) => {
@@ -567,6 +575,17 @@ function UserManagementPanel() {
)}
</div>
)}
{/* Confirmation Modal */}
<ConfirmModal
open={!!pendingConfirm}
title={pendingConfirm?.title}
message={pendingConfirm?.message}
confirmText={pendingConfirm?.confirmText}
variant="danger"
onConfirm={pendingConfirm?.onConfirm}
onCancel={() => setPendingConfirm(null)}
/>
</div>
);
}

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useCallback } from 'react';
import { X, MessageSquare, Send, Loader, AlertCircle, Clock, Shield, Trash2 } from 'lucide-react';
import ConfirmModal from '../ConfirmModal';
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
@@ -45,6 +46,7 @@ export default function ComplianceDetailPanel({ hostname, onClose, onNoteAdded,
const [selectedMetrics, setSelectedMetrics] = useState([]);
const [submitting, setSubmitting] = useState(false);
const [noteError, setNoteError] = useState(null);
const [pendingConfirm, setPendingConfirm] = useState(null);
const fetchDetail = useCallback(async () => {
setLoading(true);
@@ -91,19 +93,26 @@ export default function ComplianceDetailPanel({ hostname, onClose, onNoteAdded,
};
const handleDeleteNote = async (noteId, hasGroup) => {
if (!window.confirm('Delete this note?')) return;
try {
const url = hasGroup
? `${API_BASE}/compliance/notes/${noteId}?group=true`
: `${API_BASE}/compliance/notes/${noteId}`;
const res = await fetch(url, { method: 'DELETE', credentials: 'include' });
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Failed to delete note');
await fetchDetail();
if (onNoteAdded) onNoteAdded();
} catch (err) {
setNoteError(err.message);
}
setPendingConfirm({
title: 'Delete Note',
message: 'Delete this note?',
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const url = hasGroup
? `${API_BASE}/compliance/notes/${noteId}?group=true`
: `${API_BASE}/compliance/notes/${noteId}`;
const res = await fetch(url, { method: 'DELETE', credentials: 'include' });
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Failed to delete note');
await fetchDetail();
if (onNoteAdded) onNoteAdded();
} catch (err) {
setNoteError(err.message);
}
},
});
};
const activeMetrics = detail?.metrics?.filter(m => m.status === 'active') || [];
@@ -371,6 +380,17 @@ export default function ComplianceDetailPanel({ hostname, onClose, onNoteAdded,
</div>
)}
</div>
{/* Confirmation Modal */}
<ConfirmModal
open={!!pendingConfirm}
title={pendingConfirm?.title}
message={pendingConfirm?.message}
confirmText={pendingConfirm?.confirmText}
variant="danger"
onConfirm={pendingConfirm?.onConfirm}
onCancel={() => setPendingConfirm(null)}
/>
</>
);
}

View File

@@ -6,11 +6,12 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import {
BookOpen, Search, Upload, RefreshCw, Loader,
AlertCircle, FileText, File, Trash2, X,
AlertCircle, FileText, File, Trash2, X, // ⚠️ CONVENTION: FileText and File are imported but unused — remove if not needed
} from 'lucide-react';
import { useAuth } from '../../contexts/AuthContext';
import KnowledgeBaseModal from '../KnowledgeBaseModal';
import KnowledgeBaseViewer from '../KnowledgeBaseViewer';
import ConfirmModal from '../ConfirmModal'; // ⚠️ CONVENTION: ConfirmModal is imported but never used — either integrate it into handleDelete or remove this import
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
const GREEN = '#10B981';
@@ -216,6 +217,7 @@ export default function KnowledgeBasePage() {
const [activeCategory, setActiveCategory] = useState('All');
const [selected, setSelected] = useState(null);
const [showUpload, setShowUpload] = useState(false);
const [pendingConfirm, setPendingConfirm] = useState(null);
// -------------------------------------------------------------------------
// Fetch
@@ -241,17 +243,24 @@ export default function KnowledgeBasePage() {
// Delete
// -------------------------------------------------------------------------
const handleDelete = useCallback(async (article) => {
if (!window.confirm(`Delete "${article.title}"? This cannot be undone.`)) return;
try {
const res = await fetch(`${API_BASE}/knowledge-base/${article.id}`, {
method: 'DELETE', credentials: 'include',
});
if (!res.ok) throw new Error('Delete failed');
setArticles(prev => prev.filter(a => a.id !== article.id));
if (selected?.id === article.id) setSelected(null);
} catch (err) {
alert(`Failed to delete: ${err.message}`);
}
setPendingConfirm({
title: 'Delete Article',
message: `Delete "${article.title}"? This cannot be undone.`,
confirmText: 'Delete',
onConfirm: async () => {
setPendingConfirm(null);
try {
const res = await fetch(`${API_BASE}/knowledge-base/${article.id}`, {
method: 'DELETE', credentials: 'include',
});
if (!res.ok) throw new Error('Delete failed');
setArticles(prev => prev.filter(a => a.id !== article.id));
if (selected?.id === article.id) setSelected(null);
} catch (err) {
alert(`Failed to delete: ${err.message}`);
}
},
});
}, [selected]);
// -------------------------------------------------------------------------
@@ -479,6 +488,17 @@ export default function KnowledgeBasePage() {
onUpdate={() => { fetchArticles(); setShowUpload(false); }}
/>
)}
{/* Confirmation Modal */}
<ConfirmModal
open={!!pendingConfirm}
title={pendingConfirm?.title}
message={pendingConfirm?.message}
confirmText={pendingConfirm?.confirmText}
variant="danger"
onConfirm={pendingConfirm?.onConfirm}
onCancel={() => setPendingConfirm(null)}
/>
</div>
);
}