Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export function AppRoutes() {
<Route path="threat-management" element={<Navigate to="/threat-management/alerts" replace />} />
<Route path="threat-management/alerts" element={<AlertsPage />} />
<Route path="threat-management/alerts/tagging-rules" element={<TaggingRulesPage />} />
<Route path="threat-management/alerts/:id" element={<AlertsPage />} />
<Route path="threat-management/incidents" element={<IncidentsPage />} />
<Route path="threat-management/adversaries" element={<AdversariesPage />} />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function AdversariesPage() {
const visibleItems = filtered.slice(0, visible)

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<ShieldAlert size={14} strokeWidth={1.75} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function AlertingRulesPage() {
}

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<ShieldAlert size={14} strokeWidth={1.75} />
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/features/alerts/components/alert-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ export function AlertRow({
</div>
</td>
<td className={TD} onClick={(e) => e.stopPropagation()}>
<StatusChangeMenu status={statusKey(a)} variant="pill" onStatus={onStatus} />
<StatusChangeMenu
status={statusKey(a)}
variant="pill"
onStatus={onStatus}
onCreateRule={() => onCreateRule(a)}
/>
</td>
<td className={`${TD} font-mono mx-auto text-[11px] text-muted-foreground`} title={a.technique}>
{a.technique || '—'}
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/features/alerts/components/status-change-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ export function StatusChangeMenu({
status,
onStatus,
variant,
onCreateRule,
}: {
status: StatusKey
onStatus: (status: number, observation: string, fp: boolean) => void
variant: Variant
onCreateRule?: () => void
}) {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
Expand Down Expand Up @@ -77,7 +79,7 @@ export function StatusChangeMenu({
{open && (
<div
onClick={(e) => e.stopPropagation()}
className="absolute left-0 top-full z-30 mt-1 w-48 rounded-md border border-border bg-popover py-1 shadow-lg"
className="absolute left-0 top-full z-30 mt-1 w-max min-w-[12rem] rounded-md border border-border bg-popover py-1 shadow-lg"
>
{(['open', 'in_review', 'completed'] as StatusKey[]).map((k) => (
<button
Expand All @@ -94,6 +96,17 @@ export function StatusChangeMenu({
>
{t('alerts.drawer.completeFalsePositive')}
</button>
{onCreateRule && (
<button
onClick={() => {
setOpen(false)
onCreateRule()
}}
className="block w-full border-t border-border px-3 py-1.5 text-left text-sm hover:bg-muted"
>
{t('alerts.row.createRuleFromAlert')}
</button>
)}
</div>
)}
{pending && (
Expand Down
31 changes: 29 additions & 2 deletions frontend/src/features/alerts/pages/AlertsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { AlertTriangle, Loader2 } from 'lucide-react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { Button } from '@/shared/components/ui/button'
import { InfiniteScrollSentinel } from '@/shared/components/ui/infinite-scroll'
Expand Down Expand Up @@ -65,7 +65,25 @@ export function AlertsPage() {
// SOC-AI chat navigation: seed the filters + time window the agent emitted.
const location = useLocation()
const navigate = useNavigate()
const { id: routeAlertName } = useParams<{ id: string }>()
const pendingOpenNameRef = useRef<string | null>(null)
const seededRef = useRef(false)

// Deep-link (/threat-management/alerts/:alertName): seed as name filter + open drawer once loaded.
useEffect(() => {
if (!routeAlertName) return
const decoded = decodeURIComponent(routeAlertName)
pendingOpenNameRef.current = decoded
setCustomFilters((c) =>
c.some((f) => f.field === 'name' && f.value === decoded)
? c
: [...c, { field: 'name', label: 'name', operator: 'IS', value: decoded }],
)
setPage(0)
// Drop the name from the URL so future edits to filters aren't fought by re-seeding.
navigate('/threat-management/alerts', { replace: true })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [routeAlertName])
useEffect(() => {
const state = location.state as { socaiFilters?: FilterType[]; socaiTime?: string } | null
if (!state?.socaiFilters?.length || seededRef.current) return
Expand Down Expand Up @@ -117,6 +135,15 @@ export function AlertsPage() {
}, [scopeFilters, statusTab, severity])

const { alerts, total, loading, error, refresh: refreshList } = useAlertsList(page, pageSize, listFilters)

useEffect(() => {
const name = pendingOpenNameRef.current
if (!name) return
const match = alerts.find((a) => a.name === name)
if (!match) return
pendingOpenNameRef.current = null
setOpenAlert(match)
}, [alerts])
const { sevCounts, statusCounts, timeline, openCount, refresh: refreshStats } = useAlertStats(scopeFilters, range.interval)
const { tagCatalog, createTag, updateTag, deleteTag } = useAlertTagCatalog((deletedName) =>
setTagFilter((tf) => tf.filter((tn) => tn !== deletedName))
Expand Down Expand Up @@ -162,7 +189,7 @@ export function AlertsPage() {
}

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<AlertsHeader total={total} openCount={openCount} view={view} onView={setView} />

<div className="mt-3 shrink-0">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/alerts/pages/TaggingRulesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export function TaggingRulesPage() {
}

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<TagIcon size={14} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/audit/pages/AuditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function AuditPage() {
}))

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<Header loading={loading} total={pageInfo.total_items} onRefresh={() => load(filters)} />

<div className="mt-6">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/billing/pages/LicensePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function LicensePage() {
const showError = !license && error

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header className="flex items-start justify-between gap-3">
<div>
<h1 className="flex items-center gap-2 text-base font-semibold">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/branding/pages/BrandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function BrandingPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header>
<h1 className="flex items-center gap-2 text-base font-semibold">
<Paintbrush size={16} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/compliance/pages/CompliancePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function CompliancePage() {
const [tab, setTab] = useState<PageTab>('frameworks')

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="shrink-0">
<div className="inline-flex rounded-md border border-border p-0.5">
<TabButton active={tab === 'frameworks'} onClick={() => setTab('frameworks')} icon={ShieldCheck} label={t('compliance.tabs.frameworks')} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export function VisualizationEditor({ initial, initialChartType }: Visualization
}

return (
<div className="mx-auto flex h-full w-full max-w-[1100px] flex-col gap-4 px-6 pb-6 pt-3">
<div className="flex h-full w-full flex-col gap-4 px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-3">
<h1 className="text-base font-semibold">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/dashboard/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ export function DashboardPage() {
previewDashboard != null && !previewDashboard.systemOwner && !editor.editing

return (
<div className="mx-auto flex h-full w-full max-w-[1800px] flex-col gap-4 px-6 pb-6 pt-3">
<div className="flex h-full w-full flex-col gap-4 px-6 pb-6 pt-3">
{inPreview && previewDashboard && (
<DashboardPreviewHeader
dashboard={previewDashboard}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export function VisualizationListPage() {
}

return (
<div className="mx-auto flex h-full w-full max-w-[1100px] flex-col gap-4 px-6 pb-6 pt-3">
<div className="flex h-full w-full flex-col gap-4 px-6 pb-6 pt-3">
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-3">
<Link
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function DataProcessingPage() {
const [nonce, setNonce] = useState(0)

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-end gap-3">
<div className="flex items-center gap-2">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export function DataSourcesPage() {
const events24h = (name: string) => stats[name]?.count ?? 0

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<Header total={total} />

<div className="mt-5 grid grid-cols-12 gap-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function FederationAdminLayout() {
<div className="min-h-screen bg-background text-foreground">
<Topbar />
<nav className="border-b border-border bg-card">
<div className="mx-auto flex w-full max-w-[1100px] gap-1 px-6">
<div className="flex w-full gap-1 px-6">
{tabs.map((tab) => (
<NavLink
key={tab.to}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function FederationTeamPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-10 pt-5">
<div className="w-full px-6 pb-10 pt-5">
<header className="flex items-center justify-between gap-3">
<div>
<h1 className="flex items-center gap-2 text-base font-semibold">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/home/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function HomePage() {
const playbooks = useActivePlaybooks()

return (
<div className="mx-auto w-full max-w-[1100px] px-6 py-10">
<div className="w-full px-6 py-10">
<ChatHero />

<div className="mt-10 grid grid-cols-12 gap-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export function IncidentAlertsTab({ incidentId, onChanged }: { incidentId: numbe
return (
<div className="overflow-hidden rounded-lg border border-border bg-card">
{rows.map((a) => (
<div
<a
href={`/threat-management/alerts/${a.alertName}`}
key={a.id}
className="group flex items-center gap-3 border-b border-border/60 px-4 py-2.5 text-xs last:border-b-0"
>
Expand All @@ -38,7 +39,7 @@ export function IncidentAlertsTab({ incidentId, onChanged }: { incidentId: numbe
>
<Trash2 size={13} />
</button>
</div>
</a>
))}
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/incidents/pages/IncidentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function IncidentsPage() {
}

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Flame size={14} strokeWidth={1.75} className="text-red-500" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export function IntegrationsPage() {
const openLive = open ? displayList.find((d) => d.id === open.id) ?? open : null

return (
<div className="mx-auto w-full max-w-[1600px] px-6 py-6">
<div className="w-full px-6 py-6">
{/* Loading overlay */}
{integrations.isLoading && (
<div className="fixed inset-0 z-40 flex items-center justify-center bg-card/60 backdrop-blur-sm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function NotificationsPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header className="flex items-start justify-between gap-3">
<div>
<h1 className="flex items-center gap-2 text-base font-semibold">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function ParsingFiltersPage() {
}

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<FileCode size={14} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/profile/pages/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export function ProfilePage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pt-5 pb-10">
<div className="w-full px-6 pt-5 pb-10">
{/* Hero */}
<header className="flex flex-col items-center gap-4 sm:flex-row sm:items-center sm:gap-6">
<div className="relative">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function RegexPatternsPage() {
}, [load])

return (
<div className="mx-auto flex h-full min-h-0 w-full max-w-[1100px] flex-col px-6 pb-6 pt-3">
<div className="flex h-full min-h-0 w-full flex-col px-6 pb-6 pt-3">
<header className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Regex size={14} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/settings/pages/AboutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function AboutPage() {
const isEnterprise = edition === 'enterprise'

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header>
<h1 className="flex items-center gap-2 text-base font-semibold">
<Info size={16} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/settings/pages/ConnectionKeyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export function ConnectionKeyPage() {
const hasToken = token !== ''

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<Header />

<div className="mt-6 space-y-5">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/settings/pages/DataRetentionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function DataRetentionPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header>
<h1 className="flex items-center gap-2 text-base font-semibold">
<HardDriveDownload size={16} strokeWidth={1.75} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/features/settings/pages/DateFormatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function DateFormatPage() {
const zoneOptions = useMemo(() => ['system', ...ALL_ZONES], [])

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header>
<h1 className="flex items-center gap-2 text-base font-semibold">
<Clock size={16} strokeWidth={1.75} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export function EmailConfigurationPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<header>
<h1 className="flex items-center gap-2 text-base font-semibold">
<Mail size={16} strokeWidth={1.75} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import type { IdentityProvider, IdentityProviderRequest } from '../types/idp.typ
function EnterpriseGate() {
const { t } = useTranslation()
return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<PageHeader />
<div className="mt-10 flex flex-col items-center justify-center rounded-xl border border-border bg-card px-6 py-16 text-center">
<span className="flex h-14 w-14 items-center justify-center rounded-full bg-amber-500/10 text-amber-500 ring-1 ring-inset ring-amber-500/30">
Expand Down Expand Up @@ -130,7 +130,7 @@ export function IdentityProvidersPage() {
}

return (
<div className="mx-auto w-full max-w-[1100px] px-6 pb-6 pt-3">
<div className="w-full px-6 pb-6 pt-3">
<PageHeader onAdd={() => setDialog({ mode: 'create' })} />

<div className="mt-5">
Expand Down
Loading
Loading