# SimpleUploadWidget with Metadata & Tags

## Overview
The `simpleUpload` endpoint and `SimpleUploadWidget` now support automatic entity linking and tagging during upload. This eliminates the need for separate linking API calls and ensures documents are properly associated with their context immediately.

## Changes Made

### 1. Backend: DocumentsController.simpleUpload()

**New Parameters (via FormData):**
```php
$_POST['entity_type']     // e.g., 'case', 'client', 'project', 'employee'
$_POST['entity_id']       // numeric ID of the entity
$_POST['entity_data']     // JSON string with metadata key-value pairs
$_POST['custom_tags']     // JSON array or comma-separated string of tags
$_POST['source_context']  // context identifier (default: 'simple-upload')
```

**Behavior:**
- If `entity_type` and `entity_id` are provided, document is automatically linked via `link_document_to_entity()` PostgreSQL function
- Entity metadata is stored in `document_metadata` table
- Custom tags are added to `document_tags` table
- System tags are auto-generated (e.g., "case:MC001")
- Response includes `linked: true` flag when entity linking succeeds

**Response:**
```json
{
  "document_id": 456,
  "version_id": "uuid-here",
  "file": {
    "name": "contract.pdf",
    "size": 107060,
    "type": "application/pdf"
  },
  "linked": true
}
```

### 2. Frontend: SimpleUploadWidget

**New Constructor Options:**
```javascript
{
  endpoint: '../api/documents/simple-upload',
  multiple: true,
  autoUpload: true,
  
  // New entity linking options
  entityType: 'case',           // Entity type
  entityId: 123,                // Entity ID
  entityData: {                 // Metadata key-value pairs
    case_number: 'MC001',
    case_title: 'Smith vs State',
    client_id: 789,
    client_name: 'John Smith',
    matter_type: 'criminal',
    status: 'active'
  },
  customTags: ['urgent', 'legal'],  // Custom tags array
  sourceContext: 'modal_upload',     // Source context
  
  onSuccess: (doc) => {
    console.log('Uploaded and linked:', doc);
  }
}
```

**Automatic Behavior:**
- Widget automatically includes entity linking data in FormData when uploading
- Works with offline queue - metadata is preserved when queued
- No need for separate `linkToEntity()` API call
- Documents are linked immediately upon upload

### 3. ModalBuilder Integration

**Configuration-Driven Metadata:**
```javascript
// ModalBuilder extracts entity context from:
// 1. element.options.filterField (determines entity type and ID)
// 2. element.options.metadataFields (maps record fields to metadata)
// 3. this.record (current record being viewed)

const uploadWidget = new SimpleUploadWidget(`#${uploadId}`, {
  multiple: true,
  autoUpload: true,
  entityType: 'case',     // Auto-detected from filterField
  entityId: 123,          // From this.currentFilters[filterField]
  entityData: {           // Built from metadataFields config
    case_number: this.record.case_number,
    case_title: this.record.title,
    // ... etc
  },
  sourceContext: 'modal_upload',
  customTags: []
});
```

## Usage Examples

### Example 1: Direct Widget Usage
```javascript
import { SimpleUploadWidget } from 'widgets/simpleuploadwidget';

const widget = new SimpleUploadWidget('#upload-container', {
  multiple: true,
  autoUpload: true,
  entityType: 'case',
  entityId: 123,
  entityData: {
    case_number: 'MC001',
    case_title: 'Smith vs State',
    client_id: 789,
    matter_type: 'criminal'
  },
  customTags: ['urgent', 'legal'],
  sourceContext: 'case_detail_page',
  onSuccess: (doc) => {
    console.log('Document uploaded and linked:', doc);
    // Refresh UI
    loadDocuments();
  }
});
```

### Example 2: Module Configuration
```json
{
  "type": "documents",
  "options": {
    "filterField": "case_id",
    "metadataFields": {
      "case_number": "case_number",
      "case_title": "title",
      "client_id": "client_id",
      "client_name": "client_name",
      "matter_type": "matter_type",
      "status": "status"
    }
  }
}
```

ModalBuilder automatically:
1. Detects entity type from `filterField` (removes `_id` suffix)
2. Gets entity ID from `currentFilters[filterField]`
3. Builds `entityData` by mapping `metadataFields` to current record
4. Passes all context to SimpleUploadWidget

### Example 3: Direct API Call
```javascript
const formData = new FormData();
formData.append('file', fileObject);
formData.append('entity_type', 'project');
formData.append('entity_id', 456);
formData.append('entity_data', JSON.stringify({
  project_name: 'Website Redesign',
  project_manager: 'Jane Doe',
  department: 'IT'
}));
formData.append('custom_tags', JSON.stringify(['design', 'website']));
formData.append('source_context', 'project_management');

