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) <noreply@anthropic.com>
This commit is contained in:
parent
5e301b68c1
commit
586cb57155
4 changed files with 63 additions and 65 deletions
38
config.json
38
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
index.html
10
index.html
|
|
@ -567,15 +567,11 @@
|
|||
<div class="stage-fields grid gap-4 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1 tooltip-trigger relative">
|
||||
Estimated No. of EANs
|
||||
Syndication Type
|
||||
<span class="ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full bg-gray-200 dark:bg-gray-600 text-xs cursor-help">i</span>
|
||||
<span class="tooltip-content hidden absolute bottom-full left-0 mb-1 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg shadow-lg w-56 z-10">Number of EANs impacts the Syndication task timeline the most.</span>
|
||||
<span class="tooltip-content hidden absolute bottom-full left-0 mb-1 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg shadow-lg w-64 z-10">Salsify Prep Only = 4d, Syndication PDP = 5d, Syndication Non-PDP = 3d.</span>
|
||||
</label>
|
||||
<select id="stage8EanVolume" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm focus:ring-2 focus:ring-brand-500 outline-none"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Syndication Complexity</label>
|
||||
<select id="stage8Complexity" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm focus:ring-2 focus:ring-brand-500 outline-none"></select>
|
||||
<select id="stage8SyndicationType" class="w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 px-3 py-2 text-sm focus:ring-2 focus:ring-brand-500 outline-none"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Syndication Base Days</label>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
28
script.js
28
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 };
|
||||
}));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue