From 23f898d7633aa10f2ae8f88cd3ae4be4eac79296 Mon Sep 17 00:00:00 2001 From: Chneemann Date: Wed, 12 Aug 2026 08:42:32 +0200 Subject: [PATCH] refactor(dashboard): move task retrieval logic to service and optimize query using SQL exists clause --- app/(app)/dashboard/page.tsx | 47 ++++++++++++++---------------------- services/task.service.ts | 40 +++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/app/(app)/dashboard/page.tsx b/app/(app)/dashboard/page.tsx index bb55dfb..d737077 100644 --- a/app/(app)/dashboard/page.tsx +++ b/app/(app)/dashboard/page.tsx @@ -1,20 +1,29 @@ /** * @file dashboard/page.tsx - * @description Server component rendering the main dashboard page, handling authentication, fetching active user-related tasks and assignees, computing trash counts, and passing data to the board container. + * @description Server component rendering the main dashboard page, handling authentication, fetching active tasks with assignees and creators, computing trash counts, and passing data to the board container. */ import { db } from "@/db"; import { tasksTable, taskAssigneesTable, usersTable } from "@/db/schema"; -import { and, count, eq, inArray, isNotNull, isNull, or } from "drizzle-orm"; +import { + and, + count, + eq, + inArray, + isNotNull, + isNull, + or, + exists, +} from "drizzle-orm"; import { auth } from "@/auth"; import { redirect } from "next/navigation"; import Board from "./Board"; import { Task } from "@/types/tasks"; +import { TaskService } from "@/services/task.service"; /** * Renders the dashboard page component with user session validation, - * database queries for active tasks, team assignees, and soft-deleted trash counts, - * before passing the structured dataset to the board container. + * optimized database queries for active tasks, team assignees, and soft-deleted trash counts. * * @async * @returns {Promise} The rendered dashboard page component. @@ -27,34 +36,13 @@ export default async function Dashboard() { const currentUserId = session.user.id; - const assignedTaskRows = await db - .select({ taskId: taskAssigneesTable.taskId }) - .from(taskAssigneesTable) - .where(eq(taskAssigneesTable.userId, currentUserId)); - - const assignedTaskIds = assignedTaskRows.map((r) => r.taskId); - - const taskWhereClause = and( - isNull(tasksTable.deletedAt), - assignedTaskIds.length > 0 - ? or( - eq(tasksTable.userId, currentUserId), - inArray(tasksTable.id, assignedTaskIds), - ) - : eq(tasksTable.userId, currentUserId), - ); - - const rawTasksWithCreator = await db - .select({ - task: tasksTable, - creatorEmail: usersTable.email, - }) - .from(tasksTable) - .innerJoin(usersTable, eq(tasksTable.userId, usersTable.id)) - .where(taskWhereClause); + // All active (not deleted) tasks for which the user is either the creator or the assignee + const rawTasksWithCreator = + await TaskService.findActiveTasksForUser(currentUserId); const allTaskIds = rawTasksWithCreator.map((item) => item.task.id); + // Load all assignees for these tasks const assigneesData = allTaskIds.length > 0 ? await db @@ -81,6 +69,7 @@ export default async function Dashboard() { commentsCount: 0, })); + // Check the trash counter const [trashCountResult] = await db .select({ count: count() }) .from(tasksTable) diff --git a/services/task.service.ts b/services/task.service.ts index f07afdd..d881ddc 100644 --- a/services/task.service.ts +++ b/services/task.service.ts @@ -8,9 +8,10 @@ import { tasksTable, taskAssigneesTable, type Task as DbTask, + usersTable, } from "@/db/schema"; import { TaskStatus } from "@/types/tasks"; -import { and, eq, or, exists, isNotNull } from "drizzle-orm"; +import { and, eq, or, exists, isNotNull, isNull } from "drizzle-orm"; /** * Service class for handling task-related operations and database interactions. @@ -98,6 +99,43 @@ export class TaskService { return (updatedTask as DbTask | undefined) || null; } + /** + * Retrieves all active (non-deleted) tasks that a user is authorized to see + * (either as the creator or as an assignee), including their creator emails. + * + * @async + * @param {string} userId - The unique identifier of the user. + * @returns {Promise} The list of active tasks with creator information. + */ + static async findActiveTasksForUser(userId: string) { + return await db + .select({ + task: tasksTable, + creatorEmail: usersTable.email, + }) + .from(tasksTable) + .innerJoin(usersTable, eq(tasksTable.userId, usersTable.id)) + .where( + and( + isNull(tasksTable.deletedAt), + or( + eq(tasksTable.userId, userId), + exists( + db + .select() + .from(taskAssigneesTable) + .where( + and( + eq(taskAssigneesTable.taskId, tasksTable.id), + eq(taskAssigneesTable.userId, userId), + ), + ), + ), + ), + ), + ); + } + /** * Soft-deletes a task by setting its deletion timestamp if the user is the creator. *