Replace window.confirm() with themed ConfirmModal across dashboard
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user