PYC Account Manager Ticket Dashboard

Open customer-support tickets that need an AM response, stale-by-business-day breakdown, per-AM workload. Data is read live from HubSpot via a Cloudflare Pages Function (server-side, key not exposed).

Loading…

1 · At a glance

Open tickets
Sales Pipeline, all open stages
Stale — ball in our court
Waiting on us, ≥3 BD since last visitor msg
Needs follow-up
Waiting on customer, ≥3 BD since our last reply
Very stale
≥7 BD on either side (us or them)
Orphaned
No owner assigned, awaiting response

2 · Per-AM workload

Loading…

3 · Tickets needing attention

Loading…
How tickets are classified & data caveats

Pipeline scope: Sales Pipeline only (HubSpot id 28263059). The "Support Pipeline" (id 0) is mostly auto form-submission tickets and is excluded from v1. Despite the confusing name, the Sales Pipeline is where real customer-support work lives at PYC today.

Open stages included: New, Waiting on customer, Waiting on us, Store being setup, In Progress (Trello). Closed stage 64204641 is excluded.

Stale (ball in our court) = stage is Waiting on us AND hs_last_message_from_visitor = true AND business days since hs_last_message_received_date ≥ threshold.

Needs follow-up (ball in their court) = stage is Waiting on customer AND hs_last_message_from_visitor = false AND business days since hs_lastactivitydate ≥ threshold. We replied; the customer has gone quiet and a nudge is overdue.

Very stale = either bucket above but ≥ 7 business days.

Orphaned = hubspot_owner_id is empty AND in an open stage AND last message was from the visitor.

Business days = weekday count strictly after the relevant timestamp through today, weekends not counted; same-day = 0 (matches CLAUDE.md fulfillment-timing convention).

"Days waiting" column reflects whichever side is currently waiting: BD since the visitor's last message when stage = Waiting on us, BD since our last activity when stage = Waiting on customer, BD since last modification otherwise.

Inactive owner flag: AMs whose HubSpot user is marked archived are labelled "inactive" so reassignment is obvious.

Architecture: Static page on Cloudflare Pages calls two Pages Functions (/api/owners, /api/open-tickets) that proxy HubSpot. The HubSpot Service Key lives only as an encrypted Cloudflare environment variable and is never sent to the browser. Both endpoints are read-only and have hard-coded query payloads. Cloudflare Access gates the entire site to @printyourcause.com emails.

Data freshness: live HubSpot calls every Refresh, with a 60s edge cache to dampen rate-limit pressure when multiple AMs reload simultaneously. v1 is capped at the 200 most-recently-modified open tickets — if "Open tickets" hits exactly 200, the underlying queue is larger than what we're loading and v2 needs Supabase sync.