fix(awooop): record grouped alert events
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||
import {
|
||||
Activity,
|
||||
BellOff,
|
||||
RefreshCw,
|
||||
AlertCircle,
|
||||
Filter,
|
||||
@@ -62,6 +63,23 @@ interface RunsResponse {
|
||||
per_page: number;
|
||||
}
|
||||
|
||||
interface PlatformEvent {
|
||||
event_id: string;
|
||||
project_id: string;
|
||||
channel_type: string;
|
||||
provider_event_id: string;
|
||||
channel_chat_id?: string | null;
|
||||
content_preview?: string | null;
|
||||
is_duplicate: boolean;
|
||||
received_at: string;
|
||||
}
|
||||
|
||||
interface RecentEventsResponse {
|
||||
events?: PlatformEvent[];
|
||||
total: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 常數
|
||||
// =============================================================================
|
||||
@@ -293,12 +311,70 @@ function RunRow({ run }: { run: Run }) {
|
||||
);
|
||||
}
|
||||
|
||||
function GroupedAlertEventsPanel({ events }: { events: PlatformEvent[] }) {
|
||||
return (
|
||||
<section className="border border-[#e0ddd4] bg-white">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<BellOff className="h-4 w-4 text-[#8a5a08]" aria-hidden="true" />
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-[#141413]">最近告警收斂</h3>
|
||||
<p className="text-xs text-[#77736a]">
|
||||
子告警不再洗 Telegram,改保留在 AwoooP 事件流
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="border border-[#d9b36f] bg-[#fff7e8] px-2 py-0.5 text-xs font-semibold text-[#8a5a08]">
|
||||
{events.length} 筆
|
||||
</span>
|
||||
</div>
|
||||
{events.length === 0 ? (
|
||||
<div className="px-4 py-4 text-sm text-[#5f5b52]">
|
||||
目前沒有近期收斂事件。若同組告警再次觸發,第二筆起會出現在這裡。
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-px bg-[#eee9dd] md:grid-cols-2 xl:grid-cols-3">
|
||||
{events.slice(0, 6).map((event) => {
|
||||
const receivedAt = event.received_at
|
||||
? new Date(event.received_at).toLocaleTimeString("zh-TW", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})
|
||||
: "--";
|
||||
return (
|
||||
<article key={event.event_id} className="bg-white px-4 py-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="truncate font-mono text-xs font-semibold text-[#141413]">
|
||||
{event.provider_event_id.replace("alert-group:", "")}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#77736a]">
|
||||
{event.project_id} · {receivedAt}
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 text-xs text-[#5f5b52]">
|
||||
internal
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-3 line-clamp-3 whitespace-pre-line text-xs leading-5 text-[#5f5b52]">
|
||||
{event.content_preview || "無摘要"}
|
||||
</p>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Main Component
|
||||
// =============================================================================
|
||||
|
||||
export default function RunsPage() {
|
||||
const [runs, setRuns] = useState<Run[]>([]);
|
||||
const [groupedEvents, setGroupedEvents] = useState<PlatformEvent[]>([]);
|
||||
const [tenants, setTenants] = useState<Tenant[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -337,6 +413,21 @@ export default function RunsPage() {
|
||||
const rows = Array.isArray(data.runs) ? data.runs : data.items;
|
||||
setRuns(Array.isArray(rows) ? rows : []);
|
||||
setTotal(data.total ?? 0);
|
||||
|
||||
const eventParams = new URLSearchParams();
|
||||
eventParams.set("channel_type", "internal");
|
||||
eventParams.set("provider_prefix", "alert-group");
|
||||
eventParams.set("limit", "6");
|
||||
if (projectFilter) eventParams.set("project_id", projectFilter);
|
||||
|
||||
const eventsRes = await fetch(
|
||||
`${API_BASE}/api/v1/platform/events/recent?${eventParams.toString()}`
|
||||
);
|
||||
if (eventsRes.ok) {
|
||||
const eventsData: RecentEventsResponse = await eventsRes.json();
|
||||
setGroupedEvents(Array.isArray(eventsData.events) ? eventsData.events : []);
|
||||
}
|
||||
|
||||
setLastRefresh(new Date());
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "載入失敗");
|
||||
@@ -427,6 +518,8 @@ export default function RunsPage() {
|
||||
})}
|
||||
</section>
|
||||
|
||||
<GroupedAlertEventsPanel events={groupedEvents} />
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-wrap items-center gap-3 border border-[#e0ddd4] bg-white p-4">
|
||||
<Filter className="w-4 h-4 text-muted-foreground flex-shrink-0" aria-hidden="true" />
|
||||
|
||||
Reference in New Issue
Block a user