fixes
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s

This commit is contained in:
mats committed 2026-09-20 09:52:49 +02:00
1 parent f30adf05af
commit 8824af8699
29 files changed
+1201 -115

No files matched your search

+70 -6
View File
@@ -34,9 +34,11 @@ import {
} from "../lib/issue-utils"
import { useSubRoute } from "../lib/routes"
import { CopyRef } from "./common/CopyRef"
import { IpButton } from "./common/IpButton"
import { IssueOccurrences } from "./IssueOccurrences"
import { NeedleTestModal } from "./NeedleTestModal"
import { FilterBar, type FilterChip } from "./FilterBar"
import { UA_CLASSES, uaClassLabel } from "../lib/ua-class"
import type { Issue, IssueSample } from "../lib/types"
export const kindMeta: Record<string, { label: string; Icon: typeof Activity }> = {
@@ -46,6 +48,12 @@ export const kindMeta: Record<string, { label: string; Icon: typeof Activity }>
}
type StatusFilter = "all" | "open" | "resolved" | "ignored"
const without = (set: Set<string>, value: string) => {
const next = new Set(set)
next.delete(value)
return next
}
type KindFilter = "all" | "span_error" | "log_error" | "frontend_error"
interface HoneypotIP {
@@ -438,6 +446,9 @@ const IssueCard = ({
{(contextSample?.userAgent || latestSample?.userAgent)?.slice(0, 80)}
</span>
)}
{(contextSample?.sourceIp || latestSample?.sourceIp) && (
<IpButton ip={(contextSample?.sourceIp || latestSample?.sourceIp)!} />
)}
{(contextSample?.release || latestSample?.release) && (
<span>release: {contextSample?.release || latestSample?.release}</span>
)}
@@ -666,6 +677,11 @@ export const IssuesVisualizer = ({
const [detectorTest, setDetectorTest] = useState<DetectorTestResult | null>(null)
const [detectorTestLoading, setDetectorTestLoading] = useState(false)
const [excludeSpamIPs, setExcludeSpamIPs] = useState(false)
// Client-class filters: an issue matches "client" when any sample came from
// one of these classes, and is hidden by "not client" when every classified
// sample did (a mixed human + bot issue stays visible).
const [clientInclude, setClientInclude] = useState<Set<string>>(new Set())
const [clientExclude, setClientExclude] = useState<Set<string>>(new Set())
const [showSpamIPs, setShowSpamIPs] = useState(false)
const [spamIPs, setSpamIPs] = useState<HoneypotIP[]>([])
const [spamIPsLoading, setSpamIPsLoading] = useState(false)
@@ -710,6 +726,11 @@ export const IssuesVisualizer = ({
) {
return false
}
if (clientInclude.size > 0 || clientExclude.size > 0) {
const classes = issue.samples.map((s) => s.uaClass).filter((c): c is string => Boolean(c))
if (clientInclude.size > 0 && !classes.some((c) => clientInclude.has(c))) return false
if (clientExclude.size > 0 && classes.length > 0 && classes.every((c) => clientExclude.has(c))) return false
}
if (selectedServices.size > 0) {
const matches =
selectedServices.has(issue.service) ||
@@ -724,7 +745,7 @@ export const IssuesVisualizer = ({
}
return true
})
}, [issues, selectedServices, searchQuery, statusFilter, kindFilter, excludeSpamIPs, spamIPSet])
}, [issues, selectedServices, searchQuery, statusFilter, kindFilter, excludeSpamIPs, spamIPSet, clientInclude, clientExclude])
const { primaryIssues, secondaryIssues } = useMemo(() => {
const sorted = [...filteredIssues].sort((a, b) => b.count - a.count)
@@ -753,7 +774,12 @@ export const IssuesVisualizer = ({
}
const activeFilterCount =
selectedServices.size + (statusFilter !== "all" ? 1 : 0) + (excludeSpamIPs ? 1 : 0) + (searchQuery ? 1 : 0)
selectedServices.size +
clientInclude.size +
clientExclude.size +
(statusFilter !== "all" ? 1 : 0) +
(excludeSpamIPs ? 1 : 0) +
(searchQuery ? 1 : 0)
const chips: FilterChip[] = [
...Array.from(selectedServices).map((service) => ({
@@ -763,6 +789,20 @@ export const IssuesVisualizer = ({
tone: "neutral" as const,
onRemove: () => toggleService(service),
})),
...Array.from(clientInclude).map((c) => ({
id: `client-${c}`,
filterKey: "client",
value: uaClassLabel(c),
tone: "neutral" as const,
onRemove: () => setClientInclude((prev) => without(prev, c)),
})),
...Array.from(clientExclude).map((c) => ({
id: `not-client-${c}`,
filterKey: "not client",
value: uaClassLabel(c),
tone: "neutral" as const,
onRemove: () => setClientExclude((prev) => without(prev, c)),
})),
...(statusFilter !== "all"
? [
{
@@ -798,15 +838,39 @@ export const IssuesVisualizer = ({
: []),
]
const addFilterOptions = uniqueServices
.filter((s) => !selectedServices.has(s))
.map((s) => ({ value: s, label: s }))
const addFilterOptions = [
...uniqueServices.filter((s) => !selectedServices.has(s)).map((s) => ({ value: s, label: s })),
...UA_CLASSES.filter((c) => !clientInclude.has(c.value)).map((c) => ({
value: `client:${c.value}`,
label: `client: ${c.label}`,
})),
...UA_CLASSES.filter((c) => !clientExclude.has(c.value)).map((c) => ({
value: `not-client:${c.value}`,
label: `hide client: ${c.label}`,
})),
]
const addFilter = (value: string) => {
if (value.startsWith("client:")) {
const c = value.slice("client:".length)
setClientInclude((prev) => new Set(prev).add(c))
setClientExclude((prev) => without(prev, c))
} else if (value.startsWith("not-client:")) {
const c = value.slice("not-client:".length)
setClientExclude((prev) => new Set(prev).add(c))
setClientInclude((prev) => without(prev, c))
} else {
toggleService(value)
}
}
const clearAll = () => {
resetFilters()
setSearchQuery("")
setStatusFilter("all")
setExcludeSpamIPs(false)
setClientInclude(new Set())
setClientExclude(new Set())
}
if (loading && issues.length === 0) {
@@ -852,7 +916,7 @@ export const IssuesVisualizer = ({
}}
chips={chips}
addFilterOptions={addFilterOptions}
onAddFilter={(v) => toggleService(v)}
onAddFilter={addFilter}
matchText={`Matching ${filteredIssues.length} of ${issues.length} issues · ${activeFilterCount} filter${activeFilterCount === 1 ? "" : "s"}`}
onClearAll={activeFilterCount > 0 ? clearAll : undefined}
/>