Initial commit
This commit is contained in:
+373
@@ -0,0 +1,373 @@
|
||||
import {
|
||||
PrismaClient,
|
||||
Role,
|
||||
ProjectStatus,
|
||||
ShotStatus,
|
||||
ShotPriority,
|
||||
ApprovalStatus,
|
||||
TaskStatus,
|
||||
TaskType,
|
||||
} from "@prisma/client";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
const db = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log("🌱 Seeding VFX Review database...");
|
||||
|
||||
// ── Admin user ──────────────────────────────────────
|
||||
const adminPassword = await bcrypt.hash("admin123", 12);
|
||||
const admin = await db.user.upsert({
|
||||
where: { email: "admin@vfxreview.local" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Admin User",
|
||||
email: "admin@vfxreview.local",
|
||||
passwordHash: adminPassword,
|
||||
role: Role.ADMIN,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Producer ─────────────────────────────────────────
|
||||
const producerPassword = await bcrypt.hash("producer123", 12);
|
||||
const producer = await db.user.upsert({
|
||||
where: { email: "producer@vfxreview.local" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Sarah Chen",
|
||||
email: "producer@vfxreview.local",
|
||||
passwordHash: producerPassword,
|
||||
role: Role.PRODUCER,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Supervisor ───────────────────────────────────────
|
||||
const supervisorPassword = await bcrypt.hash("supervisor123", 12);
|
||||
const supervisor = await db.user.upsert({
|
||||
where: { email: "supervisor@vfxreview.local" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "James Park",
|
||||
email: "supervisor@vfxreview.local",
|
||||
passwordHash: supervisorPassword,
|
||||
role: Role.SUPERVISOR,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Artist ──────────────────────────────────────────
|
||||
const artistPassword = await bcrypt.hash("artist123", 12);
|
||||
const artist = await db.user.upsert({
|
||||
where: { email: "artist@vfxreview.local" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Maya Torres",
|
||||
email: "artist@vfxreview.local",
|
||||
passwordHash: artistPassword,
|
||||
role: Role.ARTIST,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Client user ──────────────────────────────────────
|
||||
const clientPassword = await bcrypt.hash("client123", 12);
|
||||
const clientUser = await db.user.upsert({
|
||||
where: { email: "client@studio.com" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Alex Morgan",
|
||||
email: "client@studio.com",
|
||||
passwordHash: clientPassword,
|
||||
role: Role.CLIENT,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Client record ────────────────────────────────────
|
||||
const client = await db.client.upsert({
|
||||
where: { email: "client@studio.com" },
|
||||
update: {},
|
||||
create: {
|
||||
company: "Stellar Productions",
|
||||
contactPerson: "Alex Morgan",
|
||||
email: "client@studio.com",
|
||||
phone: "+1 (555) 000-0001",
|
||||
notes: "Demo client for seed data.",
|
||||
},
|
||||
});
|
||||
|
||||
// Link client user to client record
|
||||
await db.clientAccess.upsert({
|
||||
where: { userId_clientId: { userId: clientUser.id, clientId: client.id } },
|
||||
update: {},
|
||||
create: { userId: clientUser.id, clientId: client.id },
|
||||
});
|
||||
|
||||
// ── Project ──────────────────────────────────────────
|
||||
const project = await db.project.upsert({
|
||||
where: { code: "NOVA-2025" },
|
||||
update: {},
|
||||
create: {
|
||||
name: "Project Nova",
|
||||
code: "NOVA-2025",
|
||||
description: "Feature film visual effects - Act 2",
|
||||
status: ProjectStatus.ACTIVE,
|
||||
clientId: client.id,
|
||||
producerId: producer.id,
|
||||
supervisorId: supervisor.id,
|
||||
dueDate: new Date("2025-09-01"),
|
||||
startDate: new Date("2025-01-15"),
|
||||
},
|
||||
});
|
||||
|
||||
// ── Shots ────────────────────────────────────────────
|
||||
const shotData = [
|
||||
{
|
||||
shotCode: "SH010",
|
||||
sequence: "010",
|
||||
description: "Wide establishing shot of the space station exterior.",
|
||||
status: ShotStatus.IN_REVIEW,
|
||||
priority: ShotPriority.HIGH,
|
||||
},
|
||||
{
|
||||
shotCode: "SH020",
|
||||
sequence: "010",
|
||||
description: "Close-up of astronaut helmet reflection.",
|
||||
status: ShotStatus.IN_REVIEW,
|
||||
priority: ShotPriority.HIGH,
|
||||
},
|
||||
{
|
||||
shotCode: "SH035",
|
||||
sequence: "020",
|
||||
description: "Explosion with debris field.",
|
||||
status: ShotStatus.REVISIONS,
|
||||
priority: ShotPriority.URGENT,
|
||||
},
|
||||
{
|
||||
shotCode: "SH050",
|
||||
sequence: "020",
|
||||
description: "Hero flying through asteroid belt.",
|
||||
status: ShotStatus.IN_PROGRESS,
|
||||
priority: ShotPriority.NORMAL,
|
||||
},
|
||||
{
|
||||
shotCode: "SH060",
|
||||
sequence: "030",
|
||||
description: "Earth from orbit, cloud layer simulation.",
|
||||
status: ShotStatus.COMPLETE,
|
||||
priority: ShotPriority.NORMAL,
|
||||
},
|
||||
{
|
||||
shotCode: "SH070",
|
||||
sequence: "030",
|
||||
description: "Warp jump effect - tunnel of light.",
|
||||
status: ShotStatus.WAITING,
|
||||
priority: ShotPriority.LOW,
|
||||
},
|
||||
];
|
||||
|
||||
for (const shot of shotData) {
|
||||
await db.shot.upsert({
|
||||
where: { projectId_shotCode: { projectId: project.id, shotCode: shot.shotCode } },
|
||||
update: {},
|
||||
create: {
|
||||
...shot,
|
||||
projectId: project.id,
|
||||
artistId: artist.id,
|
||||
fps: 24,
|
||||
frameStart: 1001,
|
||||
frameEnd: 1080,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Tasks ─────────────────────────────────────────────
|
||||
// Helper: find-or-create task by title + projectId
|
||||
async function upsertTask(data: {
|
||||
title: string;
|
||||
type: TaskType;
|
||||
status: TaskStatus;
|
||||
priority: ShotPriority;
|
||||
sortOrder: number;
|
||||
projectId: string;
|
||||
shotId?: string;
|
||||
assetId?: string;
|
||||
assignedArtistId?: string;
|
||||
dueDate?: Date;
|
||||
estimatedHours?: number;
|
||||
}) {
|
||||
const existing = await db.task.findFirst({
|
||||
where: { projectId: data.projectId, title: data.title, shotId: data.shotId ?? null, assetId: data.assetId ?? null },
|
||||
});
|
||||
if (existing) return existing;
|
||||
return db.task.create({
|
||||
data: { ...data, createdById: producer.id },
|
||||
});
|
||||
}
|
||||
|
||||
const shots = await db.shot.findMany({ where: { projectId: project.id } });
|
||||
const firstShot = shots.find((s) => s.shotCode === "SH010")!;
|
||||
const sh020 = shots.find((s) => s.shotCode === "SH020")!;
|
||||
const sh035 = shots.find((s) => s.shotCode === "SH035")!;
|
||||
|
||||
// ── Tasks must be created first (versions belong to tasks) ──
|
||||
// Create tasks early so we can attach versions to them
|
||||
const sh010CompTask = await upsertTask({ title: "SH010 Comp", type: TaskType.COMP, status: TaskStatus.CLIENT_REVIEW, priority: ShotPriority.HIGH, sortOrder: 2, projectId: project.id, shotId: firstShot.id, assignedArtistId: artist.id, dueDate: new Date("2025-06-15"), estimatedHours: 24 });
|
||||
|
||||
// ── Versions now belong to tasks, not shots ──
|
||||
const existingV1 = await db.version.findFirst({
|
||||
where: { taskId: sh010CompTask.id, versionNumber: 1 },
|
||||
});
|
||||
if (!existingV1) {
|
||||
await db.version.create({
|
||||
data: {
|
||||
versionNumber: 1,
|
||||
taskId: sh010CompTask.id,
|
||||
artistId: artist.id,
|
||||
fileUrl: "/placeholder/sh010_comp_v001.mp4",
|
||||
fileName: "sh010_comp_v001.mp4",
|
||||
fps: 24,
|
||||
duration: 3.375,
|
||||
frameCount: 81,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
notes: "Initial blocking pass. Camera move rough.",
|
||||
approvalStatus: ApprovalStatus.NEEDS_CHANGES,
|
||||
isLatest: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let latestVersion = await db.version.findFirst({
|
||||
where: { taskId: sh010CompTask.id, versionNumber: 2 },
|
||||
});
|
||||
if (!latestVersion) {
|
||||
latestVersion = await db.version.create({
|
||||
data: {
|
||||
versionNumber: 2,
|
||||
taskId: sh010CompTask.id,
|
||||
artistId: artist.id,
|
||||
fileUrl: "/placeholder/sh010_comp_v002.mp4",
|
||||
fileName: "sh010_comp_v002.mp4",
|
||||
fps: 24,
|
||||
duration: 3.375,
|
||||
frameCount: 81,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
notes: "Revised camera move. Fixed comp integration. Added lens flares.",
|
||||
approvalStatus: ApprovalStatus.PENDING_REVIEW,
|
||||
isClientVisible: true,
|
||||
isLatest: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Sample Comments ──────────────────────────────────
|
||||
const c1 = await db.comment.create({
|
||||
data: {
|
||||
versionId: latestVersion.id,
|
||||
authorId: supervisor.id,
|
||||
frameNumber: 22,
|
||||
timestamp: 22 / 24,
|
||||
text: "The tracking slips on frame 22 — the station feels like it's drifting slightly to the left. Can you lock this down?",
|
||||
},
|
||||
});
|
||||
|
||||
await db.commentReply.create({
|
||||
data: {
|
||||
commentId: c1.id,
|
||||
authorId: artist.id,
|
||||
text: "Got it — I'll re-track from scratch using the new markers. Should have a fix by EOD.",
|
||||
},
|
||||
});
|
||||
|
||||
await db.comment.create({
|
||||
data: {
|
||||
versionId: latestVersion.id,
|
||||
authorId: clientUser.id,
|
||||
frameNumber: 55,
|
||||
timestamp: 55 / 24,
|
||||
text: "Love the lens flare here! Can we make it 20% brighter and hold it two frames longer?",
|
||||
},
|
||||
});
|
||||
|
||||
await db.comment.create({
|
||||
data: {
|
||||
versionId: latestVersion.id,
|
||||
authorId: supervisor.id,
|
||||
frameNumber: 70,
|
||||
timestamp: 70 / 24,
|
||||
text: "Edge matte at the bottom — there's a clean line around the station hull. Needs feathering.",
|
||||
isResolved: true,
|
||||
},
|
||||
});
|
||||
|
||||
// ── Assets ────────────────────────────────────────────
|
||||
const assetSpaceship = await db.asset.upsert({
|
||||
where: { projectId_assetCode: { projectId: project.id, assetCode: "SHIP-01" } },
|
||||
update: {},
|
||||
create: {
|
||||
projectId: project.id,
|
||||
assetCode: "SHIP-01",
|
||||
name: "Hero Spaceship",
|
||||
description: "Main hero vehicle — used in SH010, SH050.",
|
||||
status: ShotStatus.IN_PROGRESS,
|
||||
priority: ShotPriority.HIGH,
|
||||
leadId: supervisor.id,
|
||||
dueDate: new Date("2025-07-01"),
|
||||
},
|
||||
});
|
||||
|
||||
const assetStation = await db.asset.upsert({
|
||||
where: { projectId_assetCode: { projectId: project.id, assetCode: "STATION-01" } },
|
||||
update: {},
|
||||
create: {
|
||||
projectId: project.id,
|
||||
assetCode: "STATION-01",
|
||||
name: "Space Station",
|
||||
description: "BG environment asset for SH010.",
|
||||
status: ShotStatus.COMPLETE,
|
||||
priority: ShotPriority.NORMAL,
|
||||
leadId: supervisor.id,
|
||||
dueDate: new Date("2025-06-01"),
|
||||
},
|
||||
});
|
||||
|
||||
// ── Shot & Asset Tasks ────────────────────────────────
|
||||
// SH010 tasks (SH010 Comp was already created above for version attachment)
|
||||
await upsertTask({ title: "SH010 Track", type: TaskType.TRACK, status: TaskStatus.DONE, priority: ShotPriority.HIGH, sortOrder: 0, projectId: project.id, shotId: firstShot.id, assignedArtistId: artist.id, dueDate: new Date("2025-04-01"), estimatedHours: 8 });
|
||||
await upsertTask({ title: "SH010 Roto", type: TaskType.ROTO, status: TaskStatus.IN_PROGRESS, priority: ShotPriority.HIGH, sortOrder: 1, projectId: project.id, shotId: firstShot.id, assignedArtistId: artist.id, dueDate: new Date("2025-05-20"), estimatedHours: 16 });
|
||||
// SH010 Comp is sh010CompTask (created earlier)
|
||||
|
||||
// SH020 tasks
|
||||
await upsertTask({ title: "SH020 Track", type: TaskType.TRACK, status: TaskStatus.DONE, priority: ShotPriority.HIGH, sortOrder: 0, projectId: project.id, shotId: sh020.id, assignedArtistId: artist.id, dueDate: new Date("2025-04-15"), estimatedHours: 4 });
|
||||
await upsertTask({ title: "SH020 Comp", type: TaskType.COMP, status: TaskStatus.INTERNAL_REVIEW, priority: ShotPriority.HIGH, sortOrder: 1, projectId: project.id, shotId: sh020.id, assignedArtistId: artist.id, dueDate: new Date("2025-05-25"), estimatedHours: 20 });
|
||||
|
||||
// SH035 tasks
|
||||
await upsertTask({ title: "SH035 FX Explosion", type: TaskType.FX, status: TaskStatus.CHANGES, priority: ShotPriority.URGENT, sortOrder: 0, projectId: project.id, shotId: sh035.id, assignedArtistId: artist.id, dueDate: new Date("2025-05-10"), estimatedHours: 32 });
|
||||
await upsertTask({ title: "SH035 Comp", type: TaskType.COMP, status: TaskStatus.TODO, priority: ShotPriority.URGENT, sortOrder: 1, projectId: project.id, shotId: sh035.id, dueDate: new Date("2025-06-01"), estimatedHours: 16 });
|
||||
|
||||
// SHIP-01 asset tasks
|
||||
await upsertTask({ title: "SHIP-01 Model", type: TaskType.MODEL, status: TaskStatus.DONE, priority: ShotPriority.HIGH, sortOrder: 0, projectId: project.id, assetId: assetSpaceship.id, assignedArtistId: artist.id, dueDate: new Date("2025-03-01"), estimatedHours: 40 });
|
||||
await upsertTask({ title: "SHIP-01 Texture", type: TaskType.TEXTURE, status: TaskStatus.IN_PROGRESS, priority: ShotPriority.HIGH, sortOrder: 1, projectId: project.id, assetId: assetSpaceship.id, assignedArtistId: artist.id, dueDate: new Date("2025-06-01"), estimatedHours: 24 });
|
||||
await upsertTask({ title: "SHIP-01 Rig", type: TaskType.RIG, status: TaskStatus.TODO, priority: ShotPriority.NORMAL, sortOrder: 2, projectId: project.id, assetId: assetSpaceship.id, dueDate: new Date("2025-07-01"), estimatedHours: 20 });
|
||||
await upsertTask({ title: "SHIP-01 Lookdev", type: TaskType.LOOKDEV, status: TaskStatus.TODO, priority: ShotPriority.NORMAL, sortOrder: 3, projectId: project.id, assetId: assetSpaceship.id, dueDate: new Date("2025-07-15"), estimatedHours: 16 });
|
||||
|
||||
// STATION-01 asset tasks
|
||||
await upsertTask({ title: "STATION-01 Model", type: TaskType.MODEL, status: TaskStatus.DONE, priority: ShotPriority.NORMAL, sortOrder: 0, projectId: project.id, assetId: assetStation.id, assignedArtistId: artist.id, dueDate: new Date("2025-02-15"), estimatedHours: 60 });
|
||||
await upsertTask({ title: "STATION-01 Lookdev", type: TaskType.LOOKDEV, status: TaskStatus.DONE, priority: ShotPriority.NORMAL, sortOrder: 1, projectId: project.id, assetId: assetStation.id, assignedArtistId: artist.id, dueDate: new Date("2025-03-15"), estimatedHours: 20 });
|
||||
|
||||
console.log("✅ Seed complete!");
|
||||
console.log("\n📋 Demo credentials:");
|
||||
console.log(" Admin: admin@vfxreview.local / admin123");
|
||||
console.log(" Producer: producer@vfxreview.local / producer123");
|
||||
console.log(" Supervisor: supervisor@vfxreview.local / supervisor123");
|
||||
console.log(" Artist: artist@vfxreview.local / artist123");
|
||||
console.log(" Client: client@studio.com / client123");
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await db.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user