Add bulk status change for tickets on the board

Select multiple tickets (any column) and open/pend/close them in one
action via a floating selection toolbar, backed by a new
PATCH /api/tickets/bulk-status endpoint. Scoped by the same
per-ticket visibility rule agents already get everywhere else.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gh2UXUQUVBWroWEnn1FLFG
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-09-07 05:42:10 +00:00
1 parent 99927aea0b
commit 4d393aec91
3 files changed
+169 -4

No files matched your search

+25
View File
@@ -13,6 +13,7 @@ import {
} from "@/lib/db/schema";
import { publishTicketEvent } from "@/lib/events/bus";
import { notifyPushForCustomerMessage } from "@/lib/push/service";
import { isTicketVisibleTo } from "@/lib/tickets/visibility";
import type {
TicketDTO,
MessageDTO,
@@ -457,6 +458,30 @@ export async function setTicketStatus(
return dto;
}
/**
* Changes status on many tickets at once, silently skipping any id that
* doesn't exist or isn't visible to the acting user (an agent selecting
* tickets only ever sees their own, so this is mostly a safety net against
* a stale client-side selection). Returns the ids actually updated.
*/
export async function bulkSetTicketStatus(
ticketIds: string[],
status: (typeof tickets.$inferSelect)["status"],
currentUser: { id: string; role: "admin" | "agent" },
): Promise<string[]> {
if (ticketIds.length === 0) return [];
const rows = await db.query.tickets.findMany({ where: inArray(tickets.id, ticketIds) });
const visible = rows.filter((row) => isTicketVisibleTo(row, currentUser));
const updated: string[] = [];
for (const row of visible) {
await setTicketStatus(row.id, status);
updated.push(row.id);
}
return updated;
}
export async function assignTicket(ticketId: string, assigneeId: string | null) {
const ticket = await touchTicket(ticketId, { assigneeId });
const dto = toTicketDTO(ticket, await customerName(ticket.customerId), await getTagsForTicket(ticket.id), await getAssigneeName(ticket.assigneeId));