|
|
# CLAUDE.md
|
|
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
|
|
## System Overview
|
|
|
|
|
|
ScalesApp is a three-tier weighing scale management system with real-time data synchronization:
|
|
|
- **Django Backend** (REST API + Server-Sent Events) on port 8000
|
|
|
- **React Frontend** (SPA) on port 3000
|
|
|
- **Serial Bridge** (Flask SSE server) on port 5000 for COM port reading
|
|
|
|
|
|
## Development Commands
|
|
|
|
|
|
### Backend (Django)
|
|
|
|
|
|
```bash
|
|
|
cd backend
|
|
|
python -m venv venv
|
|
|
venv\Scripts\activate # Windows: venv\Scripts\activate | Unix: source venv/bin/activate
|
|
|
pip install -r requirements.txt
|
|
|
|
|
|
# Database operations
|
|
|
python manage.py makemigrations
|
|
|
python manage.py migrate
|
|
|
python manage.py createsuperuser
|
|
|
|
|
|
# Run server
|
|
|
python manage.py runserver # Development (WSGI)
|
|
|
uvicorn scalesapp.asgi:application --reload # Development (ASGI - for SSE)
|
|
|
|
|
|
# Testing (when implemented)
|
|
|
python manage.py test
|
|
|
python manage.py test api.tests.test_models # Single test file
|
|
|
```
|
|
|
|
|
|
### Frontend (React)
|
|
|
|
|
|
```bash
|
|
|
cd frontend
|
|
|
npm install
|
|
|
npm start # Development server
|
|
|
npm run build # Production build
|
|
|
npm test # Run tests
|
|
|
```
|
|
|
|
|
|
### Serial Bridge
|
|
|
|
|
|
```bash
|
|
|
cd serial_bridge
|
|
|
python -m venv venv
|
|
|
venv\Scripts\activate
|
|
|
pip install -r requirements.txt
|
|
|
python app.py # Run Flask SSE server
|
|
|
|
|
|
# Build standalone executable
|
|
|
pyinstaller serial_bridge.spec
|
|
|
```
|
|
|
|
|
|
### Testing COM Port (requires com0com)
|
|
|
|
|
|
```bash
|
|
|
cd test_comport_writer
|
|
|
python test_writer.py # Sends test data to virtual COM port
|
|
|
```
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
### Django Backend Structure
|
|
|
|
|
|
**Three Django Apps:**
|
|
|
- **`api/`** - Core API, user management, COM port readings, report templates
|
|
|
- **`vehicles/`** - Vehicle weighing management with dynamic nomenclature data
|
|
|
- **`nomenclatures/`** - Runtime-configurable schema system (lookup tables and custom fields)
|
|
|
|
|
|
**Custom User Model:** `api.User` (extends AbstractUser)
|
|
|
- Fields: `role` (employee/viewer), `is_admin` (boolean)
|
|
|
- All authentication requires JWT tokens
|
|
|
|
|
|
**Database:** PostgreSQL (production), SQLite (development fallback)
|
|
|
|
|
|
**Key Models:**
|
|
|
- `User` - Custom user with roles
|
|
|
- `ComPortReading` - Serial port data log
|
|
|
- `Report` - Report templates (JSONField stores element definitions)
|
|
|
- `Vehicle` - Core weighing entity (vehicle_number, tare, gross, with user/timestamp tracking)
|
|
|
- `VehicleExtra` - OneToOne with Vehicle, JSONField for dynamic nomenclature data
|
|
|
- `Nomenclature` - Configurable data types (applies_to: vehicle/container, kind: lookup/field)
|
|
|
- `NomenclatureField` - Field definitions (text, number, bool, choice types)
|
|
|
- `NomenclatureEntry` - Data entries for lookup tables
|
|
|
|
|
|
**ViewSet Pattern:** All views use DRF's ModelViewSet with custom actions via `@action` decorator
|
|
|
- Examples: `@action(detail=True, methods=['post']) def set_tare()`
|
|
|
|
|
|
### Real-Time Communication (SSE)
|
|
|
|
|
|
**Implementation:** `backend/scalesapp/sse.py`
|
|
|
- ASGI-based async SSE endpoint: `/sse-connect/`
|
|
|
- JWT authentication via query parameter or Authorization header
|
|
|
- Global `updates_queue` with 100-event buffer
|
|
|
- Event ID tracking for reconnection/replay of missed events
|
|
|
- `sse_broadcast_update(operation, model, data)` called from views after mutations
|
|
|
- Auto-reconnect with Last-Event-ID header support
|
|
|
|
|
|
**Usage in Views:**
|
|
|
```python
|
|
|
from scalesapp.sse import sse_broadcast_update
|
|
|
|
|
|
def create(self, request):
|
|
|
# ... create logic ...
|
|
|
sse_broadcast_update('insert', 'vehicle', serializer.data)
|
|
|
```
|
|
|
|
|
|
### Nomenclature System (Dynamic Schema)
|
|
|
|
|
|
**Purpose:** Runtime-configurable extra fields without database migrations
|
|
|
|
|
|
**Flow:**
|
|
|
1. Admin creates `Nomenclature` (e.g., "CARGO_TYPE", kind=lookup, applies_to=vehicle)
|
|
|
2. Defines `NomenclatureField`s (e.g., name: text, code: text)
|
|
|
3. Creates `NomenclatureEntry` records (e.g., "Coal", "Iron Ore")
|
|
|
4. Frontend loads nomenclatures on mount via REST API
|
|
|
5. User selects from dropdown → stores entry ID in `vehicle.extra.data.CARGO_TYPE` (JSONField)
|
|
|
6. Validation in `VehicleExtraSerializer` enforces type constraints
|
|
|
|
|
|
### React Frontend Structure
|
|
|
|
|
|
**Context Providers (State Management):**
|
|
|
- **`AuthContext`** - JWT token management, auto-refresh every 14 minutes, axios interceptor for 401 handling
|
|
|
- **`NomenclatureContext`** - Centralized data cache with SSE subscription to Django `/sse-connect/`
|
|
|
- Real-time updates (insert/update operations)
|
|
|
- Maintains sorted arrays for all nomenclatures
|
|
|
- Auto-reconnect on connection loss
|
|
|
- Parallel loading of all nomenclature endpoints on mount
|
|
|
- **`DataContext`** - Report editor data fetching (for preview mode)
|
|
|
- **`BandContext`** - Report editor band iteration state
|
|
|
|
|
|
**Key Components:**
|
|
|
- **`Main.jsx`** - Primary weighing interface
|
|
|
- Left panel: Vehicle list with search
|
|
|
- Right panel: Selected vehicle details with dynamic nomenclature fields
|
|
|
- Real-time weight display from serial bridge
|
|
|
- Tare/Gross weight setting with user/timestamp tracking
|
|
|
- SSE-based automatic UI updates
|
|
|
- **`ReportEditor/`** - Custom report builder (see Report Editor section)
|
|
|
|
|
|
**API Communication:** `services/api.js`
|
|
|
- Axios instance with JWT interceptor
|
|
|
- Token refresh queue (pauses failed requests during refresh)
|
|
|
- Auto-logout on refresh failure
|
|
|
|
|
|
**Routing:**
|
|
|
- `/` - Main weighing interface (protected)
|
|
|
- `/report-editor` - Report designer (protected)
|
|
|
- Shows Login component if not authenticated
|
|
|
|
|
|
### Serial Bridge Architecture
|
|
|
|
|
|
**Components:**
|
|
|
- `app.py` - Flask SSE server on port 5000
|
|
|
- `serial_reader.py` - SerialPortReader class with threading
|
|
|
- `tray_icon.py` - Windows system tray integration
|
|
|
|
|
|
**Data Flow:**
|
|
|
1. Thread reads from COM port continuously (pyserial)
|
|
|
2. Data queued in global `data_queue`
|
|
|
3. SSE endpoint `/events` streams to frontend
|
|
|
4. Frontend's `useSerialData` hook receives updates
|
|
|
5. **Note:** Serial bridge is independent of Django (direct SSE to frontend)
|
|
|
|
|
|
**Configuration:** `.env` file with COM_PORT, BAUD_RATE, TIMEOUT, READ_INTERVAL
|
|
|
|
|
|
### Real-Time Data Flow Example
|
|
|
|
|
|
```
|
|
|
COM Port → Serial Bridge (Flask SSE :5000) → Frontend useSerialData hook
|
|
|
↓
|
|
|
Main.jsx displays weight
|
|
|
↓
|
|
|
User clicks "Set Tare"
|
|
|
↓
|
|
|
POST /api/vehicles/{id}/set-tare/
|
|
|
↓
|
|
|
Django sse_broadcast_update()
|
|
|
↓
|
|
|
SSE → Frontend NomenclatureContext
|
|
|
↓
|
|
|
Vehicle list auto-updates
|
|
|
```
|
|
|
|
|
|
## Report Editor System
|
|
|
|
|
|
**Architecture:** Custom drag-drop report designer with grid-based layout
|
|
|
|
|
|
**Grid System:**
|
|
|
- Page dimensions: 80 columns × 66 rows (character grid)
|
|
|
- All elements positioned in grid coordinates
|
|
|
- Pixel conversion: `x * charWidth`, `y * charHeight`
|
|
|
|
|
|
**Element Types (OOP Class Hierarchy):**
|
|
|
```
|
|
|
Element (base class)
|
|
|
├── TextElement (static text)
|
|
|
├── DBTextElement (data-bound field with objectKey/fieldPath)
|
|
|
├── FrameElement (borders with box-drawing characters)
|
|
|
├── HorizontalLineElement
|
|
|
├── VerticalLineElement
|
|
|
├── SymbolElement (single character)
|
|
|
└── BandElement (repeating section with children[])
|
|
|
```
|
|
|
|
|
|
**Band Types:**
|
|
|
- `header` - Page header
|
|
|
- `footer` - Page footer
|
|
|
- `detail` - Main repeating data section
|
|
|
- `subdetail` - Nested repeating (child of detail)
|
|
|
- `summary` - Summary/totals section
|
|
|
|
|
|
**Key Components:**
|
|
|
- `ReportEditor.jsx` - Main orchestrator with save/load to Django `/api/reports/`
|
|
|
- `EditorCanvas.jsx` - Canvas rendering, drag/drop, multi-select
|
|
|
- `Toolbar.jsx` - Tool selection (text, frame, lines, bands, etc.)
|
|
|
- `ObjectInspector.jsx` - Property editor for selected elements
|
|
|
- `ConfigPanel.jsx` - API endpoint configuration
|
|
|
- `CharacterPalette.jsx` - Box-drawing character picker
|
|
|
- `models/Element.jsx` - Element class definitions
|
|
|
|
|
|
**Data Binding:**
|
|
|
- `DBTextElement` has `objectKey` and `fieldPath`
|
|
|
- Example: objectKey="invoice", fieldPath="customer.name"
|
|
|
- Preview mode fetches data from configured `apiEndpoint`
|
|
|
- BandContext provides current item during band iteration
|
|
|
|
|
|
**Persistence:**
|
|
|
- Entire report serialized to JSON
|
|
|
- Saved to Django `Report` model (JSONField stores elements array)
|
|
|
|
|
|
**Features:**
|
|
|
- Multi-select with drag/drop
|
|
|
- Resize handles
|
|
|
- Keyboard shortcuts (Delete, Ctrl+S, etc.)
|
|
|
- Grid snapping
|
|
|
- Character palette for box-drawing
|
|
|
- Preview mode with live data
|
|
|
|
|
|
## Authentication & Security
|
|
|
|
|
|
**JWT Token Flow:**
|
|
|
- Access token: 15 minutes lifetime
|
|
|
- Refresh token: 7 days lifetime
|
|
|
- Stored in localStorage (XSS risk - consider httpOnly cookies for production)
|
|
|
- Frontend auto-refreshes tokens every 14 minutes
|
|
|
- All API requests require Bearer token (except `/api/token/` and `/api/token/refresh/`)
|
|
|
- SSE connection authenticated via query param: `/sse-connect/?token=<jwt>` or Authorization header
|
|
|
|
|
|
**CORS Configuration:**
|
|
|
- Allowed origins: http://localhost:3000, http://127.0.0.1:3000, http://localhost:5174
|
|
|
- Credentials allowed (for cookies if needed)
|
|
|
|
|
|
**User Roles:**
|
|
|
- `employee` - Full access to weighing operations
|
|
|
- `viewer` - Read-only access
|
|
|
- `is_admin` - Access to Django admin panel
|
|
|
|
|
|
## Key Technical Decisions
|
|
|
|
|
|
**Why SSE instead of WebSockets?**
|
|
|
- Simpler server implementation (one-way communication sufficient)
|
|
|
- Auto-reconnect built into EventSource API
|
|
|
- Event ID-based replay for missed events
|
|
|
- HTTP-based (easier to proxy/firewall)
|
|
|
|
|
|
**Why ASGI?**
|
|
|
- Required for async SSE endpoint
|
|
|
- Uses uvicorn server
|
|
|
- `sync_to_async` wrapper for ORM queries in SSE handler
|
|
|
|
|
|
**Why Separate Serial Bridge?**
|
|
|
- Independence from Django (can restart Django without losing COM connection)
|
|
|
- System tray integration for user control
|
|
|
- Portable executable deployment
|
|
|
- Dedicated threading for serial I/O
|
|
|
|
|
|
**Why Nomenclature System?**
|
|
|
- Avoid schema migrations for custom fields
|
|
|
- Different deployments have different requirements
|
|
|
- User-configurable without code changes
|
|
|
- Validation still enforced via serializers
|
|
|
|
|
|
## Important Files for Understanding Architecture
|
|
|
|
|
|
1. `backend/scalesapp/settings.py` - Django configuration, JWT, CORS, database
|
|
|
2. `backend/scalesapp/sse.py` - Real-time SSE implementation
|
|
|
3. `backend/api/models.py` - Core models (User, ComPortReading, Report)
|
|
|
4. `backend/vehicles/models.py` - Vehicle and VehicleExtra with JSONField
|
|
|
5. `backend/nomenclatures/models.py` - Dynamic schema system
|
|
|
6. `frontend/src/App.jsx` - Routing and AuthProvider setup
|
|
|
7. `frontend/src/contexts/NomenclatureContext.jsx` - SSE client and data cache
|
|
|
8. `frontend/src/contexts/AuthContext.jsx` - JWT token management
|
|
|
9. `frontend/src/components/Main.jsx` - Primary weighing interface
|
|
|
10. `frontend/src/components/ReportEditor/ReportEditor.jsx` - Report builder
|
|
|
11. `serial_bridge/app.py` - Flask SSE server for COM port
|
|
|
|
|
|
## Configuration Files
|
|
|
|
|
|
**Backend:** `backend/.env`
|
|
|
```
|
|
|
SECRET_KEY=your-secret-key
|
|
|
DEBUG=True
|
|
|
ALLOWED_HOSTS=localhost,127.0.0.1
|
|
|
DB_NAME=scalesapp
|
|
|
DB_USER=postgres
|
|
|
DB_PASSWORD=
|
|
|
DB_HOST=localhost
|
|
|
DB_PORT=5432
|
|
|
```
|
|
|
|
|
|
**Frontend:** `frontend/.env`
|
|
|
```
|
|
|
REACT_APP_API_URL=http://localhost:8000
|
|
|
```
|
|
|
|
|
|
**Serial Bridge:** `serial_bridge/.env`
|
|
|
```
|
|
|
COM_PORT=COM1
|
|
|
BAUD_RATE=9600
|
|
|
BACKEND_URL=http://localhost:8000
|
|
|
AUTO_CONNECT=True
|
|
|
DEBUG=False
|
|
|
```
|
|
|
|
|
|
## Running the Full Application
|
|
|
|
|
|
1. Start Django: `cd backend && python manage.py runserver`
|
|
|
2. Start Serial Bridge: `cd serial_bridge && python app.py`
|
|
|
3. Start React: `cd frontend && npm start`
|
|
|
|
|
|
For SSE to work properly in Django, use uvicorn instead: `uvicorn scalesapp.asgi:application --reload`
|
|
|
|
|
|
## Common Patterns
|
|
|
|
|
|
**Adding a new model:**
|
|
|
1. Define model in appropriate app (api/vehicles/nomenclatures)
|
|
|
2. Create serializer in `serializers.py`
|
|
|
3. Create ViewSet in `views.py` with `sse_broadcast_update()` calls
|
|
|
4. Register route in `urls.py`
|
|
|
5. Run `python manage.py makemigrations && python manage.py migrate`
|
|
|
6. Update frontend Context to subscribe to SSE updates
|
|
|
|
|
|
**Adding a new nomenclature field:**
|
|
|
1. Create Nomenclature via Django admin or API
|
|
|
2. Define NomenclatureFields
|
|
|
3. Frontend auto-loads on next mount
|
|
|
4. No code changes needed (dynamic rendering in Main.jsx)
|
|
|
|
|
|
**Adding a report element type:**
|
|
|
1. Create new Element subclass in `models/Element.jsx`
|
|
|
2. Add component file (e.g., `MyElement.jsx`)
|
|
|
3. Register in `EditorCanvas.jsx` render switch
|
|
|
4. Add tool button in `Toolbar.jsx`
|
|
|
5. Add properties in `ObjectInspector.jsx`
|