feat(routing): add client picker to user rules (#6271)

* feat(routing): add client picker to user rules

Replace the free-text user criterion with a searchable multi-select backed by existing panel clients. Preserve saved values that no longer exist so editing legacy rules remains lossless.

* feat(routing): polish user picker states

Align the routing user selector with the inbound-tag multi-select, including search, clear, loading, empty, and error states. Localize the new copy across every supported locale and cover legacy saved users with a regression test.

* fix(routing): keep custom user identifiers

Use tags mode with comma tokenization so the user picker suggests panel clients without rejecting HTTP, Mixed, or raw-template identifiers. Restore the comma hint and cover custom entries with a regression test.
This commit is contained in:
Zane
2026-08-23 05:13:34 +08:00
committed by GitHub
parent 326009e9d3
commit 02002dc1c3
16 changed files with 206 additions and 4 deletions
@@ -0,0 +1,33 @@
import { useQuery } from '@tanstack/react-query';
import { z } from 'zod';
import { HttpUtil } from '@/utils';
import { parseMsg } from '@/utils/zodValidate';
import { keys } from '@/api/queryKeys';
import { ClientRecordSchema, type ClientRecord } from '@/schemas/client';
const ClientRecordListSchema = z
.array(ClientRecordSchema)
.nullable()
.transform((value) => value ?? []);
async function fetchClients(): Promise<ClientRecord[]> {
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 ?? [];
}
export function useClientOptions(enabled = true) {
return useQuery({
queryKey: keys.clients.all(),
queryFn: fetchClients,
enabled,
staleTime: 30_000,
select: (clients) =>
clients
.map((client) => client.email.trim())
.filter(Boolean)
.sort((a, b) => a.localeCompare(b)),
});
}