added vehicles and vehicles extra, dynamic tables for entities, dynamic dropdowns with overlay for selecting and manage data
This commit is contained in:
@@ -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`
|
||||
Reference in New Issue
Block a user