import { BlueprintTarget, FieldType } from "@planarchy/shared"; import { PrismaClient } from "@prisma/client"; 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() { 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());