739 lines
35 KiB
TypeScript
739 lines
35 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import {
|
|
OperationsHealth,
|
|
ComponentHealthStatus,
|
|
HealthComponentKey,
|
|
OperationsOverviewData,
|
|
OperationsNavId,
|
|
} from './types';
|
|
import { HEALTH_COMPONENT_METADATA } from '../../lib/ops-data';
|
|
|
|
interface OperationsOverviewProps {
|
|
overview?: OperationsOverviewData | null;
|
|
health: OperationsHealth;
|
|
onRefresh: () => Promise<void> | void;
|
|
isRefreshing: boolean;
|
|
onSelectIncident?: (caseRef: string) => void;
|
|
onNavigateNav?: (nav: OperationsNavId) => void;
|
|
}
|
|
|
|
export function OperationsOverview({
|
|
overview,
|
|
health,
|
|
onRefresh,
|
|
isRefreshing,
|
|
onSelectIncident,
|
|
onNavigateNav,
|
|
}: OperationsOverviewProps) {
|
|
const componentKeys: HealthComponentKey[] = [
|
|
'operations_api',
|
|
'operations_database',
|
|
'session_validation',
|
|
'audit_persistence',
|
|
];
|
|
|
|
const effectiveHealth = overview?.health ?? health;
|
|
|
|
function getStatusBadge(status: ComponentHealthStatus) {
|
|
switch (status) {
|
|
case 'available':
|
|
return {
|
|
label: 'Available',
|
|
icon: 'check_circle',
|
|
classes: 'bg-emerald-950/60 text-emerald-400 border-emerald-800/60',
|
|
};
|
|
case 'degraded':
|
|
return {
|
|
label: 'Degraded',
|
|
icon: 'warning',
|
|
classes: 'bg-amber-950/60 text-amber-400 border-amber-800/60',
|
|
};
|
|
case 'unavailable':
|
|
return {
|
|
label: 'Unavailable',
|
|
icon: 'cancel',
|
|
classes: 'bg-rose-950/60 text-rose-400 border-rose-800/60',
|
|
};
|
|
case 'unknown':
|
|
default:
|
|
return {
|
|
label: 'Unknown',
|
|
icon: 'help',
|
|
classes: 'bg-surface-container-highest text-on-surface-variant border-outline-variant/40',
|
|
};
|
|
}
|
|
}
|
|
|
|
function getSeverityBadge(severity: string) {
|
|
switch (severity) {
|
|
case 'critical':
|
|
return 'bg-rose-950/60 text-rose-400 border border-rose-800/60';
|
|
case 'high':
|
|
return 'bg-amber-950/60 text-amber-400 border border-amber-800/60';
|
|
case 'medium':
|
|
return 'bg-yellow-950/60 text-yellow-400 border border-yellow-800/60';
|
|
case 'low':
|
|
default:
|
|
return 'bg-blue-950/60 text-blue-400 border border-blue-800/60';
|
|
}
|
|
}
|
|
|
|
function getStatusBadgeClass(status: string) {
|
|
switch (status) {
|
|
case 'open':
|
|
return 'bg-rose-950/60 text-rose-300 border border-rose-800/60';
|
|
case 'investigating':
|
|
return 'bg-amber-950/60 text-amber-300 border border-amber-800/60';
|
|
case 'monitoring':
|
|
return 'bg-cyan-950/60 text-cyan-300 border border-cyan-800/60';
|
|
case 'resolved':
|
|
case 'closed':
|
|
default:
|
|
return 'bg-emerald-950/60 text-emerald-300 border border-emerald-800/60';
|
|
}
|
|
}
|
|
|
|
const effectiveCheckedAt = effectiveHealth.checked_at;
|
|
const formattedCheckedAt = effectiveCheckedAt
|
|
? new Date(effectiveCheckedAt).toLocaleString('en-US', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
timeZoneName: 'short',
|
|
})
|
|
: '--';
|
|
|
|
return (
|
|
<div className="space-y-8 w-full max-w-none">
|
|
{/* Header Row: Title & Manual Refresh */}
|
|
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 pb-5 border-b border-outline-variant/30">
|
|
<div>
|
|
<span className="text-xs font-mono text-primary-fixed-dim font-semibold">
|
|
System Health & Operations
|
|
</span>
|
|
<h1 className="font-headline-xl text-2xl sm:text-3xl lg:text-4xl font-extrabold text-on-surface tracking-tight mt-1.5">
|
|
System Health & Operations Overview
|
|
</h1>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant mt-1.5">
|
|
Unified telemetry, platform activity, and operational security triage.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4 shrink-0">
|
|
<div className="text-right">
|
|
<span className="block text-[11px] font-mono text-outline font-semibold">
|
|
Last checked
|
|
</span>
|
|
<time
|
|
dateTime={effectiveCheckedAt}
|
|
className="text-xs sm:text-sm font-mono text-on-surface font-semibold"
|
|
>
|
|
{formattedCheckedAt}
|
|
</time>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => void onRefresh()}
|
|
disabled={isRefreshing}
|
|
aria-label="Refresh Operations health status"
|
|
className="inline-flex items-center gap-2 px-4 py-2.5 rounded-xl bg-surface-container-high hover:bg-surface-bright text-primary-container border border-primary-container/40 text-xs sm:text-sm font-semibold shadow-sm transition-all focus-visible:ring-2 focus-visible:ring-primary-container focus-visible:outline-none disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
|
|
>
|
|
<span
|
|
className={`material-symbols-outlined text-[18px] ${
|
|
isRefreshing ? 'animate-spin' : ''
|
|
}`}
|
|
>
|
|
refresh
|
|
</span>
|
|
<span>{isRefreshing ? 'Checking...' : 'Refresh'}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 1. KPI Row (4 Cards) */}
|
|
<div
|
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5"
|
|
aria-label="Operations Key Metrics"
|
|
>
|
|
{/* Card 1: Registered Users */}
|
|
<article className="p-5 sm:p-6 rounded-2xl bg-surface-container-low border border-outline-variant/30 flex flex-col justify-between min-h-[140px] relative overflow-hidden group hover:border-outline-variant/60 transition-all shadow-sm">
|
|
<div className="flex items-start justify-between">
|
|
<span className="text-xs font-mono text-outline font-semibold">
|
|
Registered Users
|
|
</span>
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-outline">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
group
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-3xl sm:text-4xl lg:text-5xl font-extrabold font-mono text-on-surface tracking-tight my-1.5">
|
|
{overview ? overview.users.total_registered.toLocaleString() : '--'}
|
|
</div>
|
|
<div className="flex items-center gap-1.5 text-xs text-emerald-400 font-mono font-medium">
|
|
<span className="material-symbols-outlined text-[16px]">trending_up</span>
|
|
<span>+{overview ? overview.users.new_this_week.toLocaleString() : 0} this week</span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
{/* Card 2: Active Users 24h / 7d */}
|
|
<article className="p-5 sm:p-6 rounded-2xl bg-surface-container-low border border-outline-variant/30 flex flex-col justify-between min-h-[140px] relative overflow-hidden group hover:border-outline-variant/60 transition-all shadow-sm">
|
|
<div className="flex items-start justify-between">
|
|
<span className="text-xs font-mono text-outline font-semibold">
|
|
Active Users (24h)
|
|
</span>
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-outline">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
bolt
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-3xl sm:text-4xl lg:text-5xl font-extrabold font-mono text-on-surface tracking-tight my-1.5">
|
|
{overview ? overview.users.active_24h.toLocaleString() : '--'}
|
|
</div>
|
|
<div className="text-xs text-on-surface-variant font-mono">
|
|
{overview ? overview.users.active_7d.toLocaleString() : '--'} active in 7 days
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
{/* Card 3: Premium Members / Conversion Rate */}
|
|
<article className="p-5 sm:p-6 rounded-2xl bg-surface-container-low border border-outline-variant/30 flex flex-col justify-between min-h-[140px] relative overflow-hidden group hover:border-outline-variant/60 transition-all shadow-sm">
|
|
<div className="flex items-start justify-between">
|
|
<span className="text-xs font-mono text-outline font-semibold">
|
|
Premium Members
|
|
</span>
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-amber-400">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
workspace_premium
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-3xl sm:text-4xl lg:text-5xl font-extrabold font-mono text-on-surface tracking-tight my-1.5">
|
|
{overview ? overview.membership.premium_members.toLocaleString() : '--'}
|
|
</div>
|
|
<div className="text-xs text-on-surface-variant font-mono">
|
|
{overview ? overview.membership.conversion_rate_pct : 0}% conv. ({overview ? overview.membership.free_members.toLocaleString() : 0} free)
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
{/* Card 4: Open Incidents */}
|
|
<article className="p-5 sm:p-6 rounded-2xl bg-surface-container-low border border-outline-variant/30 flex flex-col justify-between min-h-[140px] relative overflow-hidden group hover:border-outline-variant/60 transition-all shadow-sm">
|
|
<div className="flex items-start justify-between">
|
|
<span className="text-xs font-mono text-outline font-semibold">
|
|
Open Incidents
|
|
</span>
|
|
<div className={`w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center ${
|
|
(overview?.incidents.critical_count ?? 0) > 0
|
|
? 'text-rose-400'
|
|
: (overview?.incidents.total_open ?? 0) > 0
|
|
? 'text-amber-400'
|
|
: 'text-emerald-400'
|
|
}`}>
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
{(overview?.incidents.total_open ?? 0) > 0 ? 'warning' : 'check_circle'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className={`text-3xl sm:text-4xl lg:text-5xl font-extrabold font-mono tracking-tight my-1.5 ${
|
|
(overview?.incidents.critical_count ?? 0) > 0
|
|
? 'text-rose-400'
|
|
: (overview?.incidents.total_open ?? 0) > 0
|
|
? 'text-amber-400'
|
|
: 'text-on-surface'
|
|
}`}>
|
|
{overview ? overview.incidents.total_open : 0}
|
|
</div>
|
|
<div className="text-xs text-on-surface-variant font-mono">
|
|
{overview ? overview.incidents.critical_count : 0} critical, {overview ? overview.incidents.high_count : 0} high
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
|
|
{/* 2. Platform Activity Split */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5" aria-label="Platform Activity">
|
|
{/* Website Activity */}
|
|
<section className="p-6 sm:p-7 rounded-2xl bg-surface-container-low border border-outline-variant/30 space-y-5 shadow-sm">
|
|
<div className="flex items-center justify-between pb-4 border-b border-outline-variant/20">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-primary-fixed-dim">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
language
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<h2 className="text-base font-bold text-on-surface tracking-tight">
|
|
Website Platform Activity
|
|
</h2>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant">
|
|
Portal logins, browser sessions, and web registrations
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<span className="text-xs font-mono px-2.5 py-1 rounded-md bg-surface-container-highest text-on-surface-variant font-semibold">
|
|
Web Ingress
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold">
|
|
24h Sessions
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.web_active_sessions_24h.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold">
|
|
24h Logins
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.web_logins_24h.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold">
|
|
7d Signups
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.web_signups_7d.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Mobile Telemetry Activity */}
|
|
<section className="p-6 sm:p-7 rounded-2xl bg-surface-container-low border border-outline-variant/30 space-y-5 shadow-sm">
|
|
<div className="flex items-center justify-between pb-4 border-b border-outline-variant/20">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-primary-fixed-dim">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
two_wheeler
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<h2 className="text-base font-bold text-on-surface tracking-tight">
|
|
Mobile Telemetry Activity
|
|
</h2>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant">
|
|
Mobile ride recording, active sessions, and sensor data ingestion
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<span className="text-xs font-mono px-2.5 py-1 rounded-md bg-surface-container-highest text-on-surface-variant font-semibold">
|
|
Mobile Ingress
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
24h Sessions
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.mobile_active_sessions_24h.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Rides Today
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.mobile_rides_today.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Active Rides
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-emerald-400">
|
|
{overview ? overview.activity.mobile_rides_active.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Data Points
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview ? overview.activity.mobile_datapoints_24h.toLocaleString() : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
24h Distance
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-cyan-400">
|
|
{overview?.activity.mobile_distance_miles_24h !== undefined ? `${overview.activity.mobile_distance_miles_24h} mi` : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Top Speed Today
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-amber-400">
|
|
{overview?.activity.mobile_top_speed_mph_today !== undefined ? `${overview.activity.mobile_top_speed_mph_today} mph` : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Avg Speed Today
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-on-surface">
|
|
{overview?.activity.mobile_avg_speed_mph_today !== undefined ? `${overview.activity.mobile_avg_speed_mph_today} mph` : '--'}
|
|
</span>
|
|
</div>
|
|
<div className="p-4 sm:p-5 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col justify-between">
|
|
<span className="block text-xs font-mono text-outline mb-1 font-semibold whitespace-nowrap">
|
|
Ingress Rate
|
|
</span>
|
|
<span className="text-2xl sm:text-3xl font-extrabold font-mono text-emerald-400">
|
|
{overview?.activity.mobile_ingress_rate_hz !== undefined ? `${overview.activity.mobile_ingress_rate_hz} Hz` : '20 Hz'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
{/* 3. Subsystem Health Telemetry (4 Cards) */}
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="text-xs font-semibold text-outline font-mono">
|
|
Core Subsystem Health
|
|
</h2>
|
|
<div
|
|
className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-md text-xs font-mono font-semibold border ${
|
|
getStatusBadge(effectiveHealth.status).classes
|
|
}`}
|
|
role="status"
|
|
aria-live="polite"
|
|
>
|
|
<span className="material-symbols-outlined text-[14px]">
|
|
{getStatusBadge(effectiveHealth.status).icon}
|
|
</span>
|
|
<span>System {getStatusBadge(effectiveHealth.status).label}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5"
|
|
aria-label="Subsystem Health Telemetry"
|
|
>
|
|
{componentKeys.map((key) => {
|
|
const meta = HEALTH_COMPONENT_METADATA[key];
|
|
const comp = effectiveHealth.components?.[key];
|
|
const status = comp?.status ?? 'unknown';
|
|
const badge = getStatusBadge(status);
|
|
|
|
return (
|
|
<article
|
|
key={key}
|
|
className="p-5 sm:p-6 rounded-2xl bg-surface-container-low border border-outline-variant/30 flex flex-col justify-between min-h-[160px] relative overflow-hidden group hover:border-outline-variant/60 transition-all shadow-sm"
|
|
>
|
|
<div className="space-y-1.5">
|
|
<span className="text-xs font-mono text-outline font-semibold">
|
|
Subsystem
|
|
</span>
|
|
<h3 className="text-base font-bold text-on-surface tracking-tight">
|
|
{meta.name}
|
|
</h3>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant leading-relaxed">
|
|
{comp?.description || meta.description}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="pt-3 border-t border-outline-variant/20 flex items-center justify-between mt-3">
|
|
<span className="text-xs text-outline font-mono font-medium">
|
|
Status
|
|
</span>
|
|
<div
|
|
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-mono font-semibold border ${badge.classes}`}
|
|
role="status"
|
|
aria-live="polite"
|
|
>
|
|
<span className="material-symbols-outlined text-[14px]">
|
|
{badge.icon}
|
|
</span>
|
|
<span>{badge.label}</span>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 4. Recent Security Errors / Denials Stream */}
|
|
<section className="p-6 sm:p-7 rounded-2xl bg-surface-container-low border border-outline-variant/30 space-y-5 shadow-sm">
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 pb-4 border-b border-outline-variant/20">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-rose-400">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
security
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<h2 className="text-base sm:text-lg font-bold text-on-surface tracking-tight">
|
|
Security Error & Denial Stream
|
|
</h2>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant">
|
|
Live monitoring of unauthorized attempts, PHP probes, and subsystem errors
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-2.5">
|
|
{overview?.errors.php_probes_24h !== undefined && (
|
|
<span className="text-xs font-mono px-3 py-1 rounded-md bg-purple-950/60 text-purple-300 border border-purple-800/60 font-bold">
|
|
{overview.errors.php_probes_24h} PHP Probes (24h)
|
|
</span>
|
|
)}
|
|
<span className="text-xs font-mono px-3 py-1 rounded-md bg-rose-950/60 text-rose-400 border border-rose-800/60 font-bold">
|
|
{overview?.errors.denials_24h ?? 0} Denials (24h)
|
|
</span>
|
|
<span className="text-xs font-mono px-3 py-1 rounded-md bg-amber-950/60 text-amber-400 border border-amber-800/60 font-bold">
|
|
{overview?.errors.errors_24h ?? 0} Errors (24h)
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{overview?.errors.recent && overview.errors.recent.length > 0 ? (
|
|
<div className="space-y-2.5">
|
|
{overview.errors.recent.map((err, idx) => (
|
|
<div
|
|
key={err.id ?? idx}
|
|
className="p-4 rounded-xl bg-surface-container-lowest border border-outline-variant/20 flex flex-col sm:flex-row sm:items-center justify-between gap-3 text-xs sm:text-sm"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<span
|
|
className={`px-2.5 py-1 rounded-md text-xs font-mono font-semibold capitalize ${
|
|
err.action === 'nginx.php_probe'
|
|
? 'bg-purple-950/60 text-purple-300 border border-purple-800/60'
|
|
: err.result === 'denied'
|
|
? 'bg-rose-950/60 text-rose-400 border border-rose-800/60'
|
|
: 'bg-amber-950/60 text-amber-400 border border-amber-800/60'
|
|
}`}
|
|
>
|
|
{err.action === 'nginx.php_probe' ? 'php probe' : err.result}
|
|
</span>
|
|
<span className="font-mono font-bold text-on-surface">
|
|
{err.action}
|
|
</span>
|
|
<span className="text-on-surface-variant font-mono text-xs">
|
|
{err.action === 'nginx.php_probe' ? `from ${err.actor_identity}` : `by ${err.actor_identity}`}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 text-xs font-mono text-outline shrink-0">
|
|
{err.target_resource && (
|
|
<span className="truncate max-w-[200px]">
|
|
res: {err.target_resource}
|
|
</span>
|
|
)}
|
|
{err.reason_code && (
|
|
<span className="truncate max-w-[160px] text-on-surface-variant">
|
|
({err.reason_code})
|
|
</span>
|
|
)}
|
|
<time dateTime={err.timestamp} className="font-semibold text-on-surface-variant">
|
|
{new Date(err.timestamp).toLocaleTimeString('en-US', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
})}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="p-6 rounded-xl bg-surface-container-lowest border border-outline-variant/20 text-center text-xs sm:text-sm text-on-surface-variant">
|
|
Zero security errors or access denials recorded in recent event window.
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
{/* 5. Open Incidents Triage */}
|
|
<section className="p-6 sm:p-7 rounded-2xl bg-surface-container-low border border-outline-variant/30 space-y-5 shadow-sm">
|
|
<div className="flex items-center justify-between pb-4 border-b border-outline-variant/20">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-amber-400">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
crisis_alert
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<h2 className="text-base sm:text-lg font-bold text-on-surface tracking-tight">
|
|
Open Incidents Triage
|
|
</h2>
|
|
<p className="text-xs sm:text-sm text-on-surface-variant">
|
|
Active incidents requiring operator assessment or active investigation
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{onNavigateNav && (
|
|
<button
|
|
type="button"
|
|
onClick={() => onNavigateNav('incidents')}
|
|
className="text-xs sm:text-sm font-mono text-primary-fixed-dim hover:text-primary-container flex items-center gap-1.5 transition-colors font-semibold cursor-pointer"
|
|
>
|
|
<span>View All</span>
|
|
<span className="material-symbols-outlined text-[16px]">arrow_forward</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{overview?.incidents.recent_open && overview.incidents.recent_open.length > 0 ? (
|
|
<div className="overflow-hidden rounded-xl border border-outline-variant/20">
|
|
{/* Desktop Table */}
|
|
<div className="hidden md:block overflow-x-auto">
|
|
<table className="w-full text-left text-xs sm:text-sm">
|
|
<thead className="bg-surface-container text-xs font-mono text-outline border-b border-outline-variant/20">
|
|
<tr>
|
|
<th scope="col" className="px-5 py-3.5 font-semibold">Case</th>
|
|
<th scope="col" className="px-5 py-3.5 font-semibold">Title</th>
|
|
<th scope="col" className="px-5 py-3.5 font-semibold">Severity</th>
|
|
<th scope="col" className="px-5 py-3.5 font-semibold">Status</th>
|
|
<th scope="col" className="px-5 py-3.5 font-semibold text-right">Created</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-outline-variant/10 bg-surface-container-lowest font-mono">
|
|
{overview.incidents.recent_open.map((inc) => (
|
|
<tr
|
|
key={inc.case_reference}
|
|
onClick={() => onSelectIncident?.(inc.case_reference)}
|
|
className={`hover:bg-surface-bright/40 transition-colors ${
|
|
onSelectIncident ? 'cursor-pointer' : ''
|
|
}`}
|
|
>
|
|
<td className="px-5 py-4 font-bold text-primary-fixed-dim whitespace-nowrap">
|
|
{inc.case_reference}
|
|
</td>
|
|
<td className="px-5 py-4 font-sans text-on-surface font-semibold max-w-sm truncate">
|
|
{inc.title}
|
|
</td>
|
|
<td className="px-5 py-4 whitespace-nowrap">
|
|
<span className={`inline-flex items-center px-2.5 py-1 rounded-md text-xs font-semibold capitalize ${getSeverityBadge(inc.severity)}`}>
|
|
{inc.severity}
|
|
</span>
|
|
</td>
|
|
<td className="px-5 py-4 whitespace-nowrap">
|
|
<span className={`inline-flex items-center px-2.5 py-1 rounded-md text-xs font-semibold capitalize ${getStatusBadgeClass(inc.status)}`}>
|
|
{inc.status}
|
|
</span>
|
|
</td>
|
|
<td className="px-5 py-4 text-right text-outline whitespace-nowrap text-xs">
|
|
{new Date(inc.created_at).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
})}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Mobile Stacked Cards */}
|
|
<div className="md:hidden divide-y divide-outline-variant/15 bg-surface-container-lowest">
|
|
{overview.incidents.recent_open.map((inc) => (
|
|
<div
|
|
key={inc.case_reference}
|
|
onClick={() => onSelectIncident?.(inc.case_reference)}
|
|
className={`p-4 space-y-2.5 ${onSelectIncident ? 'cursor-pointer active:bg-surface-bright/40' : ''}`}
|
|
>
|
|
<div className="flex items-center justify-between gap-2">
|
|
<span className="font-mono text-xs font-bold text-primary-fixed-dim">
|
|
{inc.case_reference}
|
|
</span>
|
|
<div className="flex items-center gap-1.5">
|
|
<span className={`px-2 py-0.5 rounded text-[10px] font-mono font-semibold capitalize ${getSeverityBadge(inc.severity)}`}>
|
|
{inc.severity}
|
|
</span>
|
|
<span className={`px-2 py-0.5 rounded text-[10px] font-mono font-semibold capitalize ${getStatusBadgeClass(inc.status)}`}>
|
|
{inc.status}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-sm text-on-surface font-semibold line-clamp-1">
|
|
{inc.title}
|
|
</div>
|
|
<div className="text-xs font-mono text-outline">
|
|
Created {new Date(inc.created_at).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="p-6 rounded-xl bg-surface-container-lowest border border-outline-variant/20 text-center text-xs sm:text-sm text-on-surface-variant">
|
|
All operational systems normal. Zero open or active triage incidents.
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
{/* 6. Operational Policy & Architecture Boundary */}
|
|
<div className="p-6 sm:p-7 rounded-2xl bg-surface-container-lowest border border-outline-variant/30 space-y-4 shadow-sm">
|
|
<div className="flex items-center gap-2.5">
|
|
<div className="w-8 h-8 rounded-lg bg-surface-container-high border border-outline-variant/30 flex items-center justify-center text-primary-container">
|
|
<span className="material-symbols-outlined text-[20px]">
|
|
verified_user
|
|
</span>
|
|
</div>
|
|
<h3 className="text-sm sm:text-base font-bold text-on-surface tracking-tight">
|
|
Operational Architecture & Data Boundary
|
|
</h3>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 text-xs sm:text-sm text-on-surface-variant pt-4 border-t border-outline-variant/20">
|
|
<div>
|
|
<span className="block font-semibold text-on-surface mb-1">
|
|
Authority Source
|
|
</span>
|
|
<p className="leading-relaxed">
|
|
PostgreSQL <code className="text-primary-fixed-dim">ops.operators</code> schema. Consumer roles and Firebase custom claims strictly bypassed.
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<span className="block font-semibold text-on-surface mb-1">
|
|
Privacy Isolation
|
|
</span>
|
|
<p className="leading-relaxed">
|
|
Zero cross-rider data access. Personal rides, GPS points, vehicles, and billing accounts are structurally inaccessible.
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<span className="block font-semibold text-on-surface mb-1">
|
|
Session Integrity
|
|
</span>
|
|
<p className="leading-relaxed">
|
|
Governed by host-prefixed <code className="text-primary-fixed-dim">__Host-motorsignal_ops</code> cookies with strict same-origin isolation.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|