Atlas Plan
Plans006 2026 02 20 Present Service

Set Up @services/present

Overview

Build the @services/present PPTX + PDF generator. Implements a PresentAdapter interface making the present layer backend-agnostic. The default implementation uses pptxgenjs to produce editable PPTX files. LibreOffice CLI converts PPTX to PDF. All 6 slide types from the reference PDF are implemented with per-unit brand colors (WLC: blue, TM: dark red).

Result: bun run present --entity IONS --period 2026-02 reads report.json and produces output/monthly/2026-02.pptx and output/monthly/2026-02.pdf.

Goals

  • Define PresentAdapter interface + output types for backend-agnostic rendering
  • Implement PptxAdapter (default) using pptxgenjs
  • Implement all 6 slide types matching the reference PDF layout and style
  • Wire PDF conversion via LibreOffice CLI (libreoffice --headless --convert-to pdf)
  • Wire CLI: --report for direct JSON input, or --entity + --period to invoke Format then Present
  • Verify: real PPTX + PDF produced from 2026-02 data matching the reference PDF structure

Non-Goals

  • Google Slides adapter (future — interface stub only)
  • NotebookLM adapter via teng-lin/notebooklm-py (future — interface stub only)
  • Component reuse with @services/dashboard — deferred (see Open Questions in architecture.md)
  • Interactive or animated slides
  • CI/CD or automated deployment
  • Cloudflare Workers deployment (present is a local CLI, not a web service)

Phases

  • Phase 1: Package scaffold + PresentAdapter interface
  • Phase 2: PptxAdapter implementation
  • Phase 3: Slide implementations (all 6 types)
  • Phase 4: Generator orchestration + LibreOffice PDF
  • Phase 5: CLI wiring + end-to-end verification

Success

  • @services/present scaffolded as Bun workspace package
  • PresentAdapter interface defined with stub GoogleSlidesAdapter and NotebookLMAdapter placeholders
  • PptxAdapter implements PresentAdapter using pptxgenjs
  • All 6 slide types implemented and producing output
  • Slide colors match reference: WLC units blue (#1565c0), TM unit dark red (#7b1111), others default blue
  • bun run present --entity IONS --period 2026-02 exits 0
  • output/monthly/2026-02.pptx exists and opens in PowerPoint/LibreOffice
  • output/monthly/2026-02.pdf exists (requires LibreOffice installed)
  • bun run test:type --filter @services/present passes

Requirements

  • Plan 004 completed: report.json exists at output/monthly/2026-02-report.json
  • pptxgenjs available (add to workspace catalogs)
  • LibreOffice installed locally (libreoffice --version works) for PDF conversion
  • @repo/typescript/lib tsconfig preset
  • @repo/lint/base ESLint config
  • Reference PDF: source/Contoh punya TM WLC.pdf as visual spec

Context

Why This Approach

  • pptxgenjs: produces native PPTX that users can open and edit in PowerPoint/LibreOffice — required per user requirement
  • PresentAdapter interface: decouples slide generation logic from the rendering backend; future adapters (Google Slides, etc.) plug in without changing the generator or CLI
  • LibreOffice CLI for PDF: simple, reliable, no additional dependencies beyond what's likely already installed; --headless --convert-to pdf is well-tested
  • Adapter stubs: Google Slides and NotebookLM adapters are stubbed (throw NotImplemented) so the interface contract is established before implementation
  • Slide colors per unit: WLC=blue, TM=dark red matches the reference PDF exactly; other units use a default blue

Key Constraints

  • pptxgenjs uses inches for positioning/sizing, not CSS pixels — all layout values are in inches
  • Slide dimensions: 13.33" × 7.5" (standard widescreen 16:9)
  • Cover slide: gradient background (dark blue → teal), bold title, two brand logos
  • Revenue comparison slide: bar chart (pptxgenjs addChart) + summary table above
  • Key comparison slide: 5-column table (Parameter, Last Year, Target, Actuals, Gap%, Status) with color-coded Status column
  • Program progress slide: 4-column table (Program, Last Year, Target, Actuals) with Status
  • School progress slide: dense matrix table — organizations as rows, years as columns; highlight cells with data
  • Channel marketing slide: 6-column table (Channel, Leads, Follow-ups, Closings, Contribution%, Status/Strategy)
  • Status badge colors: Completed=green, On Track=light blue, Needs Attention=salmon/red, High Performer=green, Solid=blue, Potensial=yellow
  • Each unit gets its own set of slides (cover is shared); slide order: cover → [revenue, key, program, school per unit] → [channel marketing combined]

Edge Cases

  • LibreOffice not installed: PDF step fails gracefully with a clear error message; PPTX is still produced
  • --report flag with a path that doesn't exist: clear error
  • Unit in report with no data for a section: slide rendered with empty table/chart (no crash)
  • Long organization names in school progress slide: truncate to fit cell width

Tradeoffs

  • pptxgenjs coordinate system (inches): more verbose layout code vs CSS, but no alternative for native PPTX
  • Monochrome charts (pptxgenjs bar charts): matching exact reference colors requires explicit fill config per bar; defaulting to solid unit color per chart is acceptable
  • LibreOffice PDF: output quality is slightly lower than native PDF renderer, but avoids heavy dependencies

Skills

  • pptx — pptxgenjs patterns and slide layout conventions
  • plan — plan file format and conventions

Boundaries

  • Always: Update Progress.md after each task completion
  • Always: Consult reference PDF (source/Contoh punya TM WLC.pdf) for layout decisions
  • Always: Slide dimensions in inches (pptxgenjs convention)
  • Ask first: Any layout deviation from the reference PDF
  • Ask first: Adding a new adapter implementation (stubs only in this plan)
  • Never: Apply business logic in the Present layer — consume report.json as-is
  • Never: Modify report.json — present is read-only on the format output

Questions

  • PPTX required? → Yes, users need to edit slides
  • PDF via LibreOffice? → Yes
  • Adapter pattern? → Yes — PresentAdapter interface with pptxgenjs as default; Google Slides and NotebookLM as future stubs
  • Component reuse with dashboard? → Deferred to architecture.md Open Questions
  • Should the CLI skip PDF generation if LibreOffice is not installed (warn only), or hard-fail?
  • Should Google Slides and NotebookLM adapter stubs live in this package or in separate packages?

On this page