115 lines
3.4 KiB
TypeScript
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}
|
|
/>
|
|
);
|
|
} |