fix(awooop): record grouped alert events
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 1m6s
CD Pipeline / build-and-deploy (push) Successful in 3m48s
CD Pipeline / post-deploy-checks (push) Successful in 1m25s

This commit is contained in:
Your Name
2026-05-07 01:35:09 +08:00
parent 1a1dea00eb
commit 251554c044
8 changed files with 373 additions and 0 deletions

View File

@@ -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" />