475 lines
18 KiB
Markdown
475 lines
18 KiB
Markdown
# 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 = '<option value="">Select Spool</option>';
|
|
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: `<select id="spool-select">`
|
|
- Value is stored when shape is created
|
|
|
|
#### When it's used:
|
|
- **Rectangle shapes**: Display spool_no inside the shape as red text
|
|
- **Shape creation**: Value is read from dropdown and assigned to `shape.spoolNo`
|
|
- **Database save**: Stored in `annotations.spool_no` column
|
|
|
|
#### Code Flow:
|
|
```javascript
|
|
// 1. User selects spool from dropdown
|
|
<select id="spool-select">
|
|
<option value="SP001">SP001</option>
|
|
<option value="SP002">SP002</option>
|
|
</select>
|
|
|
|
// 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 = '<option value="">Select Weld</option>';
|
|
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: `<select id="weld-select">`
|
|
- Value is stored when shape is created
|
|
|
|
#### When it's used:
|
|
- **Triangle shapes**: Display joint_type inside the shape as red text
|
|
- **Circle shapes**: Display joint_type inside the shape as red text
|
|
- **Shape creation**: Value is read from dropdown and assigned to `shape.weldNo`
|
|
- **Database save**: Stored in `annotations.joint_type` column
|
|
|
|
#### Code Flow:
|
|
```javascript
|
|
// 1. User selects weld from dropdown
|
|
<select id="weld-select">
|
|
<option value="W001">W001</option>
|
|
<option value="W002">W002</option>
|
|
</select>
|
|
|
|
// 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)
|
|
|