From 586cb57155a0cc5e6fe55f204fd79283f5871a2d Mon Sep 17 00:00:00 2001 From: Phil Dore Date: Fri, 8 May 2026 13:24:02 +0100 Subject: [PATCH] Simplify Stage 8 syndication and derive Advisor Stage 6 complexity - Stage 8: replace 3x3 complexity x EAN grid with single syndicationType dropdown -- Salsify Prep Only (4d), Syndication PDP (5d), Syndication Non-PDP (3d). Advisor maps contentType to PDP/Non-PDP only; Salsify Prep Only is not surfaced in the Advisor (team decision). - Stage 6 (Advisor): derive complexity from staticWorkType / videoWorkType / needsHTML toggles via deriveProductionComplexity helper. Precedence bespoke > creation > complex > simple, max across enabled toggles. HTML-only falls back to complex (placeholder, may revisit). - Bump config.json cache-bust to 2026050801. Co-Authored-By: Claude Opus 4.7 (1M context) --- config.json | 38 ++++++++--------------------------- index.html | 10 +++------- market-script.js | 52 ++++++++++++++++++++++++++++++++++++++---------- script.js | 28 ++++++++++---------------- 4 files changed, 63 insertions(+), 65 deletions(-) diff --git a/config.json b/config.json index b223fe2..498c613 100755 --- a/config.json +++ b/config.json @@ -359,39 +359,17 @@ "stage8": { "label": "8. Syndication", - "tooltip": "Syndication complexity and EAN count determine the timeline. Complex syndication (Brand Store) can take 2-4 weeks.", + "tooltip": "Syndication type determines the base days. Salsify Prep Only = 4d, Syndication PDP = 5d, Syndication Non-PDP = 3d.", "fields": { - "eanVolume": { - "label": "Estimated Number of EANs", - "tooltip": "Number of EANs impacts the Syndication task timeline the most.", + "syndicationType": { + "label": "Syndication Type", + "tooltip": "Type of syndication work required — drives the base days.", "options": [ - { "value": "1_5", "label": "1 - 5 EANs", "impactPercent": 0 }, - { "value": "5_10", "label": "5 - 10 EANs", "impactPercent": 50 }, - { "value": "10_15", "label": "10 - 15 EANs", "impactPercent": 100 } + { "value": "salsify_prep_only", "label": "Salsify Prep Only", "days": 4, "definition": "Prepping PDP content in Salsify, completing any necessary workflows or manual asset selections." }, + { "value": "syndication_pdp", "label": "Syndication PDP", "days": 5, "definition": "Prepping PDP content in Salsify, extracting information from Salsify through channels, loading into SharePoint or a retailer portal and sending any necessary communications." }, + { "value": "syndication_non_pdp", "label": "Syndication Non-PDP", "days": 3, "definition": "Sharing non-PDP information directly with a retailer." } ], - "default": "1_5" - }, - "complexity": { - "label": "Syndication Complexity", - "tooltip": "Complexity of the syndication work required.", - "options": [ - { "value": "simple", "label": "Simple", "definition": "Salsify Enrichment (adding PDP, Copy) + Standard Syndication (downloading assets, Salsify channel excel, SharePoint, email distribution). Caveat: Assuming all info is available and Salsify channels are set up." }, - { "value": "mid", "label": "Mid", "definition": "Standard Syndication plus: ASIN creation, BTF content creation/maintenance (e.g., Amazon Comparison Table), Virtual Bundle creation/maintenance (Amazon only)." }, - { "value": "complex", "label": "Complex", "definition": "Bespoke tasks for Amazon: Brand Store Creation or Brand Store Maintenance. Takes 2-4 weeks." } - ], - "default": "simple" - }, - "crossReferenceTable": { - "_comment": "Syndication days by complexity x EAN volume. Key format: complexity_eanVolume", - "simple_1_5": 3, - "simple_5_10": 5, - "simple_10_15": 7, - "mid_1_5": 4, - "mid_5_10": 6, - "mid_10_15": 8, - "complex_1_5": 5, - "complex_5_10": 7, - "complex_10_15": 9 + "default": "syndication_pdp" } } } diff --git a/index.html b/index.html index 068e364..be0ae49 100755 --- a/index.html +++ b/index.html @@ -567,15 +567,11 @@
- -
-
- - +
diff --git a/market-script.js b/market-script.js index e070d96..bb7153e 100644 --- a/market-script.js +++ b/market-script.js @@ -64,7 +64,7 @@ document.addEventListener('DOMContentLoaded', async () => { async function loadConfig() { try { - const res = await fetch('config.json?v=2026050601'); + const res = await fetch('config.json?v=2026050801'); CONFIG = await res.json(); } catch (e) { console.error('Failed to load config.json:', e); @@ -228,6 +228,30 @@ function determineBriefType() { return 'country_pull_simple'; } +// ---- Derive Stage 6 production complexity from Advisor toggles ---- +// Precedence: bespoke > creation > complex > simple — take the max across enabled toggles. +// HTML has no sub-type, so when HTML is the ONLY production toggle it falls back to 'complex' +// (team decision 2026-05-08; revisit if HTML needs its own production track). +function deriveProductionComplexity({ needsStatic, needsVideo, needsHTML, staticWorkType, videoWorkType }) { + const order = ['simple', 'complex', 'creation', 'bespoke']; + const rank = { simple: 0, complex: 1, creation: 2, bespoke: 3 }; + let level = -1; + + if (needsStatic) { + const map = { simple: 'simple', adaptation: 'complex', creation: 'creation' }; + level = Math.max(level, rank[map[staticWorkType] || 'simple']); + } + if (needsVideo) { + const map = { adapting: 'complex', new_asset: 'creation' }; + level = Math.max(level, rank[map[videoWorkType] || 'complex']); + } + if (needsHTML && level < 0) { + level = rank.complex; + } + + return level >= 0 ? order[level] : 'simple'; +} + // ---- Business Day Arithmetic ---- function addBusinessDays(startDate, days) { const result = new Date(startDate); @@ -316,12 +340,19 @@ function calculateAndRender(overrideBriefId) { matrix[4] = needsTranslation; // Translation (Asset) matrix[7] = needsSyndication; // Syndication - // Urgent Brief scenario: eventing + 0–30 assets + static resizing/cropping only + // Production toggles + work-type radios — needed for Stage 6 complexity derivation + const contentType = document.getElementById('contentType').value; + const needsStatic = document.getElementById('needsStatic').checked; + const needsVideo = document.getElementById('needsVideo').checked; + const needsHTML = document.getElementById('needsHTML').checked; const staticWorkType = document.querySelector('input[name="staticWorkType"]:checked')?.value; + const videoWorkType = document.querySelector('input[name="videoWorkType"]:checked')?.value; + + // Urgent Brief scenario: eventing + 0–30 assets + static resizing/cropping only const isUrgentScenario = briefId === 'urgent_brief' && - document.getElementById('contentType').value === 'eventing' && + contentType === 'eventing' && assetVolume === '0_50' && - document.getElementById('needsStatic').checked && + needsStatic && staticWorkType === 'simple'; // Combined feedback days from production toggles (static/video/html) @@ -377,7 +408,7 @@ function calculateAndRender(overrideBriefId) { if (isUrgentScenario) { return { wip: 1, feedback: fb.static, revisions: 0, rounds: 0 }; } - const complexity = cfg.stage6.fields.complexity.default; + const complexity = deriveProductionComplexity({ needsStatic, needsVideo, needsHTML, staticWorkType, videoWorkType }); const key = complexity + '_' + assetVolume; const table = cfg.stage6.fields.crossReferenceTable; const prodFeedback = productionFeedback > 0 ? productionFeedback : cfg.stage6.fields.marketApprovalDays.default; @@ -396,13 +427,12 @@ function calculateAndRender(overrideBriefId) { stages.push(s7); currentDate = s7.end; - // Stage 8: Syndication + // Stage 8: Syndication — Advisor only chooses PDP vs Non-PDP from contentType. + // Salsify Prep Only is not surfaced here (team decision 2026-05-08). const s8 = calcStage(7, matrix[7], currentDate, () => { - const complexity = cfg.stage8.fields.complexity.default; - const eanVolume = cfg.stage8.fields.eanVolume.default; - const key = complexity + '_' + eanVolume; - const table = cfg.stage8.fields.crossReferenceTable; - return { wip: table[key] || 0, feedback: 0, revisions: 0, rounds: 0 }; + const typeKey = contentType === 'pdp' ? 'syndication_pdp' : 'syndication_non_pdp'; + const opt = cfg.stage8.fields.syndicationType.options.find(o => o.value === typeKey); + return { wip: opt ? opt.days : 0, feedback: 0, revisions: 0, rounds: 0 }; }); stages.push(s8); diff --git a/script.js b/script.js index 3584d07..1f4c39e 100755 --- a/script.js +++ b/script.js @@ -72,7 +72,7 @@ document.addEventListener('DOMContentLoaded', async () => { async function loadConfig() { try { - const res = await fetch('config.json?v=2026050601'); + const res = await fetch('config.json?v=2026050801'); CONFIG = await res.json(); populateBriefTypes(); populateStageDropdowns(); @@ -111,8 +111,8 @@ function populateStageDropdowns() { populateSelect('stage6AssetVolume', cfg.stage6.fields.assetVolume.options, 'value', 'label'); // Stage 8 - populateSelect('stage8EanVolume', cfg.stage8.fields.eanVolume.options, 'value', 'label'); - populateSelect('stage8Complexity', cfg.stage8.fields.complexity.options, 'value', 'label'); + populateSelect('stage8SyndicationType', cfg.stage8.fields.syndicationType.options, 'value', 'label'); + document.getElementById('stage8SyndicationType').value = cfg.stage8.fields.syndicationType.default; } function populateSelect(id, options, valueKey, labelKey) { @@ -245,10 +245,8 @@ function bindEvents() { }); }); - // Syndication complexity/EAN change -> update base days - ['stage8Complexity', 'stage8EanVolume'].forEach(id => { - document.getElementById(id).addEventListener('change', updateSyndicationBaseDays); - }); + // Syndication type change -> update base days + document.getElementById('stage8SyndicationType').addEventListener('change', updateSyndicationBaseDays); } // ---- Brief Type Change ---- @@ -461,11 +459,9 @@ function updateOperaDays() { } function updateSyndicationBaseDays() { - const complexity = document.getElementById('stage8Complexity').value; - const eanVolume = document.getElementById('stage8EanVolume').value; - const key = complexity + '_' + eanVolume; - const table = CONFIG.stageConfigs.stage8.fields.crossReferenceTable; - const days = table[key]; + const value = document.getElementById('stage8SyndicationType').value; + const opt = CONFIG.stageConfigs.stage8.fields.syndicationType.options.find(o => o.value === value); + const days = opt ? opt.days : undefined; document.getElementById('stage8BaseDays').textContent = days !== undefined ? days + ' days' : '--'; } @@ -607,11 +603,9 @@ function calculateSLA() { // Stage 8: Syndication results.push(calcGenericStage(7, activeStages[7], currentDate, () => { - const complexity = document.getElementById('stage8Complexity').value; - const eanVolume = document.getElementById('stage8EanVolume').value; - const key = complexity + '_' + eanVolume; - const table = cfg.stage8.fields.crossReferenceTable; - const wip = table[key] || 0; + const value = document.getElementById('stage8SyndicationType').value; + const opt = cfg.stage8.fields.syndicationType.options.find(o => o.value === value); + const wip = opt ? opt.days : 0; return { handover: 0, wip, feedback: 0, revisions: 0, rounds: 0, noFeedback: true }; }));