# Live Preview Implementation - New MARC Form Structure

## Overview

Live preview concatenates subfield input values (with spaces) and displays them in real-time. When MARC punctuation is enabled, it uses AJAX to apply the server-side punctuation algorithm.

---

## Structure and Placement

### HTML Structure (marc_form.blade.php)

The live preview is placed **before** the collapsible panel (outside the `panel-collapse` div) so it's always visible:

```blade
{{-- Lines 181-190 in marc_form.blade.php --}}
{{-- Live preview --}}
<div class="col-md-12">
    @if (!empty($id_unique))
        {{ \Theme::partial('marc.preview_alert', $previewParams) }}
    @endif
</div>

{{-- Then the collapsible panel with subfields --}}
<div class="col-md-12 panel-collapse collapse {{ $collapsein }}" id="tag_{{ $tagfield->tagfield }}_{{ $id }}">
    <ul class="sortable_subfield">
        {{-- Subfields rendered here --}}
    </ul>
</div>
```

This matches the legacy structure where preview was placed before `panel-collapse`.

---

## How It Works

### 1. Input Attributes (subfields_list.blade.php)

Each subfield input gets:
- `data-preview="preview_<id_unique>"` - Links input to preview element
- `data-subfield="<code>"` - Subfield code (a, x, y, z, etc.)
- Class `live-subfield` - Triggers preview update on input

**Lines 64-65, 61 in subfields_list.blade.php:**
```php
if (!empty($id_unique)) {
    $extra['data-preview'] = 'preview_' . $id_unique;
}
$extra['data-subfield'] = $subfield->tagsubfield;
```

### 2. Preview Element (preview_alert.blade.php)

The preview alert partial renders:
- Container: `.preview-alert-container` with `id="preview_alert_<id_unique>"`
- Preview span: `id="preview_<id_unique>"` (this is what gets updated)
- Eye icon and Bootstrap alert styling

**Lines 21-26 in preview_alert.blade.php:**
```blade
<div class="preview-alert-container" id="preview_alert_{{ $id_unique }}">
    <div class="col-md-6 col-md-offset-6">
        <div class="alert alert-info">
            <span class="fa fa-eye"></span>
            <span class="live-preview" id="preview_{{ $id_unique }}">{{ $previewText }}</span>
        </div>
    </div>
</div>
```

### 3. JavaScript: Collecting Subfields and Updating Preview

**Function: `updateLivePreview(previewId)`** (create.blade.php ~369-465)

1. **Parse previewId**: `preview_<tagfield>_<random>` → extract `tagfield` and `random`
2. **Collect subfields**: Find all inputs with `data-preview="preview_<tagfield>_<random>"`:
   ```javascript
   $('input[data-preview="' + fullPreview + '"]').each(function () {
       const sf = $(this).data('subfield');  // e.g., 'a', 'x', 'y'
       const value = $(this).val();
       if (value) {
           subfields[sf] = value;  // e.g., { a: 'Title', x: 'Subtitle' }
       }
   });
   ```
3. **Generate preview**:
   - **If punctuation enabled**: Use AJAX → `/issue/apply-marc-rules` (sends `tagfield` and `subfields` object)
   - **If punctuation disabled**: Use client-side `generatePreviewClientSide()` → joins values with spaces

4. **Update DOM**: Set `$('#preview_<id_unique>').text(previewText)` and show/hide container

### 4. Event Handler (create.blade.php ~491-500)

On any `.live-subfield` input change:
```javascript
$('.live-subfield').on('input', function () {
    const previewId = $(this).data('preview');
    if (previewId) {
        if (marcPunctuationEnabled) {
            debouncedUpdatePreview(previewId);  // AJAX (300ms debounce)
        } else if (isNewMode) {
            debouncedUpdatePreview(previewId);  // Client-side (300ms debounce)
        } else {
            debouncedUpdatePreview(previewId);  // AJAX (300ms debounce)
        }
    }
});
```

**Debounce**: 300ms delay to avoid excessive updates while typing.

---

## Punctuation Algorithm (When Enabled)

### AJAX Endpoint: `/issue/apply-marc-rules`

