mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 10:23:27 +00:00
- Merged AI Assistant + Lead 360 tabs into single context-aware panel - Context section shows: lead profile, AI insight (live from event bus), appointments, activity - "On call with" banner only shows during active calls (isInCall prop) - AI Chat always available at bottom of panel - Phase 1 only — AI Chat panel needs full redesign with Vercel AI SDK tool calling (Phase 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
681 lines
24 KiB
JavaScript
681 lines
24 KiB
JavaScript
/**
|
||
* 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);
|
||
});
|