mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 15:44:21 +00:00
Load users in AdminDashboard
This commit is contained in:
@@ -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 }) => {
|
||||
<Button variant="default" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSubmit}>Create User</Button>
|
||||
<Button onClick={handleSubmit} loading={loading}>
|
||||
Create User
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
@@ -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}
|
||||
>
|
||||
<IconTrash size={16} />
|
||||
</ActionIcon>
|
||||
@@ -143,7 +143,20 @@ const AdminDashboard = ({ opened, onClose }) => {
|
||||
|
||||
return (
|
||||
<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">
|
||||
<Text size="xl" fw={700}>
|
||||
User Management
|
||||
@@ -173,6 +186,7 @@ const AdminDashboard = ({ opened, onClose }) => {
|
||||
opened={createModalOpened}
|
||||
onClose={() => setCreateModalOpened(false)}
|
||||
onCreateUser={handleCreateUser}
|
||||
loading={loading}
|
||||
/>
|
||||
</Box>
|
||||
</Modal>
|
||||
|
||||
155
frontend/src/hooks/useAdmin.js
Normal file
155
frontend/src/hooks/useAdmin.js
Normal 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,
|
||||
};
|
||||
};
|
||||
39
frontend/src/services/adminApi.js
Normal file
39
frontend/src/services/adminApi.js
Normal 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();
|
||||
};
|
||||
Reference in New Issue
Block a user