Components

This page documents the reusable React components in frontend/src/components/.


Layout Components

Location:

src/components/layout/

Components that define the overall page structure.

Layout.tsx

Main layout wrapper that provides the header, sidebar, and content area.

import { Layout } from '@/components/layout/Layout'

// Used in App.tsx as the root route element
<Route path="/" element={<Layout />}>
  <Route index element={<RunListPage />} />
  {/* Child routes render in the Outlet */}
</Route>

Structure:

┌─────────────────────────────────────────────────┐
│                    Header                        │
├──────────┬──────────────────────────────────────┤
│          │                                       │
│ Sidebar  │              Content                  │
│          │             (Outlet)                  │
│          │                                       │
└──────────┴──────────────────────────────────────┘

Header.tsx

Top navigation bar with logo, title, and global actions.

<header className="h-14 border-b bg-background flex items-center px-4">
  <Logo />
  <span className="font-semibold">FUSION</span>
  <div className="ml-auto">
    {/* Theme toggle, user menu, etc. */}
  </div>
</header>

Run Components

Location:

src/components/runs/

Components for displaying and managing simulation runs.

RunCard.tsx

Card component displaying a single run in the list view.

interface RunCardProps {
  run: Run
  onClick?: () => void
}

function RunCard({ run, onClick }: RunCardProps) {
  return (
    <div className="border rounded-lg p-4 cursor-pointer hover:border-primary">
      <div className="flex items-center justify-between">
        <span className="font-medium">{run.name || run.id}</span>
        <RunStatusBadge status={run.status} />
      </div>
      <div className="text-sm text-muted-foreground">
        Created: {formatDate(run.created_at)}
      </div>
    </div>
  )
}

RunStatusBadge.tsx

Status indicator badge with appropriate colors.

interface RunStatusBadgeProps {
  status: RunStatus
}

const statusColors = {
  PENDING: 'bg-yellow-100 text-yellow-800',
  RUNNING: 'bg-blue-100 text-blue-800',
  COMPLETED: 'bg-green-100 text-green-800',
  FAILED: 'bg-red-100 text-red-800',
  CANCELLED: 'bg-gray-100 text-gray-800',
}

function RunStatusBadge({ status }: RunStatusBadgeProps) {
  return (
    <span className={`px-2 py-1 rounded-full text-xs ${statusColors[status]}`}>
      {status}
    </span>
  )
}

ProgressChart.tsx

Visual progress indicator for running simulations.

interface ProgressChartProps {
  progress: RunProgress | null
}

function ProgressChart({ progress }: ProgressChartProps) {
  if (!progress) return null

  const percent = progress.percent_complete ?? 0

  return (
    <div className="space-y-2">
      <div className="flex justify-between text-sm">
        <span>Erlang {progress.current_erlang}</span>
        <span>{percent.toFixed(1)}%</span>
      </div>
      <div className="h-2 bg-gray-200 rounded">
        <div
          className="h-full bg-primary rounded"
          style={{ width: `${percent}%` }}
        />
      </div>
    </div>
  )
}

Topology Components

Location:

src/components/topology/

Components for network visualization.

NetworkGraph.tsx

Interactive network graph using D3.js force-directed layout.

interface NetworkGraphProps {
  topology: TopologyResponse
  onNodeClick?: (nodeId: number) => void
  selectedNode?: number | null
}

function NetworkGraph({ topology, onNodeClick, selectedNode }: NetworkGraphProps) {
  const svgRef = useRef<SVGSVGElement>(null)

  useEffect(() => {
    // D3 force simulation setup
    const simulation = d3.forceSimulation(topology.nodes)
      .force('link', d3.forceLink(topology.links))
      .force('charge', d3.forceManyBody())
      .force('center', d3.forceCenter(width / 2, height / 2))

    // Draw nodes and links...
  }, [topology])

  return <svg ref={svgRef} className="w-full h-full" />
}

Features:

  • Force-directed layout with draggable nodes

  • Click to select nodes

  • Zoom and pan controls

  • Link highlighting for selected node

  • Optional utilization coloring

UtilizationLegend.tsx

Color legend for link utilization visualization.

function UtilizationLegend() {
  return (
    <div className="flex items-center gap-2">
      <span className="text-sm">Utilization:</span>
      <div className="flex">
        <div className="w-4 h-4 bg-green-500" title="0-25%" />
        <div className="w-4 h-4 bg-yellow-500" title="25-50%" />
        <div className="w-4 h-4 bg-orange-500" title="50-75%" />
        <div className="w-4 h-4 bg-red-500" title="75-100%" />
      </div>
    </div>
  )
}

Artifact Components

Location:

src/components/artifacts/

Components for browsing simulation output files.

ArtifactBrowser.tsx

File browser for simulation artifacts with folder navigation.

interface ArtifactBrowserProps {
  runId: string
}

function ArtifactBrowser({ runId }: ArtifactBrowserProps) {
  const [currentPath, setCurrentPath] = useState('')

  const { data: artifacts } = useQuery({
    queryKey: ['artifacts', runId, currentPath],
    queryFn: () => artifactsApi.list(runId, currentPath),
  })

  return (
    <div>
      <Breadcrumb path={currentPath} onNavigate={setCurrentPath} />
      <FileList
        items={artifacts?.items ?? []}
        onFolderClick={(name) => setCurrentPath(`${currentPath}/${name}`)}
        onFileClick={(item) => window.open(item.downloadUrl)}
      />
    </div>
  )
}

UI Components

Location:

src/components/ui/

Generic reusable UI components (buttons, inputs, cards, etc.).

These typically follow the shadcn/ui pattern - unstyled, composable components with Tailwind classes.

Common Components

  • Button - Primary, secondary, outline variants

  • Input - Text input with label and error state

  • Select - Dropdown selection

  • Card - Container with border and shadow

  • Badge - Small status indicators

  • Tabs - Tab navigation component

  • Dialog - Modal dialog

  • Toast - Notification messages

Example Usage

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Card, CardHeader, CardContent } from '@/components/ui/card'

function MyComponent() {
  return (
    <Card>
      <CardHeader>
        <h2>Form Title</h2>
      </CardHeader>
      <CardContent>
        <Input placeholder="Enter name" />
        <Button variant="primary">Submit</Button>
      </CardContent>
    </Card>
  )
}