const response = await fetch('../api/documents/simple-upload', {
  method: 'POST',
  body: formData,
  credentials: 'include'
});

const result = await response.json();
console.log('Uploaded:', result);
// result.linked === true means entity linking succeeded
```

### Example 4: Offline Queue
When offline, uploads are automatically queued with metadata:
```javascript
// Widget detects offline and queues upload
const widget = new SimpleUploadWidget('#upload', {
  entityType: 'client',
  entityId: 789,
  entityData: { client_name: 'ACME Corp' },
  customTags: ['contract']
});

// When user uploads while offline:
// 1. Upload is queued with all metadata
// 2. When back online, queue processor sends complete FormData
// 3. Document is uploaded AND linked automatically
```

## Database Schema

### document_metadata
Stores all entity relationships and custom metadata:
```sql
INSERT INTO document_metadata (document_id, metadata_key, metadata_value)
VALUES 
  (456, 'case_id', '123'),
  (456, 'case_number', 'MC001'),
  (456, 'case_title', 'Smith vs State'),
  (456, 'client_id', '789'),
  (456, 'client_name', 'John Smith'),
  (456, 'matter_type', 'criminal'),
  (456, 'status', 'active');
```

### document_tags
Stores system-generated and custom tags:
```sql
INSERT INTO document_tags (document_id, tag, tag_type, created_by)
VALUES 
  (456, 'case', 'system', 1),        -- Auto-generated from entity_type
  (456, 'case:MC001', 'system', 1),  -- Auto-generated from entity_data
  (456, 'urgent', 'custom', 1),      -- From customTags
  (456, 'legal', 'custom', 1);       -- From customTags
```

## Benefits

1. **Single API Call**: Upload and link in one request
2. **Offline Support**: Metadata preserved in offline queue
3. **Configuration-Driven**: No code changes for new entity types
4. **Automatic Tagging**: System tags auto-generated from entity context
5. **Consistent**: Same pattern across all modules
6. **Generic**: Works with ANY entity type (case, client, project, employee, etc.)

## Migration Guide

### Before (Two API Calls)
```javascript
// Step 1: Upload
const uploadResponse = await fetch('../api/documents/simple-upload', {
  method: 'POST',
  body: formData
});
const doc = await uploadResponse.json();

// Step 2: Link (separate API call)
await fetch('../api/documents/link-to-entity', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    document_id: doc.document_id,
    entity_type: 'case',
    entity_id: 123,
    entity_data: { /* ... */ }
  })
});
```

### After (Single API Call)
```javascript
const formData = new FormData();
formData.append('file', fileObject);
formData.append('entity_type', 'case');
formData.append('entity_id', 123);
formData.append('entity_data', JSON.stringify({ /* ... */ }));

const response = await fetch('../api/documents/simple-upload', {
  method: 'POST',
  body: formData
});
const doc = await response.json();
// doc.linked === true
```

### Or Use Widget (Even Simpler)
```javascript
const widget = new SimpleUploadWidget('#container', {
  entityType: 'case',
  entityId: 123,
  entityData: { /* ... */ },
  onSuccess: (doc) => console.log('Done!', doc)
});
```

## Testing Checklist

- [ ] Upload document with entity metadata from case module
- [ ] Verify metadata stored in `document_metadata` table
- [ ] Verify system tags auto-generated in `document_tags` table
- [ ] Verify custom tags added to `document_tags` table
- [ ] Test upload without entity metadata (should work as before)
- [ ] Test with different entity types (client, project, employee)
- [ ] Test offline queue includes metadata
- [ ] Test online/offline transitions preserve metadata
- [ ] Verify `linked: true` in response when metadata provided
- [ ] Test comma-separated custom tags string
- [ ] Test JSON array custom tags
- [ ] Verify backward compatibility with code not passing metadata

## Backward Compatibility

✅ **Fully backward compatible:**
- If no entity metadata provided, upload works exactly as before
- Existing code not passing metadata continues to work
- Response includes `linked: false` when no entity data provided
- Can still use separate `linkToEntity()` endpoint if needed

## Related Files

- `api/controllers/DocumentsController.php` - simpleUpload() method
- `FrontEnd/js/widgets/simpleUploadWidget.js` - Widget implementation
- `FrontEnd/js/core/ModalBuilder.js` - Configuration-driven integration
- `sql/modules.sql` - Module configurations with metadataFields
- `docs/GenericDocumentSystem.md` - Complete system documentation
- `docs/DocumentsControllerRefactoring.md` - Controller refactoring details

## Summary

Documents uploaded via `simple-upload` endpoint now automatically link to entities when metadata is provided. This streamlines the upload process, ensures data consistency, and works seamlessly with offline queuing. The entire flow is configuration-driven and works with ANY entity type without code changes.
