# BLUEPRINT: Fix Bootstrap-Select Tidak Berfungsi di Modal Variant Settings

## Ringkasan Masalah

Halaman `ProductVariantSettings/edit` (view: `application/views/products/variant_settings.php`) adalah **standalone full HTML document** (`<!doctype html>`, `<html>`, `<head>`, `<body>` dengan CSS/JS sendiri). Halaman ini berfungsi normal ketika:

| Point | Cara Buka | Mekanisme | Status |
|-------|-----------|-----------|--------|
| 1 | Master Data Produk (Data controller) | **`<iframe>`** di dalam halaman edit produk | ✅ Berfungsi |
| 2 | New Tab / Open New Window | **Full page load** | ✅ Berfungsi |
| 3 | `variant_cutover` module | **`BootstrapDialog.show()`** via **AJAX `.load()`** | ❌ Bootstrap-select gagal |
| 4 | `konversi_varian` module | Sama seperti point 3 | ❌ Bootstrap-select gagal |

---

## Root Cause Analysis

### Arsitektur Bermasalah

`variant_settings.php` adalah **full HTML document** (772 baris) yang mengandung:

- **`<head>`**: 8 CSS `<link>`, 8 JS `<script src="...">` (termasuk jQuery, Bootstrap, DataTables, **bootstrap-select**)
- **`<body>`**: 15+ blok `<script>` inline (logika bisnis, event handlers, init functions)
- **`<style>`**: CSS kustom untuk halaman

Ketika dimuat via `$('<div></div>').load(url)` ke dalam BootstrapDialog, terjadi beberapa masalah:

### Problem #1: Script External Tidak Pernah Diload Ulang

Di `shoppingCart.php:580-586`:

```javascript
content.find('script').each(function() {
    $.globalEval(this.text || this.textContent || this.innerHTML || '');
});
```

- Untuk `<script src="...">` (external): `this.text` = **string kosong** → `$.globalEval("")` → **TIDAK ADA EFEK**
- jQuery, Bootstrap, bootstrap-select, DataTables dari `<head>` variant_settings.php **TIDAK PERNAH** di-load ulang

**Dampak**: Tidak langsung fatal karena **parent page sudah memiliki semua library tersebut** (template `transaksi-modul.html` line 34-53 memuat jQuery, bootstrap-select, dll).

### Problem #2: CSS `<link>` dari `<head>` Diproses Ulang oleh Browser

Ketika full HTML document di-set sebagai `innerHTML` dari sebuah `<div>`, browser HTML parser:

- `<head>` children (`<link>`, `<style>`, `<script>`) — perilaku tergantung browser
- `<style>` inline — ada yang tetap di dalam `<div>`, ada yang dipindah ke `<head>` dokumen
- **CSS bootstrap-select mungkin tidak teraplikasi secara konsisten** di konteks modal

### Problem #3: Urutan Inisialisasi Bootstrap-Select Salah (ROOT CAUSE UTAMA)

Di `shoppingCart.php:568-586`, handler `onshown` menjalankan:

**Step A (line 570-578)** — SEBELUM inline script dievaluasi:
```javascript
content.find('.selectpicker').selectpicker('render');
```

Ini menginisialisasi selectpicker dengan **DEFAULT options** (tanpa `liveSearch: true`, tanpa konfigurasi kustom).

**Step B (line 580-586)** — Evaluasi inline scripts:
```javascript
content.find('script').each(function() { $.globalEval(...); });
```

Salah satu inline script yang dievaluasi adalah `scheduleAttrMasterInit()` (line 750) yang memanggil `initAttrMasterSelect()` (line 213):

```javascript
window.initAttrMasterSelect = function() {
    var el = window.jq('#sel-attr-master');
    if (el.parent('.bootstrap-select').length > 0) {
        el.selectpicker('refresh');  // SUDAH DI-INIT oleh Step A!
        return;                       // KELUAR TANPA menerapkan konfigurasi kustom!
    }
    // Kode berikut TIDAK PERNAH tercapai:
    el.selectpicker({liveSearch: true, width: '320px', dropupAuto: false});
    el.selectpicker('refresh');
};
```

