website refactor
This commit is contained in:
115
apps/website/app/admin/AdminUsersClient.tsx
Normal file
115
apps/website/app/admin/AdminUsersClient.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
'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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user