fixes
This commit is contained in:
1 parent
c8cc0fb28a
commit
e790c02d59
16 files changed
+659
-15
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useState } from "react"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import * as Tabs from "@radix-ui/react-tabs"
|
||||
import {
|
||||
Activity,
|
||||
@@ -28,6 +28,7 @@ import { TelemetryFilters } from "./TelemetryFilters"
|
||||
import { PatternsVisualizer } from "./PatternsVisualizer"
|
||||
import { RolloutsVisualizer } from "./RolloutsVisualizer"
|
||||
import { SourceIpsVisualizer } from "./SourceIpsVisualizer"
|
||||
import { TodosVisualizer } from "./TodosVisualizer"
|
||||
|
||||
|
||||
type SeverityMeta = {
|
||||
@@ -269,15 +270,20 @@ export const TabsComponent = () => {
|
||||
const { traces, logs, activeTab, setActiveTab } = useObservabilityData()
|
||||
const issuesState = useIssues()
|
||||
const [statusBusy, setStatusBusy] = useState(false)
|
||||
const [statusNotice, setStatusNotice] = useState<{ text: string; undo?: () => void; error?: boolean } | null>(null)
|
||||
// Trends tab only exists when the backend has ClickHouse-backed stats;
|
||||
// null while the capability check is in flight.
|
||||
const statsAvailable = useStatsAvailability()
|
||||
|
||||
// Triage action: PATCH the issue status, then refresh so counts/badges
|
||||
// reflect the server state. Failures surface via the issues error banner.
|
||||
// Triage action: apply optimistically, PATCH, then refresh so counts and
|
||||
// badges match the server. A failure rolls back and says so; a success
|
||||
// offers Undo because the card moves into the collapsed resolved/ignored
|
||||
// group and would otherwise just vanish.
|
||||
const handleIssueStatusChange = useCallback(
|
||||
async (issue: Issue, status: string) => {
|
||||
const previous = issue.status ?? "open"
|
||||
setStatusBusy(true)
|
||||
void issuesState.patch(issue.id, { status, regressedAt: undefined })
|
||||
try {
|
||||
const res = await authFetch(`/api/issues/${issue.id}`, {
|
||||
method: "PATCH",
|
||||
@@ -285,8 +291,14 @@ export const TabsComponent = () => {
|
||||
body: JSON.stringify({ status }),
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
const verb = status === "resolved" ? "Resolved" : status === "ignored" ? "Ignored" : "Reopened"
|
||||
setStatusNotice({
|
||||
text: `${verb}: ${issue.title}`,
|
||||
undo: status === "open" ? undefined : () => void handleIssueStatusChange({ ...issue, status }, previous),
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Failed to update issue status:", err)
|
||||
setStatusNotice({ text: `Couldn't update issue: ${err instanceof Error ? err.message : String(err)}`, error: true })
|
||||
} finally {
|
||||
await issuesState.refresh()
|
||||
setStatusBusy(false)
|
||||
@@ -295,6 +307,12 @@ export const TabsComponent = () => {
|
||||
[issuesState],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!statusNotice) return
|
||||
const t = setTimeout(() => setStatusNotice(null), statusNotice.error ? 8000 : 6000)
|
||||
return () => clearTimeout(t)
|
||||
}, [statusNotice])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<TelemetryFilters />
|
||||
@@ -335,6 +353,9 @@ export const TabsComponent = () => {
|
||||
<Tabs.Content className="outline-none py-2" value="tab10">
|
||||
{activeTab === "tab10" && <ViewErrorBoundary><SourceIpsVisualizer /></ViewErrorBoundary>}
|
||||
</Tabs.Content>
|
||||
<Tabs.Content className="outline-none py-2" value="tab11">
|
||||
{activeTab === "tab11" && <ViewErrorBoundary><TodosVisualizer /></ViewErrorBoundary>}
|
||||
</Tabs.Content>
|
||||
<Tabs.Content
|
||||
className="outline-none py-2"
|
||||
value="tab2"
|
||||
@@ -374,6 +395,28 @@ export const TabsComponent = () => {
|
||||
{activeTab === "tab6" && <ViewErrorBoundary><SearchAndWatcher /></ViewErrorBoundary>}
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
{statusNotice && (
|
||||
<div
|
||||
role="status"
|
||||
className={`fixed bottom-6 left-1/2 z-50 flex max-w-[min(560px,calc(100vw-120px))] -translate-x-1/2 items-center gap-3 rounded-[10px] border bg-ds-surface px-4 py-2.5 text-[13px] ${
|
||||
statusNotice.error ? "border-ds-critical/40 text-ds-critical" : "border-ds-line-strong text-ds-text"
|
||||
}`}
|
||||
>
|
||||
<span className="truncate">{statusNotice.text}</span>
|
||||
{statusNotice.undo && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
statusNotice.undo?.()
|
||||
setStatusNotice(null)
|
||||
}}
|
||||
className="shrink-0 font-semibold text-ds-accent hover:underline"
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user