**Controller:** `app/controllers/Admin/Issue/RuleController.php` → `applyRules()` (line 32)

**Request:**
- `tagfield`: e.g., "245"
- `subfields`: Object like `{ a: 'Title', b: 'Subtitle', c: 'Author' }`

**Response:**
- `{ preview: "formatted text with punctuation" }`

**Algorithm (apply_punctuation in app/Helpers/marc.php):**
1. Normalize subfields (convert arrays to single values)
2. Sort subfields alphabetically
3. Get rules for the tagfield from database
4. For each subfield:
   - Find matching rule (by `apply_field`)
   - Check required subfields/fields exist
   - Remove special chars/punctuation from value
   - Apply punctuation mark (`before` or `after` position)
5. Join processed subfields with spaces
6. Add end punctuation
7. Return formatted string

**Example:**
- Input: `{ a: 'Title', b: 'Subtitle' }`
- Rules: `a` → no mark, `b` → `: ` before
- Output: `"Title : Subtitle"`

---

## Files and Key Lines

| File | Lines | Purpose |
|------|-------|---------|
| **marc_form.blade.php** | 181-190 | Renders preview alert partial (before panel-collapse) |
| **preview_alert.blade.php** | 1-30 | Preview alert HTML structure |
| **subfields_list.blade.php** | 64-65, 61 | Sets `data-preview` and `data-subfield` on inputs |
| **create.blade.php** | 269-296 | Loads `marcRules` JSON for client-side (when punctuation disabled) |
| **create.blade.php** | 298-366 | `generatePreviewClientSide()` - client-side preview (no punctuation) |
| **create.blade.php** | 369-465 | `updateLivePreview()` - main preview update function |
| **create.blade.php** | 491-500 | Event handler: `.live-subfield` input → trigger preview update |
| **create.blade.php** | 449 | `debouncedUpdatePreview` - 300ms debounce wrapper |
| **RuleController.php** | 32-58 | AJAX endpoint: `/issue/apply-marc-rules` |
| **marc.php** | 435-520 | `apply_punctuation()` - server-side punctuation algorithm |
| **routes.php** | 1260 | Route: `GET /issue/apply-marc-rules` → `RuleController@applyRules` |

---

## Flow Diagram

```
User types in subfield input
    ↓
.live-subfield 'input' event fires
    ↓
Extract data-preview="preview_245_xyz" from input
    ↓
updateLivePreview('preview_245_xyz')
    ↓
Parse: tagfield='245', random='xyz'
    ↓
Collect all inputs with data-preview="preview_245_xyz"
    ↓
Build subfields object: { a: 'Title', b: 'Subtitle' }
    ↓
┌─────────────────────────────────────┐
│ Is punctuation enabled?              │
└─────────────────────────────────────┘
    │                    │
   YES                   NO
    │                    │
    ↓                    ↓
AJAX Request      Client-side join
/apply-marc-rules  Object.values(subfields).join(' ')
    │                    │
    ↓                    ↓
Server applies      Simple concatenation
punctuation algo    with spaces
    │                    │
    ↓                    ↓
Update #preview_245_xyz
Show/hide container
```

---

## Key Changes Made

1. **Punctuation → AJAX**: When `marcPunctuationEnabled === true`, use AJAX even in new mode (not client-side) to apply the server-side punctuation algorithm.

2. **Structure preserved**: Preview placement matches legacy (before `panel-collapse`, always visible).

3. **Debouncing**: All preview updates use 300ms debounce to reduce server load and improve performance.

4. **Fallback**: If punctuation disabled, use simple client-side join (faster, no server round-trip).

---

## Testing Checklist

- [ ] Type in subfield → preview updates (concatenates with spaces)
- [ ] Multiple subfields → all appear in preview, separated by spaces
- [ ] Punctuation enabled → preview uses AJAX and applies punctuation marks
- [ ] Punctuation disabled → preview uses client-side (fast, no AJAX)
- [ ] Preview shows/hides correctly when subfields are empty/filled
- [ ] Preview updates when subfields are filled via classification/reference
- [ ] Preview works for duplicated fields (new 650 after "keep old one")
