CoolFace
Apppublic

l3on3/maximum-martech-ops-demo

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
app.js803 linesDownload Raw Back to root
1'use strict';2 3const viewMeta = {4  overview: ['CONTENT-TO-REVENUE / OVERVIEW', 'The engine, at a glance.'],5  leads: ['DEMAND OPERATIONS / LEADS & CRM', 'Qualified demand, in motion.'],6  content: ['CONTENT OPERATIONS / PRODUCTION', 'The machine behind the media.'],7  attribution: ['DATA / REVENUE ATTRIBUTION', 'Every dollar has a journey.'],8  automations: ['SYSTEMS / AUTOMATION HEALTH', 'Reliable by design.']9};10 11const $ = (selector, root = document) => root.querySelector(selector);12const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];13const wait = (milliseconds) => new Promise((resolve) => window.setTimeout(resolve, milliseconds));14 15const appState = {16  activeView: 'overview',17  demoStatus: 'idle',18  currentRun: null,19  pipeline: 148400,20  capturedLeads: 486,21  qualifiedLeads: 154,22  campaignLeads: 84,23  campaignPipeline: 51200,24  eventsProcessed: 12842,25  approvedContent: false,26  genericRetryComplete: false,27  takeoverActive: false,28  recordsByEmail: new Map([29    ['maya@northstarhealth.com', { id: 'MM-1028', email: 'maya@northstarhealth.com', name: 'Maya Chen', company: 'Northstar Health', existing: true }]30  ])31};32 33const sidebar = $('#sidebar');34const sidebarScrim = $('#sidebarScrim');35const mobileMenu = $('#mobileMenu');36const pageEyebrow = $('#pageEyebrow');37const pageTitle = $('#pageTitle');38const demoTray = $('#demoTray');39const demoForm = $('#demoLeadForm');40const demoFormError = $('#demoFormError');41const demoState = $('#demoState');42const demoSteps = $('#demoSteps');43const demoPerson = $('#demoPerson');44const demoSource = $('#demoSource');45const demoSummary = $('#demoSummary');46const demoAudit = $('#demoAudit');47const demoAuditList = $('#demoAuditList');48const mockNotification = $('#mockNotification');49const replayDemoButton = $('#replayDemo');50const retryDemoButton = $('#retryDemo');51const takeoverLeadButton = $('#takeoverLead');52const inspectLeadButton = $('#inspectLead');53 54function switchView(viewName, focusHeading = false) {55  if (!viewMeta[viewName]) return;56  appState.activeView = viewName;57  $$('.nav-item').forEach((item) => {58    const active = item.dataset.view === viewName;59    item.classList.toggle('is-active', active);60    if (active) item.setAttribute('aria-current', 'page');61    else item.removeAttribute('aria-current');62  });63  $$('[data-view-panel]').forEach((panel) => {64    panel.hidden = panel.dataset.viewPanel !== viewName;65  });66  [pageEyebrow.textContent, pageTitle.textContent] = viewMeta[viewName];67  closeMobileNavigation();68  window.scrollTo({ top: 0, behavior: 'smooth' });69  if (focusHeading) {70    pageTitle.setAttribute('tabindex', '-1');71    pageTitle.focus({ preventScroll: true });72  }73}74 75function openMobileNavigation() {76  sidebar.classList.add('is-open');77  sidebarScrim.hidden = false;78  mobileMenu.setAttribute('aria-expanded', 'true');79}80 81function closeMobileNavigation() {82  sidebar.classList.remove('is-open');83  sidebarScrim.hidden = true;84  mobileMenu.setAttribute('aria-expanded', 'false');85}86 87function toast(title, detail, type = 'success') {88  const region = $('#toastRegion');89  const node = document.createElement('div');90  node.className = `toast ${type === 'error' ? 'error' : ''}`;91  node.innerHTML = `<i>${type === 'error' ? '!' : '✓'}</i><div><strong>${escapeHtml(title)}</strong><span>${escapeHtml(detail)}</span></div>`;92  region.append(node);93  window.setTimeout(() => {94    node.style.opacity = '0';95    node.style.transform = 'translateY(6px)';96    window.setTimeout(() => node.remove(), 220);97  }, 3600);98}99 100function escapeHtml(value) {101  return String(value)102    .replaceAll('&', '&amp;')103    .replaceAll('<', '&lt;')104    .replaceAll('>', '&gt;')105    .replaceAll('"', '&quot;')106    .replaceAll("'", '&#039;');107}108 109function normalizeSpaces(value) {110  return String(value || '').trim().replace(/\s+/g, ' ');111}112 113function normalizeSubmission(formData) {114  const utm = normalizeSpaces(formData.get('utm'))115    .toLowerCase()116    .replace(/[^a-z0-9]+/g, '_')117    .replace(/^_+|_+$/g, '');118  return {119    name: normalizeSpaces(formData.get('name')),120    email: normalizeSpaces(formData.get('email')).toLowerCase(),121    company: normalizeSpaces(formData.get('company')),122    source: normalizeSpaces(formData.get('source')),123    utm,124    budget: Number(formData.get('budget')),125    need: normalizeSpaces(formData.get('need')),126    timing: normalizeSpaces(formData.get('timing')),127    simulateFailure: formData.get('simulateFailure') === 'on'128  };129}130 131function validateSubmission(record) {132  const errors = [];133  if (record.name.length < 2) errors.push('Contact name is required.');134  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(record.email)) errors.push('Enter a valid work email.');135  if (record.company.length < 2) errors.push('Company is required.');136  if (!record.source) errors.push('Source is required.');137  if (!record.utm) errors.push('UTM campaign is required.');138  if (!Number.isFinite(record.budget) || record.budget <= 0) errors.push('Budget is required.');139  if (!record.need) errors.push('Primary need is required.');140  if (!record.timing) errors.push('Timing is required.');141  return errors;142}143 144function scoreLead(record) {145  const budgetPoints = record.budget >= 20000 ? 30 : record.budget >= 10000 ? 24 : record.budget >= 5000 ? 15 : 6;146  const needPoints = {147    automation: 25,148    'content-system': 23,149    attribution: 22,150    'landing-crm': 20151  }[record.need] || 12;152  const timingPoints = { now: 20, '30d': 15, '90d': 9, exploring: 3 }[record.timing] || 3;153  const sourcePoints = {154    YouTube: 15,155    LinkedIn: 14,156    Newsletter: 12,157    Podcast: 11,158    'Organic search': 8159  }[record.source] || 7;160  const campaignPoints = record.utm ? 10 : 0;161  const total = Math.min(100, budgetPoints + needPoints + timingPoints + sourcePoints + campaignPoints);162  const tier = total >= 80 ? 'A' : total >= 60 ? 'B' : 'C';163  const stage = tier === 'A' ? 'Qualified' : tier === 'B' ? 'MQL' : 'Nurture';164  const owner = tier === 'A' ? 'Mackenzie' : tier === 'B' ? 'Max' : 'Nurture bot';165  return {166    total,167    tier,168    stage,169    owner,170    reasons: [171      ['Budget fit', budgetPoints, 30],172      ['Need fit', needPoints, 25],173      ['Timing', timingPoints, 20],174      ['Source intent', sourcePoints, 15],175      ['UTM quality', campaignPoints, 10]176    ]177  };178}179 180function stableId(email) {181  let hash = 0;182  for (const character of email) hash = ((hash << 5) - hash + character.charCodeAt(0)) | 0;183  return `MM-${String(1100 + (Math.abs(hash) % 8800)).padStart(4, '0')}`;184}185 186function initials(name) {187  return normalizeSpaces(name).split(' ').slice(0, 2).map((part) => part[0]?.toUpperCase() || '').join('') || 'LD';188}189 190function formatMoney(value) {191  return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(value);192}193 194function formatCompact(value) {195  if (value >= 1000000) return `$${(value / 1000000).toFixed(1)}M`;196  if (value >= 1000) return `$${(value / 1000).toFixed(value % 1000 === 0 ? 0 : 1)}K`;197  return formatMoney(value);198}199 200function setDemoState(state, title, detail) {201  demoState.dataset.state = state;202  $('strong', demoState).textContent = title;203  $('span', demoState).textContent = detail;204}205 206function resetStepVisuals() {207  $$('[data-demo-step]').forEach((step, index) => {208    step.classList.remove('is-running', 'is-complete', 'is-error');209    $('em', step).textContent = '—';210    $('i span', step).textContent = String(index + 1);211  });212  $('#demoScoreReasons').replaceChildren();213}214 215function markStep(stepName, state, timing = '') {216  const step = $(`[data-demo-step="${stepName}"]`);217  if (!step) return;218  step.classList.remove('is-running', 'is-complete', 'is-error');219  step.classList.add(`is-${state}`);220  $('em', step).textContent = timing || (state === 'complete' ? 'DONE' : state === 'error' ? 'FAILED' : 'RUNNING');221}222 223function appendAudit(eventName, result, detail, variant = '') {224  demoAudit.hidden = false;225  const row = document.createElement('div');226  row.className = `audit-row ${variant}`;227  const time = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });228  row.innerHTML = `<time>${time}</time><span>${escapeHtml(eventName)} · ${escapeHtml(detail)}</span><b>${escapeHtml(result)}</b>`;229  demoAuditList.prepend(row);230  const count = demoAuditList.children.length;231  $('#auditCount').textContent = `${count} event${count === 1 ? '' : 's'}`;232}233 234function appendSystemLog(title, detail, status = 'success') {235  const list = $('#automationLog');236  if (!list) return;237  const entry = document.createElement('div');238  entry.className = `log-entry ${status === 'error' ? 'is-error' : ''}`;239  const now = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });240  entry.innerHTML = `<i class="log-status ${status === 'error' ? 'error' : 'success'}">${status === 'error' ? '!' : '✓'}</i><div><header><strong>${escapeHtml(title)}</strong><span>${now}</span></header><p>${escapeHtml(detail)}</p></div><button type="button" aria-label="Expand log event">⌄</button>`;241  list.prepend(entry);242}243 244function prepareDemoDetails(record) {245  $('.avatar', demoPerson).textContent = initials(record.name);246  $('#demoPersonName').textContent = record.name;247  $('#demoPersonCompany').textContent = record.company;248  $('#demoLeadBadge').textContent = 'INBOUND';249  const originAssets = {250    YouTube: '“The Founder Content Flywheel”',251    LinkedIn: '“Build an engine, not a feed”',252    Newsletter: '“The Operator Brief — Issue 18”',253    Podcast: '“Founder Systems: Scale Without Drift”',254    'Organic search': '“Content Operations for Founder Brands”'255  };256  $('#demoOriginAsset').textContent = originAssets[record.source] || 'Maximum content touchpoint';257  $('#demoOriginPath').textContent = `${record.source} → Content OS landing page`;258  $('#demoUtm').textContent = `utm_campaign=${record.utm}`;259  $('p', $('[data-demo-step="capture"]')).textContent = 'Identity resolved • normalized sample record accepted';260  demoPerson.hidden = false;261  demoSource.hidden = false;262  demoSteps.hidden = false;263}264 265function showValidationError(errors) {266  demoFormError.textContent = errors.join(' ');267  demoFormError.hidden = false;268  setDemoState('error', 'Submission needs attention', 'Nothing was written. Correct the highlighted fields and submit again.');269  appendAudit('submission.validation_failed', 'NO WRITE', errors.join(' '), 'error');270  toast('Lead not submitted', errors[0], 'error');271  appState.demoStatus = 'validation-error';272}273 274function clearValidation() {275  demoFormError.hidden = true;276  demoFormError.textContent = '';277  $$('input, select', demoForm).forEach((field) => field.classList.remove('is-invalid'));278}279 280function setControlsForRun() {281  replayDemoButton.hidden = true;282  retryDemoButton.hidden = true;283  takeoverLeadButton.hidden = true;284  inspectLeadButton.hidden = true;285  demoSummary.hidden = true;286  mockNotification.hidden = true;287}288 289async function submitDemoLead(event) {290  event.preventDefault();291  if (appState.demoStatus === 'running') return;292  clearValidation();293  const record = normalizeSubmission(new FormData(demoForm));294  const errors = validateSubmission(record);295  if (errors.length) {296    $$('input[required], select[required]', demoForm).forEach((field) => {297      if (!normalizeSpaces(field.value)) field.classList.add('is-invalid');298      if (field.name === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field.value.trim())) field.classList.add('is-invalid');299    });300    showValidationError(errors);301    return;302  }303 304  appState.demoStatus = 'running';305  setControlsForRun();306  resetStepVisuals();307  demoAuditList.replaceChildren();308  $('#auditCount').textContent = '0 events';309  demoForm.hidden = true;310  prepareDemoDetails(record);311  setDemoState('loading', 'Validating and normalizing submission', 'Email, source, campaign, budget, need, and timing are being normalized locally.');312  record.score = scoreLead(record);313  record.id = stableId(record.email);314  record.startedAt = performance.now();315  record.pausedDuration = 0;316  record.uiCreated = false;317  record.attributed = false;318  appState.currentRun = record;319 320  markStep('capture', 'running');321  appendAudit('lead.received', 'ACCEPTED', `source=${record.source}; utm=${record.utm}`);322  appendSystemLog('Sample lead received', `${record.email} • source=${record.source} • campaign=${record.utm}`);323  await wait(480);324 325  const duplicate = appState.recordsByEmail.get(record.email);326  if (duplicate) {327    record.duplicateOf = duplicate.id;328    markStep('capture', 'complete', '0.3s');329    const captureText = $('p', $('[data-demo-step="capture"]'));330    captureText.textContent = `Existing contact ${duplicate.id} matched • CRM create skipped`;331    appendAudit('identity.duplicate_detected', 'NO CREATE', `${record.email} → ${duplicate.id}`, 'duplicate');332    appendAudit('crm.activity_appended', 'UPDATED', 'New content touch attached to existing record');333    appendSystemLog('Duplicate lead safely merged', `${record.email} matched ${duplicate.id}; CRM create skipped`);334    $('#demoLeadBadge').textContent = 'DUPLICATE';335    setDemoState('duplicate', 'Duplicate detected before CRM write', `Matched ${duplicate.id}. One activity was appended; CRM record count stayed unchanged.`);336    appState.demoStatus = 'duplicate';337    replayDemoButton.hidden = false;338    inspectLeadButton.hidden = false;339    takeoverLeadButton.hidden = false;340    return;341  }342 343  appendAudit('lead.normalized', 'VALID', `${record.email}; campaign=${record.utm}`);344  markStep('capture', 'complete', '0.4s');345  await runScoreStep(record);346}347 348async function runScoreStep(record) {349  markStep('score', 'running');350  setDemoState('loading', 'Calculating a deterministic fit score', 'The same inputs always return the same points, tier, stage, and owner.');351  await wait(520);352  const score = record.score;353  $('#demoScoreResult').textContent = `${score.total} / ${score.tier}-tier • ${score.tier === 'A' ? 'high commercial intent' : score.tier === 'B' ? 'qualified for review' : 'nurture until intent rises'}`;354  const reasons = $('#demoScoreReasons');355  reasons.replaceChildren(...score.reasons.map(([label, points, max]) => {356    const item = document.createElement('span');357    item.textContent = `${label} +${points}/${max}`;358    return item;359  }));360  appendAudit('lead.scored', `${score.total}/${score.tier}`, score.reasons.map(([name, value]) => `${name} +${value}`).join('; '));361  appendSystemLog('Lead scored deterministically', `${record.id} • ${score.total}/${score.tier} • ${score.stage}`);362  markStep('score', 'complete', '0.5s');363  await runCrmStep(record);364}365 366async function runCrmStep(record) {367  markStep('crm', 'running');368  setDemoState('loading', 'Writing one idempotent CRM record', `Key ${record.id} is reserved from the normalized email.`);369  await wait(520);370  if (!appState.recordsByEmail.has(record.email)) {371    appState.recordsByEmail.set(record.email, record);372    updateCrmUi(record);373  }374  const crmText = $('p', $('[data-demo-step="crm"]'));375  crmText.textContent = `${record.id} • ${formatMoney(record.budget)} • ${record.score.stage}`;376  appendAudit('crm.contact_upserted', 'CREATED', `${record.id}; idempotency_key=${record.id}`);377  appendAudit('crm.opportunity_upserted', 'CREATED', `${formatMoney(record.budget)}; stage=${record.score.stage}`);378  appendSystemLog('CRM opportunity created', `${record.id} • ${record.company} • ${formatMoney(record.budget)} • ${record.score.stage}`);379  markStep('crm', 'complete', '0.5s');380  await runRouteStep(record, false);381}382 383async function runRouteStep(record, isRetry) {384  markStep('route', 'running');385  setDemoState('loading', isRetry ? 'Replaying only the failed Slack delivery' : 'Assigning owner and creating follow-up', isRetry ? `CRM write ${record.id} is protected and will not run again.` : 'Routing by tier, segment, and operator capacity.');386  await wait(isRetry ? 650 : 540);387  const routeText = $('p', $('[data-demo-step="route"]'));388  routeText.textContent = `${record.score.owner} • #new-qualified-leads • SLA ${record.score.tier === 'A' ? '5m' : '30m'}`;389  if (!record.taskCreated) {390    record.taskCreated = true;391    appendAudit('task.followup_created', 'CREATED', `owner=${record.score.owner}; SLA=${record.score.tier === 'A' ? '5m' : '30m'}`);392  }393 394  if (record.simulateFailure && !isRetry && !record.failureSimulated) {395    record.failureSimulated = true;396    record.pausedAt = performance.now();397    markStep('route', 'error', 'TIMEOUT');398    appendAudit('slack.notification_failed', 'RETRY QUEUED', `HTTP 504; attempt 1/3; crm=${record.id} preserved`, 'error');399    appendSystemLog('Mock Slack delivery timed out', `${record.id} • retry queued • CRM write preserved`, 'error');400    setDemoState('error', 'Slack mock timed out after the CRM write', 'The lead and task are safe. Retry only the failed delivery or take over manually.');401    appState.demoStatus = 'integration-error';402    retryDemoButton.hidden = false;403    takeoverLeadButton.hidden = false;404    inspectLeadButton.hidden = false;405    syncStaticFailure(record);406    return;407  }408 409  record.slackDelivered = true;410  markStep('route', 'complete', isRetry ? 'RETRIED' : '0.6s');411  appendAudit('slack.notification_sent', isRetry ? 'RECOVERED' : 'DELIVERED', `#new-qualified-leads; owner=${record.score.owner}`);412  appendSystemLog(isRetry ? 'Slack handoff recovered' : 'Owner assigned and Slack brief sent', `${record.id} → ${record.score.owner} • SLA started`);413  showMockNotification(record, isRetry);414  await runAttributionStep(record);415}416 417async function runAttributionStep(record) {418  markStep('attribute', 'running');419  setDemoState('loading', 'Resolving campaign-to-pipeline attribution', `${record.source}, ${record.utm}, and ${record.id} are joined into one buyer path.`);420  await wait(540);421  if (!record.attributed) {422    record.attributed = true;423    updateAttributionUi(record);424  }425  const attributionText = $('p', $('[data-demo-step="attribute"]'));426  attributionText.textContent = `${record.utm} • ${formatMoney(record.budget)} pipeline • content influenced`;427  appendAudit('attribution.touch_resolved', 'MATCHED', `${record.source} → ${record.utm} → ${record.id}`);428  appendAudit('attribution.pipeline_updated', `+${formatMoney(record.budget)}`, 'Campaign and content influence recalculated');429  appendSystemLog('Revenue attribution updated', `${record.utm} +${formatMoney(record.budget)} pipeline • ${record.id}`);430  markStep('attribute', 'complete', '0.5s');431  finishDemo(record);432}433 434function finishDemo(record) {435  appState.demoStatus = 'success';436  const duration = ((performance.now() - record.startedAt - (record.pausedDuration || 0)) / 1000).toFixed(1);437  setDemoState('success', 'Flow complete with an auditable handoff', `${record.id} is qualified, assigned, task-backed, notified, and attributed.`);438  $('#demoDuration').textContent = `${duration}s`;439  $('strong', demoSummary).childNodes[0].textContent = `Lead captured → ${formatCompact(record.budget)} pipeline in `;440  demoSummary.hidden = false;441  replayDemoButton.hidden = false;442  takeoverLeadButton.hidden = false;443  inspectLeadButton.hidden = false;444  retryDemoButton.hidden = true;445  $('#demoLeadBadge').textContent = `${record.score.total} / ${record.score.tier}`;446  appState.eventsProcessed += 7;447  $('#eventsProcessed').textContent = appState.eventsProcessed.toLocaleString('en-US');448  toast('Content-to-revenue flow complete', `${record.name} became ${formatCompact(record.budget)} in attributable sample pipeline.`);449}450 451function updateCrmUi(record) {452  if (record.uiCreated) return;453  record.uiCreated = true;454  appState.capturedLeads += 1;455  if (record.score.tier !== 'C') appState.qualifiedLeads += 1;456  appState.pipeline += record.budget;457  $('#leadMetric').textContent = appState.capturedLeads.toLocaleString('en-US');458  $('#qualifiedMetric').textContent = appState.qualifiedLeads.toLocaleString('en-US');459  $('#flowQualifiedMetric').textContent = appState.qualifiedLeads.toLocaleString('en-US');460  $('#navLeadCount').textContent = appState.qualifiedLeads.toLocaleString('en-US');461  $('#pipelineMetric').textContent = formatCompact(appState.pipeline);462  $('#flowPipelineMetric').textContent = formatCompact(appState.pipeline);463  $('#pipelineTotal').textContent = formatMoney(appState.pipeline);464  ['#leadMetric', '#qualifiedMetric', '#flowQualifiedMetric', '#pipelineMetric', '#flowPipelineMetric'].forEach((selector) => flashMetric($(selector)));465  insertLeadRow(record);466  insertPipelineCard(record);467}468 469function insertLeadRow(record) {470  const table = $('.lead-table');471  const row = document.createElement('button');472  const scoreClass = record.score.tier === 'A' ? 'score-a' : record.score.tier === 'B' ? 'score-b' : 'score-c';473  const statusClass = record.score.tier === 'A' ? 'qualified' : 'nurture';474  row.type = 'button';475  row.className = 'data-row lead-row new-demo-record';476  row.dataset.lead = [record.name, record.score.total, `${record.need} + ${record.timing}`, record.score.owner, record.score.stage, record.score.tier, record.company, record.source].join('|');477  row.setAttribute('role', 'row');478  row.innerHTML = `<span><b>${escapeHtml(record.name)}</b><small>${escapeHtml(record.company)} • SAMPLE</small></span><span><em class="score ${scoreClass}">${record.score.total} / ${record.score.tier}</em></span><span><b>${escapeHtml(record.need.replaceAll('-', ' '))}</b><small>${escapeHtml(record.utm)}</small></span><span><i class="mini-avatar">${escapeHtml(initials(record.score.owner))}</i>${escapeHtml(record.score.owner)}</span><span><em class="pill ${statusClass}">${escapeHtml(record.score.stage)}</em></span>`;479  table.insertBefore(row, $('.lead-row', table));480}481 482function insertPipelineCard(record) {483  const column = $('.pipeline-column');484  const card = document.createElement('div');485  card.className = 'deal-card featured new-demo-record';486  card.innerHTML = `<div><i class="source-icon video">✦</i><span>${escapeHtml(formatMoney(record.budget))}</span></div><strong>${escapeHtml(record.company)} — ${escapeHtml(record.need.replaceAll('-', ' '))}</strong><small>${escapeHtml(record.name)} • ${record.score.total} score • SAMPLE</small><footer><i class="mini-avatar">${escapeHtml(initials(record.score.owner))}</i><span>SLA active</span></footer>`;487  column.insertBefore(card, $('.deal-card', column));488}489 490function updateAttributionUi(record) {491  appState.campaignLeads += 1;492  appState.campaignPipeline += record.budget;493  $('#demoCampaignLeads').textContent = appState.campaignLeads.toLocaleString('en-US');494  $('#demoCampaignPipeline').textContent = formatCompact(appState.campaignPipeline);495  $('#demoCampaignRow').classList.add('new-demo-record');496  flashMetric($('#demoCampaignLeads'));497  flashMetric($('#demoCampaignPipeline'));498}499 500function flashMetric(element) {501  if (!element) return;502  element.classList.remove('metric-flash');503  void element.offsetWidth;504  element.classList.add('metric-flash');505}506 507function showMockNotification(record, recovered = false) {508  $('#mockSlackTitle').textContent = `${recovered ? 'Recovered: ' : ''}New ${record.score.tier}-tier lead assigned to ${record.score.owner}`;509  $('#mockSlackBody').textContent = `${record.name} • ${record.company} • ${formatCompact(record.budget)} potential • SLA due in ${record.score.tier === 'A' ? '5' : '30'} minutes`;510  mockNotification.hidden = false;511}512 513function syncStaticFailure(record) {514  $('#slackLastRun').textContent = 'just now';515  $('#slackStatus').textContent = '1 retry';516  $('#slackStatus').className = 'pill warning';517  const failed = $('#failedLog');518  failed.classList.add('is-error');519  $('header strong', failed).textContent = 'Sample Slack delivery timed out';520  $('p', failed).innerHTML = `<code>${escapeHtml(record.id)}</code> • attempt 1/3 • CRM write preserved`;521}522 523async function retryDemoFailure() {524  const record = appState.currentRun;525  if (!record || appState.demoStatus !== 'integration-error') return;526  if (record.pausedAt) {527    record.pausedDuration += performance.now() - record.pausedAt;528    record.pausedAt = null;529  }530  retryDemoButton.disabled = true;531  retryDemoButton.textContent = 'Retrying…';532  appendAudit('retry.requested', 'QUEUED', `slack step only; idempotency_key=${record.id}`);533  await runRouteStep(record, true);534  retryDemoButton.disabled = false;535  retryDemoButton.textContent = 'Retry failed step';536  resolveStaticFailure();537}538 539function resolveStaticFailure() {540  appState.genericRetryComplete = true;541  $('#slackLastRun').textContent = 'just now';542  $('#slackSuccess').textContent = '99.1%';543  $('#slackStatus').textContent = 'Healthy';544  $('#slackStatus').className = 'pill success';545  const failed = $('#failedLog');546  if (failed) {547    failed.classList.remove('is-error');548    const status = $('.log-status', failed);549    status.className = 'log-status success';550    status.textContent = '✓';551    $('header strong', failed).textContent = 'Slack delivery recovered on retry';552    $('.log-error-detail', failed)?.remove();553    const button = $('[data-retry]', failed);554    if (button) button.replaceWith(document.createTextNode('Recovered'));555  }556}557 558async function genericRetry() {559  if (appState.demoStatus === 'integration-error') {560    await retryDemoFailure();561    return;562  }563  if (appState.genericRetryComplete) {564    toast('Already recovered', 'The retry was idempotent and the workflow is healthy.');565    return;566  }567  $$('[data-retry]').forEach((button) => {568    button.disabled = true;569    button.textContent = 'Retrying…';570  });571  toast('Retry queued', 'Only the failed mock Slack delivery will replay.');572  await wait(850);573  resolveStaticFailure();574  appendSystemLog('Slack delivery recovered', 'evt_83fa • attempt 3/3 • CRM write not repeated');575  toast('Delivery recovered', 'The CRM write was not repeated; the Slack step is healthy.');576}577 578async function takeOverCurrentLead() {579  const record = appState.currentRun;580  if (!record) {581    toast('No lead selected', 'Run or select a sample lead before taking over.', 'error');582    return;583  }584  if (record.manualTakeover) {585    toast('Already in manual control', `${record.name} is assigned to Max with full context.`);586    return;587  }588  record.manualTakeover = true;589  if (record.pausedAt) {590    record.pausedDuration += performance.now() - record.pausedAt;591    record.pausedAt = null;592  }593  record.score.owner = 'Max (manual)';594  appendAudit('operator.takeover', 'ASSIGNED', `Max; automation paused for ${record.id}`);595  appendSystemLog('Operator took over sample lead', `${record.id} → Max • event state preserved`);596  takeoverLeadButton.textContent = 'Manual owner: Max';597  $('#demoLeadBadge').textContent = 'MANUAL';598  setDemoState('success', 'Max now owns this lead manually', 'Automation state and all prior writes were preserved for a clean handoff.');599  toast('Manual takeover active', `${record.name} is now assigned to Max.`);600  if (appState.demoStatus === 'integration-error') {601    markStep('route', 'complete', 'MANUAL');602    retryDemoButton.hidden = true;603    mockNotification.hidden = true;604    await runAttributionStep(record);605  }606}607 608function resetDemoForAnotherLead() {609  appState.demoStatus = 'idle';610  appState.currentRun = null;611  demoForm.hidden = false;612  demoPerson.hidden = true;613  demoSource.hidden = true;614  demoSteps.hidden = true;615  demoSummary.hidden = true;616  demoAudit.hidden = true;617  mockNotification.hidden = true;618  demoAuditList.replaceChildren();619  resetStepVisuals();620  replayDemoButton.hidden = true;621  retryDemoButton.hidden = true;622  takeoverLeadButton.hidden = true;623  takeoverLeadButton.textContent = 'Take over manually';624  inspectLeadButton.hidden = true;625  clearValidation();626  setDemoState('empty', 'Ready for a sample submission', 'Complete fields above, then run the deterministic workflow.');627  $('input[name="email"]', demoForm).focus();628}629 630function openDemo() {631  if (['success', 'duplicate'].includes(appState.demoStatus)) resetDemoForAnotherLead();632  demoTray.classList.add('is-open');633  demoTray.setAttribute('aria-hidden', 'false');634  document.body.style.overflow = window.innerWidth <= 650 ? 'hidden' : '';635  window.setTimeout(() => {636    const focusTarget = demoForm.hidden ? $('#closeDemo') : $('input[name="name"]', demoForm);637    focusTarget?.focus();638  }, 380);639}640 641function closeDemo() {642  demoTray.classList.remove('is-open');643  demoTray.setAttribute('aria-hidden', 'true');644  document.body.style.overflow = '';645}646 647function selectLeadRow(row) {648  $$('.lead-row').forEach((lead) => lead.classList.toggle('is-selected', lead === row));649  const [name, score, signal, owner, stage, tier, company, source] = row.dataset.lead.split('|');650  $('#inspectorName').textContent = name;651  $('#inspectorCompany').textContent = company;652  const scoreRing = $('#inspectorScore');653  scoreRing.style.setProperty('--score', score);654  $('strong', scoreRing).textContent = score;655  $('span', scoreRing).textContent = `${tier}-TIER`;656  $('#inspectorSource').textContent = source;657  $('#inspectorAction').textContent = tier === 'A' ? `Send personalized audit before ${stage.toLowerCase()}` : tier === 'B' ? `Review ${signal.toLowerCase()} and qualify` : 'Keep in nurture until intent rises';658  $('#assignLead').textContent = owner === 'Unassigned' ? 'Assign owner' : `Owner: ${owner}`;659}660 661function approveContent(action) {662  const spotlight = $('#approvalSpotlight');663  if (action === 'approve') {664    if (appState.approvedContent) {665      toast('Already scheduled', 'This sample asset is queued for Jul 27 at 10:00 AM.');666      return;667    }668    appState.approvedContent = true;669    $('#awaitingCount').textContent = '1';670    $('#boardApprovalCount').textContent = '1';671    $('#scheduledCount').textContent = '7';672    $('[data-content-action="approve"]').textContent = '✓ Approved • scheduled';673    $('[data-content-action="approve"]').disabled = true;674    spotlight.classList.add('new-demo-record');675    appendSystemLog('Content asset approved', 'asset_0201 → LinkedIn • scheduled Jul 27 10:00');676    toast('Approved and scheduled', 'The sample LinkedIn video moved to Jul 27 at 10:00 AM.');677  } else {678    $('#approvalTitle').textContent = 'Why founder content breaks at scale — revision requested';679    $('.approval-meta .pill').textContent = 'REVISION REQUESTED';680    toast('Revision routed', 'The editor received the sample note with the current cut attached.');681  }682}683 684function updateAttributionModel(model) {685  const options = {686    influence: ['$42,800', '<strong>Content influence</strong> credits verified content interactions that occurred before opportunity creation and materially advanced the buyer.'],687    linear: ['$39,460', '<strong>Linear multi-touch</strong> splits credit evenly across all verified touches in the known buyer journey.'],688    first: ['$31,200', '<strong>First touch</strong> assigns revenue credit to the first known acquisition source in the journey.'],689    last: ['$36,900', '<strong>Last touch</strong> assigns credit to the final known interaction before opportunity creation.']690  };691  const [revenue, explanation] = options[model] || options.influence;692  $('#influencedRevenue').textContent = revenue;693  $('p', $('#modelExplanation')).innerHTML = explanation;694  flashMetric($('#influencedRevenue'));695}696 697async function copyUtmRules() {698  const rules = 'utm_source={channel}&utm_medium={format}&utm_campaign={initiative}&utm_content={asset_slug}';699  try {700    await navigator.clipboard.writeText(rules);701    toast('UTM rules copied', rules);702  } catch {703    toast('UTM naming rule', rules);704  }705}706 707function toggleGlobalTakeover() {708  appState.takeoverActive = !appState.takeoverActive;709  const button = $('#takeoverButton');710  const status = $('#takeoverWorkflow strong');711  if (appState.takeoverActive) {712    status.innerHTML = '<i style="background:#a66b17"></i> Manual queue active';713    status.style.color = '#a66b17';714    button.textContent = 'Resume automation';715    appendSystemLog('Human takeover enabled', 'New lead events enter the visible manual queue');716    toast('Manual queue active', 'In-flight events finished safely; new leads await an operator.');717  } else {718    status.innerHTML = '<i></i> Automation active';719    status.style.color = '';720    button.textContent = 'Pause & take over';721    appendSystemLog('Automation resumed', 'Queued lead events are processing in received order');722    toast('Automation resumed', 'Queued sample events are processing in order.');723  }724}725 726$$('.nav-item').forEach((button) => button.addEventListener('click', () => switchView(button.dataset.view)));727$$('[data-view-jump]').forEach((button) => button.addEventListener('click', () => switchView(button.dataset.viewJump, true)));728mobileMenu.addEventListener('click', () => sidebar.classList.contains('is-open') ? closeMobileNavigation() : openMobileNavigation());729sidebarScrim.addEventListener('click', closeMobileNavigation);730 731$$('[data-run-demo]').forEach((button) => button.addEventListener('click', openDemo));732$('#closeDemo').addEventListener('click', closeDemo);733demoForm.addEventListener('submit', submitDemoLead);734replayDemoButton.addEventListener('click', resetDemoForAnotherLead);735retryDemoButton.addEventListener('click', retryDemoFailure);736takeoverLeadButton.addEventListener('click', takeOverCurrentLead);737inspectLeadButton.addEventListener('click', () => {738  closeDemo();739  switchView('leads', true);740  const first = $('.lead-row');741  if (first) selectLeadRow(first);742});743$('#duplicatePreset').addEventListener('click', () => {744  $('input[name="name"]', demoForm).value = 'Maya Chen';745  $('input[name="email"]', demoForm).value = 'maya@northstarhealth.com';746  $('input[name="company"]', demoForm).value = 'Northstar Health';747  toast('Duplicate fixture loaded', 'Submit it to verify that the CRM create is skipped.');748});749$('#auditToggle').addEventListener('click', (event) => {750  const expanded = event.currentTarget.getAttribute('aria-expanded') === 'true';751  event.currentTarget.setAttribute('aria-expanded', String(!expanded));752  demoAuditList.hidden = expanded;753  $('i', event.currentTarget).textContent = expanded ? '⌄' : '⌃';754});755 756document.addEventListener('click', (event) => {757  const leadRow = event.target.closest('.lead-row');758  if (leadRow) selectLeadRow(leadRow);759  const retry = event.target.closest('[data-retry]');760  if (retry && retry !== retryDemoButton) genericRetry();761  const contentAction = event.target.closest('[data-content-action]');762  if (contentAction) approveContent(contentAction.dataset.contentAction);763  const rowMenu = event.target.closest('.row-menu');764  if (rowMenu) toast('Workflow controls', 'Run history, owner, retry policy, and integration boundary are available in production.');765});766 767$('#leadSearch').addEventListener('input', (event) => {768  const query = event.target.value.trim().toLowerCase();769  $$('.lead-row').forEach((row) => { row.hidden = !row.textContent.toLowerCase().includes(query); });770});771$('#rescoreLead').addEventListener('click', async (event) => {772  event.currentTarget.disabled = true;773  event.currentTarget.textContent = 'Scoring…';774  await wait(600);775  event.currentTarget.disabled = false;776  event.currentTarget.textContent = 'Re-score sample';777  toast('Score unchanged', 'Deterministic inputs produced the same tier and explanation.');778});779$('#assignLead').addEventListener('click', (event) => {780  event.currentTarget.textContent = 'Owner: Max';781  toast('Owner assigned', 'A sample follow-up task and SLA were created for Max.');782});783$('#attributionModel').addEventListener('change', (event) => updateAttributionModel(event.target.value));784$('#copyUtm').addEventListener('click', copyUtmRules);785$('#takeoverButton').addEventListener('click', toggleGlobalTakeover);786$('#clearLogFilter').addEventListener('click', (event) => {787  event.currentTarget.textContent = 'Showing all events';788  toast('All events visible', 'Success, retry, manual, and error states are included.');789});790 791document.addEventListener('keydown', (event) => {792  if (event.key === 'Escape') {793    if (demoTray.classList.contains('is-open')) closeDemo();794    else closeMobileNavigation();795  }796});797 798window.addEventListener('resize', () => {799  if (window.innerWidth > 860) closeMobileNavigation();800});801 802switchView('overview');803