Files
citrus-cms/resources/views/guide/PDF-EDITOR-DATABASE-SCHEMA.md
T
2026-04-28 21:15:09 +03:00

18 KiB

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

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):

    // 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:

// 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:

    // 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:

// 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:

// 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.phpsyncAnnotations()

Request Body:

{
  "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:

{
  "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.phpindex()

Response:

{
  "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.phpgenerateQRCode()

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

// 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)