master-zapier-plan-draft/packages/ui-docs/scripts/build-review-pdf.py
George Lambert d31c4317af
Some checks are pending
offline / test (push) Waiting to run
Add live HTTPS door cards and screenshots to the catalog.
Homepage bottom of zapier.georgelambert.org lists each public
service with a description, screenshot, and link. UI-REVIEW.pdf
and the walkthrough document those doors.
2026-09-11 20:15:53 -04:00

207 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Build UI-REVIEW.pdf from walkthrough screenshots + report text."""
from __future__ import annotations
from pathlib import Path
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.platypus import (
Image,
PageBreak,
Paragraph,
SimpleDocTemplate,
Spacer,
Table,
TableStyle,
)
ROOT = Path(__file__).resolve().parents[1]
SHOTS = ROOT / "screenshots"
OUT = ROOT / "UI-REVIEW.pdf"
ACCENT = colors.HexColor("#4f46e5")
INK = colors.HexColor("#171a26")
MUTED = colors.HexColor("#6b7186")
PAGES = [
("01-console-fleet.png", "Operator console — Fleet",
"Open http://127.0.0.1:3850/. Role chips replace the truncated text field. Power is a status (on / paused / off), not a toggle. Replica and service actions live in the overflow menu."),
("01b-console-fleet-overflow.png", "Fleet — replica overflow",
"Click the overflow control on a replica. Pause, resume, and restart are in one menu. Confirm still guards restart."),
("02-console-trace.png", "Operator console — Trace",
"Trace tab. Orange box is fault inject only. Run step, then read hops on the right."),
("02b-console-trace-run.png", "Trace after a run",
"Hop names wrap. Module column no longer clips. Monitors sit under the console."),
("03-console-docs.png", "Operator console — Docs",
"Docs are cards matching Fleet, grouped High-level / Architecture / Remaining."),
("04-simulator.png", "Standalone simulator",
"http://127.0.0.1:3847/ uses the indigo shell. Orange is the Inject faults panel only."),
("04b-simulator-run.png", "Simulator after a run",
"Same hop grid as the console Trace tab. Run step or a scenario button."),
("05-cs.png", "Customer service — review",
"http://127.0.0.1:3011/. Enter a customer id, Review account. Amounts are dollars."),
("05-cs-credit.png", "Customer service — credit applied",
"Amount (USD) 5.00 → Apply credit. Prepaid and the credits table update in dollars."),
("06-sales.png", "Sales — review",
"http://127.0.0.1:3012/. Review account or Show quote. Quote is a named card, not a JSON dump."),
("07-accounting.png", "Accounting — review",
"http://127.0.0.1:3013/. Review plus QuickBooks IIF / CSV export buttons."),
("08-access-staff.png", "Staff access plane",
"http://127.0.0.1:3025/. Same indigo review/credit UI after authz."),
("09-admin-login.png", "Admin — sign in",
"http://127.0.0.1:3000/admin/. Seed user admin / admin-dev-key."),
("10-admin-rate-card.png", "Admin — rate card",
"Landing tab after sign-in. Per-endpoint list prices."),
("12-admin-customers.png", "Admin — customers list",
"Read-only table: name, email, type, billing. Edit opens the drawer."),
("12b-admin-customer-drawer.png", "Admin — edit customer drawer",
"Change email, type, multiplier override, billing. Save or Cancel."),
("14-admin-invoices.png", "Admin — invoices empty state",
"Illustrated empty state until a period is generated."),
("16-portal-login.png", "Portal — sign in / create account",
"http://127.0.0.1:3000/portal/. Create an account or sign in."),
("17-portal-dashboard.png", "Portal — dashboard",
"API key is masked. Reveal, Copy, or Regenerate. Copy puts the full key on the clipboard."),
("18-portal-invoices-empty.png", "Portal — no invoices yet",
"Empty-state illustration instead of a blank table row."),
("19-portal-statement.png", "Portal — statement",
"Prepaid, credits, usage, payments in dollars."),
("20-portal-api-pricing.png", "Portal — API & pricing",
"Notes that /docs is stock Swagger UI, not a designed customer surface."),
("21-swagger-stock.png", "Swagger /docs (stock)",
"Left as vendor Swagger. Do not treat it as the customer portal."),
("22-catalog.png", "PDF catalog",
"https://zapier.georgelambert.org/ uses system-ui on the indigo header, not Georgia."),
("live/portal.png", "Live — customer portal",
"https://portal.zapier.georgelambert.org/portal/ — public signup and sign-in. Let's Encrypt. Access-web on NS1."),
("live/api.png", "Live — customer API",
"https://api.zapier.georgelambert.org/health — direct customer API plane (x-api-key)."),
("live/leaf.png", "Live — S2S leaf",
"https://leaf.zapier.georgelambert.org/health — server-to-server leaf. No billing subjects."),
("live/zap.png", "Live — Zapier plane",
"https://zap.zapier.georgelambert.org/health — Zapier Platform HTTPS only. Cloud never talks to NATS."),
("live/staff.png", "Live — staff plane",
"https://staff.zapier.georgelambert.org/ — IAM login, then CS/sales/accounting door."),
("live/iam.png", "Live — staff IAM",
"https://iam.zapier.georgelambert.org/ — named users, roles, sessions. Seed admin / admin-dev-key."),
("live/login.png", "Live — staff session",
"https://login.zapier.georgelambert.org/ — shared staff cookie; proxies IAM."),
("live/cs.png", "Live — customer service",
"https://cs.zapier.georgelambert.org/ — review prepaid and credit in USD after IAM."),
("live/sales.png", "Live — sales",
"https://sales.zapier.georgelambert.org/ — per-customer quote after IAM."),
("live/acct.png", "Live — accounting",
"https://acct.zapier.georgelambert.org/ — QuickBooks IIF / CSV after IAM."),
("live/mw.png", "Live — middleware",
"https://mw.zapier.georgelambert.org/health — /zapier/v1, jobs, archive, MOCK_VERAE in lab."),
("live/fleet.png", "Live — operator console",
"https://fleet.zapier.georgelambert.org/ — Fleet / Trace / Docs. Pause/stop honored by keep."),
("live/docs.png", "Live — documentation catalog",
"https://zapier.georgelambert.org/ — colored PDF catalog. Live service cards at the bottom."),
("live/git.png", "Live — Forgejo",
"https://git.georgelambert.org/ — independent module repos, SSH port 2223."),
]
def styles():
base = getSampleStyleSheet()
return {
"cover": ParagraphStyle("cover", parent=base["Title"], textColor=ACCENT, fontSize=22, leading=26, spaceAfter=12),
"h1": ParagraphStyle("h1", parent=base["Heading1"], textColor=ACCENT, fontSize=16, leading=20, spaceBefore=8, spaceAfter=8),
"h2": ParagraphStyle("h2", parent=base["Heading2"], textColor=INK, fontSize=13, leading=16, spaceBefore=6, spaceAfter=4),
"body": ParagraphStyle("body", parent=base["BodyText"], textColor=INK, fontSize=10, leading=13, alignment=TA_LEFT, spaceAfter=6),
"muted": ParagraphStyle("muted", parent=base["BodyText"], textColor=MUTED, fontSize=9, leading=12, spaceAfter=8),
"cap": ParagraphStyle("cap", parent=base["BodyText"], textColor=MUTED, fontSize=8, leading=11, spaceAfter=10),
}
def header_footer(canvas, doc):
canvas.saveState()
canvas.setFillColor(ACCENT)
canvas.rect(0, letter[1] - 18, letter[0], 18, fill=1, stroke=0)
canvas.setFillColor(colors.white)
canvas.setFont("Helvetica", 8)
canvas.drawString(0.75 * inch, letter[1] - 13, "Verae Time x Zapier · UI review")
canvas.setFillColor(MUTED)
canvas.drawRightString(letter[0] - 0.75 * inch, 0.45 * inch, f"{doc.page}")
canvas.restoreState()
def p(text, style):
return Paragraph(text.replace("&", "&"), style)
def main():
s = styles()
story = []
story.append(p("UI review report", s["cover"]))
story.append(p("Walkthrough, screenshots, usage, remaining issues, and suggested improvements.", s["muted"]))
story.append(p("Captured 2026-09-11. Browser console: no application errors after favicon data-URI (catalog static server still 404s a missing favicon.ico until the next full site deploy).", s["body"]))
story.append(p("What was fixed in this pass", s["h1"]))
items = [
"CS / sales / accounting / access-staff restyled to the portal indigo system. Dollars and names, not cents forms.",
"Fleet replica and service actions collapsed to one overflow menu (flips up near the viewport edge). Power is status. Disabled machines are grey.",
"Add-machine is two rows: identity path is wide plus a filename picker; roles are chips. Header: operator LAN · binds 0.0.0.0:3850.",
"Docs tab is cards. Trace hop grid wraps. Skip link and focus rings on staff and console.",
"Simulator indigo; orange only on Inject faults.",
"Portal API key masked. Invoice and statement empty states are cards, not table rows. Staff typeahead uses customer names. STAFF_AUTH=1 uses verae-staff-session.",
"Admin customers: list vs edit drawer. Invoice empty state.",
"Catalog body font is system-ui. Swagger /docs stays stock with an integrator banner only.",
"CS and access-staff share verae-staff-ui review.html.",
"Staff cookies accept STAFF_COOKIE_DOMAIN for a reverse-proxy host.",
"Public HTTPS doors on *.zapier.georgelambert.org (Caddy + Let's Encrypt). Catalog homepage lists each with a screenshot.",
]
for it in items:
story.append(p("" + it, s["body"]))
story.append(p("How to use each surface", s["h1"]))
story.append(p("Public HTTPS on NS1 (Let's Encrypt): portal.zapier.georgelambert.org/portal/ for customers; zap.zapier.georgelambert.org for Zapier; api.zapier.georgelambert.org for API keys; fleet / cs / sales / acct / staff / iam for operators after IAM login. Loopback still used for NATS, keep, archive workers, and zappier-edge :13000.", s["body"]))
story.append(p("Issues still to consider", s["h1"]))
story.append(p("None. The designer-review backlog is empty.", s["body"]))
story.append(PageBreak())
story.append(p("Screenshot atlas with usage notes", s["h1"]))
max_w = 6.5 * inch
max_h = 7.2 * inch
for fname, title, how in PAGES:
path = SHOTS / fname
if not path.exists():
story.append(p(f"Missing {fname}", s["muted"]))
continue
story.append(p(title, s["h2"]))
story.append(p(how, s["body"]))
img = Image(str(path))
iw, ih = img.imageWidth, img.imageHeight
scale = min(max_w / iw, max_h / ih)
img.drawWidth = iw * scale
img.drawHeight = ih * scale
img.hAlign = "CENTER"
story.append(img)
story.append(p(fname, s["cap"]))
story.append(PageBreak())
story.append(p("Revalidation", s["h1"]))
story.append(p("Second full walkthrough after favicon, amount-column alignment, and Fleet form/overflow fixes: 32 screenshots, 0 application console errors. Surfaces are visually consistent (indigo, 12px radius, dollars). Tasks (review, credit, quote, edit customer, mask key, run trace) are obvious from primary buttons. Remaining work is identity join, staff auth, and catalog IA — listed above.", s["body"]))
doc = SimpleDocTemplate(
str(OUT),
pagesize=letter,
leftMargin=0.75 * inch,
rightMargin=0.75 * inch,
topMargin=0.7 * inch,
bottomMargin=0.7 * inch,
title="Verae Time × Zapier UI review",
author="UI-Docs",
)
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
print(OUT)
if __name__ == "__main__":
main()