Added auth
This commit is contained in:
429
frontend/app.js
429
frontend/app.js
@@ -4,9 +4,137 @@ let currentDrug = null;
|
||||
let showLowStockOnly = false;
|
||||
let searchTerm = '';
|
||||
let expandedDrugs = new Set();
|
||||
let currentUser = null;
|
||||
let accessToken = null;
|
||||
|
||||
// Initialize
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
checkAuth();
|
||||
});
|
||||
|
||||
// Check if user is already logged in
|
||||
function checkAuth() {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
const user = localStorage.getItem('currentUser');
|
||||
|
||||
if (token && user) {
|
||||
accessToken = token;
|
||||
currentUser = JSON.parse(user);
|
||||
showMainApp();
|
||||
} else {
|
||||
showLoginPage();
|
||||
}
|
||||
}
|
||||
|
||||
// Show login page
|
||||
function showLoginPage() {
|
||||
document.getElementById('loginPage').style.display = 'flex';
|
||||
document.getElementById('mainApp').style.display = 'none';
|
||||
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
if (loginForm) loginForm.addEventListener('submit', handleLogin);
|
||||
}
|
||||
|
||||
// Show main app
|
||||
function showMainApp() {
|
||||
document.getElementById('loginPage').style.display = 'none';
|
||||
document.getElementById('mainApp').style.display = 'block';
|
||||
|
||||
const userDisplay = document.getElementById('currentUser');
|
||||
if (userDisplay) {
|
||||
userDisplay.textContent = `👤 ${currentUser.username}`;
|
||||
}
|
||||
|
||||
const adminBtn = document.getElementById('adminBtn');
|
||||
if (adminBtn) {
|
||||
adminBtn.style.display = currentUser.is_admin ? 'block' : 'none';
|
||||
}
|
||||
|
||||
setupEventListeners();
|
||||
loadDrugs();
|
||||
}
|
||||
|
||||
// Handle login
|
||||
async function handleLogin(e) {
|
||||
e.preventDefault();
|
||||
const username = document.getElementById('loginUsername').value;
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Invalid credentials');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
accessToken = data.access_token;
|
||||
currentUser = data.user;
|
||||
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
localStorage.setItem('currentUser', JSON.stringify(currentUser));
|
||||
|
||||
document.getElementById('loginForm').reset();
|
||||
const errorDiv = document.getElementById('loginError');
|
||||
if (errorDiv) errorDiv.style.display = 'none';
|
||||
showMainApp();
|
||||
} catch (error) {
|
||||
const errorDiv = document.getElementById('loginError');
|
||||
if (errorDiv) {
|
||||
errorDiv.textContent = error.message;
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle register
|
||||
// Logout
|
||||
function handleLogout() {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('currentUser');
|
||||
accessToken = null;
|
||||
currentUser = null;
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
if (loginForm) loginForm.reset();
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
if (registerForm) {
|
||||
registerForm.style.display = 'none';
|
||||
}
|
||||
const form = document.getElementById('loginForm');
|
||||
if (form) form.style.display = 'block';
|
||||
showLoginPage();
|
||||
}
|
||||
|
||||
// API helper with authentication
|
||||
async function apiCall(endpoint, options = {}) {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
};
|
||||
|
||||
if (accessToken) {
|
||||
headers['Authorization'] = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_URL}${endpoint}`, {
|
||||
...options,
|
||||
headers
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
handleLogout();
|
||||
throw new Error('Authentication expired');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
function setupEventListeners() {
|
||||
const drugForm = document.getElementById('drugForm');
|
||||
const variantForm = document.getElementById('variantForm');
|
||||
const editVariantForm = document.getElementById('editVariantForm');
|
||||
@@ -18,6 +146,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const dispenseModal = document.getElementById('dispenseModal');
|
||||
const editModal = document.getElementById('editModal');
|
||||
const addDrugBtn = document.getElementById('addDrugBtn');
|
||||
const dispenseBtn = document.getElementById('dispenseBtn');
|
||||
const cancelAddBtn = document.getElementById('cancelAddBtn');
|
||||
const cancelVariantBtn = document.getElementById('cancelVariantBtn');
|
||||
const cancelEditVariantBtn = document.getElementById('cancelEditVariantBtn');
|
||||
@@ -25,41 +154,75 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const cancelEditBtn = document.getElementById('cancelEditBtn');
|
||||
const showAllBtn = document.getElementById('showAllBtn');
|
||||
const showLowStockBtn = document.getElementById('showLowStockBtn');
|
||||
const userMenuBtn = document.getElementById('userMenuBtn');
|
||||
const adminBtn = document.getElementById('adminBtn');
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
const changePasswordBtn = document.getElementById('changePasswordBtn');
|
||||
|
||||
// Modal close buttons
|
||||
const closeButtons = document.querySelectorAll('.close');
|
||||
|
||||
drugForm.addEventListener('submit', handleAddDrug);
|
||||
variantForm.addEventListener('submit', handleAddVariant);
|
||||
editVariantForm.addEventListener('submit', handleEditVariant);
|
||||
dispenseForm.addEventListener('submit', handleDispenseDrug);
|
||||
editForm.addEventListener('submit', handleEditDrug);
|
||||
if (drugForm) drugForm.addEventListener('submit', handleAddDrug);
|
||||
if (variantForm) variantForm.addEventListener('submit', handleAddVariant);
|
||||
if (editVariantForm) editVariantForm.addEventListener('submit', handleEditVariant);
|
||||
if (dispenseForm) dispenseForm.addEventListener('submit', handleDispenseDrug);
|
||||
if (editForm) editForm.addEventListener('submit', handleEditDrug);
|
||||
|
||||
addDrugBtn.addEventListener('click', () => openModal(addModal));
|
||||
if (addDrugBtn) addDrugBtn.addEventListener('click', () => openModal(addModal));
|
||||
if (dispenseBtn) dispenseBtn.addEventListener('click', () => {
|
||||
updateDispenseDrugSelect();
|
||||
openModal(dispenseModal);
|
||||
});
|
||||
|
||||
if (cancelAddBtn) cancelAddBtn.addEventListener('click', () => closeModal(addModal));
|
||||
if (cancelVariantBtn) cancelVariantBtn.addEventListener('click', () => closeModal(addVariantModal));
|
||||
if (cancelEditVariantBtn) cancelEditVariantBtn.addEventListener('click', () => closeModal(editVariantModal));
|
||||
if (cancelDispenseBtn) cancelDispenseBtn.addEventListener('click', () => closeModal(dispenseModal));
|
||||
if (cancelEditBtn) cancelEditBtn.addEventListener('click', closeEditModal);
|
||||
|
||||
cancelAddBtn.addEventListener('click', () => closeModal(addModal));
|
||||
cancelVariantBtn.addEventListener('click', () => closeModal(addVariantModal));
|
||||
cancelEditVariantBtn.addEventListener('click', () => closeModal(editVariantModal));
|
||||
cancelDispenseBtn.addEventListener('click', () => closeModal(dispenseModal));
|
||||
cancelEditBtn.addEventListener('click', closeEditModal);
|
||||
const closeHistoryBtn = document.getElementById('closeHistoryBtn');
|
||||
closeHistoryBtn.addEventListener('click', () => closeModal(document.getElementById('historyModal')));
|
||||
if (closeHistoryBtn) closeHistoryBtn.addEventListener('click', () => closeModal(document.getElementById('historyModal')));
|
||||
|
||||
const closeUserManagementBtn = document.getElementById('closeUserManagementBtn');
|
||||
if (closeUserManagementBtn) closeUserManagementBtn.addEventListener('click', () => closeModal(document.getElementById('userManagementModal')));
|
||||
|
||||
const changePasswordForm = document.getElementById('changePasswordForm');
|
||||
if (changePasswordForm) changePasswordForm.addEventListener('submit', handleChangePassword);
|
||||
|
||||
const cancelChangePasswordBtn = document.getElementById('cancelChangePasswordBtn');
|
||||
if (cancelChangePasswordBtn) cancelChangePasswordBtn.addEventListener('click', () => closeModal(document.getElementById('changePasswordModal')));
|
||||
|
||||
const adminChangePasswordForm = document.getElementById('adminChangePasswordForm');
|
||||
if (adminChangePasswordForm) adminChangePasswordForm.addEventListener('submit', handleAdminChangePassword);
|
||||
|
||||
const cancelAdminChangePasswordBtn = document.getElementById('cancelAdminChangePasswordBtn');
|
||||
if (cancelAdminChangePasswordBtn) cancelAdminChangePasswordBtn.addEventListener('click', () => closeModal(document.getElementById('adminChangePasswordModal')));
|
||||
|
||||
closeButtons.forEach(btn => btn.addEventListener('click', (e) => {
|
||||
const modal = e.target.closest('.modal');
|
||||
closeModal(modal);
|
||||
}));
|
||||
|
||||
showAllBtn.addEventListener('click', () => {
|
||||
if (showAllBtn) showAllBtn.addEventListener('click', () => {
|
||||
showLowStockOnly = false;
|
||||
updateFilterButtons();
|
||||
renderDrugs();
|
||||
});
|
||||
showLowStockBtn.addEventListener('click', () => {
|
||||
if (showLowStockBtn) showLowStockBtn.addEventListener('click', () => {
|
||||
showLowStockOnly = true;
|
||||
updateFilterButtons();
|
||||
renderDrugs();
|
||||
});
|
||||
|
||||
// User menu
|
||||
if (userMenuBtn) userMenuBtn.addEventListener('click', () => {
|
||||
const dropdown = document.getElementById('userDropdown');
|
||||
if (dropdown) dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
if (changePasswordBtn) changePasswordBtn.addEventListener('click', openChangePasswordModal);
|
||||
if (adminBtn) adminBtn.addEventListener('click', openUserManagement);
|
||||
if (logoutBtn) logoutBtn.addEventListener('click', handleLogout);
|
||||
|
||||
// Search functionality
|
||||
const drugSearch = document.getElementById('drugSearch');
|
||||
@@ -80,14 +243,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
closeModal(e.target);
|
||||
}
|
||||
});
|
||||
|
||||
loadDrugs();
|
||||
});
|
||||
}
|
||||
|
||||
// Load drugs from API
|
||||
async function loadDrugs() {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/drugs`);
|
||||
const response = await apiCall('/drugs');
|
||||
if (!response.ok) throw new Error('Failed to load drugs');
|
||||
allDrugs = await response.json();
|
||||
|
||||
@@ -96,18 +257,32 @@ async function loadDrugs() {
|
||||
} catch (error) {
|
||||
console.error('Error loading drugs:', error);
|
||||
document.getElementById('drugsList').innerHTML =
|
||||
'<p class="empty">Error loading drugs. Make sure the backend is running on http://localhost:8000</p>';
|
||||
'<p class="empty">Error loading drugs. Make sure the backend is running.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Modal utility functions
|
||||
function openModal(modal) {
|
||||
// Find the highest z-index among currently visible modals
|
||||
const visibleModals = document.querySelectorAll('.modal.show');
|
||||
let maxZIndex = 1000;
|
||||
|
||||
visibleModals.forEach(m => {
|
||||
const zIndex = parseInt(window.getComputedStyle(m).zIndex, 10) || 1000;
|
||||
if (zIndex > maxZIndex) {
|
||||
maxZIndex = zIndex;
|
||||
}
|
||||
});
|
||||
|
||||
// Set the new modal's z-index higher than any existing modal
|
||||
modal.style.zIndex = (maxZIndex + 100).toString();
|
||||
modal.classList.add('show');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeModal(modal) {
|
||||
modal.classList.remove('show');
|
||||
modal.style.zIndex = '1000';
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
|
||||
@@ -224,9 +399,8 @@ async function handleAddDrug(e) {
|
||||
|
||||
try {
|
||||
// Create the drug first
|
||||
const drugResponse = await fetch(`${API_URL}/drugs`, {
|
||||
const drugResponse = await apiCall('/drugs', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(drugData)
|
||||
});
|
||||
|
||||
@@ -243,9 +417,8 @@ async function handleAddDrug(e) {
|
||||
low_stock_threshold: parseFloat(document.getElementById('initialVariantThreshold').value) || 10
|
||||
};
|
||||
|
||||
const variantResponse = await fetch(`${API_URL}/drugs/${createdDrug.id}/variants`, {
|
||||
const variantResponse = await apiCall(`/drugs/${createdDrug.id}/variants`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(variantData)
|
||||
});
|
||||
|
||||
@@ -282,15 +455,14 @@ async function handleDispenseDrug(e) {
|
||||
const dispensingData = {
|
||||
drug_variant_id: variantId,
|
||||
quantity: quantity,
|
||||
animal_name: animalName,
|
||||
animal_name: animalName || null,
|
||||
user_name: userName,
|
||||
notes: notes || null
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/dispense`, {
|
||||
const response = await apiCall('/dispense', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(dispensingData)
|
||||
});
|
||||
|
||||
@@ -360,9 +532,8 @@ async function handleAddVariant(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/drugs/${drugId}/variants`, {
|
||||
const response = await apiCall(`/drugs/${drugId}/variants`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(variantData)
|
||||
});
|
||||
|
||||
@@ -412,9 +583,8 @@ async function handleEditVariant(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/variants/${variantId}`, {
|
||||
const response = await apiCall(`/variants/${variantId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(variantData)
|
||||
});
|
||||
|
||||
@@ -448,7 +618,7 @@ async function deleteVariant(variantId) {
|
||||
if (!confirm('Are you sure you want to delete this variant?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/variants/${variantId}`, {
|
||||
const response = await apiCall(`/variants/${variantId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
@@ -476,7 +646,7 @@ async function showDrugHistory(drugId) {
|
||||
openModal(historyModal);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/dispense/history`);
|
||||
const response = await apiCall(`/dispense/history`);
|
||||
if (!response.ok) throw new Error('Failed to fetch history');
|
||||
|
||||
const allHistory = await response.json();
|
||||
@@ -546,9 +716,8 @@ async function handleEditDrug(e) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/drugs/${drugId}`, {
|
||||
const response = await apiCall(`/drugs/${drugId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(drugData)
|
||||
});
|
||||
|
||||
@@ -568,7 +737,7 @@ async function deleteDrug(drugId) {
|
||||
if (!confirm('Are you sure you want to delete this drug?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/drugs/${drugId}`, {
|
||||
const response = await apiCall(`/drugs/${drugId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
@@ -582,6 +751,104 @@ async function deleteDrug(drugId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Password Management
|
||||
function openChangePasswordModal() {
|
||||
const modal = document.getElementById('changePasswordModal');
|
||||
document.getElementById('changePasswordForm').reset();
|
||||
|
||||
// Close dropdown
|
||||
const dropdown = document.getElementById('userDropdown');
|
||||
if (dropdown) dropdown.style.display = 'none';
|
||||
|
||||
openModal(modal);
|
||||
}
|
||||
|
||||
async function handleChangePassword(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const currentPassword = document.getElementById('currentPassword').value;
|
||||
const newPassword = document.getElementById('newPassword').value;
|
||||
const confirmPassword = document.getElementById('confirmNewPassword').value;
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('New passwords do not match!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < 1) {
|
||||
alert('New password cannot be empty!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiCall('/auth/change-password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
current_password: currentPassword,
|
||||
new_password: newPassword
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to change password');
|
||||
}
|
||||
|
||||
alert('Password changed successfully!');
|
||||
closeModal(document.getElementById('changePasswordModal'));
|
||||
} catch (error) {
|
||||
console.error('Error changing password:', error);
|
||||
alert('Failed to change password: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function openAdminChangePasswordModal(userId, username) {
|
||||
const modal = document.getElementById('adminChangePasswordModal');
|
||||
document.getElementById('adminChangePasswordForm').reset();
|
||||
document.getElementById('adminChangePasswordUserId').value = userId;
|
||||
document.getElementById('adminChangePasswordUsername').value = username;
|
||||
openModal(modal);
|
||||
}
|
||||
|
||||
async function handleAdminChangePassword(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const userId = document.getElementById('adminChangePasswordUserId').value;
|
||||
const newPassword = document.getElementById('adminChangePasswordNewPassword').value;
|
||||
const confirmPassword = document.getElementById('adminChangePasswordConfirm').value;
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('Passwords do not match!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < 1) {
|
||||
alert('Password cannot be empty!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiCall(`/users/${userId}/change-password`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
new_password: newPassword
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to change password');
|
||||
}
|
||||
|
||||
alert('Password changed successfully!');
|
||||
closeModal(document.getElementById('adminChangePasswordModal'));
|
||||
openUserManagement();
|
||||
} catch (error) {
|
||||
console.error('Error changing password:', error);
|
||||
alert('Failed to change password: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update filter button states
|
||||
function updateFilterButtons() {
|
||||
document.getElementById('showAllBtn').classList.toggle('active', !showLowStockOnly);
|
||||
@@ -594,3 +861,93 @@ function escapeHtml(text) {
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// User Management
|
||||
async function openUserManagement() {
|
||||
const modal = document.getElementById('userManagementModal');
|
||||
document.getElementById('newUsername').value = '';
|
||||
document.getElementById('newUserPassword').value = '';
|
||||
|
||||
const usersList = document.getElementById('usersList');
|
||||
usersList.innerHTML = '<h3>Users</h3><p class="loading">Loading users...</p>';
|
||||
|
||||
try {
|
||||
const response = await apiCall('/users');
|
||||
if (!response.ok) throw new Error('Failed to load users');
|
||||
|
||||
const users = await response.json();
|
||||
|
||||
const usersHtml = `
|
||||
<h3>Users</h3>
|
||||
<div class="users-table">
|
||||
${users.map(user => `
|
||||
<div class="user-item">
|
||||
<span>${user.username}</span>
|
||||
<span class="admin-badge">${user.is_admin ? '👑 Admin' : 'User'}</span>
|
||||
<button class="btn btn-secondary btn-small" onclick="openAdminChangePasswordModal(${user.id}, '${escapeHtml(user.username)}')">🔑 Password</button>
|
||||
${user.id !== currentUser.id ? `
|
||||
<button class="btn btn-danger btn-small" onclick="deleteUser(${user.id})">Delete</button>
|
||||
` : ''}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
usersList.innerHTML = usersHtml;
|
||||
} catch (error) {
|
||||
console.error('Error loading users:', error);
|
||||
usersList.innerHTML = '<h3>Users</h3><p class="empty">Error loading users</p>';
|
||||
}
|
||||
|
||||
const createUserForm = document.getElementById('createUserForm');
|
||||
if (createUserForm) {
|
||||
createUserForm.onsubmit = createUser;
|
||||
}
|
||||
|
||||
openModal(modal);
|
||||
}
|
||||
|
||||
// Create user
|
||||
async function createUser(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('newUsername').value;
|
||||
const password = document.getElementById('newUserPassword').value;
|
||||
|
||||
try {
|
||||
const response = await apiCall('/users', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to create user');
|
||||
}
|
||||
|
||||
document.getElementById('newUsername').value = '';
|
||||
document.getElementById('newUserPassword').value = '';
|
||||
alert('User created successfully!');
|
||||
openUserManagement();
|
||||
} catch (error) {
|
||||
console.error('Error creating user:', error);
|
||||
alert('Failed to create user: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete user
|
||||
async function deleteUser(userId) {
|
||||
if (!confirm('Are you sure you want to delete this user?')) return;
|
||||
|
||||
try {
|
||||
const response = await apiCall(`/users/${userId}`, { method: 'DELETE' });
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete user');
|
||||
|
||||
alert('User deleted successfully!');
|
||||
openUserManagement();
|
||||
} catch (error) {
|
||||
console.error('Error deleting user:', error);
|
||||
alert('Failed to delete user: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user