Files
Nexus/packages/db/src/update-blueprints.ts
T
Hartmut 4a5edeef3e
CI / Unit Tests (pull_request) Successful in 5m46s
CI / Lint (pull_request) Failing after 3m49s
CI / E2E Tests (pull_request) Has been skipped
CI / Fresh-Linux Docker Deploy (pull_request) Has been skipped
CI / Assistant Split Regression (pull_request) Failing after 35s
CI / Architecture Guardrails (pull_request) Failing after 2m14s
CI / Typecheck (pull_request) Successful in 4m22s
CI / Build (pull_request) Has been skipped
CI / Release Images (pull_request) Has been skipped
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI
- @capakraken/* → @nexus/* across 12 packages (root + 11 workspaces),
  1551 import lines migrated via codemod
- User-visible brand strings renamed (emails, page titles, PWA
  manifest, mobile header, MFA backup-codes header, tooltips, signin
  page, invite page, weekly digest, install prompt)
- TOTP issuer "CapaKraken" → "Nexus" (existing secrets still valid;
  re-enrollment relabels them in users' authenticator apps)
- Function rename: assertCapaKrakenDbTarget → assertNexusDbTarget
- LocalStorage migration shim in apps/web/src/app/layout.tsx copies
  capakraken_* → nexus_* on first load (guarded by nexus_migrated_v1
  sentinel; runs once per browser, then never again)
- Service-worker cache name capakraken-v2 → nexus-v2 with one-time
  caches.delete('capakraken-v2') from the same shim
- Email-domain fixtures @capakraken.{dev,app} → @nexus.{dev,app} in
  seed data, e2e specs, SMTP default fallback
- Dockerfile.dev / Dockerfile.prod / all .github/workflows/*.yml
  pnpm --filter @capakraken/* → @nexus/*
- README, CLAUDE.md, LEARNINGS.md, all docs/*.md, .env.example,
  tooling/deploy/.env.production.example brand sweep

Phase 1 deliberately leaves untouched (handled in Phase 3 cutover):
- PostgreSQL DB name "capakraken" and POSTGRES_USER "capakraken"
- Volume names capakraken_pgdata etc.
- Compose project name "capakraken" / "capakraken-prod"
- db-target-guard default expectedDatabase
- env-var CAPAKRAKEN_EXPECTED_DB_NAME
- Container DNS names in docker-compose.ci.yml

Quality gates green: pnpm typecheck (7/7), pnpm test:unit (7/7),
pnpm lint (0 errors), check:exports/imports/architecture all pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 15:10:44 +02:00

1418 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 "@nexus/shared";
import { PrismaClient } from "@prisma/client";
import { loadWorkspaceEnv } from "./load-workspace-env.js";
import { assertNexusDbTarget } 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() {
assertNexusDbTarget("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());