Karena Step A sudah `render()` selectpicker, maka `el.parent('.bootstrap-select').length > 0` = **TRUE**, dan fungsi **return tanpa** menerapkan opsi `liveSearch: true` dan `width: '320px'`.

**Akibat**: Bootstrap-select terinisialisasi dengan konfigurasi default (bukan kustom), menyebabkan:
- `liveSearch` mungkin tidak aktif (walaupun ada `data-live-search` di HTML, tergantung versi bootstrap-select)
- `width` tidak sesuai
- Event handler kustom (line 289-306, 360-379) mungkin ter-attach ke elemen yang salah atau tidak berfungsi karena DOM sudah di-modify oleh bootstrap-select

### Problem #4: Event Handlers Mungkin Tidak Terikat ke Elemen yang Benar

Script inline di line 289-306 mengikat event `focus` dan `mousedown` ke `#sel-attr-master` (native `<select>`). Namun bootstrap-select MENYEMBUNYIKAN `<select>` asli dan membuat elemen `<button>` baru. Event handler terikat ke elemen yang tersembunyi, bukan ke tombol bootstrap-select yang terlihat.

### Problem #5: jQuery `jq` Reference

View mendefinisikan `window.jq = window.$ || window.jQuery || null` di line 211. Karena external scripts tidak di-reload, `window.$` tetap milik parent page. Ini seharusnya OK karena library yang sama.

### Problem #6: DOMContentLoaded Listener Tidak Berguna

Line 756-770 menggunakan `DOMContentLoaded` yang sudah terlewat saat script dievaluasi via `$.globalEval()`. Badge coloring logic tidak akan berjalan.

---

## File-File yang Terlibat

| File | Path | Peran |
|------|------|-------|
| **Controller** | `application/controllers/ProductVariantSettings.php` | Controller halaman variant settings |
| **View (utama)** | `application/views/products/variant_settings.php` | Standalone HTML page (772 baris) |
| **View (copy)** | `application/views/products/variant_settings - Copy.php` | Backup/old version (544 baris) |
| **Module shoppingCart #1** | `application/modules/variant_cutover/views/shoppingCart.php` | Line 561-589 memanggil modal BootstrapDialog |
| **Module shoppingCart #2** | `application/modules/variant_cutover_backup/views/shoppingCart.php` | Sama (backup) |
| **Module shoppingCart #3** | `application/modules/konversi_varian/views/shoppingCart.php` | Sama |
| **Config** | `application/config/heDataBehaviour.php` | Line 1405: deklarasi `dataExtRelation` untuk iframe |
| **Template Data** | `application/template/data.html` | Template untuk master data (punya bootstrap-select) |
| **Template Module** | `application/modules/variant_cutover/template/transaksi-modul.html` | Template untuk variant_cutover (punya bootstrap-select) |
| **Route** | `application/config/routes.php` | Line 63-68: routing ProductVariantSettings |

---

## Pendekatan Solusi

Ada **2 opsi** pendekatan. Pilih salah satu:

### Opsi A: Konversi View Menjadi Partial + Inisialisasi Manual (DIREKOMENDASIKAN)

**Strategi**: Pisahkan view menjadi 2 versi:
1. **Full page** — untuk akses direct (new tab, iframe)
2. **Partial** — untuk AJAX load ke modal

Atau buat view yang bisa **auto-detect context** dan berperilaku sesuai.

#### Langkah-langkah:

##### 1. `application/controllers/ProductVariantSettings.php`
- Tambahkan parameter `?partial=1` atau `?attached=1` yang:
  - Jika `partial=1`: load view partial (tanpa `<html>`, `<head>`, `<body>`)
  - Jika tidak: load view full page seperti sekarang
- Atau buat method terpisah: `edit_partial()` yang hanya render konten

