Calendário 360 · 2026

PMO Master v5 · Plano A · Atualizado 12 mai 26
T1 · Franquia de Produto
T2 · Construção
T3 · Comercial
T4 · AO · Always-on
Tier
Canal
Produto
Evento

Timeline · Mai → Dez 2026

15 campanhas · 18 eventos · cabeçalho de meses fixo
CAMPANHA
Produção (D-60 a D-7)
Ativação pós-drop
Drop (D-0)
Eventos:
Marca · Produto
Marca · Propósito
Comercial
Trade · B2B
Endo MKT

Matriz Tier × Canal

Nível de execução esperado por canal · contrato com Performance, Retail, CRM, Comercial
CANAL T1 · FRANQUIA DE PRODUTO T2 · CONSTRUÇÃO T3 · COMERCIAL T4 · ALWAYS ON

Cronograma Reverso · GTM por Campanha

D-90 → D-0 · 12 etapas · cada área sabe o que fazer e quando
Selecione a campanha

Acompanhamento por Etapas · Execução real

Cada campanha entrega só os canais do contrato do seu tier · hover na pílula para nível LIVO
Campanha
Status por canal (Mídia · Social · PR · CRM · VM · Seeding · Trade · Site · Evento)
%

T4 · Always-On · ações ligadas

Cupons perenes + parcerias de fabricante · infraestrutura comercial 365
activity:'Produção criativa (KV, filme, peças)', area:'Branding · AVAL', areaClass:'area-branding', owner:'Débora Moreira', notes:'Criação de KV, roteiro de filme, kit de peças.'}, {step:4, dMinus:60, label:'D−60', activity:'Plano de mídia paga (split + targeting)', area:'Performance', areaClass:'area-perf', owner:'Renato', notes:'Orçamento, canais (Meta, Google, TikTok), formatos.'}, {step:5, dMinus:45, label:'D−45', activity:'Briefing de seeding / influencers', area:'PR · Influência', areaClass:'area-pr', owner:'Camila / Aline', notes:'Long list, filtragem, contato e contratação. Definir kits.'}, {step:6, dMinus:45, label:'D−45', activity:'Plano de eventos / ativações', area:'Eventos', areaClass:'area-eventos', owner:'Luiza', notes:'Local, data, fornecedores, RSVP, logística.'}, {step:7, dMinus:30, label:'D−30', activity:'Setup CRM (segmentação + jornadas)', area:'CRM', areaClass:'area-crm', owner:'Gabriel', notes:'Listas email/WhatsApp, automações, push.'}, {step:8, dMinus:30, label:'D−30', activity:'Setup sistema (cupom, link, integração)', area:'Sistema · TI', areaClass:'area-ti', owner:'Luis Fernando', notes:'Cadastro cupom ERP, URL, deep link, teste.'}, {step:9, dMinus:21, label:'D−21', activity:'Comunicação interna + treinamento de loja', area:'Trade · Loja', areaClass:'area-trade', owner:'Trade MKT', notes:'Sales kit, vídeo de treinamento, FAQ.'}, {step:10, dMinus:14, label:'D−14', activity:'Aprovação final assets + revisão jurídica', area:'Branding · Comercial', areaClass:'area-branding', owner:'Pietro', notes:'Última rodada antes de imprimir/disparar.'}, {step:11, dMinus:7, label:'D−7', activity:'Envio de seeding + go-live em VM', area:'PR · Trade', areaClass:'area-pr', owner:'Camila / David', notes:'Kits despachados (SLA estado). VM pronta nas lojas.'}, {step:12, dMinus:0, label:'D−0 · DROP', activity:'GO-LIVE: drop + ativação multicanal', area:'Branding · Performance · Trade', areaClass:'area-branding', owner:'Pietro · Renato', notes:'Anúncio público. Mídia, social, CRM, loja ativados.'} ]; // ============================================================ // HELPERS // ============================================================ const TIMELINE_START = new Date('2026-05-01'); const TIMELINE_END = new Date('2026-12-31'); const MONTHS = ['Mai','Jun','Jul','Ago','Set','Out','Nov','Dez']; const TOTAL_DAYS = (TIMELINE_END - TIMELINE_START) / 86400000; function pctFromDate(s){ const d = new Date(s); if(d < TIMELINE_START) return 0; if(d > TIMELINE_END) return 100; return ((d - TIMELINE_START)/86400000) / TOTAL_DAYS * 100; } function fmtDate(s){ const d = new Date(s); const m = ['jan','fev','mar','abr','mai','jun','jul','ago','set','out','nov','dez'][d.getMonth()]; return `${String(d.getDate()).padStart(2,'0')} ${m}`; } function fmtDateFull(s){ const d = new Date(s); return `${String(d.getDate()).padStart(2,'0')}/${String(d.getMonth()+1).padStart(2,'0')}/${d.getFullYear()}`; } function addDays(s, days){ const d = new Date(s); d.setDate(d.getDate() + days); return d.toISOString().slice(0,10); } function statusCode(s){ if(!s) return 'n'; const x = s.toLowerCase(); if(x.includes('conclu')) return 'c'; if(x.includes('andamento')) return 'a'; if(x === 'n/a' || x.includes('n/a')) return 'na'; if(x.includes('iniciado') && !x.includes('não')) return 'i'; return 'n'; } function statusLabel(code){ return {c:'Concluído', a:'Em andamento', i:'Iniciado', n:'Não iniciado', na:'N/A'}[code] || s; } // ============================================================ // RENDER · MONTHS HEADER // ============================================================ document.getElementById('months-row').innerHTML = MONTHS.map(m => `
${m}2026
` ).join(''); // ============================================================ // RENDER · TIMELINE BODY // ============================================================ function renderTimeline(){ const body = document.getElementById('timeline-body'); let html = ''; CAMPAIGNS.forEach(c => { const tier = c.tier.toLowerCase(); let eventFlag = ''; if(c.hasEvent && c.eventType === 'product'){ eventFlag = `⊙ Evento · Produto`; } else if(c.hasEvent && c.eventType === 'purpose'){ eventFlag = `⊙ Evento · Propósito`; } else { eventFlag = `sem evento`; } // Constrói os bloquinhos da timeline let trackBlocks = ''; if(Array.isArray(c.moments) && c.moments.length){ // Campanha multi-momentos: cada momento vira um bloquinho pontual c.moments.forEach(m => { const mDate = m.date; const prodStartPct = pctFromDate(addDays(mDate, -m.prodDays)); const momPct = pctFromDate(mDate); const activEndPct = pctFromDate(addDays(mDate, m.activDays)); trackBlocks += `
${m.short}
`; }); } else { // Campanha padrão: barra contínua const startPct = pctFromDate(c.start); const endPct = pctFromDate(c.end); const dropPct = pctFromDate(c.drop); trackBlocks = `
`; } html += `
${c.tier} ${eventFlag}
${c.name} ${c.owner}
${MONTHS.map(() => `
`).join('')} ${trackBlocks}
`; }); // Divisor entre campanhas e eventos html += `
Eventos ${EVENTS.length} marcos
Cada evento ocupa uma linha · posicionado no mês e semana de realização
`; // Ordenar eventos por data e renderizar cada um como linha própria const sortedEvents = [...EVENTS].sort((a,b) => new Date(a.date) - new Date(b.date)); const catLabel = {launch:'Lançamento', comm:'Comercial', trade:'Trade · B2B', endo:'Endo MKT', impact:'Impacto'}; sortedEvents.forEach(ev => { const dropPct = pctFromDate(ev.date); // Barra fininha cobrindo a semana do evento (3 dias antes + 3 dias depois) const startWeekPct = pctFromDate(addDays(ev.date, -3)); const endWeekPct = pctFromDate(addDays(ev.date, 3)); const weekLabel = `Semana de ${fmtDate(ev.date)}`; const cleanName = ev.name.replace(/·\s*$/,''); html += `
${catLabel[ev.category]} ${ev.short} ${weekLabel}
${MONTHS.map(() => `
`).join('')}
${fmtDate(ev.date)} · ${ev.short}
`; }); body.innerHTML = html; } // ============================================================ // RENDER · MATRIX // ============================================================ document.getElementById('matrix-body').innerHTML = MATRIX.map(row => `${row.map((cell,i) => `${cell}`).join('')}` ).join(''); // ============================================================ // RENDER · REVERSE PICKER & TABLE // ============================================================ function renderReversePicker(){ const pick = document.getElementById('reverse-picker'); pick.innerHTML = CAMPAIGNS.map((c,i) => `` ).join(''); pick.querySelectorAll('.picker-chip').forEach(b => b.addEventListener('click', () => { pick.querySelectorAll('.picker-chip').forEach(x => x.classList.remove('active')); b.classList.add('active'); renderReverseTable(b.dataset.id); })); } function renderReverseTable(campId){ const camp = CAMPAIGNS.find(c => c.id === campId); if(!camp) return; const out = document.getElementById('reverse-output'); const tier = camp.tier.toLowerCase(); const drop = camp.drop; // Calcular datas + decidir status default por etapa (heurística: se já passou hoje → exibir conforme campanha) const today = new Date('2026-05-12'); const isReaders = camp.id === 'readers'; let rowsHtml = ''; REVERSE_TEMPLATE.forEach(step => { const stepDate = addDays(drop, -step.dMinus); const passed = new Date(stepDate) < today; let status = 'n'; // Heurísticas baseadas nos status reais do PMO if(camp.status === 'Concluído') status = 'c'; else if(isReaders){ if(step.step <= 2) status = 'c'; else if(step.step === 3) status = 'a'; else status = 'n'; } else if(camp.id === 'jules'){ if(step.step <= 4) status = 'c'; else if(step.step <= 7) status = passed ? 'a' : 'i'; else status = 'n'; } else if(camp.id === 'maes'){ status = 'c'; } else if(passed){ status = 'i'; } const dropFlag = step.dMinus === 0 ? 'drop' : ''; rowsHtml += ` ${step.step} ${step.label} ${step.activity} ${step.area} ${step.owner} ${fmtDateFull(stepDate)} ${statusLabel(status)} ${step.notes} `; }); // Resumo por área const areas = {}; REVERSE_TEMPLATE.forEach(s => { const k = s.area.split(' ·')[0].split(' /')[0]; if(!areas[k]) areas[k] = 0; areas[k]++; }); out.innerHTML = `

