fix(compiler): migrate 6 list pages to useQuery (set-state-in-effect)
Replaces the useState + useEffect + apiFetch pattern with TanStack Query in six admin list pages — same pattern, mechanical refactor: - admin/tags/tag-list - admin/ports/port-list - admin/roles/role-list - admin/users/user-list - admin/document-templates/template-list - admin/webhooks/page - dashboard/timezone-drift-banner (also: detected-tz reads via useSyncExternalStore so render stays pure) Side benefits: list refetches now share a query cache across tabs (via @tanstack/query-broadcast-client-experimental that was wired up earlier this branch), so when admin A edits a role in one tab, admin B's tab sees the updated row without a manual reload. set-state-in-effect warnings: 51 → 45. Verified: tsc clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { type ColumnDef } from '@tanstack/react-table';
|
||||
import { Pencil, Trash2, Plus, ShieldCheck, ShieldOff, Power, PowerOff } from 'lucide-react';
|
||||
|
||||
@@ -27,27 +28,36 @@ interface UserRow {
|
||||
assignedAt: string;
|
||||
}
|
||||
|
||||
const USERS_QUERY_KEY = ['admin', 'users'] as const;
|
||||
|
||||
export function UserList() {
|
||||
const [users, setUsers] = useState<UserRow[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const queryClient = useQueryClient();
|
||||
const [formOpen, setFormOpen] = useState(false);
|
||||
const [editingUser, setEditingUser] = useState<UserRow | null>(null);
|
||||
const [deletingId, setDeletingId] = useState<string | null>(null);
|
||||
const [togglingId, setTogglingId] = useState<string | null>(null);
|
||||
|
||||
const fetchUsers = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await apiFetch<{ data: UserRow[] }>('/api/v1/admin/users');
|
||||
setUsers(res.data);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
const { data: users = [], isLoading: loading } = useQuery<UserRow[]>({
|
||||
queryKey: USERS_QUERY_KEY,
|
||||
queryFn: () => apiFetch<{ data: UserRow[] }>('/api/v1/admin/users').then((r) => r.data),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
void fetchUsers();
|
||||
}, [fetchUsers]);
|
||||
const fetchUsers = () => queryClient.invalidateQueries({ queryKey: USERS_QUERY_KEY });
|
||||
|
||||
const removeMutation = useMutation({
|
||||
mutationFn: (userId: string) => apiFetch(`/api/v1/admin/users/${userId}`, { method: 'DELETE' }),
|
||||
onSuccess: () => fetchUsers(),
|
||||
});
|
||||
|
||||
const toggleMutation = useMutation({
|
||||
mutationFn: (user: UserRow) =>
|
||||
apiFetch(`/api/v1/admin/users/${user.userId}`, {
|
||||
method: 'PATCH',
|
||||
body: { isActive: !user.isActive },
|
||||
}),
|
||||
onSuccess: () => fetchUsers(),
|
||||
});
|
||||
|
||||
const deletingId = removeMutation.isPending ? removeMutation.variables : null;
|
||||
const togglingId = toggleMutation.isPending ? (toggleMutation.variables?.userId ?? null) : null;
|
||||
|
||||
function handleNewUser() {
|
||||
setEditingUser(null);
|
||||
@@ -59,27 +69,12 @@ export function UserList() {
|
||||
setFormOpen(true);
|
||||
}
|
||||
|
||||
async function handleRemoveUser(userId: string) {
|
||||
setDeletingId(userId);
|
||||
try {
|
||||
await apiFetch(`/api/v1/admin/users/${userId}`, { method: 'DELETE' });
|
||||
await fetchUsers();
|
||||
} finally {
|
||||
setDeletingId(null);
|
||||
}
|
||||
function handleRemoveUser(userId: string) {
|
||||
removeMutation.mutate(userId);
|
||||
}
|
||||
|
||||
async function handleToggleActive(user: UserRow) {
|
||||
setTogglingId(user.userId);
|
||||
try {
|
||||
await apiFetch(`/api/v1/admin/users/${user.userId}`, {
|
||||
method: 'PATCH',
|
||||
body: { isActive: !user.isActive },
|
||||
});
|
||||
await fetchUsers();
|
||||
} finally {
|
||||
setTogglingId(null);
|
||||
}
|
||||
function handleToggleActive(user: UserRow) {
|
||||
toggleMutation.mutate(user);
|
||||
}
|
||||
|
||||
const columns: ColumnDef<UserRow, unknown>[] = [
|
||||
|
||||
Reference in New Issue
Block a user