Initial commit
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user