Load users in AdminDashboard

This commit is contained in:
2024-11-07 22:12:37 +01:00
parent 0480c165ae
commit 51751a5af6
3 changed files with 254 additions and 46 deletions

View File

@@ -11,47 +11,33 @@ import {
Text, Text,
ActionIcon, ActionIcon,
Box, Box,
LoadingOverlay,
Alert,
} from '@mantine/core'; } 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 CreateUserModal = ({ opened, onClose, onCreateUser, loading }) => {
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 [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [displayName, setDisplayName] = useState(''); const [displayName, setDisplayName] = useState('');
const [role, setRole] = useState('viewer'); const [role, setRole] = useState('viewer');
const handleSubmit = () => { const handleSubmit = async () => {
onCreateUser({ email, password, displayName, role }); const result = await onCreateUser({ email, password, displayName, role });
if (result.success) {
setEmail(''); setEmail('');
setPassword(''); setPassword('');
setDisplayName(''); setDisplayName('');
setRole('viewer'); setRole('viewer');
onClose(); onClose();
}
}; };
return ( return (
@@ -92,7 +78,9 @@ const CreateUserModal = ({ opened, onClose, onCreateUser }) => {
<Button variant="default" onClick={onClose}> <Button variant="default" onClick={onClose}>
Cancel Cancel
</Button> </Button>
<Button onClick={handleSubmit}>Create User</Button> <Button onClick={handleSubmit} loading={loading}>
Create User
</Button>
</Group> </Group>
</Stack> </Stack>
</Modal> </Modal>
@@ -100,20 +88,31 @@ const CreateUserModal = ({ opened, onClose, onCreateUser }) => {
}; };
const AdminDashboard = ({ opened, onClose }) => { 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 [createModalOpened, setCreateModalOpened] = useState(false);
const { user: currentUser } = useAuth();
const handleCreateUser = (newUser) => { const handleCreateUser = async (userData) => {
// In production, make an API call here return await create(userData);
setUsers([
...users,
{ ...newUser, id: users.length + 1, createdAt: new Date().toISOString() },
]);
}; };
const handleDeleteUser = (userId) => { const handleDeleteUser = async (userId) => {
// In production, make an API call here if (userId === currentUser.id) {
setUsers(users.filter((user) => user.id !== userId)); notifications.show({
title: 'Error',
message: 'You cannot delete your own account',
color: 'red',
});
return;
}
return await deleteUser(userId);
}; };
const rows = users.map((user) => ( const rows = users.map((user) => (
@@ -133,6 +132,7 @@ const AdminDashboard = ({ opened, onClose }) => {
variant="subtle" variant="subtle"
color="red" color="red"
onClick={() => handleDeleteUser(user.id)} onClick={() => handleDeleteUser(user.id)}
disabled={user.id === currentUser.id}
> >
<IconTrash size={16} /> <IconTrash size={16} />
</ActionIcon> </ActionIcon>
@@ -143,7 +143,20 @@ const AdminDashboard = ({ opened, onClose }) => {
return ( return (
<Modal opened={opened} onClose={onClose} size="xl" title="Admin Dashboard"> <Modal opened={opened} onClose={onClose} size="xl" title="Admin Dashboard">
<Box> <Box pos="relative">
<LoadingOverlay visible={loading} />
{error && (
<Alert
icon={<IconAlertCircle size={16} />}
title="Error"
color="red"
mb="md"
>
{error}
</Alert>
)}
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Text size="xl" fw={700}> <Text size="xl" fw={700}>
User Management User Management
@@ -173,6 +186,7 @@ const AdminDashboard = ({ opened, onClose }) => {
opened={createModalOpened} opened={createModalOpened}
onClose={() => setCreateModalOpened(false)} onClose={() => setCreateModalOpened(false)}
onCreateUser={handleCreateUser} onCreateUser={handleCreateUser}
loading={loading}
/> />
</Box> </Box>
</Modal> </Modal>

View File

@@ -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,
};
};

View File

@@ -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();
};