feat(analyzer): browse-tickets page + analysis-view typography
- /analyzer/tickets — period chips (today/yesterday/this+last week/30d/60d/all), client + issue-type Selects, debounced search, per-row Analyze/Re-analyze plus View shortcut when an analysis already exists. - API: /api/analyzer/tickets/list (period/companyId/issueType/search, paginated via COUNT(*) OVER) and /filter-options (companies that actually have tickets, active issue types). - ProseText helper in analysis-view splits on blank lines and renders each chunk with leading-7 — Summary, Next Step, rationale, and Post-Resolution now have proper paragraph rhythm. Next Step card re-styled with bg-primary/5 tint, ArrowRight icon, and an indented rationale block. - Top-level "Analyzer" nav menu (Browse Tickets + Needs Review). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
966376e6b6
commit
b20c94ea1a
6 changed files with 879 additions and 19 deletions
|
|
@ -414,6 +414,85 @@ This file is updated after each phase ships.
|
|||
|
||||
---
|
||||
|
||||
## Phase 9 — Ticket browser + analysis-view formatting
|
||||
|
||||
Reactive to first-use feedback: the analysis page Summary / Next Step
|
||||
text was bare and dense, and there was no way to discover tickets to
|
||||
analyze without typing the URL.
|
||||
|
||||
**Delivered**
|
||||
|
||||
- `<ProseText>` helper inside `analysis-view.tsx` — splits text on blank
|
||||
lines, renders each chunk as a separate `<p>` with `leading-7` and
|
||||
`whitespace-pre-line`. Applied to Summary, Next Step, Next Step
|
||||
rationale, and Post-Resolution Analysis. Single-paragraph text still
|
||||
renders cleanly.
|
||||
- Summary + Post-Resolution headers got an uppercase tracking-wide
|
||||
treatment to act as section dividers, and body text bumped to
|
||||
`text-base text-foreground` so it reads as a finding rather than a
|
||||
caption.
|
||||
- Next Step card now has a subtle `bg-primary/5` tint, an `ArrowRight`
|
||||
icon next to "Recommended Next Step", a stronger separator before
|
||||
the rationale collapsible, and the rationale itself renders in a
|
||||
bordered indented block.
|
||||
- New `/analyzer/tickets` browse page — pill-style period chips
|
||||
(Today, Yesterday, This week, Last week, Last 30/60 days, All time),
|
||||
a client (company) Select, an issue-type Select, and a debounced
|
||||
free-text search across ticket_number/title. Compact table with
|
||||
per-row Analyze/Re-analyze button (reusing `<AnalyzeButton>`) and a
|
||||
"View" button shortcut to the existing analysis when one is
|
||||
recorded. Active-filter count + clear-all in the filter card header.
|
||||
- New API `GET /api/analyzer/tickets/list` — filters by `period`
|
||||
(computed in Postgres against `last_activity_date`), `companyId`,
|
||||
`issueType`, `search`. Returns 50 rows + total via `COUNT(*) OVER ()`,
|
||||
plus `latestAnalysisId` from a LATERAL join into `analyzer_analyses`.
|
||||
- New API `GET /api/analyzer/tickets/filter-options` — companies that
|
||||
have at least one non-deleted ticket (drops dormant accounts) +
|
||||
active issue types ordered by `sort_order, label`.
|
||||
- Top-level "Analyzer" nav menu added to `app-navigation.tsx`, with
|
||||
"Browse Tickets" + "Needs Review". Earlier phases left this off
|
||||
intentionally; this phase opts in.
|
||||
|
||||
**Decisions worth flagging**
|
||||
|
||||
- **Period filters on `last_activity_date`, not `create_date`.**
|
||||
"Today" surfaces tickets that had activity today (new tickets,
|
||||
re-opened, status churn) — much more useful for an analyzer-driven
|
||||
triage flow than tickets created today. A new ticket created today
|
||||
also has activity today, so we don't lose those.
|
||||
- **Period math runs in Postgres via `date_trunc('day', NOW())` etc.**
|
||||
Database server-clock = app-process clock for an internal Docker
|
||||
stack, so naive timestamps and naive `NOW()` agree. If users
|
||||
complain about edge-of-day drift, swap to
|
||||
`NOW() AT TIME ZONE 'America/New_York'` — Pulse's primary user base.
|
||||
- **Default period is `last_30d`.** "All time" pulls many thousands of
|
||||
rows; defaulting wide-open hurts first-page latency. 30 days hits
|
||||
~7K rows in our DB, paginates cleanly.
|
||||
- **Per-row analyze button reuses `<AnalyzeButton>` directly.** Each
|
||||
row gets its own component instance — no shared state, the running
|
||||
state lives per-button. The button navigates on completion, which
|
||||
feels right: click Analyze, watch the stages, land on the analysis
|
||||
page.
|
||||
- **`force=true` is set automatically on tickets that already have an
|
||||
analysis.** Re-analyze should re-run, not short-circuit to the
|
||||
cached row. The "View" button covers the cached path.
|
||||
- **No Linear/JIRA-style multi-select filters.** Single-value Selects
|
||||
are simpler and match the rest of Pulse.
|
||||
|
||||
**Deliberately left out**
|
||||
|
||||
- **No saved views.** A filter URL is shareable, but there's no
|
||||
bookmark / saved-view UX. Add when someone asks.
|
||||
- **No `latest_analysis_status` exposure.** A failed analysis doesn't
|
||||
show up — the LATERAL join filters by `status='complete'`. So a
|
||||
ticket whose only analysis failed looks like an un-analyzed ticket.
|
||||
Acceptable: re-running is the intended action there anyway.
|
||||
- **Search is `ILIKE '%...%'`.** No tsvector / trigram index. 7K-row
|
||||
scans are sub-100ms in this DB; if the corpus grows past low six
|
||||
digits, swap in `pg_trgm`.
|
||||
|
||||
---
|
||||
|
||||
## Status after each phase
|
||||
|
||||
| Phase | Tests | tsc | Notes |
|
||||
|
|
@ -426,3 +505,4 @@ This file is updated after each phase ships.
|
|||
| 6 | 128 | clean | frontend (no FE tests) |
|
||||
| 7 | 128 | clean | share email via existing SMTP transport |
|
||||
| 8 | 128 | clean | operator runbook + README link |
|
||||
| 9 | 128 | clean | browse page + analysis-view formatting + nav entry |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue