From 7044e42e947ad36010825debb67af71183ce939f Mon Sep 17 00:00:00 2001 From: LordMathis Date: Sun, 18 May 2025 11:53:17 +0200 Subject: [PATCH] Migrate admin dashboard to ts --- ...{AdminDashboard.jsx => AdminDashboard.tsx} | 18 ++++- .../{AdminStatsTab.jsx => AdminStatsTab.tsx} | 13 +++- .../{AdminUsersTab.jsx => AdminUsersTab.tsx} | 39 ++++++---- .../settings/admin/AdminWorkspacesTab.jsx | 67 ----------------- .../settings/admin/AdminWorkspacesTab.tsx | 73 +++++++++++++++++++ app/src/hooks/useAdminData.ts | 12 ++- 6 files changed, 126 insertions(+), 96 deletions(-) rename app/src/components/settings/admin/{AdminDashboard.jsx => AdminDashboard.tsx} (72%) rename app/src/components/settings/admin/{AdminStatsTab.jsx => AdminStatsTab.tsx} (84%) rename app/src/components/settings/admin/{AdminUsersTab.jsx => AdminUsersTab.tsx} (76%) delete mode 100644 app/src/components/settings/admin/AdminWorkspacesTab.jsx create mode 100644 app/src/components/settings/admin/AdminWorkspacesTab.tsx diff --git a/app/src/components/settings/admin/AdminDashboard.jsx b/app/src/components/settings/admin/AdminDashboard.tsx similarity index 72% rename from app/src/components/settings/admin/AdminDashboard.jsx rename to app/src/components/settings/admin/AdminDashboard.tsx index e095dc5..d3d1e00 100644 --- a/app/src/components/settings/admin/AdminDashboard.jsx +++ b/app/src/components/settings/admin/AdminDashboard.tsx @@ -6,13 +6,23 @@ import AdminUsersTab from './AdminUsersTab'; import AdminWorkspacesTab from './AdminWorkspacesTab'; import AdminStatsTab from './AdminStatsTab'; -const AdminDashboard = ({ opened, onClose }) => { +interface AdminDashboardProps { + opened: boolean; + onClose: () => void; +} + +type AdminTabValue = 'users' | 'workspaces' | 'stats'; + +const AdminDashboard: React.FC = ({ opened, onClose }) => { const { user: currentUser } = useAuth(); - const [activeTab, setActiveTab] = useState('users'); + const [activeTab, setActiveTab] = useState('users'); return ( - + setActiveTab(value as AdminTabValue)} + > }> Users @@ -26,7 +36,7 @@ const AdminDashboard = ({ opened, onClose }) => { - + {currentUser && } diff --git a/app/src/components/settings/admin/AdminStatsTab.jsx b/app/src/components/settings/admin/AdminStatsTab.tsx similarity index 84% rename from app/src/components/settings/admin/AdminStatsTab.jsx rename to app/src/components/settings/admin/AdminStatsTab.tsx index 3b7346f..f661223 100644 --- a/app/src/components/settings/admin/AdminStatsTab.jsx +++ b/app/src/components/settings/admin/AdminStatsTab.tsx @@ -4,8 +4,13 @@ import { IconAlertCircle } from '@tabler/icons-react'; import { useAdminData } from '../../../hooks/useAdminData'; import { formatBytes } from '../../../utils/formatBytes'; -const AdminStatsTab = () => { - const { data: stats, loading, error } = useAdminData('stats'); +interface StatsRow { + label: string; + value: string | number; +} + +const AdminStatsTab: React.FC = () => { + const { data: stats, loading, error } = useAdminData<'stats'>('stats'); if (loading) { return ; @@ -19,7 +24,7 @@ const AdminStatsTab = () => { ); } - const statsRows = [ + const statsRows: StatsRow[] = [ { label: 'Total Users', value: stats.totalUsers }, { label: 'Active Users', value: stats.activeUsers }, { label: 'Total Workspaces', value: stats.totalWorkspaces }, @@ -33,7 +38,7 @@ const AdminStatsTab = () => { System Statistics - +
Metric diff --git a/app/src/components/settings/admin/AdminUsersTab.jsx b/app/src/components/settings/admin/AdminUsersTab.tsx similarity index 76% rename from app/src/components/settings/admin/AdminUsersTab.jsx rename to app/src/components/settings/admin/AdminUsersTab.tsx index f6d5b99..a7e7558 100644 --- a/app/src/components/settings/admin/AdminUsersTab.jsx +++ b/app/src/components/settings/admin/AdminUsersTab.tsx @@ -20,8 +20,14 @@ import { useUserAdmin } from '../../../hooks/useUserAdmin'; import CreateUserModal from '../../modals/user/CreateUserModal'; import EditUserModal from '../../modals/user/EditUserModal'; import DeleteUserModal from '../../modals/user/DeleteUserModal'; +import { User } from '../../../types/authApi'; +import { CreateUserRequest, UpdateUserRequest } from '../../../types/adminApi'; -const AdminUsersTab = ({ currentUser }) => { +interface AdminUsersTabProps { + currentUser: User; +} + +const AdminUsersTab: React.FC = ({ currentUser }) => { const { users, loading, @@ -31,19 +37,24 @@ const AdminUsersTab = ({ currentUser }) => { delete: deleteUser, } = useUserAdmin(); - const [createModalOpened, setCreateModalOpened] = useState(false); - const [editModalData, setEditModalData] = useState(null); - const [deleteModalData, setDeleteModalData] = useState(null); + const [createModalOpened, setCreateModalOpened] = useState(false); + const [editModalData, setEditModalData] = useState(null); + const [deleteModalData, setDeleteModalData] = useState(null); - const handleCreateUser = async (userData) => { + const handleCreateUser = async ( + userData: CreateUserRequest + ): Promise => { return await create(userData); }; - const handleEditUser = async (id, userData) => { + const handleEditUser = async ( + id: number, + userData: UpdateUserRequest + ): Promise => { return await update(id, userData); }; - const handleDeleteClick = (user) => { + const handleDeleteClick = (user: User): void => { if (user.id === currentUser.id) { notifications.show({ title: 'Error', @@ -55,20 +66,20 @@ const AdminUsersTab = ({ currentUser }) => { setDeleteModalData(user); }; - const handleDeleteConfirm = async () => { + const handleDeleteConfirm = async (): Promise => { if (!deleteModalData) return; - const result = await deleteUser(deleteModalData.id); - if (result.success) { + const success = await deleteUser(deleteModalData.id); + if (success) { setDeleteModalData(null); } }; - const rows = users.map((user) => ( + const renderUserRow = (user: User) => ( {user.email} {user.displayName} - {user.role} + {user.role} {new Date(user.createdAt).toLocaleDateString()} @@ -91,7 +102,7 @@ const AdminUsersTab = ({ currentUser }) => { - )); + ); return ( @@ -130,7 +141,7 @@ const AdminUsersTab = ({ currentUser }) => { Actions - {rows} + {users.map(renderUserRow)}
{ - const { data: workspaces, loading, error } = useAdminData('workspaces'); - - const rows = workspaces.map((workspace) => ( - - {workspace.userEmail} - {workspace.workspaceName} - - {new Date(workspace.workspaceCreatedAt).toLocaleDateString()} - - {workspace.totalFiles} - {formatBytes(workspace.totalSize)} - - )); - - return ( - - - - {error && ( - } - title="Error" - color="red" - mb="md" - > - {error} - - )} - - - - Workspace Management - - - - - - - Owner - Name - Created At - Total Files - Total Size - - - {rows} -
-
- ); -}; - -export default AdminWorkspacesTab; diff --git a/app/src/components/settings/admin/AdminWorkspacesTab.tsx b/app/src/components/settings/admin/AdminWorkspacesTab.tsx new file mode 100644 index 0000000..5b64db3 --- /dev/null +++ b/app/src/components/settings/admin/AdminWorkspacesTab.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Table, Group, Text, Box, LoadingOverlay, Alert } from '@mantine/core'; +import { IconAlertCircle } from '@tabler/icons-react'; +import { useAdminData } from '../../../hooks/useAdminData'; +import { formatBytes } from '../../../utils/formatBytes'; +import { FileCountStats, WorkspaceStats } from '../../../types/adminApi'; + +const AdminWorkspacesTab: React.FC = () => { + const { + data: workspaces, + loading, + error, + } = useAdminData<'workspaces'>('workspaces'); + + const renderWorkspaceRow = (workspace: WorkspaceStats) => { + const fileStats: FileCountStats = workspace.fileCountStats || { + totalFiles: 0, + totalSize: 0, + }; + + return ( + + {workspace.userEmail} + {workspace.workspaceName} + + {new Date(workspace.workspaceCreatedAt).toLocaleDateString()} + + {fileStats.totalFiles} + {formatBytes(fileStats.totalSize)} + + ); + }; + + return ( + + + + {error && ( + } + title="Error" + color="red" + mb="md" + > + {error} + + )} + + + + Workspace Management + + + + + + + Owner + Name + Created At + Total Files + Total Size + + + + {!loading && !error && workspaces.map(renderWorkspaceRow)} + +
+
+ ); +}; + +export default AdminWorkspacesTab; diff --git a/app/src/hooks/useAdminData.ts b/app/src/hooks/useAdminData.ts index 36bb66c..4b85e1a 100644 --- a/app/src/hooks/useAdminData.ts +++ b/app/src/hooks/useAdminData.ts @@ -1,9 +1,7 @@ import { useState, useEffect } from 'react'; import { notifications } from '@mantine/notifications'; import { getUsers, getWorkspaces, getSystemStats } from '@/api/admin'; -import { SystemStats } from '@/types/adminApi'; -import { Workspace } from '@/types/workspace'; -import { User } from '@/types/authApi'; +import { SystemStats, UserStats, WorkspaceStats } from '@/types/adminApi'; // Possible types of admin data type AdminDataType = 'stats' | 'workspaces' | 'users'; @@ -12,9 +10,9 @@ type AdminDataType = 'stats' | 'workspaces' | 'users'; type AdminData = T extends 'stats' ? SystemStats : T extends 'workspaces' - ? Workspace[] + ? WorkspaceStats[] : T extends 'users' - ? User[] + ? UserStats[] : never; // Define the return type of the hook @@ -34,9 +32,9 @@ export const useAdminData = ( if (type === 'stats') { return {} as SystemStats as AdminData; } else if (type === 'workspaces') { - return [] as Workspace[] as AdminData; + return [] as WorkspaceStats[] as AdminData; } else if (type === 'users') { - return [] as User[] as AdminData; + return [] as UserStats[] as AdminData; } else { // This case should never happen due to type constraints, // but TypeScript requires us to handle it