Archived Brief
This brief has been completed and is retained as a build record.
Brief B-CAT-02¶
L4 RTO Customer Portal — Price List + Price It Drawer¶
Status: Ready to build
Precondition: B-CAT-01 confirmed deployed. RTP-PREVIEW-MONTHLY type confirmed as per_month, display reads $100.00/month.
Cloudflare Account: e5a9830215a8d88961dc6c80a8c7442a
Surface: L4 RTO Customer Portal
Do Not Touch: engine-db products table schema, Stripe integration, L3 catalogue manager UI, any existing portal nav outside the Commercial section
Context¶
B-CAT-01 built the catalogue and the ops console to manage it. B-CAT-02 builds the customer-facing disclosure surface — the price list the RTO sees in their portal — and the persistent bottom drawer that will become the docket mechanic across the Composer canvas.
Two deliverables in this brief:
- Price List page — L4 RTO portal, read-only, pulls live from L3, logged view
- Price It drawer — persistent bottom drawer, running total, foundation for the Composer docket
→ ALEX¶
Deliverable 1 — Price List Page¶
Location: L4 RTO Customer Portal → Commercial → Pricing
Data source: Live pull from L3 catalogue API (GET /v1/catalogue?catalogue=rtopacks&status=active)
Never hardcode a price anywhere in any frontend. Ever.
Page behaviour:
- Pulls current active RTOpacks catalogue on load
- Displays all active products grouped by type:
- Course & Content — flat and per_unit products
- Preview Lab — preview tier products
- Video Production — per_minute products
- Each product row shows: Name, SKU, Price (use
amount_displayfrom API), Type label, GST included flag - Version stamp at bottom of page: "Pricing current as at [catalogue_version] — [timestamp]"
- If catalogue changes between sessions, show banner: "Pricing has been updated since your last visit. This is the current schedule."
Logged view — every page load writes a record:
New table in engine-db: price_list_views
CREATE TABLE price_list_views (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rto_id TEXT NOT NULL,
account_id TEXT NOT NULL,
catalogue_version TEXT NOT NULL,
viewed_at TEXT NOT NULL DEFAULT (datetime('now')),
ip_address TEXT,
user_agent TEXT
);
This is the "you were told" audit trail. Every view logged. Immutable. No delete endpoint.
Ops stub required at deploy:
ops.ucca.online → COMMERCIAL → Price List Views
- Table: rto_id, account_id, catalogue_version, viewed_at
- Filter by RTO, filter by date range
- Row count visible at top: "X views across Y RTOs"
Deliverable 2 — Price It Drawer¶
This is the foundation of the Composer docket mechanic. Build it now as a portal-level component so it's ready to wire into the Composer canvas in B-COMP-01.
Behaviour:
- Persistent tab/handle fixed to bottom of portal viewport — always visible inside the portal shell
- Label: "Price It" with a subtle running total visible on the collapsed tab even when closed: "$0.00"
- Click to expand — slides up, does not navigate away, does not cover more than 40% of viewport
- Collapsed by default on every page load
Expanded state:
┌─────────────────────────────────────────────────────┐
│ Price It ▼ Close │
├─────────────────────────────────────────────────────┤
│ No items yet. │
│ Items will appear here as you build. │
├─────────────────────────────────────────────────────┤
│ Subtotal $0.00 │
│ GST (inc) $0.00 │
│ Total $0.00 │
└─────────────────────────────────────────────────────┘
When items are present:
┌─────────────────────────────────────────────────────┐
│ Price It ▼ Close │
├─────────────────────────────────────────────────────┤
│ Generic Unit Pack × 1 $29.00 │
│ AI Rendered Video × 3 min $24.00 │
│ Preview Lab — 7 Day × 1 $90.00 │
├─────────────────────────────────────────────────────┤
│ Subtotal $143.00 │
│ GST (inc) $13.00 │
│ Total $143.00 │
└─────────────────────────────────────────────────────┘
- Line items pulled from L3 catalogue by SKU — never hardcoded amounts
- GST calculated as amount/11 (inclusive pricing, consistent with catalogue API)
- Running total updates live as items are added or removed
- Items stored in session state — not persisted to DB yet (that's B-COMP-01 territory when the session record is built)
- Remove icon (×) on each line item
This session: drawer is wired to a demo add-item control for testing purposes only — a simple SKU picker that lets Alex verify the live catalogue pull and running total work correctly. The Composer canvas wiring comes in B-COMP-01.
FastAPI endpoints required¶
Price list views:
- POST /v1/portal/price-list-views — write view record (called on page load)
- GET /v1/ops/price-list-views — ops console read (with rto_id and date range filters)
Price It drawer: - No new endpoints required this brief — drawer operates on session state + existing catalogue API
OPS STUB REQUIRED AT DEPLOY¶
ops.ucca.online → COMMERCIAL → Price List Views must be live and showing data before B-CAT-03 drops.
Confirm deployed with:
1. Screenshot of Price List page in RTO portal showing live catalogue data
2. Screenshot of Price It drawer expanded (collapsed and expanded states)
3. Screenshot of ops console Price List Views table showing at least one logged view
4. Confirm amount_display values match B-CAT-01 seeded data exactly
→ TIM¶
- When Alex confirms B-CAT-02 deployed: load RTO portal, view the price list, confirm your view appears in ops console Price List Views. That's the "you were told" loop closed end-to-end.
- B-CAT-03 brief (public pricing page on rtopacks.com.au) drops after B-CAT-02 confirmed.
- The Price It drawer is the foundation of the Composer docket. It needs to feel right before B-COMP-01 builds on top of it — check the UX on deploy, not after B-COMP-01 is in flight.
UCCA Inc · Brief B-CAT-02 · Session 18 · 18 March 2026
Cloudflare Account: e5a9830215a8d88961dc6c80a8c7442a