From 4b2c82cf90a2f2376e701a1b7e36aebd85e84d6e Mon Sep 17 00:00:00 2001 From: Josh Wright Date: Sun, 12 Apr 2026 11:50:23 -0400 Subject: [PATCH] Add Radarr/Sonarr links and richer metadata to alert detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Thread titleSlug through RadarrMovie/SonarrSeries → MediaEntry → AlertCandidate, building a direct mediaUrl server-side at alert generation time (RADARR_URL/movie/slug, SONARR_URL/series/slug) - Alert detail page: single-column full-width layout, metadata chips row showing opened/closed dates, media type, and a "View in Radarr/ Sonarr" external link button when available - Comments section sits below the full-width overview card Co-Authored-By: Claude Sonnet 4.6 --- src/app/alerts/[id]/AlertDetail.tsx | 199 +++++++++++++++++----------- src/lib/alerts.ts | 8 ++ src/lib/db.ts | 4 + src/lib/radarr.ts | 2 +- src/lib/sonarr.ts | 1 + src/lib/types.ts | 4 + 6 files changed, 143 insertions(+), 75 deletions(-) diff --git a/src/app/alerts/[id]/AlertDetail.tsx b/src/app/alerts/[id]/AlertDetail.tsx index 1f5fa23..92a8f5d 100644 --- a/src/app/alerts/[id]/AlertDetail.tsx +++ b/src/app/alerts/[id]/AlertDetail.tsx @@ -4,8 +4,6 @@ import { useState, useRef, useEffect } from "react"; import Link from "next/link"; import { Alert, AlertSeverity, AlertComment } from "@/lib/types"; -// ── Severity theming ───────────────────────────────────────────────────────── - const severityAccent: Record = { danger: "border-l-red-500", warning: "border-l-yellow-500", @@ -24,8 +22,6 @@ const severityLabel: Record = { info: "Info", }; -// ── Helpers ─────────────────────────────────────────────────────────────────── - function timeAgo(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const mins = Math.floor(diff / 60_000); @@ -36,12 +32,33 @@ function timeAgo(iso: string): string { return `${Math.floor(hrs / 24)}d ago`; } +function fullDate(iso: string): string { + return new Date(iso).toLocaleString(undefined, { + month: "short", day: "numeric", year: "numeric", + hour: "numeric", minute: "2-digit", + }); +} + function shortDate(iso: string): string { return new Date(iso).toLocaleDateString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", }); } +// ── Meta chip ───────────────────────────────────────────────────────────────── + +function Chip({ label, dim }: { label: string; dim?: boolean }) { + return ( + + {label} + + ); +} + // ── Comment row ─────────────────────────────────────────────────────────────── function CommentRow({ comment }: { comment: AlertComment }) { @@ -85,7 +102,7 @@ function CommentRow({ comment }: { comment: AlertComment }) { ); } -// ── Main component ──────────────────────────────────────────────────────────── +// ── Main ────────────────────────────────────────────────────────────────────── export default function AlertDetail({ initialAlert }: { initialAlert: Alert }) { const [alert, setAlert] = useState(initialAlert); @@ -145,8 +162,9 @@ export default function AlertDetail({ initialAlert }: { initialAlert: Alert }) { const statusTime = isOpen ? alert.firstSeen : (alert.closedAt ?? alert.firstSeen); return ( -
+
+ {/* Back */} )} -
+ {/* ── Alert overview ──────────────────────────────────────────────── */} +
+
- {/* ── Alert detail ─────────────────────────────────────────────── */} -
-
- - {/* Status row */} -
+ {/* Top row: status + action */} +
+
+ + {severityLabel[alert.severity]} + + · {isOpen && } {isOpen ? "Open" : isResolved ? "Auto-resolved" : "Closed"} - · - {timeAgo(statusTime)} - - -
- - {/* Severity + title + description */} -
- - {severityLabel[alert.severity]} - -

{alert.title}

-

{alert.description}

+ · + {timeAgo(statusTime)}
+
-
- {/* ── Comments ─────────────────────────────────────────────────── */} -
+ {/* Title + description */} +
+

{alert.title}

+

{alert.description}

+
-

- Comments -

+ {/* Metadata row */} +
-
- {alert.comments.length === 0 && ( -

No comments yet.

+ {/* Dates */} + + {!isOpen && alert.closedAt && ( + )} - {alert.comments.map((c) => ( - - ))} -
-
-
-