Files
gridpilot.gg/apps/website/app/admin/AdminUsersClient.tsx
2026-01-12 19:24:59 +01:00

115 lines
3.4 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback } from 'react';
import { AdminUsersTemplate } from '@/templates/AdminUsersTemplate';
import { AdminUsersViewData } from '@/templates/AdminUsersViewData';
import { AdminUsersPresenter } from '@/lib/presenters/AdminUsersPresenter';
import { AdminUsersPageQuery } from '@/lib/page-queries/AdminUsersPageQuery';
import { updateUserStatus, deleteUser } from './actions';
export function AdminUsersClient() {
const [viewData, setViewData] = useState<AdminUsersViewData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [search, setSearch] = useState('');
const [roleFilter, setRoleFilter] = useState('');
const [statusFilter, setStatusFilter] = useState('');
const [deletingUser, setDeletingUser] = useState<string | null>(null);
const loadUsers = useCallback(async () => {
try {
setLoading(true);
setError(null);
const query = new AdminUsersPageQuery();
const result = await query.execute({
search: search || undefined,
role: roleFilter || undefined,
status: statusFilter || undefined,
page: 1,
limit: 50,
});
if (result.status === 'ok') {
const data = AdminUsersPresenter.present(result.dto);
setViewData(data);
} else if (result.status === 'notFound') {
setError('Access denied - You must be logged in as an Owner or Admin');
} else {
setError('Failed to load users');
}
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to load users';
setError(message);
} finally {
setLoading(false);
}
}, [search, roleFilter, statusFilter]);
useEffect(() => {
const timeout = setTimeout(() => {
loadUsers();
}, 300);
return () => clearTimeout(timeout);
}, [loadUsers]);
const handleUpdateStatus = async (userId: string, newStatus: string) => {
try {
await updateUserStatus(userId, newStatus);
await loadUsers();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to update status');
}
};
const handleDeleteUser = async (userId: string) => {
if (!confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
return;
}
try {
setDeletingUser(userId);
await deleteUser(userId);
await loadUsers();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to delete user');
} finally {
setDeletingUser(null);
}
};
const handleClearFilters = () => {
setSearch('');
setRoleFilter('');
setStatusFilter('');
};
if (!viewData) {
return (
<div className="flex flex-col items-center justify-center py-20 space-y-3">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-blue"></div>
<div className="text-gray-400">Loading users...</div>
</div>
);
}
return (
<AdminUsersTemplate
viewData={viewData}
onRefresh={loadUsers}
onSearch={setSearch}
onFilterRole={setRoleFilter}
onFilterStatus={setStatusFilter}
onClearFilters={handleClearFilters}
onUpdateStatus={handleUpdateStatus}
onDeleteUser={handleDeleteUser}
search={search}
roleFilter={roleFilter}
statusFilter={statusFilter}
loading={loading}
error={error}
deletingUser={deletingUser}
/>
);
}