feat(clients): selective bulk attach + new bulk detach

Inbounds page:
- AttachClientsModal now shows a per-client selection table (email,
  comment, enabled tag) with search and a live "selected of total"
  counter; all clients are pre-selected so the old "attach all"
  workflow stays a single OK click.
- New DetachClientsModal on the inbound row menu lets you pick which
  clients to remove from that inbound (records are kept so they can be
  re-attached later; for full removal use Delete).

Clients page:
- New "Attach (N)" bulk-action button + BulkAttachInboundsModal that
  attaches selected clients to one or more multi-user inbounds.
- New "Detach (N)" bulk-action button + BulkDetachInboundsModal that
  removes selected clients from chosen inbounds; (email, inbound) pairs
  where the client isn't attached are silently skipped.

Backend adds POST /panel/api/clients/bulkDetach, wrapping the existing
Detach service for each email and reporting per-email
detached/skipped/errors. ClientRecord rows are kept on detach to match
the single-client endpoint; bulkDel remains the path for full removal.
This commit is contained in:
MHSanaei
2026-05-28 11:08:52 +02:00
parent a07b68894c
commit 72b68cce22
15 changed files with 809 additions and 13 deletions
+14
View File
@@ -97,6 +97,18 @@ export const DelDepletedResultSchema = z.object({
deleted: z.number().optional(),
});
export const BulkAttachResultSchema = z.object({
attached: z.array(z.string()).nullable().transform((v) => v ?? []),
skipped: z.array(z.string()).nullable().transform((v) => v ?? []),
errors: z.array(z.string()).nullable().transform((v) => v ?? []),
});
export const BulkDetachResultSchema = z.object({
detached: z.array(z.string()).nullable().transform((v) => v ?? []),
skipped: z.array(z.string()).nullable().transform((v) => v ?? []),
errors: z.array(z.string()).nullable().transform((v) => v ?? []),
});
export const OnlinesSchema = nullableStringArray;
export const GroupSummarySchema = z.object({
@@ -167,6 +179,8 @@ export type ClientHydrate = z.infer<typeof ClientHydrateSchema>;
export type BulkAdjustResult = z.infer<typeof BulkAdjustResultSchema>;
export type BulkDeleteResult = z.infer<typeof BulkDeleteResultSchema>;
export type BulkCreateResult = z.infer<typeof BulkCreateResultSchema>;
export type BulkAttachResult = z.infer<typeof BulkAttachResultSchema>;
export type BulkDetachResult = z.infer<typeof BulkDetachResultSchema>;
export type ClientBulkAddFormValues = z.infer<typeof ClientBulkAddFormSchema>;
export type ClientBulkAdjustFormValues = z.infer<typeof ClientBulkAdjustFormSchema>;
export type ClientFormValues = z.infer<typeof ClientFormSchema>;