Files
CapaKraken/packages/db/src/update-blueprints.ts
T

1278 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { BlueprintTarget, FieldType } from "@capakraken/shared";
import { PrismaClient } from "@prisma/client";
import { loadWorkspaceEnv } from "./load-workspace-env.js";
import { assertCapaKrakenDbTarget } from "./safe-destructive-env.js";
loadWorkspaceEnv();
const prisma = new PrismaClient();
// ─── Shared option sets ─────────────────────────────────────────────────────
const deliveryFormatOptions = [
{ value: "WebHD_1080p", label: "Web HD (1080p)" },
{ value: "4K_UHD", label: "4K UHD (3840×2160)" },
{ value: "8K", label: "8K (7680×4320)" },
{ value: "DCP", label: "DCP (Cinema)" },
{ value: "Custom", label: "Custom / TBD" },
];
const frameRateOptions = [
{ value: "24", label: "24 fps (Film)" },
{ value: "25", label: "25 fps (PAL)" },
{ value: "30", label: "30 fps (NTSC)" },
{ value: "60", label: "60 fps (HFR)" },
];
const colorSpaceOptions = [
{ value: "sRGB_Rec709", label: "sRGB / Rec.709" },
{ value: "DCI-P3", label: "DCI-P3 (Cinema)" },
{ value: "Rec2020_HDR", label: "Rec.2020 / HDR" },
];
// ─── 1. Studio Resource Blueprint ──────────────────────────────────────────
const resourceFieldDefs = [
// Group: Basic Info
{
id: "fd-client-unit",
label: "Client Unit / Account",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Basic Info",
},
{
id: "fd-work-type",
label: "Type of Work / Specialization",
key: "workType",
type: FieldType.TEXT,
required: false,
order: 1,
group: "Basic Info",
},
{
id: "fd-city",
label: "Office Location (Metro City)",
key: "city",
type: FieldType.TEXT,
required: false,
order: 2,
group: "Basic Info",
},
{
id: "fd-employee-type",
label: "Employee Type",
key: "employeeType",
type: FieldType.SELECT,
required: false,
order: 3,
group: "Basic Info",
options: [
{ value: "Employee", label: "Employee" },
{ value: "Freelancer", label: "Freelancer" },
{ value: "Intern", label: "Intern" },
{ value: "External", label: "External Contractor" },
],
},
// Group: Work Setup
{
id: "fd-remote-eligible",
label: "Eligible for Remote Work",
key: "remoteEligible",
type: FieldType.BOOLEAN,
required: false,
order: 4,
group: "Work Setup",
},
{
id: "fd-timezone",
label: "Timezone",
key: "timezone",
type: FieldType.SELECT,
required: false,
order: 5,
group: "Work Setup",
options: [
{ value: "UTC-5", label: "UTC-5 (EST)" },
{ value: "UTC-4", label: "UTC-4 (EDT)" },
{ value: "UTC+0", label: "UTC+0 (GMT/WET)" },
{ value: "UTC+1", label: "UTC+1 (CET)" },
{ value: "UTC+2", label: "UTC+2 (EET/CEST)" },
{ value: "UTC+3", label: "UTC+3 (MSK)" },
{ value: "UTC+5.5", label: "UTC+5:30 (IST)" },
{ value: "UTC+8", label: "UTC+8 (CST/HKT)" },
{ value: "UTC+9", label: "UTC+9 (JST)" },
],
},
{
id: "fd-primary-software",
label: "Primary Software",
key: "primarySoftware",
type: FieldType.MULTI_SELECT,
required: false,
order: 6,
group: "Work Setup",
options: [
{ value: "Maya", label: "Maya" },
{ value: "Cinema4D", label: "Cinema 4D" },
{ value: "Houdini", label: "Houdini" },
{ value: "Blender", label: "Blender" },
{ value: "UnrealEngine", label: "Unreal Engine" },
{ value: "AfterEffects", label: "After Effects" },
{ value: "Nuke", label: "Nuke" },
{ value: "Photoshop", label: "Photoshop" },
{ value: "SubstancePainter", label: "Substance Painter" },
{ value: "ZBrush", label: "ZBrush" },
{ value: "PremierePro", label: "Premiere Pro" },
{ value: "DaVinciResolve", label: "DaVinci Resolve" },
],
},
// Group: Background
{
id: "fd-years-experience",
label: "Years of Industry Experience",
key: "yearsOfExperience",
type: FieldType.NUMBER,
required: false,
order: 7,
group: "Background",
description: "Total years working in the industry",
},
{
id: "fd-languages",
label: "Spoken Languages",
key: "spokenLanguages",
type: FieldType.TEXT,
required: false,
order: 8,
group: "Background",
placeholder: "e.g. English, German, French",
},
{
id: "fd-portfolio-notes",
label: "Portfolio / Work Notes",
key: "portfolioNotes",
type: FieldType.TEXTAREA,
required: false,
order: 9,
group: "Background",
description: "Links or notes about portfolio / notable projects",
},
{
id: "fd-internal-notes",
label: "Internal Notes",
key: "internalNotes",
type: FieldType.TEXTAREA,
required: false,
order: 10,
group: "Background",
description: "HR / team notes (not visible to the resource)",
},
];
// ─── 2. Studio Project Blueprint ───────────────────────────────────────────
const projectFieldDefs = [
// Group: Client & Billing
{
id: "fd-proj-client-unit",
label: "Client Unit Tag",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Client & Billing",
placeholder: "e.g. [DAI], [BMW]",
},
{
id: "fd-person-hours-sold",
label: "Person Hours Sold",
key: "personHoursSold",
type: FieldType.NUMBER,
required: false,
order: 1,
group: "Client & Billing",
description: "Planned billable person hours agreed with client",
},
{
id: "fd-classification",
label: "Classification",
key: "classification",
type: FieldType.SELECT,
required: false,
order: 2,
group: "Client & Billing",
options: [
{ value: "Confidential", label: "Confidential" },
{ value: "Not Confidential", label: "Not Confidential" },
],
},
{
id: "fd-client-contact",
label: "Client Contact / Account Mgr",
key: "clientContact",
type: FieldType.TEXT,
required: false,
order: 3,
group: "Client & Billing",
},
{
id: "fd-crm-reference",
label: "CRM Reference / Opportunity",
key: "crmReference",
type: FieldType.TEXT,
required: false,
order: 4,
group: "Client & Billing",
description: "CRM opportunity ID or ticket reference",
},
// Group: Delivery
{
id: "fd-delivery-deadline",
label: "Final Delivery Date",
key: "deliveryDeadline",
type: FieldType.DATE,
required: false,
order: 5,
group: "Delivery",
},
{
id: "fd-delivery-format",
label: "Delivery Format",
key: "deliveryFormat",
type: FieldType.SELECT,
required: false,
order: 6,
group: "Delivery",
options: deliveryFormatOptions,
},
{
id: "fd-frame-rate",
label: "Frame Rate",
key: "frameRate",
type: FieldType.SELECT,
required: false,
order: 7,
group: "Delivery",
options: frameRateOptions,
},
{
id: "fd-color-space",
label: "Color Space",
key: "colorSpace",
type: FieldType.SELECT,
required: false,
order: 8,
group: "Delivery",
options: colorSpaceOptions,
},
// Group: Scope
{
id: "fd-approval-rounds",
label: "Client Approval Rounds",
key: "clientApprovalRounds",
type: FieldType.NUMBER,
required: false,
order: 9,
group: "Scope",
description: "Estimated number of client review cycles",
},
{
id: "fd-revision-budget",
label: "Revision Budget (hours)",
key: "revisionBudgetHours",
type: FieldType.NUMBER,
required: false,
order: 10,
group: "Scope",
description: "Person-hours reserved for revisions and corrections",
},
{
id: "fd-notes",
label: "Project Notes",
key: "notes",
type: FieldType.TEXTAREA,
required: false,
order: 11,
group: "Scope",
description: "Technical or creative notes / special requirements",
},
];
// ─── 3. 3D Content Production ──────────────────────────────────────────────
const content3DFieldDefs = [
// Group: Client & Billing
{
id: "fd-3d-client-unit",
label: "Client Unit Tag",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Client & Billing",
placeholder: "e.g. [DAI], [BMW]",
},
{
id: "fd-3d-hours-sold",
label: "Person Hours Sold",
key: "personHoursSold",
type: FieldType.NUMBER,
required: false,
order: 1,
group: "Client & Billing",
description: "Planned billable person hours agreed with client",
},
{
id: "fd-3d-classification",
label: "Classification",
key: "classification",
type: FieldType.SELECT,
required: false,
order: 2,
group: "Client & Billing",
options: [
{ value: "Confidential", label: "Confidential" },
{ value: "Not Confidential", label: "Not Confidential" },
],
},
{
id: "fd-3d-client-contact",
label: "Client Contact / Account Mgr",
key: "clientContact",
type: FieldType.TEXT,
required: false,
order: 3,
group: "Client & Billing",
},
{
id: "fd-3d-crm",
label: "CRM Reference / Opportunity",
key: "crmReference",
type: FieldType.TEXT,
required: false,
order: 4,
group: "Client & Billing",
description: "CRM opportunity ID or ticket reference",
},
// Group: Technical Specs
{
id: "fd-3d-render-engine",
label: "Render Engine",
key: "renderEngine",
type: FieldType.SELECT,
required: false,
order: 5,
group: "Technical Specs",
options: [
{ value: "Arnold", label: "Arnold" },
{ value: "VRay", label: "V-Ray" },
{ value: "Redshift", label: "Redshift" },
{ value: "Cycles", label: "Cycles (Blender)" },
{ value: "Octane", label: "Octane" },
{ value: "Corona", label: "Corona" },
{ value: "KeyShot", label: "KeyShot" },
],
},
{
id: "fd-3d-render-farm",
label: "Render Farm",
key: "renderFarm",
type: FieldType.SELECT,
required: false,
order: 6,
group: "Technical Specs",
options: [
{ value: "InhouseCPU", label: "In-house CPU" },
{ value: "InhouseGPU", label: "In-house GPU" },
{ value: "CloudAWS", label: "Cloud (AWS)" },
{ value: "CloudGCP", label: "Cloud (GCP)" },
{ value: "Hybrid", label: "Hybrid" },
],
},
{
id: "fd-3d-delivery-format",
label: "Delivery Format",
key: "deliveryFormat",
type: FieldType.SELECT,
required: false,
order: 7,
group: "Technical Specs",
options: deliveryFormatOptions,
},
{
id: "fd-3d-frame-rate",
label: "Frame Rate",
key: "frameRate",
type: FieldType.SELECT,
required: false,
order: 8,
group: "Technical Specs",
options: frameRateOptions,
},
{
id: "fd-3d-color-space",
label: "Color Space",
key: "colorSpace",
type: FieldType.SELECT,
required: false,
order: 9,
group: "Technical Specs",
options: colorSpaceOptions,
},
{
id: "fd-3d-texture-res",
label: "Texture Resolution",
key: "textureResolution",
type: FieldType.SELECT,
required: false,
order: 10,
group: "Technical Specs",
options: [
{ value: "2K", label: "2K Textures" },
{ value: "4K", label: "4K Textures" },
{ value: "8K", label: "8K Textures" },
],
},
// Group: Asset Scope
{
id: "fd-3d-num-assets",
label: "Number of Assets",
key: "numberOfAssets",
type: FieldType.NUMBER,
required: false,
order: 11,
group: "Asset Scope",
description: "Total 3D models / assets to produce",
},
{
id: "fd-3d-asset-complexity",
label: "Asset Complexity",
key: "assetComplexity",
type: FieldType.SELECT,
required: false,
order: 12,
group: "Asset Scope",
options: [
{ value: "Low", label: "Low" },
{ value: "Medium", label: "Medium" },
{ value: "High", label: "High" },
{ value: "VeryHigh", label: "Very High" },
],
},
{
id: "fd-3d-lighting-setups",
label: "Lighting Setups",
key: "numberOfLightingSetups",
type: FieldType.NUMBER,
required: false,
order: 13,
group: "Asset Scope",
description: "Number of distinct lighting scenarios",
},
{
id: "fd-3d-render-hours",
label: "Render Hours / Frame",
key: "estimatedRenderHoursPerFrame",
type: FieldType.NUMBER,
required: false,
order: 14,
group: "Asset Scope",
},
{
id: "fd-3d-iterations",
label: "Iterations per Asset",
key: "numberOfIterations",
type: FieldType.NUMBER,
required: false,
order: 15,
group: "Asset Scope",
},
{
id: "fd-3d-post-processing",
label: "Post-Processing Required",
key: "postProcessingRequired",
type: FieldType.BOOLEAN,
required: false,
order: 16,
group: "Asset Scope",
description: "Compositing / retouching needed",
},
// Group: Scope
{
id: "fd-3d-scope-approval-rounds",
label: "Client Approval Rounds",
key: "clientApprovalRounds",
type: FieldType.NUMBER,
required: false,
order: 17,
group: "Scope",
description: "Estimated number of client review cycles",
},
{
id: "fd-3d-scope-revision-budget",
label: "Revision Budget (hours)",
key: "revisionBudgetHours",
type: FieldType.NUMBER,
required: false,
order: 18,
group: "Scope",
description: "Person-hours reserved for revisions and corrections",
},
{
id: "fd-3d-scope-notes",
label: "Project Notes",
key: "notes",
type: FieldType.TEXTAREA,
required: false,
order: 19,
group: "Scope",
description: "Technical or creative notes / special requirements",
},
];
const rolePresets3D = [
{ id: "rp-3d-pm", role: "Project Manager", requiredSkills: ["Project Manager"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-3d-lead", role: "3D Lead", requiredSkills: ["3D Modeling", "3D Lighting"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-3d-art", role: "3D Artist", requiredSkills: ["3D Modeling"], hoursPerDay: 8, headcount: 2 },
{ id: "rp-3d-comp", role: "Compositor", requiredSkills: ["Compositing"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-3d-ad", role: "Art Director", requiredSkills: ["Art Direction"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-3d-td", role: "Technical Director", requiredSkills: ["Pipeline", "3D Modeling"], hoursPerDay: 6, headcount: 1 },
];
// ─── 4. Animation Production ────────────────────────────────────────────────
const animationFieldDefs = [
// Group: Client & Billing
{
id: "fd-anim-client-unit",
label: "Client Unit Tag",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Client & Billing",
placeholder: "e.g. [DAI], [BMW]",
},
{
id: "fd-anim-hours-sold",
label: "Person Hours Sold",
key: "personHoursSold",
type: FieldType.NUMBER,
required: false,
order: 1,
group: "Client & Billing",
description: "Planned billable person hours agreed with client",
},
{
id: "fd-anim-classification",
label: "Classification",
key: "classification",
type: FieldType.SELECT,
required: false,
order: 2,
group: "Client & Billing",
options: [
{ value: "Confidential", label: "Confidential" },
{ value: "Not Confidential", label: "Not Confidential" },
],
},
{
id: "fd-anim-client-contact",
label: "Client Contact / Account Mgr",
key: "clientContact",
type: FieldType.TEXT,
required: false,
order: 3,
group: "Client & Billing",
},
{
id: "fd-anim-crm",
label: "CRM Reference / Opportunity",
key: "crmReference",
type: FieldType.TEXT,
required: false,
order: 4,
group: "Client & Billing",
description: "CRM opportunity ID or ticket reference",
},
// Group: Animation Specs
{
id: "fd-anim-style",
label: "Animation Style",
key: "animationStyle",
type: FieldType.SELECT,
required: false,
order: 5,
group: "Animation Specs",
options: [
{ value: "Realistic", label: "Realistic / Photoreal" },
{ value: "Stylized", label: "Stylized" },
{ value: "MotionCapture", label: "Motion Capture" },
{ value: "Procedural", label: "Procedural" },
{ value: "CelShaded", label: "Cel-Shaded / Toon" },
{ value: "Mixed", label: "Mixed / Hybrid" },
],
},
{
id: "fd-anim-duration",
label: "Duration (seconds)",
key: "durationSeconds",
type: FieldType.NUMBER,
required: false,
order: 6,
group: "Animation Specs",
},
{
id: "fd-anim-scenes",
label: "Number of Scenes",
key: "sceneCount",
type: FieldType.NUMBER,
required: false,
order: 7,
group: "Animation Specs",
},
{
id: "fd-anim-shots",
label: "Number of Shots",
key: "shotCount",
type: FieldType.NUMBER,
required: false,
order: 8,
group: "Animation Specs",
},
{
id: "fd-anim-delivery",
label: "Delivery Format",
key: "deliveryFormat",
type: FieldType.SELECT,
required: false,
order: 9,
group: "Animation Specs",
options: deliveryFormatOptions,
},
{
id: "fd-anim-fps",
label: "Frame Rate",
key: "frameRate",
type: FieldType.SELECT,
required: false,
order: 10,
group: "Animation Specs",
options: frameRateOptions,
},
// Group: Characters & Rigging
{
id: "fd-anim-chars",
label: "Number of Characters",
key: "characterCount",
type: FieldType.NUMBER,
required: false,
order: 11,
group: "Characters & Rigging",
},
{
id: "fd-anim-rig-complexity",
label: "Rig Complexity",
key: "rigComplexity",
type: FieldType.SELECT,
required: false,
order: 12,
group: "Characters & Rigging",
options: [
{ value: "Simple", label: "Simple (basic bones)" },
{ value: "Standard", label: "Standard (FK/IK)" },
{ value: "Complex", label: "Complex (full-body IK)" },
{ value: "Hero", label: "Hero (simulation + secondary motion)" },
],
},
{
id: "fd-anim-mocap",
label: "Motion Capture Required",
key: "mocapRequired",
type: FieldType.BOOLEAN,
required: false,
order: 13,
group: "Characters & Rigging",
},
{
id: "fd-anim-storyboard",
label: "Storyboard Provided",
key: "storyboardProvided",
type: FieldType.BOOLEAN,
required: false,
order: 14,
group: "Characters & Rigging",
description: "Client provides storyboard",
},
// Group: Post & Audio
{
id: "fd-anim-music",
label: "Music / Sound Design",
key: "musicPostRequired",
type: FieldType.BOOLEAN,
required: false,
order: 15,
group: "Post & Audio",
},
{
id: "fd-anim-colgrade",
label: "Color Grading",
key: "colorGradingRequired",
type: FieldType.BOOLEAN,
required: false,
order: 16,
group: "Post & Audio",
},
// Group: Scope
{
id: "fd-anim-scope-approval-rounds",
label: "Client Approval Rounds",
key: "clientApprovalRounds",
type: FieldType.NUMBER,
required: false,
order: 17,
group: "Scope",
description: "Estimated number of client review cycles",
},
{
id: "fd-anim-scope-revision-budget",
label: "Revision Budget (hours)",
key: "revisionBudgetHours",
type: FieldType.NUMBER,
required: false,
order: 18,
group: "Scope",
description: "Person-hours reserved for revisions and corrections",
},
{
id: "fd-anim-scope-notes",
label: "Project Notes",
key: "notes",
type: FieldType.TEXTAREA,
required: false,
order: 19,
group: "Scope",
description: "Technical or creative notes / special requirements",
},
];
const rolePresetsAnimation = [
{ id: "rp-anim-pm", role: "Project Manager", requiredSkills: ["Project Manager"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-anim-dir", role: "Animation Director", requiredSkills: ["Animation", "Unreal Engine"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-anim-anim", role: "Animator", requiredSkills: ["Animation"], hoursPerDay: 8, headcount: 2 },
{ id: "rp-anim-rig", role: "Rigger / TD", requiredSkills: ["Rigging"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-anim-comp", role: "Compositor", requiredSkills: ["Compositing"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-anim-ad", role: "Art Director", requiredSkills: ["Art Direction"], hoursPerDay: 4, headcount: 1 },
];
// ─── 5. VFX / Compositing (new) ─────────────────────────────────────────────
const vfxFieldDefs = [
// Group: Client & Billing
{
id: "fd-vfx-client-unit",
label: "Client Unit Tag",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Client & Billing",
placeholder: "e.g. [DAI], [BMW]",
},
{
id: "fd-vfx-hours-sold",
label: "Person Hours Sold",
key: "personHoursSold",
type: FieldType.NUMBER,
required: false,
order: 1,
group: "Client & Billing",
description: "Planned billable person hours agreed with client",
},
{
id: "fd-vfx-classification",
label: "Classification",
key: "classification",
type: FieldType.SELECT,
required: false,
order: 2,
group: "Client & Billing",
options: [
{ value: "Confidential", label: "Confidential" },
{ value: "Not Confidential", label: "Not Confidential" },
],
},
{
id: "fd-vfx-client-contact",
label: "Client Contact / Account Mgr",
key: "clientContact",
type: FieldType.TEXT,
required: false,
order: 3,
group: "Client & Billing",
},
{
id: "fd-vfx-crm",
label: "CRM Reference / Opportunity",
key: "crmReference",
type: FieldType.TEXT,
required: false,
order: 4,
group: "Client & Billing",
description: "CRM opportunity ID or ticket reference",
},
// Group: VFX Specs
{
id: "fd-vfx-type",
label: "VFX Type(s)",
key: "vfxType",
type: FieldType.MULTI_SELECT,
required: false,
order: 5,
group: "VFX Specs",
options: [
{ value: "GreenScreen", label: "Green Screen / Chroma Key" },
{ value: "CGIIntegration", label: "CGI Integration" },
{ value: "MotionTracking", label: "Motion Tracking" },
{ value: "Rotoscoping", label: "Rotoscoping" },
{ value: "ParticleFX", label: "Particle FX" },
{ value: "FluidSim", label: "Fluid Simulation" },
{ value: "MattePainting", label: "Matte Painting" },
{ value: "TitleSequence", label: "Title Sequence" },
],
},
{
id: "fd-vfx-shot-count",
label: "Number of VFX Shots",
key: "shotCount",
type: FieldType.NUMBER,
required: false,
order: 6,
group: "VFX Specs",
},
{
id: "fd-vfx-complexity",
label: "Avg. Shot Complexity",
key: "avgShotComplexity",
type: FieldType.SELECT,
required: false,
order: 7,
group: "VFX Specs",
options: [
{ value: "Low", label: "Low (cleanup / grade)" },
{ value: "Medium", label: "Medium (integration)" },
{ value: "High", label: "High (simulation)" },
{ value: "Photoreal", label: "Photoreal" },
],
},
{
id: "fd-vfx-delivery",
label: "Delivery Format",
key: "deliveryFormat",
type: FieldType.SELECT,
required: false,
order: 8,
group: "VFX Specs",
options: deliveryFormatOptions,
},
{
id: "fd-vfx-fps",
label: "Frame Rate",
key: "frameRate",
type: FieldType.SELECT,
required: false,
order: 9,
group: "VFX Specs",
options: frameRateOptions,
},
{
id: "fd-vfx-color-space",
label: "Color Space",
key: "colorSpace",
type: FieldType.SELECT,
required: false,
order: 10,
group: "VFX Specs",
options: colorSpaceOptions,
},
// Group: Source Material
{
id: "fd-vfx-footage",
label: "Footage Provided by Client",
key: "footageProvided",
type: FieldType.BOOLEAN,
required: false,
order: 11,
group: "Source Material",
},
{
id: "fd-vfx-audio-sync",
label: "Audio Sync Required",
key: "audioSyncRequired",
type: FieldType.BOOLEAN,
required: false,
order: 12,
group: "Source Material",
},
{
id: "fd-vfx-onset-sup",
label: "VFX Supervisor On Set",
key: "hasOnSetVFXSup",
type: FieldType.BOOLEAN,
required: false,
order: 13,
group: "Source Material",
},
// Group: Scope
{
id: "fd-vfx-scope-approval-rounds",
label: "Client Approval Rounds",
key: "clientApprovalRounds",
type: FieldType.NUMBER,
required: false,
order: 14,
group: "Scope",
description: "Estimated number of client review cycles",
},
{
id: "fd-vfx-scope-revision-budget",
label: "Revision Budget (hours)",
key: "revisionBudgetHours",
type: FieldType.NUMBER,
required: false,
order: 15,
group: "Scope",
description: "Person-hours reserved for revisions and corrections",
},
{
id: "fd-vfx-scope-notes",
label: "Project Notes",
key: "notes",
type: FieldType.TEXTAREA,
required: false,
order: 16,
group: "Scope",
description: "Technical or creative notes / special requirements",
},
];
const rolePresetsVFX = [
{ id: "rp-vfx-producer", role: "VFX Producer", requiredSkills: ["Project Manager"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-vfx-sup", role: "VFX Supervisor", requiredSkills: ["Compositing", "Art Direction"], hoursPerDay: 6, headcount: 1 },
{ id: "rp-vfx-sr-comp", role: "Senior Compositor", requiredSkills: ["Compositing", "Nuke"], hoursPerDay: 8, headcount: 2 },
{ id: "rp-vfx-comp", role: "Compositor", requiredSkills: ["Compositing"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-vfx-roto", role: "Roto / Paint Artist",requiredSkills: ["Rotoscoping"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-vfx-tracker", role: "Motion Tracker", requiredSkills: ["Motion Tracking"], hoursPerDay: 8, headcount: 1 },
];
// ─── 6. Motion Design (new) ─────────────────────────────────────────────────
const motionDesignFieldDefs = [
// Group: Client & Billing
{
id: "fd-mog-client-unit",
label: "Client Unit Tag",
key: "clientUnit",
type: FieldType.TEXT,
required: false,
order: 0,
group: "Client & Billing",
placeholder: "e.g. [DAI], [BMW]",
},
{
id: "fd-mog-hours-sold",
label: "Person Hours Sold",
key: "personHoursSold",
type: FieldType.NUMBER,
required: false,
order: 1,
group: "Client & Billing",
description: "Planned billable person hours agreed with client",
},
{
id: "fd-mog-classification",
label: "Classification",
key: "classification",
type: FieldType.SELECT,
required: false,
order: 2,
group: "Client & Billing",
options: [
{ value: "Confidential", label: "Confidential" },
{ value: "Not Confidential", label: "Not Confidential" },
],
},
{
id: "fd-mog-client-contact",
label: "Client Contact / Account Mgr",
key: "clientContact",
type: FieldType.TEXT,
required: false,
order: 3,
group: "Client & Billing",
},
{
id: "fd-mog-crm",
label: "CRM Reference / Opportunity",
key: "crmReference",
type: FieldType.TEXT,
required: false,
order: 4,
group: "Client & Billing",
description: "CRM opportunity ID or ticket reference",
},
// Group: Motion Specs
{
id: "fd-mog-style",
label: "Motion Style",
key: "motionStyle",
type: FieldType.SELECT,
required: false,
order: 5,
group: "Motion Specs",
options: [
{ value: "Flat2D", label: "2D Flat / Icon Animation" },
{ value: "KineticType", label: "Kinetic Typography" },
{ value: "TwoHalfD", label: "2.5D" },
{ value: "ThreeD", label: "3D Motion" },
{ value: "Mixed", label: "Mixed / Hybrid" },
],
},
{
id: "fd-mog-duration",
label: "Duration (seconds)",
key: "durationSeconds",
type: FieldType.NUMBER,
required: false,
order: 6,
group: "Motion Specs",
},
{
id: "fd-mog-formats",
label: "Delivery Formats",
key: "deliveryFormats",
type: FieldType.MULTI_SELECT,
required: false,
order: 7,
group: "Motion Specs",
options: [
{ value: "Social_916", label: "Social (9:16 vertical)" },
{ value: "Landscape_169", label: "Landscape (16:9)" },
{ value: "Square_11", label: "Square (1:1)" },
{ value: "Banner", label: "Banner / Display Ads" },
{ value: "Custom", label: "Custom" },
],
},
{
id: "fd-mog-variants",
label: "Number of Variants",
key: "numberOfVariants",
type: FieldType.NUMBER,
required: false,
order: 8,
group: "Motion Specs",
description: "Format / language / size variants",
},
// Group: Assets & Design
{
id: "fd-mog-design-system",
label: "Client Design System Provided",
key: "hasDesignSystem",
type: FieldType.BOOLEAN,
required: false,
order: 9,
group: "Assets & Design",
description: "Brand guidelines, fonts, and color palette provided",
},
{
id: "fd-mog-storyboard",
label: "Storyboard / Animatic Required",
key: "storyboardRequired",
type: FieldType.BOOLEAN,
required: false,
order: 10,
group: "Assets & Design",
},
{
id: "fd-mog-assets",
label: "Design Assets Provided",
key: "assetsProvided",
type: FieldType.BOOLEAN,
required: false,
order: 11,
group: "Assets & Design",
description: "Logos, images, and fonts provided by client",
},
// Group: Post & Audio
{
id: "fd-mog-voiceover",
label: "Voice-Over Required",
key: "voiceoverRequired",
type: FieldType.BOOLEAN,
required: false,
order: 12,
group: "Post & Audio",
},
{
id: "fd-mog-music",
label: "Music Licensing Required",
key: "musicLicensingRequired",
type: FieldType.BOOLEAN,
required: false,
order: 13,
group: "Post & Audio",
},
{
id: "fd-mog-sound",
label: "Custom Sound Design",
key: "soundDesignRequired",
type: FieldType.BOOLEAN,
required: false,
order: 14,
group: "Post & Audio",
},
// Group: Scope
{
id: "fd-mog-scope-approval-rounds",
label: "Client Approval Rounds",
key: "clientApprovalRounds",
type: FieldType.NUMBER,
required: false,
order: 15,
group: "Scope",
description: "Estimated number of client review cycles",
},
{
id: "fd-mog-scope-revision-budget",
label: "Revision Budget (hours)",
key: "revisionBudgetHours",
type: FieldType.NUMBER,
required: false,
order: 16,
group: "Scope",
description: "Person-hours reserved for revisions and corrections",
},
{
id: "fd-mog-scope-notes",
label: "Project Notes",
key: "notes",
type: FieldType.TEXTAREA,
required: false,
order: 17,
group: "Scope",
description: "Technical or creative notes / special requirements",
},
];
const rolePresetsMotion = [
{ id: "rp-mog-designer", role: "Motion Designer", requiredSkills: ["After Effects", "Motion Design"], hoursPerDay: 8, headcount: 2 },
{ id: "rp-mog-sr-designer", role: "Senior Motion Designer", requiredSkills: ["After Effects", "Motion Design", "Art Direction"], hoursPerDay: 8, headcount: 1 },
{ id: "rp-mog-ad", role: "Art Director", requiredSkills: ["Art Direction"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-mog-producer", role: "Producer", requiredSkills: ["Project Manager"], hoursPerDay: 4, headcount: 1 },
{ id: "rp-mog-sound", role: "Sound Designer", requiredSkills: ["Sound Design"], hoursPerDay: 4, headcount: 1 },
];
// ─── Main ───────────────────────────────────────────────────────────────────
async function main() {
assertCapaKrakenDbTarget("db:update:blueprints");
console.log("Starting blueprint update...\n");
// Blueprints to update in-place (by name — preserves PKs and FKs)
const updates = [
{
name: "Studio Resource Blueprint",
data: {
fieldDefs: resourceFieldDefs,
defaults: {},
validationRules: [],
},
},
{
name: "Studio Project Blueprint",
data: {
fieldDefs: projectFieldDefs,
defaults: { classification: "Not Confidential" },
validationRules: [],
},
},
{
name: "3D Content Production",
data: {
fieldDefs: content3DFieldDefs,
defaults: { classification: "Not Confidential" },
validationRules: [],
rolePresets: rolePresets3D,
},
},
{
name: "Animation Production",
data: {
fieldDefs: animationFieldDefs,
defaults: { classification: "Not Confidential" },
validationRules: [],
rolePresets: rolePresetsAnimation,
},
},
];
for (const u of updates) {
const existing = await prisma.blueprint.findFirst({ where: { name: u.name } });
if (existing) {
await prisma.blueprint.update({ where: { id: existing.id }, data: u.data });
console.log(`Updated: ${u.name}`);
} else {
console.warn(`WARNING: Blueprint not found in DB: "${u.name}"`);
}
}
// New blueprints — upsert by name (safe to re-run)
const newBlueprints = [
{
name: "VFX / Compositing",
target: BlueprintTarget.PROJECT,
description: "Blueprint for VFX and compositing projects",
fieldDefs: vfxFieldDefs,
defaults: { classification: "Not Confidential" },
validationRules: [],
rolePresets: rolePresetsVFX,
},
{
name: "Motion Design",
target: BlueprintTarget.PROJECT,
description: "Blueprint for motion graphics and animation",
fieldDefs: motionDesignFieldDefs,
defaults: { classification: "Not Confidential" },
validationRules: [],
rolePresets: rolePresetsMotion,
},
];
for (const bp of newBlueprints) {
const existing = await prisma.blueprint.findFirst({ where: { name: bp.name } });
if (!existing) {
await prisma.blueprint.create({ data: bp });
console.log(`Created: ${bp.name}`);
} else {
await prisma.blueprint.update({
where: { id: existing.id },
data: {
fieldDefs: bp.fieldDefs,
defaults: bp.defaults,
validationRules: bp.validationRules,
rolePresets: bp.rolePresets,
description: bp.description,
},
});
console.log(`Updated (already existed): ${bp.name}`);
}
}
console.log("\nDone.");
}
main().catch(console.error).finally(() => prisma.$disconnect());