Ship live alerts, pool status, AI monitor, remote agent commands, build manager, and uninstall flow; wire Calibrate settings (WS ping, pool traffic log, retention limits) at runtime and exclude server/data from git.
138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
import { Link } from 'react-router-dom';
|
|
import NeonCard from '../NeonCard/NeonCard';
|
|
import {
|
|
FORGE_VS_CALIBRATE,
|
|
PIPELINE_STEPS,
|
|
ROADMAP_FEATURES,
|
|
} from '../../help/cheatSheetContent';
|
|
import './VisualComponents.css';
|
|
|
|
interface PipelineFlowProps {
|
|
activeStep?: string;
|
|
compact?: boolean;
|
|
}
|
|
|
|
export function PipelineFlow({ activeStep, compact }: PipelineFlowProps) {
|
|
return (
|
|
<div className={`pipeline-flow ${compact ? 'pipeline-compact' : ''}`}>
|
|
{PIPELINE_STEPS.map((step, i) => (
|
|
<div key={step.id} className="pipeline-step-wrap">
|
|
<div className={`pipeline-step ${activeStep === step.id ? 'pipeline-active' : ''}`}>
|
|
<div className="pipeline-icon">{step.icon}</div>
|
|
<div className="pipeline-text">
|
|
<strong>{step.title}</strong>
|
|
<span>{step.subtitle}</span>
|
|
</div>
|
|
</div>
|
|
{i < PIPELINE_STEPS.length - 1 && <div className="pipeline-connector" aria-hidden />}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface FleetPipelineStatusProps {
|
|
hasBuilds: boolean;
|
|
agentCount: number;
|
|
onlineCount: number;
|
|
hasHashrate: boolean;
|
|
hasShares: boolean;
|
|
}
|
|
|
|
export function FleetPipelineStatus({
|
|
hasBuilds,
|
|
agentCount,
|
|
onlineCount,
|
|
hasHashrate,
|
|
hasShares,
|
|
}: FleetPipelineStatusProps) {
|
|
const steps = [
|
|
{ id: 'forge', label: 'Forged', ok: hasBuilds, hint: 'At least one build exists' },
|
|
{ id: 'deploy', label: 'Deployed', ok: agentCount > 0, hint: 'Agent registered on dashboard' },
|
|
{ id: 'connect', label: 'Online', ok: onlineCount > 0, hint: `${onlineCount} node(s) live` },
|
|
{ id: 'mine', label: 'Hashing', ok: hasHashrate, hint: 'Fleet hashrate > 0' },
|
|
{ id: 'shares', label: 'Shares', ok: hasShares, hint: 'Shares submitted to pool' },
|
|
];
|
|
|
|
return (
|
|
<div className="fleet-pipeline-status">
|
|
{steps.map((s, i) => (
|
|
<div key={s.id} className="fleet-pipeline-item">
|
|
<div className={`fleet-pipeline-node ${s.ok ? 'ok' : 'pending'}`} title={s.hint}>
|
|
<span className="fleet-pipeline-dot" />
|
|
<span className="fleet-pipeline-label font-tech">{s.label}</span>
|
|
</div>
|
|
{i < steps.length - 1 && <div className={`fleet-pipeline-line ${s.ok ? 'lit' : ''}`} />}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface ActivityPulseProps {
|
|
items: { id: string; label: string; ok: boolean; time?: string }[];
|
|
}
|
|
|
|
export function ActivityPulse({ items }: ActivityPulseProps) {
|
|
if (items.length === 0) {
|
|
return <p className="activity-empty font-tech">Awaiting fleet activity…</p>;
|
|
}
|
|
return (
|
|
<div className="activity-pulse">
|
|
{items.slice(0, 12).map((item) => (
|
|
<div key={item.id} className={`activity-blip ${item.ok ? 'ok' : 'bad'}`} title={item.time || item.label}>
|
|
<span className="activity-blip-core" />
|
|
<span className="activity-blip-label">{item.label}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface ForgeCalibrateCompareProps {
|
|
compact?: boolean;
|
|
}
|
|
|
|
export function ForgeCalibrateCompare({ compact }: ForgeCalibrateCompareProps) {
|
|
return (
|
|
<div className={`compare-grid ${compact ? 'compare-compact' : ''}`}>
|
|
<NeonCard accent="cyan" className="compare-card">
|
|
<h4>{FORGE_VS_CALIBRATE.forge.title}</h4>
|
|
<ul>
|
|
{FORGE_VS_CALIBRATE.forge.items.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ul>
|
|
{!compact && (
|
|
<Link to="/forge" className="btn btn-outline btn-sm">Go to Forge</Link>
|
|
)}
|
|
</NeonCard>
|
|
<NeonCard accent="amber" className="compare-card">
|
|
<h4>{FORGE_VS_CALIBRATE.calibrate.title}</h4>
|
|
<ul>
|
|
{FORGE_VS_CALIBRATE.calibrate.items.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ul>
|
|
{!compact && (
|
|
<Link to="/settings" className="btn btn-outline btn-sm">Go to Calibrate</Link>
|
|
)}
|
|
</NeonCard>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function RoadmapGrid() {
|
|
return (
|
|
<div className="roadmap-grid">
|
|
{ROADMAP_FEATURES.map((f) => (
|
|
<div key={f.title} className={`roadmap-card roadmap-${f.priority}`}>
|
|
<span className={`roadmap-priority font-tech`}>{f.priority}</span>
|
|
<h4>{f.title}</h4>
|
|
<p>{f.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|