${camp.name}

Owner: ${camp.owner} Início: ${fmtDateFull(camp.start)} Encerra: ${fmtDateFull(camp.end)} DROP · ${fmtDateFull(camp.drop)}
${rowsHtml}
# D− Atividade Área Owner Data Status Notas / Entregáveis
Resumo por área: ${Object.entries(areas).map(([area, count]) => `${area}: ${count} etapa${count>1?'s':''}`).join('')}
`; } // ============================================================ // RENDER · EXECUTION TRACKER // ============================================================ function renderExec(){ const grid = document.getElementById('exec-grid'); const headerRow = grid.querySelector('.exec-row.header').outerHTML; const order = ['midia','social','pr','crm','vm','seeding','trade','site','evento']; const labels = {midia:'Mid', social:'Soc', pr:'PR', crm:'CRM', vm:'VM', seeding:'Seed', trade:'Trade', site:'Site', evento:'Evt'}; let rows = ''; CAMPAIGNS.forEach(c => { const tierContract = TIER_MATRIX[c.tier] || {}; let done = 0, total = 0; const naChannels = []; // canais que o tier NÃO entrega const pills = order.map(k => { const tierLevel = tierContract[k]; // string com nível LIVO ou null const isApplicable = tierLevel !== null && tierLevel !== undefined; if(!isApplicable){ naChannels.push(CHANNEL_FULL_LABEL[k]); return ''; // omite a pílula } const code = statusCode(c.channels[k]); // Se canal é aplicável pela matriz mas marcado N/A na campanha, força 'n' const realCode = (code === 'na') ? 'n' : code; total++; if(realCode === 'c') done++; else if(realCode === 'a') done += 0.66; else if(realCode === 'i') done += 0.33; const statusReadable = {c:'Concluído', a:'Em andamento', i:'Iniciado', n:'Não iniciado'}[realCode]; const tooltip = `${CHANNEL_FULL_LABEL[k]} · ${tierLevel} · ${statusReadable}`; return `${labels[k]}`; }).filter(Boolean).join(''); const pct = total ? Math.round(done/total*100) : 0; const skipText = naChannels.length ? `— sem ${naChannels.join(' · ')}` : '— contrato 360 completo'; rows += `
${c.tier}${c.name}
Drop ${fmtDate(c.drop)} · ${c.owner}
${total} entregas por matriz ${skipText}
${pills}
${pct}%
`; }); grid.innerHTML = headerRow + rows; } // ============================================================ // FILTERS // ============================================================ let filterState = { tier:'all', channel:'all', product:'all', event:'all' }; function passesEventFilter(camp){ if(!camp) return true; switch(filterState.event){ case 'all': return true; case 'sim': return camp.hasEvent === true; case 'nao': return camp.hasEvent === false; case 'product': return camp.hasEvent && camp.eventType === 'product'; case 'purpose': return camp.hasEvent && camp.eventType === 'purpose'; default: return true; } } function applyFilters(){ document.querySelectorAll('[data-id]').forEach(el => { const tier = el.dataset.tier; const product = el.dataset.product; const camp = CAMPAIGNS.find(c => c.id === el.dataset.id); let visible = true; if(filterState.tier !== 'all' && tier !== filterState.tier) visible = false; if(filterState.product !== 'all' && product !== filterState.product) visible = false; if(filterState.channel !== 'all' && camp){ const code = statusCode(camp.channels[filterState.channel]); if(code === 'n' || code === 'na') visible = false; } if(!passesEventFilter(camp)) visible = false; el.classList.toggle('dim', !visible); }); document.querySelectorAll('.exec-row:not(.header)').forEach(row => { const tier = row.dataset.tier; const product = row.dataset.product; let visible = true; if(filterState.tier !== 'all' && tier !== filterState.tier) visible = false; if(filterState.product !== 'all' && product !== filterState.product) visible = false; row.classList.toggle('hidden', !visible); }); } function bindFilters(){ ['tier-filter','channel-filter','product-filter','event-filter'].forEach(id => { document.querySelectorAll(`#${id} .chip`).forEach(c => c.addEventListener('click', () => { document.querySelectorAll(`#${id} .chip`).forEach(x => x.classList.remove('active')); c.classList.add('active'); if(id === 'tier-filter') filterState.tier = c.dataset.tier; if(id === 'channel-filter') filterState.channel = c.dataset.channel; if(id === 'product-filter') filterState.product = c.dataset.product; if(id === 'event-filter') filterState.event = c.dataset.event; applyFilters(); })); }); } // ============================================================ // BOOT // ============================================================ renderTimeline(); renderReversePicker(); renderReverseTable('readers'); renderExec(); bindFilters();