Sprint 2 UI and brand refresh
Workbench: ramp-adjusted headline (TCO, net run-rate, NPV, IRR, payback), financial model panel with internal-only investment, year-by-year cash flow, scenario panel (presets + custom, presented scenario, report comparison), overlap heat map, shelfware and expansion panel, editable implementation timeline (drag, resize, row editor, dependencies, reset), version history with compare and restore, deal status, customer logo. Deliverables: report, Word and new PowerPoint deck present the chosen scenario with net figures only; investment and dossier entitlements never itemized (covered by the leak suite). Exports and printing snapshot automatically. New pages: Portfolio dashboard, Settings (company name, logo, accent). Knowledge base: duplicate queue with merge, confidence and sourcing, TCO defaults, needs-review filter. Brand: VMware by Broadcom palette per Brand Guidelines.pdf; Darkmode navy dark theme; AA-safe accent ink; business units remapped to palette. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,33 @@ try {
|
||||
t('Word still carries a mapped product', /Avi Load Balancer|VCF Networking|Layer7/.test(docx));
|
||||
|
||||
execFileSync('rm', ['-f', tmp.pathname]);
|
||||
|
||||
/* ---- our pricing and dossier entitlements stay internal (sprint 2) ---- */
|
||||
const PRICE = 7_654_321, SERVICES = 1_234_567;
|
||||
const fmt = (n) => '$' + n.toLocaleString('en-US');
|
||||
const priced = await json(`/api/assessments/${a.id}`);
|
||||
priced.finance = { discountRate: 10, fteCost: 150000, investment: { annual: PRICE, services: SERVICES } };
|
||||
priced.candidates[0].alreadyEntitled = 'VMware Avi Load Balancer';
|
||||
await send(`/api/assessments/${a.id}`, 'PUT', priced);
|
||||
|
||||
const deckBuf = Buffer.from(await (await fetch(`${B}/api/assessments/${a.id}/deck`)).arrayBuffer());
|
||||
const deckTmp = new URL('./.leak-test.pptx', import.meta.url);
|
||||
writeFileSync(deckTmp, deckBuf);
|
||||
const deck = execFileSync('sh', ['-c', `unzip -p '${deckTmp.pathname}' 'ppt/slides/*.xml'`], { maxBuffer: 64e6 }).toString('utf8');
|
||||
t('deck is a readable pptx with slides', deck.includes('<p:sld') && deck.length > 5000);
|
||||
const docx2Buf = Buffer.from(await (await fetch(`${B}/api/assessments/${a.id}/docx`)).arrayBuffer());
|
||||
writeFileSync(tmp, docx2Buf);
|
||||
const docx2 = execFileSync('unzip', ['-p', tmp.pathname, 'word/document.xml'], { maxBuffer: 32e6 }).toString('utf8');
|
||||
const safe2 = JSON.stringify(await json(`/api/assessments/${a.id}/report`));
|
||||
|
||||
for (const [name, text] of [['deck', deck], ['Word', docx2]]) {
|
||||
t(`${name} omits our annual price`, !text.includes(fmt(PRICE)) && !text.includes('7.7M'));
|
||||
t(`${name} omits our services figure`, !text.includes(fmt(SERVICES)));
|
||||
t(`${name} never says "subscription"`, !/subscription/i.test(text));
|
||||
t(`${name} never names an entitlement`, !/entitle/i.test(text));
|
||||
}
|
||||
t('projection drops entitlement flags', !safe2.includes('alreadyEntitled'));
|
||||
execFileSync('rm', ['-f', tmp.pathname, deckTmp.pathname]);
|
||||
} finally {
|
||||
await fetch(`${B}/api/assessments/${a.id}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user