refactor(dashboard): move task retrieval logic to service and optimize query using SQL exists clause
This commit is contained in:
parent
674adacc74
commit
23f898d763
2 changed files with 57 additions and 30 deletions
|
|
@ -1,20 +1,29 @@
|
||||||
/**
|
/**
|
||||||
* @file dashboard/page.tsx
|
* @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 { db } from "@/db";
|
||||||
import { tasksTable, taskAssigneesTable, usersTable } from "@/db/schema";
|
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 { auth } from "@/auth";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Board from "./Board";
|
import Board from "./Board";
|
||||||
import { Task } from "@/types/tasks";
|
import { Task } from "@/types/tasks";
|
||||||
|
import { TaskService } from "@/services/task.service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the dashboard page component with user session validation,
|
* Renders the dashboard page component with user session validation,
|
||||||
* database queries for active tasks, team assignees, and soft-deleted trash counts,
|
* optimized database queries for active tasks, team assignees, and soft-deleted trash counts.
|
||||||
* before passing the structured dataset to the board container.
|
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @returns {Promise<JSX.Element>} The rendered dashboard page component.
|
* @returns {Promise<JSX.Element>} The rendered dashboard page component.
|
||||||
|
|
@ -27,34 +36,13 @@ export default async function Dashboard() {
|
||||||
|
|
||||||
const currentUserId = session.user.id;
|
const currentUserId = session.user.id;
|
||||||
|
|
||||||
const assignedTaskRows = await db
|
// All active (not deleted) tasks for which the user is either the creator or the assignee
|
||||||
.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);
|
|
||||||
|
|
||||||
|
const rawTasksWithCreator =
|
||||||
|
await TaskService.findActiveTasksForUser(currentUserId);
|
||||||
const allTaskIds = rawTasksWithCreator.map((item) => item.task.id);
|
const allTaskIds = rawTasksWithCreator.map((item) => item.task.id);
|
||||||
|
|
||||||
|
// Load all assignees for these tasks
|
||||||
const assigneesData =
|
const assigneesData =
|
||||||
allTaskIds.length > 0
|
allTaskIds.length > 0
|
||||||
? await db
|
? await db
|
||||||
|
|
@ -81,6 +69,7 @@ export default async function Dashboard() {
|
||||||
commentsCount: 0,
|
commentsCount: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Check the trash counter
|
||||||
const [trashCountResult] = await db
|
const [trashCountResult] = await db
|
||||||
.select({ count: count() })
|
.select({ count: count() })
|
||||||
.from(tasksTable)
|
.from(tasksTable)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ import {
|
||||||
tasksTable,
|
tasksTable,
|
||||||
taskAssigneesTable,
|
taskAssigneesTable,
|
||||||
type Task as DbTask,
|
type Task as DbTask,
|
||||||
|
usersTable,
|
||||||
} from "@/db/schema";
|
} from "@/db/schema";
|
||||||
import { TaskStatus } from "@/types/tasks";
|
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.
|
* Service class for handling task-related operations and database interactions.
|
||||||
|
|
@ -98,6 +99,43 @@ export class TaskService {
|
||||||
return (updatedTask as DbTask | undefined) || null;
|
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<any[]>} 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.
|
* Soft-deletes a task by setting its deletion timestamp if the user is the creator.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue