Explorar o código

fix(groups): fetch full client list for Add/Remove/SubLinks modals

GroupsPage was sourcing modal candidates from useClients(), which is server-paginated at 25 rows — so "Add clients to group" only ever offered the first page, "Remove" missed members past page 1, and SubLinks silently skipped emails whose record wasn't in the cached page. Pull the unpaginated list via /panel/api/clients/list when any of the three modals open.
MHSanaei hai 5 horas
pai
achega
ffe661d212
Modificáronse 2 ficheiros con 27 adicións e 5 borrados
  1. 1 0
      frontend/src/api/queryKeys.ts
  2. 26 5
      frontend/src/pages/groups/GroupsPage.tsx

+ 1 - 0
frontend/src/api/queryKeys.ts

@@ -19,6 +19,7 @@ export const keys = {
   clients: {
     root: () => ['clients'] as const,
     list: (params: unknown) => ['clients', 'list', params] as const,
+    all: () => ['clients', 'all'] as const,
     onlines: () => ['clients', 'onlines'] as const,
     lastOnline: () => ['clients', 'lastOnline'] as const,
     groups: () => ['clients', 'groups'] as const,

+ 26 - 5
frontend/src/pages/groups/GroupsPage.tsx

@@ -34,6 +34,7 @@ import {
   UsergroupDeleteOutlined,
 } from '@ant-design/icons';
 import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { z } from 'zod';
 
 import { useTheme } from '@/hooks/useTheme';
 import { useMediaQuery } from '@/hooks/useMediaQuery';
@@ -44,9 +45,16 @@ import { setMessageInstance } from '@/utils/messageBus';
 import AppSidebar from '@/components/AppSidebar';
 import LazyMount from '@/components/LazyMount';
 import { keys } from '@/api/queryKeys';
-import { GroupSummaryListSchema, type GroupSummary } from '@/schemas/client';
+import {
+  ClientRecordSchema,
+  GroupSummaryListSchema,
+  type ClientRecord,
+  type GroupSummary,
+} from '@/schemas/client';
 import { parseMsg } from '@/utils/zodValidate';
 
+const ClientRecordListSchema = z.array(ClientRecordSchema).nullable().transform((v) => v ?? []);
+
 const SubLinksModal = lazy(() => import('../clients/SubLinksModal'));
 const ClientBulkAdjustModal = lazy(() => import('../clients/ClientBulkAdjustModal'));
 const GroupAddClientsModal = lazy(() => import('./GroupAddClientsModal'));
@@ -81,7 +89,7 @@ export default function GroupsPage() {
   useEffect(() => { setMessageInstance(messageApi); }, [messageApi]);
   const queryClient = useQueryClient();
 
-  const { clients, subSettings, bulkAdjust, bulkAddToGroup, bulkRemoveFromGroup, bulkDelete } = useClients();
+  const { subSettings, bulkAdjust, bulkAddToGroup, bulkRemoveFromGroup, bulkDelete } = useClients();
 
   const groupsQuery = useQuery({
     queryKey: keys.clients.groups(),
@@ -133,6 +141,19 @@ export default function GroupsPage() {
   const [groupEmails, setGroupEmails] = useState<string[]>([]);
   const [groupForAction, setGroupForAction] = useState<GroupSummary | null>(null);
 
+  const allClientsQuery = useQuery<ClientRecord[]>({
+    queryKey: keys.clients.all(),
+    queryFn: async () => {
+      const msg = await HttpUtil.get('/panel/api/clients/list', undefined, { silent: true });
+      if (!msg?.success) throw new Error(msg?.msg || 'Failed to load clients');
+      const validated = parseMsg(msg, ClientRecordListSchema, 'clients/list');
+      return validated.obj ?? [];
+    },
+    enabled: addClientsOpen || removeClientsOpen || subLinksOpen,
+    staleTime: 30_000,
+  });
+  const allClients = allClientsQuery.data ?? [];
+
   const totalGroups = groups.length;
   const totalClients = useMemo(
     () => groups.reduce((acc, g) => acc + (g.clientCount || 0), 0),
@@ -529,7 +550,7 @@ export default function GroupsPage() {
           <SubLinksModal
             open={subLinksOpen}
             emails={groupEmails}
-            clients={clients}
+            clients={allClients}
             subSettings={subSettings}
             onOpenChange={setSubLinksOpen}
           />
@@ -561,7 +582,7 @@ export default function GroupsPage() {
           <GroupAddClientsModal
             open={addClientsOpen}
             groupName={groupForAction?.name ?? null}
-            candidates={clients.filter((c) => c.group !== groupForAction?.name)}
+            candidates={allClients.filter((c) => c.group !== groupForAction?.name)}
             onClose={() => setAddClientsOpen(false)}
             onSubmit={async (emails) => {
               const msg = await bulkAddToGroup(emails, groupForAction?.name ?? '');
@@ -577,7 +598,7 @@ export default function GroupsPage() {
           <GroupRemoveClientsModal
             open={removeClientsOpen}
             groupName={groupForAction?.name ?? null}
-            members={clients.filter((c) => c.group === groupForAction?.name)}
+            members={allClients.filter((c) => c.group === groupForAction?.name)}
             onClose={() => setRemoveClientsOpen(false)}
             onSubmit={async (emails) => {
               const msg = await bulkRemoveFromGroup(emails);