feat: status-report impact (#2258) master
* feat(db): per-component impact join table on status report updates Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(services): componentImpacts on status report create/add-update Membership syncs from impact-named components; resolve writes explicit operational rows; impacts ride in the update audit metadata. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(services): componentImpacts create/add-update/resolve semantics Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(api): derive status page colors and manual uptime from component impacts Events carry per-component impact intervals; manual-mode uptime weighs major/partial as downtime, degraded/operational as up. Reports without impact rows keep the legacy flat-orange, full-duration behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(proto): component_impacts on status report wire format Additive only (buf breaking clean): PageComponentImpact enum, ComponentImpact message, repeated fields on update/create/add-update. UNSPECIFIED means the caller doesn't speak impact — never operational. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(server): map component impacts through connectrpc handlers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(dashboard): per-component impact pickers on report forms + impact column Create form defaults every selected component to operational (fresh reports are never legacy); add-update prefills current impacts; legacy reports stay legacy unless the operator sets a non-operational impact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(status-page): per-update impact change badges in the report timeline Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(importers): preserve per-update component impacts on import Statuspage maps affected_components.new_status per update; Betterstack maps per-update affected_resources (richer than the report-level draft assumed); Instatus stays legacy. Writer inserts updates one-by-one to pair impact rows deterministically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: format + regenerated openapi static artifact Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: invalid title schema * chore: add migration files * feat(dashboard): edit component impacts on existing status report updates updateStatusReportUpdate accepts componentImpacts with replace-set semantics; the per-update cards show a picker prefilled with the update's own rows and 'No change' for components it didn't touch. The dashboard only sends the set when actually edited, so untouched (incl. legacy) updates keep their rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(status-page): extract impact label messages Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(dashboard): component impacts in the status report update sheets The list page's expanded-row sheets get the picker too: the edit sheet prefills the update's own rows with 'No change' for untouched components (replace-set sent only when edited); the create sheet prefills current impacts with the same legacy guard as the report detail page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(services): surface membership and impacts in status report audit snapshots pageComponentIds on report create/update snapshots and componentImpacts on update snapshots, sorted for stable diffs — the audit CHANGES UI only renders before/after, so join-table changes were invisible. Also fixes membership-only updateStatusReport edits being dropped entirely as empty-diff no-ops. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: align impact folds on shared helpers + review fixes currentImpactsFromUpdates/worstImpact now single-sourced in constants; getEvents gets the (date, id) tie-break and defensive copy; rpc status-page groups impact rows by update id; rename the relation-shaped update type to avoid clashing with the service-side name; revert unused optionalMetadata. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(status-page): show the day's worst impact in the bar hover card Manual-mode day cards carry the worst report impact ('Partial outage', 'Major outage', ...) instead of the generic Degraded/Error label; legacy reports and mixed days where legacy dominates keep the generic label. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * wip: * fix: review * chore: status-page design * fix: review * fix: weight and review * chore: docs * fix: overlapping times * wip: * fix: assistant * chore: improve slack agent * fix: col header * chore: changelog * fix: agent prompt * fix: bun version * Revert "fix: bun version" This reverts commit 8c4e945f55387c09760a42bf966cee97462897b0. * wip: --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>