All files / src/views/backoffice UsersAdminPage.tsx

0% Statements 0/92
0% Branches 0/1
0% Functions 0/1
0% Lines 0/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                                                                                                                                                                                                       
import { useMemo } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import type { AdminUserFilters } from '@/models/admin';
import { deactivateAdminUser, listAdminUsers } from '@/services/adminService';
import { useAdminListViewModel } from '@/viewmodels/admin/useAdminListViewModel';
import { useAdminMutation } from '@/viewmodels/admin/useAdminMutation';
import { BackofficeConfirmAction, BackofficeIdCell, BackofficePageShell, BackofficePagination, BackofficeStatusPill, BackofficeTableState, formatAdminDate } from './BackofficeListParts';
 
const INITIAL_FILTERS: AdminUserFilters = {
  q: '',
  status: '',
  role: undefined,
  created_from: '',
  created_to: '',
};
 
export default function UsersAdminPage() {
  const { token } = useAuth();
  const initialFilters = useMemo(() => INITIAL_FILTERS, []);
  const vm = useAdminListViewModel({
    token,
    initialFilters,
    fetchPage: listAdminUsers,
  });
  const mutation = useAdminMutation(vm.retry);
 
  return (
    <BackofficePageShell
      title="Users"
      subtitle="Search accounts by identity, status, role, and creation window."
      filters={(
        <>
          <input aria-label="Search users" placeholder="Search username, email, phone" value={vm.filters.q ?? ''} onChange={(event) => vm.setFilter('q', event.target.value)} />
          <select aria-label="User status" value={vm.filters.status ?? ''} onChange={(event) => vm.setFilter('status', event.target.value as AdminUserFilters['status'])}>
            <option value="">Any status</option>
            <option value="active">active</option>
            <option value="deactivated">deactivated</option>
          </select>
          <select aria-label="User role" value={vm.filters.role ?? ''} onChange={(event) => vm.setFilter('role', event.target.value === '' ? undefined : event.target.value as AdminUserFilters['role'])}>
            <option value="">Any role</option>
            <option value="USER">USER</option>
            <option value="ADMIN">ADMIN</option>
          </select>
          <input aria-label="Created from" type="datetime-local" value={vm.filters.created_from ?? ''} onChange={(event) => vm.setFilter('created_from', event.target.value)} />
          <input aria-label="Created to" type="datetime-local" value={vm.filters.created_to ?? ''} onChange={(event) => vm.setFilter('created_to', event.target.value)} />
          <button type="button" onClick={vm.applyFilters}>Apply</button>
          <button type="button" className="bo-secondary-button" onClick={vm.clearFilters}>Clear</button>
        </>
      )}
    >
      <BackofficeTableState isLoading={vm.isLoading} error={vm.error} empty={vm.items.length === 0} onRetry={vm.retry} />
      <div className="bo-table-wrap">
        <table className="bo-table">
          <thead>
            <tr>
              <th>ID</th>
              <th>Username</th>
              <th>Email</th>
              <th>Phone</th>
              <th>Role</th>
              <th>Status</th>
              <th>Verified</th>
              <th>Created</th>
              <th>Updated</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {vm.items.map((user) => (
              <tr key={user.id}>
                <td><BackofficeIdCell id={user.id} /></td>
                <td>{user.username}</td>
                <td>{user.email}</td>
                <td>{user.phone_number ?? '-'}</td>
                <td>{user.role}</td>
                <td><BackofficeStatusPill status={user.status} /></td>
                <td>{user.email_verified ? 'Yes' : 'No'}</td>
                <td>{formatAdminDate(user.created_at)}</td>
                <td>{formatAdminDate(user.updated_at)}</td>
                <td>
                  <BackofficeConfirmAction
                    label="Deactivate"
                    confirmLabel="Deactivate"
                    disabled={user.status === 'deactivated'}
                    busy={mutation.busyId === user.id}
                    onConfirm={() => void mutation.run(user.id, () => deactivateAdminUser(token!, user.id), 'User deactivated.')}
                  />
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      {mutation.error && <div className="bo-state bo-state-error">{mutation.error}</div>}
      {mutation.message && <div className="bo-result" role="status"><span>{mutation.message}</span></div>}
      <BackofficePagination {...vm} onPrevious={vm.previousPage} onNext={vm.nextPage} onLimitChange={vm.changeLimit} />
    </BackofficePageShell>
  );
}