Files
vfxreview/app/(dashboard)/dashboard/page.tsx
T
twotalesanimation 0fbe856dce Initial commit
2026-05-19 22:20:29 +02:00

213 lines
6.8 KiB
TypeScript

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { StatsCards } from "@/components/dashboard/StatsCards";
import { ShotQueue } from "@/components/dashboard/ShotQueue";
import { RecentActivity } from "@/components/dashboard/RecentActivity";
import { TaskWidgets } from "@/components/dashboard/TaskWidgets";
import { ScheduleWidgets } from "@/components/dashboard/ScheduleWidgets";
import type { DashboardStats } from "@/types";
export const metadata = { title: "Dashboard" };
async function getDashboardData(userId: string, role: string) {
const isArtist = role === "ARTIST";
const isClient = role === "CLIENT";
const now = new Date();
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const todayEnd = new Date(todayStart.getTime() + 86400000);
const [awaitingReview, needsRevisions, approved, activeProjects,
tasksDueToday, tasksInReview, tasksOverdue, myTasksCount] =
await Promise.all([
db.version.count({ where: { approvalStatus: "PENDING_REVIEW" } }),
db.version.count({ where: { approvalStatus: "NEEDS_CHANGES" } }),
db.version.count({ where: { approvalStatus: "APPROVED" } }),
db.project.count({ where: { status: "ACTIVE" } }),
db.task.count({
where: {
dueDate: { gte: todayStart, lt: todayEnd },
status: { not: "DONE" },
...(isArtist ? { assignedArtistId: userId } : {}),
},
}),
db.task.count({
where: {
status: { in: ["INTERNAL_REVIEW", "CLIENT_REVIEW"] },
...(isArtist ? { assignedArtistId: userId } : {}),
},
}),
db.task.count({
where: {
dueDate: { lt: todayStart },
status: { not: "DONE" },
...(isArtist ? { assignedArtistId: userId } : {}),
},
}),
db.task.count({
where: { assignedArtistId: userId, status: { not: "DONE" } },
}),
]);
const shotsWhere = isArtist ? { artistId: userId } : {};
const shots = await db.shot.findMany({
where: { ...shotsWhere, status: { not: "COMPLETE" } },
take: 15,
orderBy: { updatedAt: "desc" },
include: {
artist: { select: { id: true, name: true, image: true, email: true } },
versions: {
take: 1,
orderBy: { versionNumber: "desc" },
include: { comments: { select: { id: true, isResolved: true } } },
},
},
});
// My tasks (for widget)
const myTasks = await db.task.findMany({
where: {
assignedArtistId: userId,
status: { not: "DONE" },
},
take: 8,
orderBy: [{ dueDate: "asc" }, { priority: "desc" }],
include: {
shot: { select: { shotCode: true } },
asset: { select: { assetCode: true } },
project: { select: { id: true, name: true, code: true } },
},
});
// Tasks in review (for supervisors/producers)
const reviewTasks = isArtist || isClient ? [] : await db.task.findMany({
where: { status: { in: ["INTERNAL_REVIEW", "CLIENT_REVIEW"] } },
take: 8,
orderBy: { updatedAt: "desc" },
include: {
shot: { select: { shotCode: true } },
asset: { select: { assetCode: true } },
project: { select: { id: true, name: true, code: true } },
assignedArtist: { select: { id: true, name: true, image: true, email: true } },
},
});
const activity = await db.notification.findMany({
where: isClient ? { userId } : {},
take: 20,
orderBy: { createdAt: "desc" },
include: { user: { select: { name: true, image: true } } },
});
// Schedule data (for producers/supervisors/admins)
const scheduledTasks =
!isArtist && !isClient
? await db.task.findMany({
where: { scheduledStartDate: { not: null }, status: { not: "DONE" } },
include: {
shot: { select: { shotCode: true } },
asset: { select: { assetCode: true } },
project: { select: { id: true, name: true, code: true } },
assignedArtist: {
select: { id: true, name: true, email: true, image: true },
},
},
orderBy: { scheduledStartDate: "asc" },
})
: [];
const scheduleArtists =
!isArtist && !isClient
? await db.user.findMany({
where: { isActive: true, role: { not: "CLIENT" } },
select: {
id: true,
name: true,
email: true,
image: true,
role: true,
},
orderBy: { name: "asc" },
})
: [];
const stats: DashboardStats = {
awaitingReview,
needsRevisions,
approved,
overdue: tasksOverdue,
activeProjects,
tasksDueToday,
tasksInReview,
tasksOverdue,
myTasksCount,
};
return { stats, shots, activity, myTasks, reviewTasks, scheduledTasks, scheduleArtists };
}
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) return null;
const { stats, shots, activity, myTasks, reviewTasks, scheduledTasks, scheduleArtists } =
await getDashboardData(session.user.id, session.user.role);
const isClient = session.user.role === "CLIENT";
const canSeeSchedule = ["ADMIN", "PRODUCER", "SUPERVISOR"].includes(
session.user.role
);
return (
<div className="p-8 space-y-6 max-w-[1600px] mx-auto">
<div className="mb-4">
<h1 className="text-3xl font-bold text-white">Dashboard</h1>
<p className="text-zinc-400 mt-1">
Welcome back, {session.user.name ?? session.user.email}
</p>
</div>
<StatsCards stats={stats} />
{!isClient && (
<TaskWidgets
myTasks={myTasks as any}
reviewTasks={reviewTasks as any}
role={session.user.role}
/>
)}
{canSeeSchedule && (
<div className="space-y-3">
<h2 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Schedule Overview
</h2>
<ScheduleWidgets
scheduledTasks={scheduledTasks as any}
artists={scheduleArtists}
/>
</div>
)}
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
<div className="xl:col-span-2 space-y-3">
<h2 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Shot Queue
</h2>
<div className="rounded-lg border border-border bg-card p-1">
<ShotQueue shots={shots as any} />
</div>
</div>
<div className="space-y-3">
<h2 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Recent Activity
</h2>
<div className="rounded-lg border border-border bg-card h-[400px] overflow-hidden">
<RecentActivity activities={activity as any} />
</div>
</div>
</div>
</div>
);
}