refactor: Convert scheduling algorithms to class-based BaseAlgorithm structure, update engine and tests, and add new algorithm and metrics documentation.
This commit is contained in:
1 parent
9c552e53fa
commit
571d8e51d2
26 files changed
+1583
-1221
No files matched your search
@@ -1,3 +1,11 @@
|
||||
import {
|
||||
Clock,
|
||||
Timer,
|
||||
Hourglass,
|
||||
Cpu,
|
||||
Zap,
|
||||
Activity,
|
||||
} from 'lucide-react';
|
||||
import type { GlobalMetrics as GlobalMetricsType } from '@core/types';
|
||||
|
||||
interface GlobalMetricsProps {
|
||||
@@ -8,14 +16,36 @@ interface MetricCardProps {
|
||||
label: string;
|
||||
value: string;
|
||||
sub?: string;
|
||||
accent?: string;
|
||||
accentColor: string; // hex or CSS color for the bar fill
|
||||
accentClass: string; // tailwind text class for the value
|
||||
icon: React.ReactNode;
|
||||
barPct?: number; // 0–100; undefined = no bar
|
||||
}
|
||||
|
||||
const MetricCard = ({ label, value, sub, accent = 'text-blue-300' }: MetricCardProps) => (
|
||||
<div className="bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 flex flex-col gap-1">
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">{label}</span>
|
||||
<span className={`text-xl font-bold font-mono ${accent}`}>{value}</span>
|
||||
const MetricCard = ({
|
||||
label,
|
||||
value,
|
||||
sub,
|
||||
accentColor,
|
||||
accentClass,
|
||||
icon,
|
||||
barPct,
|
||||
}: MetricCardProps) => (
|
||||
<div className="bg-slate-800/70 border border-slate-700/80 rounded-xl px-4 py-3 flex flex-col gap-1.5 hover:border-slate-600 transition-colors">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">{label}</span>
|
||||
<span className="text-slate-500">{icon}</span>
|
||||
</div>
|
||||
<span className={`text-2xl font-bold font-mono leading-none ${accentClass}`}>{value}</span>
|
||||
{sub && <span className="text-[10px] text-slate-500">{sub}</span>}
|
||||
{barPct !== undefined && (
|
||||
<div className="mt-1 h-1.5 w-full bg-slate-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-500"
|
||||
style={{ width: `${Math.min(100, barPct)}%`, backgroundColor: accentColor }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,48 +60,74 @@ const GlobalMetrics = ({ metrics }: GlobalMetricsProps) => {
|
||||
} = metrics;
|
||||
|
||||
const utilizationPct = Math.min(100, cpuUtilization);
|
||||
// Bars for time metrics are proportional to the total simulation time
|
||||
const toPct = (v: number) =>
|
||||
totalSimulationTime > 0 ? Math.min(100, (v / totalSimulationTime) * 100) : 0;
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<MetricCard
|
||||
label="Avg Response Time"
|
||||
value={avgResponseTime.toFixed(2)}
|
||||
sub="unidades de tempo"
|
||||
accent="text-amber-300"
|
||||
accentColor="#f59e0b"
|
||||
accentClass="text-amber-300"
|
||||
icon={<Clock size={13} />}
|
||||
barPct={toPct(avgResponseTime)}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Avg Turnaround Time"
|
||||
value={avgTurnaroundTime.toFixed(2)}
|
||||
sub="unidades de tempo"
|
||||
accent="text-blue-300"
|
||||
accentColor="#60a5fa"
|
||||
accentClass="text-blue-300"
|
||||
icon={<Timer size={13} />}
|
||||
barPct={toPct(avgTurnaroundTime)}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Avg Waiting Time"
|
||||
value={avgWaitingTime.toFixed(2)}
|
||||
sub="unidades de tempo"
|
||||
accent="text-orange-300"
|
||||
accentColor="#fb923c"
|
||||
accentClass="text-orange-300"
|
||||
icon={<Hourglass size={13} />}
|
||||
barPct={toPct(avgWaitingTime)}
|
||||
/>
|
||||
<MetricCard
|
||||
label="CPU Throughput"
|
||||
value={cpuThroughput.toFixed(3)}
|
||||
sub="processos / u.t."
|
||||
accent="text-emerald-300"
|
||||
accentColor="#34d399"
|
||||
accentClass="text-emerald-300"
|
||||
icon={<Zap size={13} />}
|
||||
/>
|
||||
<MetricCard
|
||||
label="Total Simulation Time"
|
||||
value={String(totalSimulationTime)}
|
||||
sub="ticks"
|
||||
accent="text-slate-200"
|
||||
accentColor="#94a3b8"
|
||||
accentClass="text-slate-200"
|
||||
icon={<Activity size={13} />}
|
||||
/>
|
||||
{/* CPU Utilization card with progress bar */}
|
||||
<div className="bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 flex flex-col gap-1">
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">CPU Utilization</span>
|
||||
<span className="text-xl font-bold font-mono text-violet-300">{utilizationPct.toFixed(1)}%</span>
|
||||
<div className="mt-1 h-1.5 w-full bg-slate-700 rounded-full overflow-hidden">
|
||||
|
||||
{/* CPU Utilization — gradient bar */}
|
||||
<div className="bg-slate-800/70 border border-slate-700/80 rounded-xl px-4 py-3 flex flex-col gap-1.5 hover:border-slate-600 transition-colors">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] text-slate-400 uppercase tracking-wider font-medium">CPU Utilization</span>
|
||||
<span className="text-slate-500"><Cpu size={13} /></span>
|
||||
</div>
|
||||
<span className="text-2xl font-bold font-mono leading-none text-violet-300">
|
||||
{utilizationPct.toFixed(1)}%
|
||||
</span>
|
||||
<span className="text-[10px] text-slate-500">do tempo total em CPU</span>
|
||||
<div className="mt-1 h-2 w-full bg-slate-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-violet-500 rounded-full transition-all duration-500"
|
||||
style={{ width: `${utilizationPct}%` }}
|
||||
className="h-full rounded-full transition-all duration-500"
|
||||
style={{
|
||||
width: `${utilizationPct}%`,
|
||||
background: `linear-gradient(90deg, #f59e0b ${0}%, #34d399 ${100}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,10 @@ import {
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
CartesianGrid,
|
||||
RadarChart,
|
||||
PolarGrid,
|
||||
PolarAngleAxis,
|
||||
Radar,
|
||||
} from 'recharts';
|
||||
import type { ProcessMetrics, Process } from '@core/types';
|
||||
|
||||
@@ -15,6 +19,15 @@ interface MetricsChartProps {
|
||||
processMap: Record<string, Process>;
|
||||
}
|
||||
|
||||
const TOOLTIP_STYLE = {
|
||||
backgroundColor: '#1e293b',
|
||||
border: '1px solid #334155',
|
||||
borderRadius: '8px',
|
||||
fontSize: '11px',
|
||||
color: '#e2e8f0',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.4)',
|
||||
};
|
||||
|
||||
const MetricsChart = ({ metrics, processMap }: MetricsChartProps) => {
|
||||
if (metrics.length === 0) {
|
||||
return (
|
||||
@@ -24,57 +37,131 @@ const MetricsChart = ({ metrics, processMap }: MetricsChartProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
const data = metrics.map((m) => ({
|
||||
name: processMap[m.processId]?.name ?? m.processId,
|
||||
'Waiting Time': m.waitingTime,
|
||||
'CPU Time': m.cpuTime,
|
||||
'I/O Time': m.ioTime,
|
||||
const getName = (id: string) => processMap[id]?.name ?? id;
|
||||
const getColor = (id: string) => processMap[id]?.color ?? '#94a3b8';
|
||||
|
||||
// ── Stacked bar data ──────────────────────────────────────────
|
||||
const barData = metrics.map((m) => ({
|
||||
name: getName(m.processId),
|
||||
'Waiting': m.waitingTime,
|
||||
'CPU': m.cpuTime,
|
||||
'I/O': m.ioTime,
|
||||
_color: getColor(m.processId),
|
||||
}));
|
||||
|
||||
// ── Radar chart data (normalized 0-100 per dimension) ─────────
|
||||
// Each "subject" is a process, each series is a dimension.
|
||||
// We invert "Response" and "Waiting" so higher = better.
|
||||
const maxT = Math.max(...metrics.map(m => m.turnaroundTime), 1);
|
||||
const maxR = Math.max(...metrics.map(m => m.responseTime), 1);
|
||||
const maxW = Math.max(...metrics.map(m => m.waitingTime), 1);
|
||||
const maxCpu = Math.max(...metrics.map(m => m.cpuTime), 1);
|
||||
const maxIo = Math.max(...metrics.map(m => m.ioTime), 1);
|
||||
|
||||
const radarDimensions = [
|
||||
'Turnaround', 'Response', 'Waiting', 'CPU Time', 'I/O Time',
|
||||
] as const;
|
||||
|
||||
// Radar: rows = dimensions, column per process → Recharts RadarChart needs
|
||||
// one row per "subject" (dimension), with one field per process.
|
||||
const radarData = radarDimensions.map(dim => {
|
||||
const row: Record<string, string | number> = { dimension: dim };
|
||||
metrics.forEach(m => {
|
||||
const key = getName(m.processId);
|
||||
switch (dim) {
|
||||
case 'Turnaround': row[key] = (m.turnaroundTime / maxT) * 100; break;
|
||||
case 'Response': row[key] = (m.responseTime / maxR) * 100; break;
|
||||
case 'Waiting': row[key] = (m.waitingTime / maxW) * 100; break;
|
||||
case 'CPU Time': row[key] = (m.cpuTime / maxCpu)* 100; break;
|
||||
case 'I/O Time': row[key] = (m.ioTime / maxIo) * 100; break;
|
||||
}
|
||||
});
|
||||
return row;
|
||||
});
|
||||
|
||||
const showRadar = metrics.length >= 2;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<BarChart data={data} margin={{ top: 4, right: 16, left: 0, bottom: 4 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#334155" vertical={false} />
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
tick={{ fill: '#94a3b8', fontSize: 11 }}
|
||||
axisLine={{ stroke: '#475569' }}
|
||||
tickLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fill: '#94a3b8', fontSize: 11 }}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
label={{
|
||||
value: 'ticks',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 10,
|
||||
style: { fill: '#64748b', fontSize: 10 },
|
||||
}}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: '#1e293b',
|
||||
border: '1px solid #334155',
|
||||
borderRadius: '6px',
|
||||
fontSize: '12px',
|
||||
color: '#e2e8f0',
|
||||
}}
|
||||
cursor={{ fill: 'rgba(255,255,255,0.05)' }}
|
||||
/>
|
||||
<Legend
|
||||
wrapperStyle={{ fontSize: '11px', paddingTop: '8px', color: '#94a3b8' }}
|
||||
/>
|
||||
<Bar dataKey="Waiting Time" stackId="a" fill="#f59e0b" radius={[0, 0, 0, 0]} />
|
||||
<Bar dataKey="CPU Time" stackId="a" fill="#22c55e" radius={[0, 0, 0, 0]} />
|
||||
<Bar dataKey="I/O Time" stackId="a" fill="#06b6d4" radius={[4, 4, 0, 0]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
<p className="text-center text-[10px] text-slate-500 mt-1">
|
||||
Barras empilhadas = Turnaround Time (Waiting + CPU + I/O)
|
||||
</p>
|
||||
<div className={`p-3 flex gap-3 ${showRadar ? 'flex-row' : 'flex-col'}`}>
|
||||
{/* ── Radar chart ───────────────────────────────────────── */}
|
||||
{showRadar && (
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-[10px] text-slate-400 uppercase tracking-wider mb-1 font-medium text-center">
|
||||
Perfil Comparativo (normalizado)
|
||||
</p>
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<RadarChart data={radarData} margin={{ top: 8, right: 20, bottom: 8, left: 20 }}>
|
||||
<PolarGrid stroke="#334155" />
|
||||
<PolarAngleAxis
|
||||
dataKey="dimension"
|
||||
tick={{ fill: '#94a3b8', fontSize: 10 }}
|
||||
/>
|
||||
{metrics.map((m) => (
|
||||
<Radar
|
||||
key={m.processId}
|
||||
name={getName(m.processId)}
|
||||
dataKey={getName(m.processId)}
|
||||
stroke={getColor(m.processId)}
|
||||
fill={getColor(m.processId)}
|
||||
fillOpacity={0.15}
|
||||
strokeWidth={2}
|
||||
dot={{ r: 3, fill: getColor(m.processId) }}
|
||||
/>
|
||||
))}
|
||||
<Tooltip
|
||||
contentStyle={TOOLTIP_STYLE}
|
||||
formatter={(value) => [typeof value === 'number' ? `${value.toFixed(1)}%` : '-', '']}
|
||||
/>
|
||||
<Legend
|
||||
wrapperStyle={{ fontSize: '10px', paddingTop: '4px', color: '#94a3b8' }}
|
||||
/>
|
||||
</RadarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Stacked bar chart ─────────────────────────────────── */}
|
||||
<div className={showRadar ? 'flex-1 min-w-0' : 'w-full'}>
|
||||
<p className="text-[10px] text-slate-400 uppercase tracking-wider mb-1 font-medium text-center">
|
||||
Breakdown por Processo (ticks)
|
||||
</p>
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<BarChart data={barData} margin={{ top: 8, right: 12, left: -8, bottom: 4 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#1e293b" vertical={false} />
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
tick={{ fill: '#94a3b8', fontSize: 11 }}
|
||||
axisLine={{ stroke: '#334155' }}
|
||||
tickLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fill: '#94a3b8', fontSize: 10 }}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
label={{
|
||||
value: 'ticks',
|
||||
angle: -90,
|
||||
position: 'insideLeft',
|
||||
offset: 14,
|
||||
style: { fill: '#475569', fontSize: 9 },
|
||||
}}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={TOOLTIP_STYLE}
|
||||
cursor={{ fill: 'rgba(255,255,255,0.04)' }}
|
||||
/>
|
||||
<Legend
|
||||
wrapperStyle={{ fontSize: '10px', paddingTop: '4px', color: '#94a3b8' }}
|
||||
/>
|
||||
<Bar dataKey="Waiting" stackId="a" fill="#f59e0b" radius={[0, 0, 0, 0]} />
|
||||
<Bar dataKey="CPU" stackId="a" fill="#22c55e" radius={[0, 0, 0, 0]} />
|
||||
<Bar dataKey="I/O" stackId="a" fill="#06b6d4" radius={[4, 4, 0, 0]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
<p className="text-center text-[9px] text-slate-600 mt-0.5">
|
||||
Waiting + CPU + I/O = Turnaround
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ const MetricsPanel = () => {
|
||||
const canExportCSV = status === 'finished' && processMetrics.length > 0;
|
||||
|
||||
return (
|
||||
<div className="border-t border-slate-700 bg-slate-950 flex flex-col shrink-0" style={{ height: '280px' }}>
|
||||
<div className="border-t border-slate-700 bg-slate-950 flex flex-col shrink-0" style={{ height: '340px' }}>
|
||||
{/* Panel header with tabs + export button */}
|
||||
<div className="flex items-center px-3 border-b border-slate-700 shrink-0 h-10 gap-1">
|
||||
{TAB_CONFIG.map(tab => (
|
||||
|
||||
@@ -12,6 +12,26 @@ function boundVariant(type: ProcessMetrics['boundType']) {
|
||||
return 'balanced';
|
||||
}
|
||||
|
||||
// Inline mini bar rendered behind the numeric value
|
||||
interface MiniBarProps {
|
||||
value: number;
|
||||
max: number;
|
||||
color: string; // tailwind bg class
|
||||
}
|
||||
|
||||
const MiniBar = ({ value, max, color }: MiniBarProps) => {
|
||||
const pct = max > 0 ? (value / max) * 100 : 0;
|
||||
return (
|
||||
<div className="relative flex items-center justify-end gap-1.5 min-w-[52px]">
|
||||
<div
|
||||
className="absolute inset-y-0 right-0 rounded-sm opacity-20 transition-all duration-300"
|
||||
style={{ width: `${pct}%`, backgroundColor: color }}
|
||||
/>
|
||||
<span className="relative z-10 font-mono text-xs">{value}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MetricsTable = ({ metrics, processMap }: MetricsTableProps) => {
|
||||
if (metrics.length === 0) {
|
||||
return (
|
||||
@@ -21,34 +41,42 @@ const MetricsTable = ({ metrics, processMap }: MetricsTableProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Pre-compute maximums for proportional bars
|
||||
const maxResponse = Math.max(...metrics.map(m => m.responseTime), 1);
|
||||
const maxTurnaround = Math.max(...metrics.map(m => m.turnaroundTime), 1);
|
||||
const maxWaiting = Math.max(...metrics.map(m => m.waitingTime), 1);
|
||||
const maxCpu = Math.max(...metrics.map(m => m.cpuTime), 1);
|
||||
const maxIo = Math.max(...metrics.map(m => m.ioTime), 1);
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs text-slate-300 border-collapse min-w-175">
|
||||
<table className="w-full text-xs text-slate-300 border-collapse min-w-[780px]">
|
||||
<thead>
|
||||
<tr className="text-[10px] text-slate-400 uppercase tracking-wider border-b border-slate-700">
|
||||
<tr className="text-[10px] text-slate-400 uppercase tracking-wider border-b border-slate-700 sticky top-0 bg-slate-950 z-10">
|
||||
<th className="text-left px-3 py-2 font-medium">Processo</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Arrival</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Start</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Finish</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Response</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Turnaround</th>
|
||||
<th className="text-right px-3 py-2 font-medium">Waiting</th>
|
||||
<th className="text-right px-3 py-2 font-medium">CPU Time</th>
|
||||
<th className="text-right px-3 py-2 font-medium">I/O Time</th>
|
||||
<th className="text-right px-3 py-2 font-medium text-amber-400">Response</th>
|
||||
<th className="text-right px-3 py-2 font-medium text-blue-400">Turnaround</th>
|
||||
<th className="text-right px-3 py-2 font-medium text-orange-400">Waiting</th>
|
||||
<th className="text-right px-3 py-2 font-medium text-emerald-400">CPU</th>
|
||||
<th className="text-right px-3 py-2 font-medium text-cyan-400">I/O</th>
|
||||
<th className="text-right px-3 py-2 font-medium">CPU%</th>
|
||||
<th className="text-center px-3 py-2 font-medium">Bound</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{metrics.map((m, i) => {
|
||||
const proc = processMap[m.processId];
|
||||
const name = proc?.name ?? m.processId;
|
||||
const proc = processMap[m.processId];
|
||||
const name = proc?.name ?? m.processId;
|
||||
const color = proc?.color ?? '#94a3b8';
|
||||
return (
|
||||
<tr
|
||||
key={m.processId}
|
||||
className={`border-b border-slate-800 hover:bg-slate-800/60 transition-colors ${i % 2 === 0 ? '' : 'bg-slate-900/30'}`}
|
||||
className={`border-b border-slate-800/80 hover:bg-slate-800/50 transition-colors ${i % 2 === 0 ? '' : 'bg-slate-900/20'}`}
|
||||
>
|
||||
{/* Process name */}
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
@@ -56,22 +84,55 @@ const MetricsTable = ({ metrics, processMap }: MetricsTableProps) => {
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
{proc?.pid !== undefined && (
|
||||
<span className="text-[11px] font-mono font-semibold text-slate-400 bg-slate-700 px-1.5 py-0.5 rounded shrink-0">
|
||||
<span className="text-[10px] font-mono font-semibold text-slate-400 bg-slate-700 px-1.5 py-0.5 rounded shrink-0">
|
||||
#{proc.pid}
|
||||
</span>
|
||||
)}
|
||||
<span className="font-medium text-slate-200">{name}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right px-3 py-2 font-mono">{m.arrivalTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono">{m.startTick}</td>
|
||||
<td className="text-right px-3 py-2 font-mono">{m.finishTick}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-amber-300">{m.responseTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-blue-300">{m.turnaroundTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-orange-300">{m.waitingTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-emerald-300">{m.cpuTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-cyan-300">{m.ioTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono">{m.cpuUtilization.toFixed(1)}%</td>
|
||||
|
||||
{/* Plain timestamps */}
|
||||
<td className="text-right px-3 py-2 font-mono text-slate-400">{m.arrivalTime}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-slate-400">{m.startTick}</td>
|
||||
<td className="text-right px-3 py-2 font-mono text-slate-400">{m.finishTick}</td>
|
||||
|
||||
{/* Cells with inline mini-bars */}
|
||||
<td className="px-3 py-2 text-amber-300">
|
||||
<MiniBar value={m.responseTime} max={maxResponse} color="#f59e0b" />
|
||||
</td>
|
||||
<td className="px-3 py-2 text-blue-300">
|
||||
<MiniBar value={m.turnaroundTime} max={maxTurnaround} color="#60a5fa" />
|
||||
</td>
|
||||
<td className="px-3 py-2 text-orange-300">
|
||||
<MiniBar value={m.waitingTime} max={maxWaiting} color="#fb923c" />
|
||||
</td>
|
||||
<td className="px-3 py-2 text-emerald-300">
|
||||
<MiniBar value={m.cpuTime} max={maxCpu} color="#34d399" />
|
||||
</td>
|
||||
<td className="px-3 py-2 text-cyan-300">
|
||||
<MiniBar value={m.ioTime} max={maxIo} color="#22d3ee" />
|
||||
</td>
|
||||
|
||||
{/* CPU utilization with a tiny arc/bar */}
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex items-center justify-end gap-1.5">
|
||||
<div className="w-10 h-1.5 bg-slate-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-300"
|
||||
style={{
|
||||
width: `${Math.min(100, m.cpuUtilization)}%`,
|
||||
background: `linear-gradient(90deg, #a78bfa, #60a5fa)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="font-mono text-slate-300 w-9 text-right">
|
||||
{m.cpuUtilization.toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* Bound badge */}
|
||||
<td className="text-center px-3 py-2">
|
||||
<Badge variant={boundVariant(m.boundType)}>{m.boundType}</Badge>
|
||||
</td>
|
||||
|
||||
Reference in new issue
Block a user