v0.3.0

Frontend Development

Guide for developing the FacilFlow frontend.

Technology Stack

Technology Purpose
React 19 UI framework with concurrent features
TypeScript Type safety
shadcn/ui Component library (built on Radix UI primitives)
Radix UI Accessible, unstyled UI primitives
TailwindCSS Utility-first CSS framework
@xyflow/react Visual pipeline builder (React Flow)
Zustand Lightweight client state management
TanStack Query Server state, caching, mutations
Socket.IO Client Real-time WebSocket communication
Webpack Build and dev server

Project Structure

src/renderer/
├── App.tsx                        # Root component, routing, providers
├── pages/                         # 28 page components
│   ├── LoginPage.tsx              # Firebase login
│   ├── SignupPage.tsx             # User registration
│   ├── ProfilePage.tsx            # User profile
│   ├── SettingsPage.tsx           # App settings
│   ├── ConnectorsPage.tsx         # Connector management
│   ├── ConnectorHubPage.tsx       # Connector type browser
│   ├── AirbyteCatalogPage.tsx     # PyAirbyte catalog
│   ├── TelegrafPluginsPage.tsx    # Telegraf plugin browser
│   ├── APIRepositoryPage.tsx      # API connector repository
│   ├── AgentsPage.tsx             # Edge agent list
│   ├── AgentDetailPage.tsx        # Agent details and metrics
│   ├── AgentConfigPage.tsx        # Agent config editor
│   ├── DataSourcesPage.tsx        # Data source browser
│   ├── ClustersPage.tsx           # Compute clusters
│   ├── PipelinesPage.tsx          # Pipeline list and builder
│   ├── MLModelsPage.tsx           # ML model registry
│   ├── SchemaRegistryPage.tsx     # Schema definitions
│   ├── DataExplorerPage.tsx       # SQL query interface
│   ├── DashboardPage.tsx          # Dashboard builder
│   ├── DataQualityPage.tsx        # Quality rules and scores
│   ├── LineageExplorerPage.tsx    # Lineage graph
│   ├── AlertsPage.tsx             # Alert rules
│   ├── SchedulerPage.tsx          # Pipeline scheduling
│   ├── StoragePage.tsx            # Storage providers
│   ├── ObjectStoreManagerPage.tsx # Object/file browser
│   ├── AuditLogsPage.tsx          # Activity audit trail
│   └── GovernancePage.tsx         # Data governance
├── components/
│   ├── ui/                        # shadcn/ui primitives
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── dialog.tsx
│   │   ├── table.tsx
│   │   └── ...
│   ├── pipeline/                  # Pipeline builder
│   │   ├── PipelineBuilder.tsx    # React Flow canvas + drag-drop
│   │   ├── NodePalette.tsx        # Draggable node types
│   │   └── nodes/                 # Custom node components
│   │       ├── SourceNode.tsx     # PI, OPC-UA, Modbus, MQTT
│   │       ├── ProcessorNode.tsx  # Filter, transform, aggregate
│   │       └── SinkNode.tsx       # InfluxDB, Kafka, S3
│   ├── agents/                    # Agent cards, status indicators
│   ├── dashboard/                 # Dashboard widgets
│   ├── data/                      # Data explorer, quality, lineage
│   ├── ml/                        # ML model cards, training UI
│   ├── storage/                   # Provider selector, bucket browser
│   └── layout/                    # Shell, sidebar, header
├── hooks/                         # Custom hooks
│   ├── usePipelines.ts
│   ├── useAgents.ts
│   ├── useClusters.ts
│   ├── useStorage.ts
│   ├── useML.ts
│   └── ...
├── services/                      # API client modules
│   ├── pipelineService.ts
│   ├── agentService.ts
│   ├── storageService.ts
│   ├── mlService.ts
│   ├── queryService.ts
│   └── ...
├── stores/                        # Zustand stores
│   ├── authStore.ts
│   ├── pipelineStore.ts
│   └── ...
└── lib/                           # Utilities, constants
    ├── utils.ts                   # cn() helper, formatters
    └── constants.ts

The app uses a 5-section sidebar:

Section Icon Pages
Connect Plug Connectors, Edge Agents, Data Sources
Infrastructure Server Clusters
Build Hammer Pipelines, ML Models, Schemas
Analyze BarChart Data Explorer, Dashboards, Data Quality, Lineage
Operate Settings Alerts, Scheduler, Storage, Audit Logs, Governance

Theme and Styling

FacilFlow uses TailwindCSS with CSS custom properties for theming. Dark mode by default.

CSS Variables

:root {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 166 82% 32%;         /* Teal #0D9488 */
  --primary-foreground: 210 40% 98%;
  --secondary: 217.2 32.6% 17.5%; /* Slate #1E293B */
  --card: 222.2 84% 4.9%;
  --border: 217.2 32.6% 17.5%;
  --ring: 166 82% 32%;
}

Using the cn() Helper

import { cn } from '@/lib/utils';

<div className={cn(
  "rounded-lg border bg-card p-4",
  isActive && "border-primary"
)} />

Component Example

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

function PipelineCard({ pipeline }) {
  return (
    <Card>
      <CardHeader>
        <CardTitle>{pipeline.name}</CardTitle>
      </CardHeader>
      <CardContent>
        <Button variant="default" onClick={handleStart}>
          Start Pipeline
        </Button>
      </CardContent>
    </Card>
  );
}

Pipeline Builder

The pipeline builder uses @xyflow/react with custom node types:

const nodeTypes = {
  source: SourceNode,
  processor: ProcessorNode,
  sink: SinkNode,
};

Adding a New Node Type

  1. Create component in components/pipeline/nodes/:
import { Handle, Position } from '@xyflow/react';

export function MyCustomNode({ data }) {
  return (
    <div className="rounded-lg border bg-card p-3 shadow-md">
      <Handle type="target" position={Position.Left} />
      <div className="text-sm font-medium">{data.label}</div>
      <Handle type="source" position={Position.Right} />
    </div>
  );
}
  1. Register in nodeTypes object in PipelineBuilder.tsx:
const nodeTypes = {
  source: SourceNode,
  processor: ProcessorNode,
  sink: SinkNode,
  myCustom: MyCustomNode,  // Add here
};
  1. Add to NodePalette for drag-and-drop:
<PaletteItem type="myCustom" label="My Custom Node" icon={<MyIcon />} />

State Management

Zustand (Client State)

Used for UI state, auth, and local preferences:

import { create } from 'zustand';

interface PipelineStore {
  selectedNodeId: string | null;
  setSelectedNode: (id: string | null) => void;
}

export const usePipelineStore = create<PipelineStore>((set) => ({
  selectedNodeId: null,
  setSelectedNode: (id) => set({ selectedNodeId: id }),
}));

TanStack Query (Server State)

Used for all API data fetching, caching, and mutations:

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function usePipelines() {
  return useQuery({
    queryKey: ['pipelines'],
    queryFn: () => pipelineService.list(),
  });
}

export function useStartPipeline() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => pipelineService.start(id),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['pipelines'] }),
  });
}

Real-time Updates

Socket.IO client connects for live data:

import { io } from 'socket.io-client';

const socket = io('/pipelines');

socket.on('pipeline:status', (data) => {
  queryClient.setQueryData(['pipelines', data.id], (old) => ({
    ...old,
    status: data.status,
  }));
});

Running Tests

npm test                          # Run all tests (Jest)
npm test -- --watch               # Watch mode
npm test -- path/to/file.test.tsx # Single file

Build

npm run dev       # Webpack dev server @ localhost:3001
npm run build     # Production build
npm run lint      # ESLint (src/**/*.ts,tsx)
esc