##### 2. `application/views/products/variant_settings.php` (atau file baru)
- Pisahkan menjadi:
  - **`variant_settings_full.php`**: Full HTML document (existing, untuk iframe & new tab)
  - **`variant_settings_partial.php`**: Hanya konten form + inline script (tanpa `<html>`, `<head>`, `<body>`, tanpa load external JS/CSS)

  Atau buat view tunggal dengan conditional:
  ```php
  <?php if (empty($partial)): ?>
  <!doctype html>
  <html>
  <head>
      <!-- CSS/JS hanya untuk full page -->
  </head>
  <body>
  <?php endif; ?>
  
  <!-- Konten form (sama untuk kedua mode) -->
  
  <?php if (empty($partial)): ?>
  </body>
  </html>
  <?php endif; ?>
  ```

##### 3. `application/modules/variant_cutover/views/shoppingCart.php` (line 561)
- Ubah URL menjadi: `...&partial=1`
- Hapus `onshown` handler untuk selectpicker 'render' (atau ganti dengan inisialisasi dari inline script)

##### 4. Inisialisasi bootstrap-select
- Pindahkan inisialisasi dari inline script ke fungsi yang dipanggil EXPLISIT setelah `.load()` selesai
- Atau pastikan inline script bisa menginisialisasi dengan benar tanpa konflik

**Keuntungan**: Arsitektur bersih, maintainable
**Kerugian**: Perubahan lebih banyak, butuh testing ulang untuk semua entry point

---

### Opsi B: Perbaiki Inisialisasi di `onshown` Handler (MINIMAL CHANGES)

**Strategi**: Hanya perbaiki `shoppingCart.php` agar menginisialisasi selectpicker dengan konfigurasi kustom.

#### Langkah-langkah:

##### 1. `application/modules/variant_cutover/views/shoppingCart.php` (line 568-586) + `konversi_varian` + `variant_cutover_backup`

Ubah `onshown` handler menjadi:

```javascript
onshown: function(dialogRef) {
    var content = dialogRef.getModalBody();

    // Set BootstrapVersion = '3' untuk Bootstrap 3 compatibility
    if (typeof $.fn.selectpicker === 'function') {
        if ($.fn.selectpicker.Constructor && typeof $.fn.tooltip === 'function' && $.fn.tooltip.Constructor) {
            $.fn.selectpicker.Constructor.BootstrapVersion = '3';
        }
    }

    // Evaluasi inline scripts TERLEBIH DAHULU
    content.find('script').each(function() {
        try {
            $.globalEval(this.text || this.textContent || this.innerHTML || '');
        } catch (e) {
            console.error('Script error in modal:', e);
        }
    });

    // Inisialisasi selectpicker SETELAH inline scripts jalan
    // Gunakan destroy + create ulang untuk memastikan konfigurasi kustom
    content.find('.selectpicker').each(function() {
        var $this = $(this);
        try {
            if ($this.parent('.bootstrap-select').length > 0) {
                $this.selectpicker('destroy');
            }
        } catch(e) { }
        $this.selectpicker({
            liveSearch: true,
            width: '320px',
            dropupAuto: false
        });
        $this.selectpicker('refresh');
    });

    // Jalankan scheduleAttrMasterInit yang sudah didefinisikan oleh inline script
    setTimeout(function() {
        if (typeof window.bootAttrMasterSelect === 'function') {
            window.bootAttrMasterSelect();
        }
    }, 100);
}
```

##### 2. `application/views/products/variant_settings.php`

**Opsional**: Tambahkan pengecekan agar `scheduleAttrMasterInit()` dan event binding di line 289-306 tidak double-fire:

```javascript
// line 750 - ubah menjadi:
if (window.scheduleAttrMasterInit && !window.__variantInited) {
    window.__variantInited = true;
    window.scheduleAttrMasterInit();
}
```

##### 3. CSS `<style>` dari `<head>` perlu dipastikan terbawa

Jika CSS `<style>` dari `<head>` tidak teraplikasi, pindahkan ke inline `<style>` di `<body>` atau load manual via:

```javascript
// Dalam onshown, setelah content di-load:
$('head').append(content.find('style').first());
```

