added vehicles and vehicles extra, dynamic tables for entities, dynamic dropdowns with overlay for selecting and manage data

This commit is contained in:
2026-02-10 17:32:33 +02:00
parent ed35a90cc0
commit 6a42099169
60 changed files with 4845 additions and 81 deletions
+360
View File
@@ -0,0 +1,360 @@
# 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`