Files
crypto_trader/docs/developer/ui_development.md

347 lines
7.9 KiB
Markdown
Raw Normal View History

# Frontend Development Guide
This guide explains how to develop and extend frontend components in Crypto Trader.
## Tech Stack
- **React 18** - UI framework
- **TypeScript** - Type safety
- **Material-UI (MUI)** - Component library
- **React Query** - Data fetching and caching
- **React Router** - Routing
- **Vite** - Build tool
## Project Structure
```
frontend/
├── src/
│ ├── pages/ # Page components
│ ├── components/ # Reusable components
│ ├── api/ # API client functions
│ ├── hooks/ # Custom React hooks
│ ├── types/ # TypeScript types
│ └── App.tsx # Main app component
├── public/ # Static assets
└── package.json # Dependencies
```
## Development Setup
```bash
cd frontend
npm install
npm run dev
```
Access at: http://localhost:3000
## Creating a New Page
1. Create page component in `src/pages/`:
```typescript
// src/pages/MyPage.tsx
import { Box, Typography } from '@mui/material';
export function MyPage() {
return (
<Box>
<Typography variant="h4">My Page</Typography>
</Box>
);
}
```
2. Add route in `src/App.tsx`:
```typescript
import { MyPage } from './pages/MyPage';
<Route path="/my-page" element={<MyPage />} />
```
## API Integration
### Creating API Functions
```typescript
// src/api/myService.ts
import { apiClient } from './client';
export const myServiceApi = {
async getData() {
const response = await apiClient.get('/api/my-service/data');
return response.data;
},
async createItem(data: CreateItemDto) {
const response = await apiClient.post('/api/my-service/items', data);
return response.data;
}
};
```
### Using React Query
```typescript
import { useQuery, useMutation } from '@tanstack/react-query';
import { myServiceApi } from '@/api/myService';
function MyComponent() {
// Fetch data
const { data, isLoading, error } = useQuery({
queryKey: ['myData'],
queryFn: myServiceApi.getData
});
// Mutate data
const mutation = useMutation({
mutationFn: myServiceApi.createItem,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['myData'] });
}
});
return (
<div>
{isLoading && <Loading />}
{data && <DataDisplay data={data} />}
<button onClick={() => mutation.mutate(formData)}>Create</button>
</div>
);
}
```
## Creating Reusable Components
```typescript
// src/components/DataTable.tsx
import { Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
interface DataTableProps<T> {
data: T[];
columns: Column<T>[];
}
export function DataTable<T>({ data, columns }: DataTableProps<T>) {
return (
<Table>
<TableHead>
<TableRow>
{columns.map(col => (
<TableCell key={col.key}>{col.header}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{data.map((row, idx) => (
<TableRow key={idx}>
{columns.map(col => (
<TableCell key={col.key}>{col.render(row)}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
}
```
## Custom Hooks
```typescript
// src/hooks/useOrders.ts
import { useQuery, useMutation } from '@tanstack/react-query';
import { tradingApi } from '@/api/trading';
export function useOrders() {
const { data: orders, isLoading, error } = useQuery({
queryKey: ['orders'],
queryFn: () => tradingApi.getOrders()
});
const placeOrder = useMutation({
mutationFn: tradingApi.placeOrder,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['orders'] });
}
});
return {
orders: orders ?? [],
isLoading,
error,
placeOrder: placeOrder.mutate
};
}
```
## WebSocket Integration
The application uses an enhanced WebSocket system with message type handling:
```typescript
// Using the WebSocket context
import { useWebSocketContext } from '@/components/WebSocketProvider';
import { useRealtimeData } from '@/hooks/useRealtimeData';
function MyComponent() {
const { isConnected, lastMessage, subscribe } = useWebSocketContext();
// Subscribe to specific message types
useEffect(() => {
const unsubscribe = subscribe('order_update', (message) => {
// Handle order update
console.log('Order updated:', message);
});
return unsubscribe;
}, [subscribe]);
// Or use the real-time data hook for automatic query invalidation
useRealtimeData(); // Automatically handles common message types
}
```
### Message Types
The WebSocket supports these message types:
- `order_update`: Order status changes
- `position_update`: Position changes
- `price_update`: Price updates
- `alert_triggered`: Alert notifications
- `strategy_signal`: Strategy signal notifications
- `system_event`: System events and errors
## Form Handling
```typescript
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const schema = yup.object({
symbol: yup.string().required(),
quantity: yup.number().positive().required()
});
function OrderForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(schema)
});
const onSubmit = async (data) => {
await tradingApi.placeOrder(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
{...register('symbol')}
error={!!errors.symbol}
helperText={errors.symbol?.message}
/>
<Button type="submit">Place Order</Button>
</form>
);
}
```
## Error Handling
The application uses a centralized Snackbar context for error handling:
```typescript
import { useSnackbar } from '@/contexts/SnackbarContext';
import ErrorDisplay from '@/components/ErrorDisplay';
function MyComponent() {
const { showError, showSuccess, showWarning, showInfo } = useSnackbar();
const handleAction = async () => {
try {
await apiCall();
showSuccess('Action completed successfully');
} catch (err) {
showError(err instanceof Error ? err.message : 'An error occurred');
}
};
// For complex error display with retry
return (
<>
{error && (
<ErrorDisplay
error={error}
title="Operation Failed"
onRetry={handleAction}
/>
)}
</>
);
}
```
### Available Components
- `ErrorDisplay`: Enhanced error display with retry functionality
- `LoadingSkeleton`: Loading placeholders for tables, cards, lists
- `ProgressOverlay`: Overlay with progress indicator
- `StatusIndicator`: Connection status indicators
- `DataFreshness`: Data freshness timestamps
- `HelpTooltip`: Contextual help tooltips
- `InfoCard`: Collapsible information cards
- `OperationsPanel`: Panel showing running operations
## Styling
### Material-UI Theming
```typescript
// src/theme.ts
import { createTheme } from '@mui/material/styles';
export const theme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#1976d2'
}
}
});
```
### Component Styling
Use Material-UI's `sx` prop for inline styles:
```typescript
<Box
sx={{
padding: 2,
backgroundColor: 'background.paper',
borderRadius: 1
}}
>
Content
</Box>
```
## Testing
See [Testing Guide](./testing.md) for frontend testing strategies.
## Best Practices
1. **Type Safety**: Always use TypeScript types
2. **Component Composition**: Prefer composition over inheritance
3. **Custom Hooks**: Extract reusable logic into hooks
4. **Error Handling**: Handle errors gracefully with user feedback
5. **Loading States**: Always show loading indicators
6. **Accessibility**: Use semantic HTML and ARIA labels
7. **Performance**: Use React.memo, useMemo, useCallback appropriately
8. **Code Splitting**: Use lazy loading for large components
9. **Form Validation**: Validate on both client and server
10. **Consistent Patterns**: Follow established patterns in the codebase