diff --git a/src/app/(admin)/dashboard/ticket-board.tsx b/src/app/(admin)/dashboard/ticket-board.tsx
index 006e5c1..34709ea 100644
--- a/src/app/(admin)/dashboard/ticket-board.tsx
+++ b/src/app/(admin)/dashboard/ticket-board.tsx
@@ -243,6 +243,17 @@ function TicketCard({ ticket }: { ticket: TicketDTO }) {
{formatRelativeTime(ticket.lastMessageAt)}
+
+
+ {ticket.assigneeName ? (
+ <>
+
+
{ticket.assigneeName}
+ >
+ ) : (
+
Не назначено
+ )}
+
);
diff --git a/src/app/(admin)/layout.tsx b/src/app/(admin)/layout.tsx
index e06ad7a..50f1fcc 100644
--- a/src/app/(admin)/layout.tsx
+++ b/src/app/(admin)/layout.tsx
@@ -22,7 +22,7 @@ export default async function AdminLayout({ children }: { children: React.ReactN
return (
-
+
@@ -41,7 +41,7 @@ export default async function AdminLayout({ children }: { children: React.ReactN
-
{children}
+
{children}
diff --git a/src/lib/tickets/service.ts b/src/lib/tickets/service.ts
index 2b343cf..2e60e5c 100644
--- a/src/lib/tickets/service.ts
+++ b/src/lib/tickets/service.ts
@@ -32,7 +32,12 @@ type AttachmentInput = {
isInline?: boolean;
};
-function toTicketDTO(ticket: typeof tickets.$inferSelect, customerName: string, tags: TagDTO[]): TicketDTO {
+function toTicketDTO(
+ ticket: typeof tickets.$inferSelect,
+ customerName: string,
+ tags: TagDTO[],
+ assigneeName: string | null = null,
+): TicketDTO {
return {
id: ticket.id,
subject: ticket.subject,
@@ -42,6 +47,7 @@ function toTicketDTO(ticket: typeof tickets.$inferSelect, customerName: string,
customerId: ticket.customerId,
customerName,
assigneeId: ticket.assigneeId,
+ assigneeName,
tags,
createdAt: ticket.createdAt.getTime(),
updatedAt: ticket.updatedAt.getTime(),
@@ -49,6 +55,13 @@ function toTicketDTO(ticket: typeof tickets.$inferSelect, customerName: string,
};
}
+/** Single-ticket lookups only — listTickets/listTicketsForCustomer LEFT JOIN users directly instead, to avoid N+1 queries across a whole board. */
+async function getAssigneeName(assigneeId: string | null): Promise {
+ if (!assigneeId) return null;
+ const user = await db.query.users.findFirst({ where: eq(users.id, assigneeId) });
+ return user?.name ?? null;
+}
+
function toMessageDTO(message: typeof messages.$inferSelect, attachments: AttachmentDTO[] = []): MessageDTO {
return {
id: message.id,
@@ -170,7 +183,7 @@ async function appendMessage(params: {
const ticket = await touchTicket(params.ticket.id, { lastMessageAt: new Date() });
const name = await customerName(ticket.customerId);
const ticketTagsList = await getTagsForTicket(ticket.id);
- const dto = toTicketDTO(ticket, name, ticketTagsList);
+ const dto = toTicketDTO(ticket, name, ticketTagsList, await getAssigneeName(ticket.assigneeId));
const messageDto = toMessageDTO(message, attachmentDTOs);
publishTicketEvent({
@@ -437,14 +450,14 @@ export async function setTicketStatus(
status: (typeof tickets.$inferSelect)["status"],
) {
const ticket = await touchTicket(ticketId, { status });
- const dto = toTicketDTO(ticket, await customerName(ticket.customerId), await getTagsForTicket(ticket.id));
+ const dto = toTicketDTO(ticket, await customerName(ticket.customerId), await getTagsForTicket(ticket.id), await getAssigneeName(ticket.assigneeId));
publishTicketEvent({ type: "ticket.updated", ticket: dto });
return dto;
}
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));
+ const dto = toTicketDTO(ticket, await customerName(ticket.customerId), await getTagsForTicket(ticket.id), await getAssigneeName(ticket.assigneeId));
publishTicketEvent({ type: "ticket.updated", ticket: dto });
return dto;
}
@@ -485,9 +498,10 @@ export async function listTickets(
}
const rows = await db
- .select({ ticket: tickets, customerName: customers.displayName })
+ .select({ ticket: tickets, customerName: customers.displayName, assigneeName: users.name })
.from(tickets)
.innerJoin(customers, eq(tickets.customerId, customers.id))
+ .leftJoin(users, eq(tickets.assigneeId, users.id))
.orderBy(desc(tickets.lastMessageAt));
const idsToKeep = matchingTicketIds;
@@ -498,7 +512,9 @@ export async function listTickets(
}
const tagsMap = await getTagsForTickets(filteredRows.map((r) => r.ticket.id));
- return filteredRows.map((row) => toTicketDTO(row.ticket, row.customerName, tagsMap.get(row.ticket.id) ?? []));
+ return filteredRows.map((row) =>
+ toTicketDTO(row.ticket, row.customerName, tagsMap.get(row.ticket.id) ?? [], row.assigneeName ?? null),
+ );
}
export async function getTicketWithMessages(ticketId: string) {
@@ -519,7 +535,7 @@ export async function getTicketWithMessages(ticketId: string) {
const ticketTagsList = await getTagsForTicket(ticketId);
return {
- ticket: toTicketDTO(row[0].ticket, row[0].customerName, ticketTagsList),
+ ticket: toTicketDTO(row[0].ticket, row[0].customerName, ticketTagsList, await getAssigneeName(row[0].ticket.assigneeId)),
messages: ticketMessages.map((m) => toMessageDTO(m, attachmentsMap.get(m.id) ?? [])),
};
}
@@ -729,7 +745,12 @@ export async function setTicketTags(ticketId: string, tagIds: string[]): Promise
const ticket = await db.query.tickets.findFirst({ where: eq(tickets.id, ticketId) });
if (ticket) {
- const dto = toTicketDTO(ticket, await customerName(ticket.customerId), await getTagsForTicket(ticketId));
+ const dto = toTicketDTO(
+ ticket,
+ await customerName(ticket.customerId),
+ await getTagsForTicket(ticketId),
+ await getAssigneeName(ticket.assigneeId),
+ );
publishTicketEvent({ type: "ticket.updated", ticket: dto });
}
return getTagsForTicket(ticketId);
diff --git a/src/lib/tickets/types.ts b/src/lib/tickets/types.ts
index caee864..55530e5 100644
--- a/src/lib/tickets/types.ts
+++ b/src/lib/tickets/types.ts
@@ -22,6 +22,7 @@ export interface TicketDTO {
customerId: string;
customerName: string;
assigneeId: string | null;
+ assigneeName: string | null;
tags: TagDTO[];
createdAt: number;
updatedAt: number;