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 };
}));