diff --git a/docs/work-plan.md b/docs/work-plan.md index 33b56fd..d175182 100644 --- a/docs/work-plan.md +++ b/docs/work-plan.md @@ -87,7 +87,7 @@ ### День 2 -- [ ] `d2-1` UI Problems: список, фильтры, `Ack/Resolve`, карточка с таймлайном +- [x] `d2-1` UI Problems: список, фильтры, `Ack/Resolve`, карточка с таймлайном - [ ] `d2-2` Dashboard: top hosts/types, `open vs resolved 24h`, drill-down - [ ] `d2-3` Ops: retention, health checks, runbook backup/restore/deploy - [ ] `dod` DoD: нет дублей `event_id`, Problems e2e, 3 правила, UI MVP, docs, push в kalinamall @@ -116,8 +116,8 @@ #### День 2 -- [ ] `09:00–11:00` UI Problems: таблица + фильтры (`status/severity/host`) -- [ ] `11:00–12:00` карточка проблемы + таймлайн событий +- [x] `09:00–11:00` UI Problems: таблица + фильтры (`status/severity/host`) +- [x] `11:00–12:00` карточка проблемы + таймлайн событий - [ ] `13:00–14:30` Dashboard: виджеты top hosts/types - [ ] `14:30–16:00` Dashboard: `open/resolved 24h` + drill-down - [ ] `16:00–17:00` retention job (`events 30–90d`, `problems 180d+`) diff --git a/frontend/src/api.ts b/frontend/src/api.ts index eb60443..8ee422d 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -111,6 +111,24 @@ export interface ProblemListResponse { page_size: number; } +export interface ProblemEventItem { + id: number; + event_id: string; + occurred_at: string; + type: string; + severity: string; + title: string; + summary: string; +} + +export interface ProblemDetail extends ProblemSummary { + events: ProblemEventItem[]; +} + +export function fetchProblem(id: number): Promise { + return apiFetch(`/api/v1/problems/${id}`); +} + export async function ackProblem(problemId: number): Promise<{ id: number; status: string }> { return apiFetch(`/api/v1/problems/${problemId}/ack`, { method: "POST" }); } diff --git a/frontend/src/router.ts b/frontend/src/router.ts index 057ab8c..9794873 100644 --- a/frontend/src/router.ts +++ b/frontend/src/router.ts @@ -5,6 +5,7 @@ import EventDetailView from "./views/EventDetailView.vue"; import HostsView from "./views/HostsView.vue"; import LoginView from "./views/LoginView.vue"; import ProblemsView from "./views/ProblemsView.vue"; +import ProblemDetailView from "./views/ProblemDetailView.vue"; import DashboardView from "./views/DashboardView.vue"; import ReportsView from "./views/ReportsView.vue"; @@ -19,6 +20,7 @@ const router = createRouter({ { path: "/reports", component: ReportsView }, { path: "/hosts", component: HostsView }, { path: "/problems", component: ProblemsView }, + { path: "/problems/:id", component: ProblemDetailView, props: true }, ], }); diff --git a/frontend/src/style.css b/frontend/src/style.css index 5692080..f397666 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -94,6 +94,11 @@ td.actions { margin-bottom: 1rem; } +.timeline-table td:nth-child(5) { + max-width: 28rem; + word-break: break-word; +} + input, select, button { diff --git a/frontend/src/views/DashboardView.vue b/frontend/src/views/DashboardView.vue index 1147cb8..3587e65 100644 --- a/frontend/src/views/DashboardView.vue +++ b/frontend/src/views/DashboardView.vue @@ -12,7 +12,7 @@
{{ data.problems_open }}
Открытых проблем
- Проблемы → + Проблемы →
{{ data.hosts_total }}
diff --git a/frontend/src/views/ProblemDetailView.vue b/frontend/src/views/ProblemDetailView.vue new file mode 100644 index 0000000..075a12a --- /dev/null +++ b/frontend/src/views/ProblemDetailView.vue @@ -0,0 +1,135 @@ + + + diff --git a/frontend/src/views/ProblemsView.vue b/frontend/src/views/ProblemsView.vue index 6fab506..3a71440 100644 --- a/frontend/src/views/ProblemsView.vue +++ b/frontend/src/views/ProblemsView.vue @@ -7,7 +7,15 @@ - + + +

{{ error }}

Загрузка…

@@ -28,13 +36,17 @@ - {{ p.id }} + + {{ p.id }} + {{ formatDt(p.updated_at) }} {{ p.hostname ?? "—" }} {{ p.severity }} {{ p.status }} {{ p.rule_id ?? "—" }} - {{ p.title }} + + {{ p.title }} +