# PDF Editor - Database Schema & Data Flow ## Database Table: `annotations` ### Location - **Migration File**: `database/migrations/2025_09_29_104240_create_annotations_table.php` - **Updated Migration**: `database/migrations/2025_10_16_131634_update_annotations_shape_type_enum.php` ### Table Structure ```sql CREATE TABLE annotations ( id BIGINT PRIMARY KEY AUTO_INCREMENT, pdf_file VARCHAR(255), page_number INT, shape_type ENUM('rect', 'circle', 'triangle', 'spool', 'weld', 'shop_weld', 'leader_line', 'text'), x_coordinate DECIMAL(10,2), y_coordinate DECIMAL(10,2), width DECIMAL(10,2), height DECIMAL(10,2), shape_id VARCHAR(100), spool_no VARCHAR(100), joint_type TEXT, classification VARCHAR(50), created_by BIGINT, created_at TIMESTAMP, updated_at TIMESTAMP ); ``` ### Field Descriptions | Field | Type | Description | Used For | |-------|------|-------------|----------| | `id` | BIGINT | Primary key | Unique identifier | | `pdf_file` | VARCHAR(255) | PDF file path | Linking annotations to specific PDFs | | `page_number` | INT | Page number | Multi-page PDF support | | `shape_type` | ENUM | Type of annotation | rect, circle, triangle, spool, weld, shop_weld, leader_line, text | | `x_coordinate` | DECIMAL(10,2) | X position | Canvas coordinate (for shapes: top-left, for lines: start point) | | `y_coordinate` | DECIMAL(10,2) | Y position | Canvas coordinate | | `width` | DECIMAL(10,2) | Width/Delta X | Shape width or line X distance | | `height` | DECIMAL(10,2) | Height/Delta Y | Shape height or line Y distance | | `shape_id` | VARCHAR(100) | Unique shape ID | Cross-referencing (leader lines to shapes, QR codes) | | `spool_no` | VARCHAR(100) | Spool number | Business data for welding | | `joint_type` | TEXT | Joint type or text content | Business data for welding OR free text content | | `classification` | VARCHAR(50) | Annotation category | 'shape_text', 'free_text', etc. | | `created_by` | BIGINT | User ID | Audit trail | | `created_at` | TIMESTAMP | Creation time | Audit trail | | `updated_at` | TIMESTAMP | Update time | Audit trail | ### Special Field Usage #### Leader Lines - `shape_type`: 'leader_line' - `x_coordinate`, `y_coordinate`: Start point (click point) - `width`: Delta X (x2 - x1) - `height`: Delta Y (y2 - y1) - Additional metadata stored in backend: - `connectedShapeId`: The shape this line connects to - `originalStartPoint`: Original click coordinates for reconnection #### Text Annotations - `shape_type`: 'text' - `joint_type`: Contains the actual text content - `classification`: - 'shape_text': Text inside shapes (red, bold, 28px) - 'free_text': Standalone notes (black, normal, 16px) #### Shapes (rect, circle, triangle, etc.) - Standard coordinate and dimension fields - `spool_no`: **Business identifier - populated from Spool Number dropdown** - Rectangle shapes: Display spool_no as text inside shape - Source: MTO (Material Take-Off) data or manually generated - Dropdown ID: `#spool-select` - `joint_type`: **Joint/Weld classification - populated from Weld Number dropdown** - Triangle/Circle shapes: Display joint_type (weld number) as text inside shape - Source: MTO welders data or manually entered - Dropdown ID: `#weld-select` - `shape_id`: Used for QR code generation and leader line connection **Shape Type → Data Mapping:** - **Rectangle** → Uses `spool_no` for text content - **Triangle** → Uses `joint_type` (weld number) for text content - **Circle** → Uses `joint_type` (weld number) for text content --- ## Data Population System ### Spool Number (`spool_no` field) #### What is it? Spool number is a unique identifier for a spool in piping/welding systems. In the PDF Editor, this value is used to label rectangle shapes. #### How it's populated: 1. **From MTO Data** (Material Take-Off): ```javascript // Function: populateMtoForms() // Populates dropdown from mtoData.spools array spoolSelect.innerHTML = ''; mtoData.spools.forEach(spool => { option.value = spool.spool_number; // e.g., "SP001" option.textContent = spool.spool_number; }); ``` 2. **Auto-Generate**: - Button: "Generate" next to Spool Number dropdown - API Call: `GET /api/mto/spools/generate-number` - Returns: New unique spool number (e.g., "SP123") 3. **Manual Selection**: - User selects from dropdown: ` // 2. User draws rectangle with leader line const spoolSelect = document.getElementById('spool-select'); const spoolNo = spoolSelect.value; // "SP001" // 3. Shape is created with spool number shape.set('spoolNo', spoolNo); // 4. Text is added inside rectangle textContent = (spoolNo && spoolNo.trim() !== '') ? spoolNo : 'N/A'; // Result: "SP001" displayed in red inside rectangle // 5. Save to database annotation = { spool_no: obj.spoolNo, // "SP001" shape_type: 'rect', ... }; ``` --- ### Weld/Joint Number (`joint_type` field) #### What is it? Weld/Joint number is a unique identifier for a weld joint in piping systems. In the PDF Editor, this value is used to label triangle and circle shapes. #### How it's populated: 1. **From MTO Welders Data**: ```javascript // Function: populateMtoForms() // Populates dropdown from mtoData.welders array weldSelect.innerHTML = ''; mtoData.welders.forEach(welder => { option.value = welder.welder_code; // e.g., "W001" option.textContent = welder.welder_code; }); ``` 2. **Auto-Generate**: - Button: "Generate" next to Weld Number dropdown - API Call: `GET /api/mto/joints/generate-number?spool_no={spoolNo}` - Returns: New joint number based on spool (e.g., "J-SP001-001") 3. **Manual Selection**: - User selects from dropdown: ` // 2. User draws triangle with leader line const weldSelect = document.getElementById('weld-select'); const weldNo = weldSelect.value; // "W001" // 3. Shape is created with weld number shape.set('weldNo', weldNo); // 4. Text is added inside triangle textContent = (weldNo && weldNo.trim() !== '') ? weldNo : 'N/A'; // Result: "W001" displayed in red inside triangle // 5. Save to database annotation = { joint_type: obj.weldNo, // "W001" shape_type: 'triangle', ... }; ``` --- ### Current Status Issue **Problem**: `spool_no` field is currently empty in database **Possible Reasons**: 1. **MTO Data Not Loaded**: `mtoData.spools` array is empty or not fetched 2. **Dropdown Not Selected**: User is creating shapes without selecting spool/weld number 3. **Sample Data**: System falls back to sample data (SP001, SP002, SP003) if MTO data is missing 4. **API Integration**: MTO API endpoints might not be returning data **How to Fix**: 1. Check MTO data loading in browser console 2. Ensure dropdown has options before creating shapes 3. Select spool/weld number from dropdown before drawing 4. Verify API endpoints are working: - `/api/mto/spools/generate-number` - `/api/mto/joints/generate-number` **Testing**: ```javascript // Open browser console on PDF Editor page console.log('MTO Data:', mtoData); console.log('Spools:', mtoData.spools); console.log('Welders:', mtoData.welders); // Check dropdown values const spoolSelect = document.getElementById('spool-select'); console.log('Spool options:', Array.from(spoolSelect.options).map(o => o.value)); ``` --- ## API Endpoints ### 1. Save/Sync Annotations **Endpoint**: `POST /api/pdf-editor/annotations/sync` **Controller**: `app/Http/Controllers/Api/AnnotationController.php` → `syncAnnotations()` **Request Body**: ```json { "pdf_file": "path/to/file.pdf", "page_number": 1, "annotations": [ { "shape_id": "shape-1729612345678", "spool_no": "SP-001", "joint_type": "Butt Weld", "classification": "", "page_number": 1, "x_coordinate": 100, "y_coordinate": 200, "width": 140, "height": 70, "shape_type": "rect", "pdf_file_path": "path/to/file.pdf", "created_by": 1 }, { "shape_id": "leader-1729612345680", "shape_type": "leader_line", "x_coordinate": 150, "y_coordinate": 180, "width": 0, "height": 50, "connectedShapeId": "shape-1729612345678", "originalStartPoint": {"x": 150, "y": 180} } ] } ``` **Response**: ```json { "success": true, "message": "Annotations synced successfully", "data": { "deleted": 2, "created": 5 } } ``` **Logic**: 1. Deletes all existing annotations for the specific PDF page 2. Inserts all new annotations from the request 3. Transaction-based for data integrity --- ### 2. Load Annotations **Endpoint**: `GET /api/pdf-editor/annotations?pdf_file={path}&page_number={num}` **Controller**: `app/Http/Controllers/Api/AnnotationController.php` → `index()` **Response**: ```json { "success": true, "data": [ { "id": 123, "pdf_file": "path/to/file.pdf", "page_number": 1, "shape_type": "triangle", "x_coordinate": 300, "y_coordinate": 400, "width": 140, "height": 140, "shape_id": "shape-1729612345690", "spool_no": "SP-002", "joint_type": "Fillet", "classification": "", "created_by": 1, "created_at": "2025-10-22 10:30:00", "updated_at": "2025-10-22 10:30:00" } ] } ``` --- ### 3. Generate QR Code **Endpoint**: `GET /api/pdf-editor/qr-code/{shapeId}` **Controller**: `app/Http/Controllers/Api/AnnotationController.php` → `generateQRCode()` **Response**: PNG image data (base64 or binary) **Used For**: Generating QR codes for shape annotations --- ## Data Flow Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ PDF EDITOR FRONTEND │ │ (pdf-editor.blade.php) │ └─────────────────────────────────────────────────────────────┘ │ │ User draws shapes, │ adds text, creates │ leader lines ▼ ┌─────────────────────────────────────────────────────────────┐ │ FABRIC.JS CANVAS │ │ - Shapes (rect, circle, triangle) │ │ - Leader Lines (connecting click point to shape edge) │ │ - Text Objects (free text notes + shape text) │ │ - QR Codes (linked to shapes) │ └─────────────────────────────────────────────────────────────┘ │ │ Save button clicked ▼ ┌─────────────────────────────────────────────────────────────┐ │ saveCurrentPageAnnotations() │ │ - Iterates through all canvas objects │ │ - Skips QR codes (not saved) │ │ - Converts shapes, lines, text to annotation format │ │ - Prepares JSON payload │ └─────────────────────────────────────────────────────────────┘ │ │ POST /api/pdf-editor/annotations/sync ▼ ┌─────────────────────────────────────────────────────────────┐ │ API: AnnotationController@syncAnnotations │ │ 1. Validates request data │ │ 2. Starts database transaction │ │ 3. Deletes old annotations for this page │ │ 4. Inserts new annotations │ │ 5. Commits transaction │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DATABASE TABLE │ │ `annotations` │ │ - All annotations stored with coordinates, types, metadata │ └─────────────────────────────────────────────────────────────┘ │ │ Page reload or PDF selection │ GET /api/pdf-editor/annotations ▼ ┌─────────────────────────────────────────────────────────────┐ │ API: AnnotationController@index │ │ - Fetches annotations for specified PDF + page │ │ - Returns JSON array │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ loadExistingAnnotations() │ │ - Receives annotation data from API │ │ - Calls loadAnnotationToCanvas() for each │ │ - Recreates shapes, lines, text on canvas │ │ - Calls reconnectLeaderLines() to re-establish connections │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ FABRIC.JS CANVAS │ │ - All annotations restored │ │ - Leader lines connected to shapes │ │ - QR codes regenerated via API │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Key Implementation Details ### Triangle Leader Line Connection - **Challenge**: Triangles have different geometry than rectangles/circles - **Solution**: Custom `calculateEdgePoint()` function in `setupConnectedMovement()` - Calculates triangle vertices and edges - Determines closest edge/vertex based on original click position - Connects leader line to appropriate edge point - Handles rotation, scaling, and movement events ### Unique Shape IDs - Generated using `Date.now()` to ensure uniqueness across page reloads - Format: `shape-{timestamp}`, `leader-{timestamp}`, `text-{timestamp}` - Prevents database conflicts when saving new annotations ### Text Handling - **Shape Text**: Red, bold, 28px, positioned inside shapes, non-selectable - **Free Text**: Black, normal, 16px, standalone notes, selectable - Both saved to same table with different `classification` values ### Leader Line Storage - Start point stored as `x_coordinate`, `y_coordinate` - End point calculated using `width` and `height` deltas - Metadata (`connectedShapeId`, `originalStartPoint`) preserved for reconnection --- ## Validation Rules ```php // app/Http/Controllers/Api/AnnotationController.php 'annotations' => 'required|array', 'annotations.*.shape_type' => 'required|in:rect,circle,triangle,spool,weld,shop_weld,leader_line,text', 'annotations.*.x_coordinate' => 'required|numeric', 'annotations.*.y_coordinate' => 'required|numeric', 'annotations.*.width' => 'nullable|numeric', 'annotations.*.height' => 'nullable|numeric', 'annotations.*.shape_id' => 'required|string', 'annotations.*.page_number' => 'required|integer', ``` --- ## Future Enhancements - Add annotation history/versioning - Implement collaborative editing with user tracking - Add annotation comments/notes - Support for more shape types - Advanced leader line routing (avoid overlaps)