# Frontend Overview
**Comprehensive guide to the Valtronics frontend application**
---
## Overview
The Valtronics frontend is a modern, responsive web application built with React that provides an intuitive interface for monitoring and managing IoT devices. The frontend features real-time data visualization, interactive dashboards, and a sci-fi themed user interface.
---
## Technology Stack
### Core Technologies
- **React 18+**: Modern React with hooks and concurrent features
- **TypeScript**: Type-safe JavaScript development
- **Redux Toolkit**: State management and data flow
- **React Router**: Client-side routing
- **Material-UI**: Component library and design system
### Development Tools
- **Vite**: Fast build tool and development server
- **ESLint**: Code quality and linting
- **Prettier**: Code formatting
- **Jest**: Unit testing framework
- **React Testing Library**: Component testing
### Styling and Design
- **CSS Modules**: Scoped CSS styling
- **Custom Themes**: Sci-fi themed design system
- **Responsive Design**: Mobile-first approach
- **CSS Variables**: Dynamic theming support
### Communication
- **Axios**: HTTP client for API requests
- **WebSocket Client**: Real-time data streaming
- **Socket.IO**: WebSocket library integration
---
## Project Structure
```
frontend/
├── public/ # Static assets
│ ├── index.html # Main HTML template
│ ├── favicon.ico # Application favicon
│ └── manifest.json # PWA manifest
├── src/ # Source code
│ ├── components/ # React components
│ │ ├── common/ # Reusable components
│ │ ├── dashboard/ # Dashboard components
│ │ ├── devices/ # Device management
│ │ ├── alerts/ # Alert management
│ │ └── analytics/ # Analytics components
│ ├── pages/ # Page components
│ │ ├── Dashboard.js # Main dashboard
│ │ ├── Devices.js # Device management page
│ │ ├── Alerts.js # Alerts page
│ │ └── Analytics.js # Analytics page
│ ├── services/ # API and business logic
│ │ ├── api.js # API client
│ │ ├── websocket.js # WebSocket client
│ │ ├── auth.js # Authentication
│ │ └── utils.js # Utility functions
│ ├── store/ # Redux store
│ │ ├── slices/ # Redux slices
│ │ └── index.js # Store configuration
│ ├── styles/ # CSS styles
│ │ ├── globals.css # Global styles
│ │ ├── variables.css # CSS variables
│ │ └── components/ # Component styles
│ ├── hooks/ # Custom React hooks
│ │ ├── useAuth.js # Authentication hook
│ │ ├── useWebSocket.js # WebSocket hook
│ │ └── useApi.js # API hook
│ ├── utils/ # Utility functions
│ │ ├── constants.js # Application constants
│ │ ├── helpers.js # Helper functions
│ │ └── validators.js # Input validators
│ ├── App.js # Main application component
│ ├── index.js # Application entry point
│ └── setupTests.js # Test configuration
├── package.json # Dependencies and scripts
├── vite.config.js # Vite configuration
├── .env.example # Environment variables template
└── README.md # Frontend documentation
```
---
## Component Architecture
### Component Hierarchy
```mermaid
graph TD
App --> Router
Router --> Layout
Layout --> Header
Layout --> Sidebar
Layout --> MainContent
MainContent --> Dashboard
MainContent --> Devices
MainContent --> Alerts
MainContent --> Analytics
Dashboard --> DeviceGrid
Dashboard --> MetricCards
Dashboard --> TelemetryChart
Dashboard --> AlertPanel
Devices --> DeviceList
Devices --> DeviceForm
Devices --> DeviceDetails
Alerts --> AlertList
Alerts --> AlertForm
Alerts --> AlertRules
Analytics --> AnalyticsCharts
Analytics --> Reports
Analytics --> Trends
```
### Component Categories
#### 1. Layout Components
- **Layout**: Main application layout
- **Header**: Application header with navigation
- **Sidebar**: Navigation sidebar
- **Footer**: Application footer
#### 2. Common Components
- **LoadingSpinner**: Loading indicator
- **ErrorBoundary**: Error handling component
- **Modal**: Modal dialog component
- **Tooltip**: Tooltip component
- **Badge**: Status badge component
#### 3. Dashboard Components
- **DeviceGrid**: Grid display of devices
- **MetricCards**: Key performance indicators
- **TelemetryChart**: Real-time data visualization
- **AlertPanel**: Alert notification panel
- **SystemStatus**: System health indicator
#### 4. Device Components
- **DeviceList**: List of devices
- **DeviceCard**: Device card display
- **DeviceForm**: Device creation/editing form
- **DeviceDetails**: Detailed device information
- **DeviceStatus**: Device status indicator
#### 5. Alert Components
- **AlertList**: List of alerts
- **AlertCard**: Alert card display
- **AlertForm**: Alert creation form
- **AlertRules**: Alert rule management
- **SeverityIndicator**: Alert severity indicator
#### 6. Analytics Components
- **AnalyticsCharts**: Analytics visualization
- **Reports**: Report generation
- **Trends**: Trend analysis
- **Metrics**: Performance metrics
---
## State Management
### Redux Store Structure
```javascript
{
auth: {
user: null,
token: null,
isAuthenticated: false,
loading: false,
error: null
},
devices: {
list: [],
selected: null,
loading: false,
error: null,
filters: {
status: null,
type: null,
location: null
}
},
telemetry: {
data: {},
realTime: {},
loading: false,
error: null
},
alerts: {
list: [],
unreadCount: 0,
loading: false,
error: null,
filters: {
severity: null,
status: null
}
},
analytics: {
systemStats: null,
devicePerformance: null,
trends: null,
loading: false,
error: null
},
ui: {
sidebarOpen: true,
theme: 'dark',
notifications: [],
loading: false
}
}
```
### Redux Slices
- **authSlice**: Authentication state management
- **devicesSlice**: Device data management
- **telemetrySlice**: Telemetry data management
- **alertsSlice**: Alert management
- **analyticsSlice**: Analytics data management
- **uiSlice**: UI state management
---
## API Integration
### API Client Configuration
```javascript
// src/services/api.js
import axios from 'axios';
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor for authentication
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for error handling
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Handle authentication error
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default apiClient;
```
### API Services
```javascript
// src/services/deviceService.js
import apiClient from './api';
export const deviceService = {
// Get all devices
getDevices: async (params = {}) => {
const response = await apiClient.get('/api/v1/devices/', { params });
return response.data;
},
// Create device
createDevice: async (deviceData) => {
const response = await apiClient.post('/api/v1/devices/', deviceData);
return response.data;
},
// Update device
updateDevice: async (id, deviceData) => {
const response = await apiClient.put(`/api/v1/devices/${id}`, deviceData);
return response.data;
},
// Delete device
deleteDevice: async (id) => {
const response = await apiClient.delete(`/api/v1/devices/${id}`);
return response.data;
},
// Get device statistics
getDeviceStats: async () => {
const response = await apiClient.get('/api/v1/devices/stats');
return response.data;
}
};
```
---
## WebSocket Integration
### WebSocket Client
```javascript
// src/services/websocket.js
class WebSocketClient {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectInterval = 1000;
this.listeners = {};
}
connect(token) {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
this.authenticate(token);
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = () => {
console.log('WebSocket disconnected');
this.reconnect(token);
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
} catch (error) {
console.error('Failed to connect WebSocket:', error);
}
}
authenticate(token) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'auth',
token: token
}));
}
}
subscribe(channel, callback) {
if (!this.listeners[channel]) {
this.listeners[channel] = [];
}
this.listeners[channel].push(callback);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'subscribe',
channel: channel
}));
}
}
unsubscribe(channel) {
if (this.listeners[channel]) {
delete this.listeners[channel];
}
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'unsubscribe',
channel: channel
}));
}
}
handleMessage(data) {
const { type, channel, payload } = data;
if (this.listeners[channel]) {
this.listeners[channel].forEach(callback => {
callback(payload);
});
}
}
reconnect(token) {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect(token);
}, this.reconnectInterval * this.reconnectAttempts);
}
}
disconnect() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}
export default WebSocketClient;
```
### WebSocket Hook
```javascript
// src/hooks/useWebSocket.js
import { useEffect, useRef, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import WebSocketClient from '../services/websocket';
import { updateTelemetry, addAlert, updateDeviceStatus } from '../store/slices';
export const useWebSocket = () => {
const dispatch = useDispatch();
const { token } = useSelector(state => state.auth);
const wsClientRef = useRef(null);
const connectWebSocket = useCallback(() => {
if (!token) return;
const wsUrl = process.env.REACT_APP_WS_URL || 'ws://localhost:8000/ws';
wsClientRef.current = new WebSocketClient(wsUrl);
wsClientRef.current.connect(token);
// Subscribe to channels
wsClientRef.current.subscribe('telemetry', (data) => {
dispatch(updateTelemetry(data));
});
wsClientRef.current.subscribe('alerts', (data) => {
dispatch(addAlert(data));
});
wsClientRef.current.subscribe('device_status', (data) => {
dispatch(updateDeviceStatus(data));
});
}, [token, dispatch]);
const disconnectWebSocket = useCallback(() => {
if (wsClientRef.current) {
wsClientRef.current.disconnect();
wsClientRef.current = null;
}
}, []);
useEffect(() => {
connectWebSocket();
return () => {
disconnectWebSocket();
};
}, [connectWebSocket, disconnectWebSocket]);
return {
connect: connectWebSocket,
disconnect: disconnectWebSocket
};
};
```
---
## Theming and Styling
### CSS Variables
```css
/* src/styles/variables.css */
:root {
/* Colors */
--primary-color: #00ffff;
--secondary-color: #ff00ff;
--accent-color: #00ff00;
--gold-color: #ffd700;
--dark-bg: #0a0a0f;
--light-bg: #1a1a2e;
--text-primary: #ffffff;
--text-secondary: #b8b8c8;
/* Gradients */
--gradient-primary: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
--gradient-gold: linear-gradient(135deg, var(--gold-color), var(--primary-color));
/* Shadows */
--neon-glow: 0 0 20px rgba(0, 255, 255, 0.5);
--gold-glow: 0 0 30px rgba(255, 215, 0, 0.6);
/* Typography */
--font-primary: 'Orbitron', monospace;
--font-secondary: 'Space Mono', monospace;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Border Radius */
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 12px;
--border-radius-xl: 20px;
}
```
### Theme Provider
```javascript
// src/components/ThemeProvider.js
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('dark');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'dark' ? 'light' : 'dark');
};
const value = {
theme,
toggleTheme,
isDark: theme === 'dark'
};
return (