**Keuntungan**: Minimal perubahan, risiko rendah
**Kerugian**: Masih ada kemungkinan konflik script, tidak mengatasi root cause arsitektur

---

### Opsi C: Hybrid — Refactor View + Perbaiki Handler (PALING RECOMMENDED)

Kombinasi Opsi A dan B:

1. Buat `variant_settings_partial.php` yang hanya berisi konten form + inline scripts (tanpa HTML wrapper, tanpa load JS/CSS)
2. Controller mendeteksi `?partial=1` dan memilih view yang sesuai
3. Semua module yang pake modal (`variant_cutover`, `konversi_varian`) panggil dengan `&partial=1`
4. `onshown` handler di-sederhanakan — cukup jalankan inline script + panggil fungsi init

---

## Hal yang Perlu Di-test Setelah Fix

| No | Skenario | Cara Test |
|----|----------|-----------|
| 1 | Point 1: Master Data (iframe) | Buka edit produk, pastikan panel varian muncul dan selectpicker berfungsi |
| 2 | Point 2: New Tab | Klik "Open New Window", pastikan halaman utuh berfungsi |
| 3 | Point 3: variant_cutover modal | Buka transaksi konversi, klik tombol varian, pastikan selectpicker muncul dan bisa search/pilih |
| 4 | Point 4: konversi_varian modal | Sama seperti point 3 |
| 5 | Save di modal | Simpan varian dari modal, pastikan sukses |
| 6 | Save di full page | Simpan varian dari full page (new tab/iframe), pastikan sukses |
| 7 | Tambah atribut baru | Tombol "Buat Atribut" di modal dan full page |
| 8 | Tambah nilai atribut | Tombol "Tambah Nilai" di modal dan full page |
| 9 | Refresh halaman setelah save | Pastikan data varian muncul setelah simpan |
| 10 | Sync ke parent (ctx=konversi) | Pastikan setelah save di modal variant_cutover, data tersinkron |

---

## Referensi Kode

### shoppingCart.php (semua 3 module) — Baris 561-589

```php
$editVarianTombol = base_url() . "ProductVariantSettings/edit?sID=$iID&ctx=konversi&jenisTr=" . $this->jenisTr;
$link_tombol = "BootstrapDialog.show({
      title: 'Tambah Data Varian Produk',
      message: $('<div></div>').load('$editVarianTombol'),
      size: BootstrapDialog.SIZE_WIDE,
      draggable: false,
      closable: true,
      onshown: function(dialogRef) {
        var content = dialogRef.getModalBody();
        if (typeof $.fn.selectpicker === 'function') {
          if ($.fn.selectpicker.Constructor && typeof $.fn.tooltip === 'function' && $.fn.tooltip.Constructor) {
            $.fn.selectpicker.Constructor.BootstrapVersion = '3';
          }
          try {
            content.find('.selectpicker').selectpicker('render');
          } catch (e) {
            console.warn('selectpicker render skipped:', e);
          }
        }
        content.find('script').each(function() {
          try {
            $.globalEval(this.text || this.textContent || this.innerHTML || '');
          } catch (e) {
            console.error('Script error in modal:', e);
          }
        });
      }
    });";
```

### Key inline scripts di variant_settings.php

| Line | Fungsi | Keterangan |
|------|--------|------------|
| 211 | `window.jq` | Referensi jQuery |
| 213-242 | `initAttrMasterSelect()` | Inisialisasi bootstrap-select dg opsi kustom |
| 244-264 | `bootAttrMasterSelect()` + `scheduleAttrMasterInit()` | Wrapper + scheduler |
| 267-287 | Toggle checkbox | Show/hide step sections |
| 289-306 | Event listener `focus`/`mousedown` pada `#sel-attr-master` | Trigger init |
| 308-336 | `syncPriceMode()` | Toggle kolom harga |
| 360-379 | `change` handler pada `#sel-attr-master` | Trigger add + refresh |
| 750-752 | Panggil `scheduleAttrMasterInit()` | Init otomatis |
