From 51751a5af6930c39349183a90b56e8908d72add5 Mon Sep 17 00:00:00 2001 From: LordMathis Date: Thu, 7 Nov 2024 22:12:37 +0100 Subject: [PATCH] Load users in AdminDashboard --- .../settings/admin/AdminDashboard.jsx | 106 ++++++------ frontend/src/hooks/useAdmin.js | 155 ++++++++++++++++++ frontend/src/services/adminApi.js | 39 +++++ 3 files changed, 254 insertions(+), 46 deletions(-) create mode 100644 frontend/src/hooks/useAdmin.js create mode 100644 frontend/src/services/adminApi.js diff --git a/frontend/src/components/settings/admin/AdminDashboard.jsx b/frontend/src/components/settings/admin/AdminDashboard.jsx index 74f468b..3345ce2 100644 --- a/frontend/src/components/settings/admin/AdminDashboard.jsx +++ b/frontend/src/components/settings/admin/AdminDashboard.jsx @@ -11,47 +11,33 @@ import { Text, ActionIcon, Box, + LoadingOverlay, + Alert, } from '@mantine/core'; -import { IconTrash, IconEdit, IconPlus } from '@tabler/icons-react'; +import { + IconTrash, + IconEdit, + IconPlus, + IconAlertCircle, +} from '@tabler/icons-react'; +import { useAdmin } from '../../../hooks/useAdmin'; +import { useAuth } from '../../../contexts/AuthContext'; -// Dummy data - replace with actual API calls in production -const DUMMY_USERS = [ - { - id: 1, - email: 'admin@example.com', - displayName: 'Admin User', - role: 'admin', - createdAt: '2024-01-01', - }, - { - id: 2, - email: 'editor@example.com', - displayName: 'Editor User', - role: 'editor', - createdAt: '2024-01-02', - }, - { - id: 3, - email: 'viewer@example.com', - displayName: 'Viewer User', - role: 'viewer', - createdAt: '2024-01-03', - }, -]; - -const CreateUserModal = ({ opened, onClose, onCreateUser }) => { +const CreateUserModal = ({ opened, onClose, onCreateUser, loading }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [displayName, setDisplayName] = useState(''); const [role, setRole] = useState('viewer'); - const handleSubmit = () => { - onCreateUser({ email, password, displayName, role }); - setEmail(''); - setPassword(''); - setDisplayName(''); - setRole('viewer'); - onClose(); + const handleSubmit = async () => { + const result = await onCreateUser({ email, password, displayName, role }); + if (result.success) { + setEmail(''); + setPassword(''); + setDisplayName(''); + setRole('viewer'); + onClose(); + } }; return ( @@ -92,7 +78,9 @@ const CreateUserModal = ({ opened, onClose, onCreateUser }) => { - + @@ -100,20 +88,31 @@ const CreateUserModal = ({ opened, onClose, onCreateUser }) => { }; const AdminDashboard = ({ opened, onClose }) => { - const [users, setUsers] = useState(DUMMY_USERS); + const { + data: users, + loading, + error, + create, + delete: deleteUser, + } = useAdmin('users'); const [createModalOpened, setCreateModalOpened] = useState(false); + const { user: currentUser } = useAuth(); - const handleCreateUser = (newUser) => { - // In production, make an API call here - setUsers([ - ...users, - { ...newUser, id: users.length + 1, createdAt: new Date().toISOString() }, - ]); + const handleCreateUser = async (userData) => { + return await create(userData); }; - const handleDeleteUser = (userId) => { - // In production, make an API call here - setUsers(users.filter((user) => user.id !== userId)); + const handleDeleteUser = async (userId) => { + if (userId === currentUser.id) { + notifications.show({ + title: 'Error', + message: 'You cannot delete your own account', + color: 'red', + }); + return; + } + + return await deleteUser(userId); }; const rows = users.map((user) => ( @@ -133,6 +132,7 @@ const AdminDashboard = ({ opened, onClose }) => { variant="subtle" color="red" onClick={() => handleDeleteUser(user.id)} + disabled={user.id === currentUser.id} > @@ -143,7 +143,20 @@ const AdminDashboard = ({ opened, onClose }) => { return ( - + + + + {error && ( + } + title="Error" + color="red" + mb="md" + > + {error} + + )} + User Management @@ -173,6 +186,7 @@ const AdminDashboard = ({ opened, onClose }) => { opened={createModalOpened} onClose={() => setCreateModalOpened(false)} onCreateUser={handleCreateUser} + loading={loading} /> diff --git a/frontend/src/hooks/useAdmin.js b/frontend/src/hooks/useAdmin.js new file mode 100644 index 0000000..08d3cc4 --- /dev/null +++ b/frontend/src/hooks/useAdmin.js @@ -0,0 +1,155 @@ +import { useState, useCallback, useEffect } from 'react'; +import { notifications } from '@mantine/notifications'; +import * as adminApi from '../services/adminApi'; + +export const useAdmin = (resource) => { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchData = useCallback(async () => { + setLoading(true); + try { + let result; + switch (resource) { + case 'users': + result = await adminApi.listUsers(); + break; + case 'stats': + result = await adminApi.getSystemStats(); + break; + default: + throw new Error(`Unknown resource type: ${resource}`); + } + setData(result); + setError(null); + } catch (err) { + console.error(`Failed to fetch ${resource}:`, err); + setError(err.message); + notifications.show({ + title: 'Error', + message: `Failed to load ${resource}. Please try again.`, + color: 'red', + }); + } finally { + setLoading(false); + } + }, [resource]); + + const createItem = useCallback( + async (itemData) => { + try { + let newItem; + switch (resource) { + case 'users': + newItem = await adminApi.createUser(itemData); + break; + default: + throw new Error(`Create not supported for resource: ${resource}`); + } + setData((prevData) => + Array.isArray(prevData) ? [...prevData, newItem] : prevData + ); + notifications.show({ + title: 'Success', + message: `${resource} created successfully`, + color: 'green', + }); + return { success: true, data: newItem }; + } catch (err) { + console.error(`Failed to create ${resource}:`, err); + notifications.show({ + title: 'Error', + message: err.message || `Failed to create ${resource}`, + color: 'red', + }); + return { success: false, error: err.message }; + } + }, + [resource] + ); + + const deleteItem = useCallback( + async (itemId) => { + try { + switch (resource) { + case 'users': + await adminApi.deleteUser(itemId); + break; + default: + throw new Error(`Delete not supported for resource: ${resource}`); + } + setData((prevData) => + Array.isArray(prevData) + ? prevData.filter((item) => item.id !== itemId) + : prevData + ); + notifications.show({ + title: 'Success', + message: `${resource} deleted successfully`, + color: 'green', + }); + return { success: true }; + } catch (err) { + console.error(`Failed to delete ${resource}:`, err); + notifications.show({ + title: 'Error', + message: err.message || `Failed to delete ${resource}`, + color: 'red', + }); + return { success: false, error: err.message }; + } + }, + [resource] + ); + + const updateItem = useCallback( + async (itemId, itemData) => { + try { + let updatedItem; + switch (resource) { + case 'users': + updatedItem = await adminApi.updateUser(itemId, itemData); + break; + default: + throw new Error(`Update not supported for resource: ${resource}`); + } + setData((prevData) => + Array.isArray(prevData) + ? prevData.map((item) => (item.id === itemId ? updatedItem : item)) + : prevData + ); + notifications.show({ + title: 'Success', + message: `${resource} updated successfully`, + color: 'green', + }); + return { success: true, data: updatedItem }; + } catch (err) { + console.error(`Failed to update ${resource}:`, err); + notifications.show({ + title: 'Error', + message: err.message || `Failed to update ${resource}`, + color: 'red', + }); + return { success: false, error: err.message }; + } + }, + [resource] + ); + + // Fetch data on mount + useEffect(() => { + fetchData(); + }, [fetchData]); + + return { + data, + loading, + error, + refresh: fetchData, + create: createItem, + delete: deleteItem, + update: updateItem, + }; +}; diff --git a/frontend/src/services/adminApi.js b/frontend/src/services/adminApi.js new file mode 100644 index 0000000..3c6c4a0 --- /dev/null +++ b/frontend/src/services/adminApi.js @@ -0,0 +1,39 @@ +import { apiCall } from './authApi'; +import { API_BASE_URL } from '../utils/constants'; + +const ADMIN_BASE_URL = `${API_BASE_URL}/admin`; + +// User Management +export const listUsers = async () => { + const response = await apiCall(`${ADMIN_BASE_URL}/users`); + return response.json(); +}; + +export const createUser = async (userData) => { + const response = await apiCall(`${ADMIN_BASE_URL}/users`, { + method: 'POST', + body: JSON.stringify(userData), + }); + return response.json(); +}; + +export const deleteUser = async (userId) => { + const response = await apiCall(`${ADMIN_BASE_URL}/users/${userId}`, { + method: 'DELETE', + }); + return response.json(); +}; + +export const updateUser = async (userId, userData) => { + const response = await apiCall(`${ADMIN_BASE_URL}/users/${userId}`, { + method: 'PUT', + body: JSON.stringify(userData), + }); + return response.json(); +}; + +// System Statistics +export const getSystemStats = async () => { + const response = await apiCall(`${ADMIN_BASE_URL}/stats`); + return response.json(); +};