/** * Helix Engage — Weekly Update (Mar 18–25, 2026) * Light Mode PowerPoint Generator via PptxGenJS */ const PptxGenJS = require("pptxgenjs"); // ── Design Tokens (Light Mode) ───────────────────────────────────────── const C = { bg: "FFFFFF", bgSubtle: "F8FAFC", bgCard: "F1F5F9", bgCardAlt: "E2E8F0", text: "1E293B", textSec: "475569", textMuted: "94A3B8", accent1: "0EA5E9", // Sky blue (telephony) accent2: "8B5CF6", // Violet (server/backend) accent3: "10B981", // Emerald (UX) accent4: "F59E0B", // Amber (features) accent5: "EF4444", // Rose (ops) accent6: "6366F1", // Indigo (timeline) white: "FFFFFF", border: "CBD5E1", }; const FONT = { heading: "Arial", body: "Arial", }; // ── Helpers ────────────────────────────────────────────────────────────── function addSlideNumber(slide, num, total) { slide.addText(`${num} / ${total}`, { x: 8.8, y: 5.2, w: 1.2, h: 0.3, fontSize: 8, color: C.textMuted, fontFace: FONT.body, align: "right", }); } function addAccentBar(slide, color) { slide.addShape("rect", { x: 0, y: 0, w: 10, h: 0.06, fill: { color }, }); } function addLabel(slide, text, color, x, y) { slide.addShape("roundRect", { x, y, w: text.length * 0.09 + 0.4, h: 0.3, fill: { color, transparency: 88 }, rectRadius: 0.15, }); slide.addText(text.toUpperCase(), { x, y, w: text.length * 0.09 + 0.4, h: 0.3, fontSize: 7, fontFace: FONT.heading, bold: true, color, align: "center", valign: "middle", letterSpacing: 2, }); } function addCard(slide, opts) { const { x, y, w, h, title, titleColor, items, badge } = opts; // Card background slide.addShape("roundRect", { x, y, w, h, fill: { color: C.bgCard }, line: { color: C.border, width: 0.5 }, rectRadius: 0.1, }); // Title const titleText = badge ? [{ text: title + " ", options: { bold: true, color: titleColor, fontSize: 11 } }, { text: badge, options: { bold: true, color: C.white, fontSize: 7, highlight: titleColor } }] : title; slide.addText(titleText, { x: x + 0.2, y: y + 0.08, w: w - 0.4, h: 0.35, fontSize: 11, fontFace: FONT.heading, bold: true, color: titleColor, }); // Items as bullet list if (items && items.length > 0) { slide.addText( items.map(item => ({ text: item, options: { fontSize: 8.5, fontFace: FONT.body, color: C.textSec, bullet: { type: "bullet", style: "arabicPeriod" }, paraSpaceAfter: 2, breakLine: true, }, })), { x: x + 0.2, y: y + 0.4, w: w - 0.4, h: h - 0.5, valign: "top", bullet: { type: "bullet" }, lineSpacingMultiple: 1.1, } ); } } // ── Build Presentation ────────────────────────────────────────────────── async function build() { const pptx = new PptxGenJS(); pptx.layout = "LAYOUT_16x9"; pptx.author = "Satya Suman Sari"; pptx.company = "FortyTwo Platform"; pptx.title = "Helix Engage — Weekly Update (Mar 18–25, 2026)"; pptx.subject = "Engineering Progress Report"; const TOTAL = 9; // ═══════════════════════════════════════════════════════════════════════ // SLIDE 1 — Title // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; // Accent bar top addAccentBar(slide, C.accent1); // Decorative side stripe slide.addShape("rect", { x: 0, y: 0, w: 0.12, h: 5.63, fill: { color: C.accent1 }, }); // Label addLabel(slide, "Weekly Engineering Update", C.accent1, 3.0, 1.2); // Title slide.addText("Helix Engage", { x: 1.0, y: 1.8, w: 8, h: 1.2, fontSize: 44, fontFace: FONT.heading, bold: true, color: C.accent1, align: "center", }); // Subtitle slide.addText("Contact Center CRM · Real-time Telephony · AI Copilot", { x: 1.5, y: 2.9, w: 7, h: 0.5, fontSize: 14, fontFace: FONT.body, color: C.textSec, align: "center", }); // Date slide.addText("March 18 – 25, 2026", { x: 3, y: 3.6, w: 4, h: 0.4, fontSize: 12, fontFace: FONT.heading, bold: true, color: C.textMuted, align: "center", letterSpacing: 3, }); // Bottom decoration slide.addShape("rect", { x: 3.5, y: 4.2, w: 3, h: 0.04, fill: { color: C.accent2 }, }); // Author slide.addText("Satya Suman Sari · FortyTwo Platform", { x: 2, y: 4.5, w: 6, h: 0.35, fontSize: 9, fontFace: FONT.body, color: C.textMuted, align: "center", }); addSlideNumber(slide, 1, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 2 — At a Glance // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent2); addLabel(slide, "At a Glance", C.accent2, 0.5, 0.3); slide.addText("Week in Numbers", { x: 0.5, y: 0.65, w: 5, h: 0.5, fontSize: 24, fontFace: FONT.heading, bold: true, color: C.text, }); // Stat cards const stats = [ { value: "78", label: "Total Commits", color: C.accent1 }, { value: "3", label: "Repositories", color: C.accent2 }, { value: "8", label: "Days Active", color: C.accent3 }, { value: "50", label: "Frontend Commits", color: C.accent4 }, ]; stats.forEach((s, i) => { const x = 0.5 + i * 2.35; // Card bg slide.addShape("roundRect", { x, y: 1.3, w: 2.1, h: 1.7, fill: { color: C.bgCard }, line: { color: C.border, width: 0.5 }, rectRadius: 0.12, }); // Accent top line slide.addShape("rect", { x: x + 0.2, y: 1.35, w: 1.7, h: 0.035, fill: { color: s.color }, }); // Number slide.addText(s.value, { x, y: 1.5, w: 2.1, h: 0.9, fontSize: 36, fontFace: FONT.heading, bold: true, color: s.color, align: "center", valign: "middle", }); // Label slide.addText(s.label, { x, y: 2.4, w: 2.1, h: 0.4, fontSize: 9, fontFace: FONT.body, color: C.textSec, align: "center", }); }); // Repo breakdown pills const repos = [ { name: "helix-engage", count: "50", clr: C.accent1 }, { name: "helix-engage-server", count: "27", clr: C.accent2 }, { name: "FortyTwoApps/SDK", count: "1", clr: C.accent3 }, ]; repos.forEach((r, i) => { const x = 1.5 + i * 2.8; slide.addShape("roundRect", { x, y: 3.4, w: 2.5, h: 0.4, fill: { color: C.bgCard }, line: { color: r.clr, width: 1 }, rectRadius: 0.2, }); slide.addText(`${r.name} ${r.count}`, { x, y: 3.4, w: 2.5, h: 0.4, fontSize: 9, fontFace: FONT.heading, bold: true, color: r.clr, align: "center", valign: "middle", }); }); // Summary text slide.addText("3 repos · 7 working days · 78 commits shipped to production", { x: 1, y: 4.2, w: 8, h: 0.35, fontSize: 10, fontFace: FONT.body, italic: true, color: C.textMuted, align: "center", }); addSlideNumber(slide, 2, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 3 — Telephony & SIP // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent1); addLabel(slide, "Core Infrastructure", C.accent1, 0.5, 0.3); slide.addText([ { text: "☎ ", options: { fontSize: 22 } }, { text: "Telephony & SIP Overhaul", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); addCard(slide, { x: 0.3, y: 1.35, w: 4.5, h: 2.0, title: "Outbound Calling", titleColor: C.accent1, badge: "Frontend", items: [ "Direct SIP call from browser — no Kookoo bridge", "Immediate call card UI with auto-answer SIP bridge", "End Call label fix, force active state after auto-answer", "Reset outboundPending on call end", ], }); addCard(slide, { x: 5.2, y: 1.35, w: 4.5, h: 2.0, title: "Ozonetel Integration", titleColor: C.accent2, badge: "Server", items: [ "Ozonetel V3 dial endpoint + webhook handler", "Set Disposition API for ACW release", "Force Ready endpoint for agent state mgmt", "Token: 10-min cache, 401 invalidation, refresh on login", ], }); addCard(slide, { x: 0.3, y: 3.55, w: 4.5, h: 1.8, title: "SIP & Agent State", titleColor: C.accent1, badge: "Frontend", items: [ "SIP driven by Agent entity with token refresh", "Centralised outbound dial into useSip().dialOutbound()", "UCID tracking from SIP headers for disposition", "Network indicator for connection health", ], }); addCard(slide, { x: 5.2, y: 3.55, w: 4.5, h: 1.8, title: "Multi-Agent & Sessions", titleColor: C.accent2, badge: "Server", items: [ "Multi-agent SIP with Redis session lockout", "Strict duplicate login — one device per agent", "Session lock stores IP + timestamp for debugging", "SSE agent state broadcast for supervisor view", ], }); addSlideNumber(slide, 3, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 4 — Call Desk & Agent UX // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent3); addLabel(slide, "User Experience", C.accent3, 0.5, 0.3); slide.addText([ { text: "🖥 ", options: { fontSize: 22 } }, { text: "Call Desk & Agent UX", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); addCard(slide, { x: 0.3, y: 1.35, w: 3.05, h: 2.6, title: "Call Desk Redesign", titleColor: C.accent3, items: [ "2-panel layout with collapsible sidebar & inline AI", "Collapsible context panel, worklist/calls tabs", "Pinned header & chat input, numpad dialler", "Ringtone support for incoming calls", ], }); addCard(slide, { x: 3.55, y: 1.35, w: 3.05, h: 2.6, title: "Post-Call Workflow", titleColor: C.accent3, items: [ "Disposition → appointment booking → follow-up", "Disposition returns straight to worklist", "Send disposition to sidecar with UCID for ACW", "Enquiry in post-call, appointment skip button", ], }); addCard(slide, { x: 6.8, y: 1.35, w: 2.9, h: 2.6, title: "UI Polish", titleColor: C.accent3, items: [ "FontAwesome Pro Duotone icon migration", "Tooltips, sticky headers, roles, search", "Fix React error #520 in prod tables", "AI scroll containment, brand tokens refresh", ], }); addSlideNumber(slide, 4, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 5 — Features Shipped // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent4); addLabel(slide, "Features Shipped", C.accent4, 0.5, 0.3); slide.addText([ { text: "🚀 ", options: { fontSize: 22 } }, { text: "Major Features", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); addCard(slide, { x: 0.3, y: 1.35, w: 4.5, h: 2.0, title: "Supervisor Module", titleColor: C.accent4, items: [ "Team performance analytics page", "Live monitor with active calls visibility", "Master data management pages", "Server: team perf + active calls endpoints", ], }); addCard(slide, { x: 5.2, y: 1.35, w: 4.5, h: 2.0, title: "Missed Call Queue (Phase 2)", titleColor: C.accent4, items: [ "Missed call queue ingestion & worklist", "Auto-assignment engine for agents", "Login redesign with role-based routing", "Lead lookup for missed callers", ], }); addCard(slide, { x: 0.3, y: 3.55, w: 4.5, h: 1.8, title: "Agent Features (Phase 1)", titleColor: C.accent4, items: [ "Agent status toggle (Ready / Not Ready / Break)", "Global search across patients, leads, calls", "Enquiry form for new patient intake", "My Performance page + logout modal", ], }); addCard(slide, { x: 5.2, y: 3.55, w: 4.5, h: 1.8, title: "Recording Analysis", titleColor: C.accent4, items: [ "Deepgram diarization + AI insights", "Redis caching layer for analysis results", "Full-stack: frontend player + server module", ], }); addSlideNumber(slide, 5, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 6 — Backend & Data // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent2); addLabel(slide, "Backend & Data", C.accent2, 0.5, 0.3); slide.addText([ { text: "⚙ ", options: { fontSize: 22 } }, { text: "Backend & Data Layer", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); addCard(slide, { x: 0.3, y: 1.35, w: 4.5, h: 2.0, title: "Platform Data Wiring", titleColor: C.accent2, items: [ "Migrated frontend to Jotai + Vercel AI SDK", "Corrected all 7 GraphQL queries (fields, LINKS/PHONES)", "Webhook handler for Ozonetel call records", "Complete seeder: 5 doctors, appointments linked", ], }); addCard(slide, { x: 5.2, y: 1.35, w: 4.5, h: 2.0, title: "Server Endpoints", titleColor: C.accent2, items: [ "Call control, recording, CDR, missed calls, live assist", "Agent summary, AHT, performance aggregation", "Token refresh endpoint for auto-renewal", "Search module with full-text capabilities", ], }); addCard(slide, { x: 0.3, y: 3.55, w: 4.5, h: 1.8, title: "Data Pages Built", titleColor: C.accent2, items: [ "Worklist table, call history, patients, dashboard", "Reports, team dashboard, campaigns, settings", "Agent detail page, campaign edit slideout", "Appointments page with data refresh on login", ], }); addCard(slide, { x: 5.2, y: 3.55, w: 4.5, h: 1.8, title: "SDK App", titleColor: C.accent3, badge: "FortyTwoApps", items: [ "Helix Engage SDK app entity definitions", "Call center CRM object model for platform", "Foundation for platform-native data integration", ], }); addSlideNumber(slide, 6, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 7 — Deployment & Ops // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent5); addLabel(slide, "Operations", C.accent5, 0.5, 0.3); slide.addText([ { text: "🛠 ", options: { fontSize: 22 } }, { text: "Deployment & DevOps", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); addCard(slide, { x: 0.3, y: 1.35, w: 3.05, h: 2.2, title: "Deployment", titleColor: C.accent5, items: [ "Deployed to Hostinger VPS with Docker", "Switched to global_healthx Ozonetel account", "Dockerfile for server-side containerization", ], }); addCard(slide, { x: 3.55, y: 1.35, w: 3.05, h: 2.2, title: "AI & Testing", titleColor: C.accent5, items: [ "Migrated AI to Vercel AI SDK + OpenAI provider", "AI flow test script — validates full pipeline", "Live call assist integration", ], }); addCard(slide, { x: 6.8, y: 1.35, w: 2.9, h: 2.2, title: "Documentation", titleColor: C.accent5, items: [ "Team onboarding README with arch guide", "Supervisor module spec + plan", "Multi-agent spec + plan", "Next session plans in commits", ], }); addSlideNumber(slide, 7, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 8 — Timeline // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent6); addLabel(slide, "Day by Day", C.accent6, 0.5, 0.3); slide.addText([ { text: "📅 ", options: { fontSize: 22 } }, { text: "Development Timeline", options: { fontSize: 22, bold: true, color: C.text } }, ], { x: 0.5, y: 0.65, w: 9, h: 0.5, fontFace: FONT.heading }); const timeline = [ { date: "MAR 18 (Tue)", title: "Foundation Day", desc: "Call desk redesign, Jotai + AI SDK migration, seeder, AI flow test, VPS deploy" }, { date: "MAR 19 (Wed)", title: "Data Layer Sprint", desc: "All data pages, post-call workflow, GraphQL fixes, Kookoo IVR, outbound UI" }, { date: "MAR 20 (Thu)", title: "Telephony Breakthrough", desc: "Direct SIP replacing Kookoo, UCID tracking, Force Ready, Set Disposition" }, { date: "MAR 21 (Fri)", title: "Agent Experience", desc: "Phase 1: status toggle, search, enquiry form, My Performance, FA icons, AHT" }, { date: "MAR 23 (Sun)", title: "Scale & Reliability", desc: "Phase 2: missed call queue, auto-assign, Redis lockout, Patient 360, SDK defs" }, { date: "MAR 24 (Mon)", title: "Supervisor Module", desc: "Team perf, live monitor, master data, SSE, UUID fix, maintenance, QA sweep" }, { date: "MAR 25 (Tue)", title: "Intelligence Layer", desc: "Deepgram diarization, AI insights, SIP via Agent entity, token refresh, network" }, ]; // Vertical line slide.addShape("rect", { x: 1.4, y: 1.3, w: 0.025, h: 4.0, fill: { color: C.accent6, transparency: 60 }, }); timeline.forEach((entry, i) => { const y = 1.3 + i * 0.56; // Dot slide.addShape("ellipse", { x: 1.32, y: y + 0.08, w: 0.18, h: 0.18, fill: { color: C.accent6 }, line: { color: C.bg, width: 2 }, }); // Date slide.addText(entry.date, { x: 1.7, y: y, w: 1.6, h: 0.22, fontSize: 7, fontFace: FONT.heading, bold: true, color: C.accent6, }); // Title slide.addText(entry.title, { x: 3.3, y: y, w: 2.0, h: 0.22, fontSize: 9, fontFace: FONT.heading, bold: true, color: C.text, }); // Description slide.addText(entry.desc, { x: 5.3, y: y, w: 4.2, h: 0.45, fontSize: 8, fontFace: FONT.body, color: C.textSec, valign: "top", }); }); addSlideNumber(slide, 8, TOTAL); } // ═══════════════════════════════════════════════════════════════════════ // SLIDE 9 — Closing // ═══════════════════════════════════════════════════════════════════════ { const slide = pptx.addSlide(); slide.background = { color: C.bg }; addAccentBar(slide, C.accent3); // Big headline slide.addText("78 commits. 8 days. Ship mode.", { x: 0.5, y: 1.4, w: 9, h: 0.8, fontSize: 32, fontFace: FONT.heading, bold: true, color: C.accent3, align: "center", }); // Ship emoji slide.addText("🚢", { x: 4.2, y: 2.3, w: 1.6, h: 0.6, fontSize: 28, align: "center", }); // Description slide.addText( "From browser-native SIP calling to AI-powered recording analysis — Helix Engage is becoming a production contact center platform.", { x: 1.5, y: 3.0, w: 7, h: 0.6, fontSize: 11, fontFace: FONT.body, color: C.textSec, align: "center", lineSpacingMultiple: 1.3, } ); // Achievement pills const achievements = [ { text: "SIP Calling ✓", color: C.accent1 }, { text: "Multi-Agent ✓", color: C.accent2 }, { text: "Supervisor ✓", color: C.accent3 }, { text: "AI Copilot ✓", color: C.accent4 }, { text: "Recording Analysis ✓", color: C.accent5 }, ]; achievements.forEach((a, i) => { const x = 0.8 + i * 1.8; slide.addShape("roundRect", { x, y: 3.9, w: 1.6, h: 0.35, fill: { color: C.bgCard }, line: { color: a.color, width: 1 }, rectRadius: 0.17, }); slide.addText(a.text, { x, y: 3.9, w: 1.6, h: 0.35, fontSize: 8, fontFace: FONT.heading, bold: true, color: a.color, align: "center", valign: "middle", }); }); // Author slide.addText("Satya Suman Sari · FortyTwo Platform", { x: 2, y: 4.7, w: 6, h: 0.3, fontSize: 9, fontFace: FONT.body, color: C.textMuted, align: "center", }); addSlideNumber(slide, 9, TOTAL); } // ── Save ────────────────────────────────────────────────────────────── const outPath = "weekly-update-mar18-25.pptx"; await pptx.writeFile({ fileName: outPath }); console.log(`✅ Presentation saved: ${outPath}`); } build().catch(err => { console.error("❌ Failed:", err.message); process.exit(1); });