feat(web): "Why this?" panel for content recommendations#587
Draft
arberx wants to merge 1 commit into
Draft
Conversation
Adds an expandable LLM-explanation panel on each content-recommendation card in the report's action plan. Clicking "Why this?" opens a small panel that hydrates from the cached explanation if one exists, otherwise auto- fires POST /analyze to generate one. Backed by the endpoints shipped in PR #586. Wiring: - `apps/web/src/api.ts` — `fetchRecommendationAnalysis()` (returns null on 404 for clean branching) + `analyzeRecommendation(body)`. - `apps/web/src/queries/mutations.ts` — `useAnalyzeRecommendation()`. Deliberately does NOT invalidate any project-scoped queries: the explanation is per-card and doesn't change the recommendation list. - `apps/web/src/pages/ReportPage.tsx`: - New `WhyThisPanel` + `ExplanationBody` components (inline — only used in one place per AGENTS.md component rule). - "Why this?" button next to the existing "Mark addressed" button. - Expand state lives on `ActionPlanSection` as a `Set<targetRef>`, matching the optimistic-dismiss state pattern. Panel UX: - Auto-fetches cached analysis on first open → falls through to POST if no cache. - Provider dropdown switches the explainer on the fly (forceRefresh = true with `provider` override; auto-fires on change for snappier feel). - Manual "Regenerate" button forces a fresh call. - Footer renders model id + cost in cents (millicents / 1000). - Loading + error states; "Try again" affordance on error. Tests: 9 new — covers cache hit, cache miss → auto-POST, error path, regenerate, provider switch, hide button. All 3123 workspace tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Frontend for the LLM-explanation feature shipped in PR #586. Adds a "Why this?" button next to "Mark addressed" on every content-recommendation card in the report's action plan. Clicking expands an inline panel that:
/analysis) — instant render for previously-analyzed cards./analyzeif no cache exists — explanation appears as soon as the LLM responds.forceRefresh: true.claude-sonnet-4-6 · ~0.0420¢).Loading + error states throughout; "Try again" affordance when the API call fails.
Architecture
useAnalyzeRecommendation()(mutation) intentionally does NOT invalidate project-scoped queries — the explanation is per-card and doesn't affect the recommendation list, health scores, or the report DTO. Mirrors how the dismiss mutation invalidates everything; the explanation mutation invalidates nothing.Test plan
pnpm -r typecheck— cleanpnpm run lint— 0 errorsnpx vitest run— 260 files, 3123 passed (+9 new)forceRefresh: trueonCloseExplanationBody: bullets, paragraphs, mixed contentWhat's not in scope
canonry report) doesn't render the explanation panel. The explanation is interactive/dashboard-only for now. A follow-up PR could wire the cached explanation into theProjectReportDtoand render it as a static block inreport-renderer.tsto maintain full parity, but that's separable./content/targetsstandalone view doesn't exist yet as a dashboard page; this PR only wires the button into the report's action plan. If a dedicated page lands later, it can reuseWhyThisPanelverbatim.Builds on #585 (capability tiers) and #586 (explain backend).
🤖 Generated with Claude Code