# 📖 Tutorial Manajemen Tema — Dashboard Admin Undangin

Panduan lengkap untuk **menambah, mengedit, dan menghapus** tema undangan di dashboard admin.

---

## 📑 Daftar Isi

- [Akses Halaman Tema](#-akses-halaman-tema)
- [Tutorial 1 — Menambah Tema Baru](#-tutorial-1--menambah-tema-baru)
- [Tutorial 2 — Edit Tema](#-tutorial-2--edit-tema)
- [Tutorial 3 — Hapus & Restore Tema](#-tutorial-3--hapus--restore-tema)
- [Tutorial 4 — Multi Jenis Acara](#-tutorial-4--multi-jenis-acara-pernikahan-khitanan-aqiqah-ulang-tahun-pengajian-event)
- [Tutorial 5 — Video Background 3D Motion](#-tutorial-5--video-background-3d-motion)
- [Troubleshooting](#-troubleshooting)
- [Catatan Keamanan](#-catatan-keamanan)
- [Workflow Cepat](#-workflow-cepat)

---

## 🚪 Akses Halaman Tema

1. Login sebagai **admin** → buka `/admin/dashboard`
2. Klik menu **Manajemen Tema** atau langsung buka URL `/admin/themes`

Anda akan melihat daftar semua tema dengan info: thumbnail, nama, status (Aktif/Nonaktif), harga, dan tombol aksi (Edit / Hapus / Restore).

---

## ➕ Tutorial 1 — Menambah Tema Baru

### Langkah-langkah

#### 1. Buka form tambah tema
Di halaman daftar tema, klik tombol biru-ungu **+ Tambah Tema** di pojok kanan atas card "Daftar Tema".

#### 2. Isi Informasi Tema

| Field | Wajib? | Keterangan |
|-------|--------|------------|
| **Nama Tema** | ✅ Ya | Contoh: `Royal Sapphire`, `Bali Sunset` |
| **Slug** | ✅ Ya | Otomatis terisi dari nama. Hanya huruf kecil, angka, dan tanda hubung. Contoh: `royal-sapphire` |
| **Harga Normal** | ❌ Opsional | Kosongkan jika ingin pakai harga default global dari `.env` |
| **Harga Promo** | ❌ Opsional | Harus lebih kecil dari Harga Normal |
| **Status Aktif** | Toggle | Centang jika ingin langsung tampil di katalog publik |

> ⚠️ **Slug bersifat permanen** — tidak bisa diubah setelah dibuat (karena jadi nama folder Blade & URL preview).

#### 3. Upload Aset (semua opsional, bisa dilakukan nanti)

- **Thumbnail** — gambar preview tema (PNG/JPG/WebP, max 4MB)
- **Musik Default** — backsound MP3 default tema (max 10MB)
- **File Blade** — upload `.blade.php` jadi (jika developer sudah siapkan template eksternal)

#### 4. Pilih Sumber Konten Blade (pilih SALAH SATU)

Ada 3 cara mengisi konten template Blade tema:

| Cara | Kapan dipakai |
|------|---------------|
| Upload file `.blade.php` | Developer sudah membuat template eksternal |
| Pilih **Duplikasi dari Tema Existing** | Mau buat variasi dari tema yang sudah ada (contoh: copy `barakah-love`, lalu ganti warna) |
| Tidak pilih keduanya | Sistem akan buat starter template minimal — Anda edit di Code Editor setelahnya |

#### 5. Klik tombol "Buat Tema"

Sistem otomatis akan:
- Membuat folder `resources/views/themes/{slug}/`
- Menyimpan file `index.blade.php`
- Memindahkan thumbnail ke `public/assets/thumbnail/{slug}.png`
- Memindahkan musik ke `public/assets/music/{slug}.mp3`
- Redirect ke halaman **Edit Tema**

---

## ✏️ Tutorial 2 — Edit Tema

Halaman Edit Tema punya **3 tab** yang bisa Anda pindahkan kapan saja:

### Tab 1️⃣ — 📝 Metadata & Aset

Untuk mengubah:
- Nama tema (slug TIDAK bisa diubah)
- Harga normal & promo
- Status aktif (toggle on/off)
- Ganti thumbnail (preview tampil di kiri sebelum upload)
- Ganti musik default (audio player bisa langsung didengar)

> Klik **"Simpan Metadata"** untuk apply perubahan.

### Tab 2️⃣ — 💻 Code Editor (Monaco)

Edit langsung kode HTML/Blade tema dengan editor full-screen yang punya fitur:

- ✅ Syntax highlight HTML
- ✅ Minimap di kanan untuk navigasi cepat
- ✅ Word wrap otomatis
- ✅ Counter jumlah baris
- ✅ Hotkey **Ctrl+S** untuk save cepat

#### Variabel yang bisa Anda pakai di template

```blade
{{ $invitation->getGroomName() }}        {{-- Nama mempelai pria --}}
{{ $invitation->getBrideName() }}        {{-- Nama mempelai wanita --}}
{{ $invitation->getAkadDatetime() }}     {{-- Tanggal akad --}}
{{ $invitation->getResepsiDatetime() }}  {{-- Tanggal resepsi --}}
{{ $invitation->getCoverImage() }}       {{-- URL cover --}}
{{ $invitation->getMusicFile() }}        {{-- URL musik --}}
{{ $invitation->getQuote() }}            {{-- Quote --}}
```

> Klik **"Simpan Konten"** atau tekan **Ctrl+S** untuk simpan.

> ⚠️ **Setelah save, view cache otomatis dibersihkan** — perubahan langsung tampak saat refresh halaman undangan.

### Tab 3️⃣ — 🪄 Duplikasi

Untuk **menimpa** konten Blade tema ini dengan salinan dari tema lain:

1. Pilih tema sumber dari dropdown
2. Konfirmasi popup peringatan
3. Klik **"Salin Konten"**

> ⚠️ **Konten lama akan ditimpa total**. Setelah duplikasi, biasanya Anda perlu sesuaikan path gambar/musik di kode supaya merujuk ke slug tema ini, bukan tema sumber.

### Tombol Preview

Di pojok kanan atas halaman edit, klik tombol **👁 Preview** → buka tab baru dengan render undangan menggunakan **data dummy** (foto Unsplash, nama contoh) untuk verifikasi tampilan tanpa perlu order asli.

---

## 🗑️ Tutorial 3 — Hapus & Restore Tema

### Hapus (Nonaktifkan)

1. Di halaman daftar tema, cari tema yang ingin dihapus
2. Klik tombol merah **🗑 Hapus** di kanan baris tema
3. Akan muncul konfirmasi:
   > *"Nonaktifkan tema 'X'? Tema akan disembunyikan dari katalog publik. Undangan yang sudah pakai tema ini tetap berfungsi."*
4. Klik **OK**

#### Apa yang terjadi setelah hapus?

Tema dinonaktifkan (`is_active = false`):

- ❌ **Tidak tampil** di katalog publik (`/themes`)
- ❌ **Tidak bisa dipilih** saat order undangan baru
- ✅ Undangan eksisting yang pakai tema ini **TETAP berfungsi normal**
- ✅ Folder Blade & aset **TIDAK dihapus** dari disk

### Restore (Aktifkan Kembali)

Tema yang dinonaktifkan muncul dengan badge abu-abu **Nonaktif**. Tombol kanan berubah menjadi tombol biru:

1. Klik tombol biru **🔄 Restore**
2. Tema langsung aktif kembali tanpa konfirmasi (aksi non-destruktif)

> 💡 **Hapus permanen?** Sengaja tidak disediakan untuk mencegah kerusakan undangan eksisting yang masih merujuk ke tema. Jika benar-benar perlu hapus permanen, lakukan manual via SSH/file manager dengan hati-hati.

---

## 🎯 Tutorial 4 — Multi Jenis Acara (Pernikahan, Khitanan, Aqiqah, Ulang Tahun, Pengajian, Event)

Platform sudah mendukung **6 jenis acara** dengan struktur form & data yang berbeda-beda.

### Daftar Jenis Acara

| Jenis Acara | Kategori Data | Field Utama |
|-------------|---------------|-------------|
| 💍 **Pernikahan** | 2 mempelai (wedding) | Nama Pria, Nama Wanita, Akad, Resepsi |
| 🌙 **Khitanan** | 1 orang (anak + ortu) | Nama Anak, Ayah, Ibu, Acara Utama |
| 🍼 **Aqiqah** | 1 orang (anak + ortu) | Nama Anak, Ayah, Ibu, Acara Utama |
| 🎂 **Ulang Tahun** | 1 orang (anak + ortu) | Nama Anak, Ayah, Ibu, Acara Utama |
| 🕌 **Pengajian** | Acara saja | Judul Acara, Penyelenggara, Deskripsi, Foto Acara |
| 🎉 **Event** | Acara saja | Judul Acara, Penyelenggara, Deskripsi, Foto Acara |

### Tema Bawaan per Jenis Acara

5 tema starter sudah dibuat untuk masing-masing jenis (selain pernikahan yang sudah punya 13+ tema):

| Slug | Jenis | Style |
|------|-------|-------|
| `barakah-khitanan` | Khitanan | Sage green + gold, Islamic |
| `emerald-aqiqah` | Aqiqah | Emerald + cream, hadits aqiqah |
| `floral-ultah` | Ulang Tahun | Pink-purple pastel, balon & cake |
| `pengajian-akbar` | Pengajian | Sage + gold, mihrab icon |
| `royal-event` | Event | Navy + gold glassmorphism, formal |
| `motion-garden` | Pernikahan | 3D Motion dengan video background |

### Cara Menambah Tema Baru per Jenis Acara

1. Admin → **Manajemen Tema** → klik **+ Tambah Tema**
2. Pilih **Jenis Acara** dengan klik salah satu kartu (radio cards di atas form)
3. Isi nama, slug otomatis ter-generate
4. Pilih sumber konten:
   - Upload `.blade.php` jadi
   - Atau **Duplikasi dari tema existing** dengan jenis acara yang sama (recommended)
   - Atau biarkan sistem buat starter template otomatis (akan menyesuaikan jenis acara)
5. Klik **Buat Tema** → masuk ke editor untuk customize

### Variabel Blade per Jenis Acara

**Pernikahan (Wedding):**
```blade
{{ $invitation->groom_name }}        {{ $invitation->bride_name }}
{{ $invitation->groom_father }}      {{ $invitation->bride_father }}
{{ $invitation->groom_mother }}      {{ $invitation->bride_mother }}
{{ $invitation->groom_photo }}       {{ $invitation->bride_photo }}
{{ $invitation->akad_datetime }}     {{ $invitation->resepsi_datetime }}
{{ $invitation->akad_location }}     {{ $invitation->resepsi_location }}
```

**Khitanan / Aqiqah / Ulang Tahun (Single Person):**
```blade
{{ $invitation->child_name }}         {{ $invitation->child_nickname }}
{{ $invitation->child_father }}       {{ $invitation->child_mother }}
{{ $invitation->child_photo }}
{{ $invitation->main_event_title }}   {{ $invitation->main_event_datetime }}
{{ $invitation->main_event_location }} {{ $invitation->main_event_address }}
```

**Pengajian / Event (Event Only):**
```blade
{{ $invitation->event_title }}        {{ $invitation->event_description }}
{{ $invitation->event_photo }}        {{ $invitation->event_host }}
{{ $invitation->main_event_datetime }} {{ $invitation->main_event_location }}
```

**Helper Universal (semua jenis):**
```blade
{{ $invitation->event_type }}         {{-- pernikahan, khitanan, dll --}}
{{ $invitation->event_type_label }}   {{-- "Pernikahan", "Khitanan", dll --}}
{{ $invitation->event_category }}     {{-- wedding, single_person, event_only --}}

@if($invitation->isWedding()) ... @endif
@if($invitation->isSinglePerson()) ... @endif
@if($invitation->isEventOnly()) ... @endif
```

### Filter Katalog (Public)

Halaman `/themes` punya tab filter di atas — pengunjung bisa filter tema berdasarkan jenis acara:

```
🎨 Semua │ 💍 Pernikahan │ 🌙 Khitanan │ 🍼 Aqiqah │ 🎂 Ulang Tahun │ 🕌 Pengajian │ 🎉 Event
```

### Form Order Adaptif

Saat customer klik **"Buat Undangan"**:
1. **Step 1**: Pilih jenis acara (6 kartu visual)
2. **Step 2**: Pilih tema — hanya tema dengan jenis acara yang dipilih yang muncul
3. **Step 3**: Form data acara otomatis menyesuaikan:
   - Wedding → 2 input nama (pria & wanita)
   - Single → 1 input nama anak
   - Event-only → input judul acara + penyelenggara
4. **Tanggal acara** (universal) + nomor WhatsApp + slug link

---

## 🎬 Tutorial 5 — Video Background 3D Motion

Tema **Motion Garden 3D** mendukung **video MP4 fullscreen** sebagai background di tiap section undangan (cover, mempelai, akad, resepsi, footer) — efek visual mirip site `the.invisimple.id/m02/`.

### Struktur Video per Section

| Slot | Background untuk Section |
|------|--------------------------|
| 🎬 **Video Cover** | Halaman pembuka & penutup (footer) |
| 🌿 **Video Utama** | Section profil mempelai |
| 🤝 **Video Akad** | Section detail akad nikah |
| 🎉 **Video Resepsi** | Section detail resepsi |

> **Fallback otomatis:** kalau slot tertentu tidak ada video, sistem cari video utama → cover image → gradient warna. Jadi semua slot opsional.

### Cara Upload Video — Client Dashboard

1. Client login → menu **Settings**
2. Scroll ke **Section 3: Media & Galeri**
3. Scroll lagi sampai melihat area **🎥 Video Background — 3D Motion** (dipisah garis putus-putus)
4. Pilih file MP4/WebM/MOV untuk slot yang diinginkan
5. Preview otomatis muncul kalau sudah ada video terupload sebelumnya
6. Klik **Simpan** di bawah form
7. Buka link undangan → video langsung jadi background fullscreen

### Spesifikasi Video Optimal

| Parameter | Saran |
|-----------|-------|
| **Format** | MP4 (H.264) — paling kompatibel di semua browser |
| **Durasi** | 5–15 detik (akan looping otomatis) |
| **Resolusi** | 720p atau 1080p |
| **Ukuran File** | 3–8 MB (dikompres tinggi agar lancar di mobile) |
| **Audio** | Tidak perlu — video di-mute otomatis |
| **Aspect Ratio** | Portrait (9:16) atau Landscape (16:9) keduanya OK, akan di-cover |

> ⚠️ **Maksimal 30 MB per file**. Lebih dari itu akan ditolak. Pakai tool kompresi seperti HandBrake atau FFmpeg untuk mengecilkan ukuran.

### Cara Pakai untuk Tema Custom (Developer)

Variabel Blade yang tersedia di tema mana pun:

```blade
{{ $invitation->cover_video_url }}    {{-- URL video cover --}}
{{ $invitation->main_video_url }}     {{-- URL video utama --}}
{{ $invitation->akad_video_url }}     {{-- URL video akad --}}
{{ $invitation->resepsi_video_url }}  {{-- URL video resepsi --}}
```

Contoh penggunaan:

```blade
<section class="video-section">
    @if($invitation->cover_video_url)
        <video class="video-bg" autoplay muted playsinline loop>
            <source src="{{ $invitation->cover_video_url }}" type="video/mp4">
        </video>
    @else
        <img class="video-bg" src="{{ asset('storage/' . $invitation->cover_image) }}">
    @endif
    <div class="overlay-content">
        <!-- konten teks di atas video -->
    </div>
</section>
```

### CSS Pattern (Wajib agar video tampil benar)

```css
.video-section {
    position: relative;
    min-height: 100vh;
    overflow: hidden;
}
.video-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
}
/* Overlay gelap agar text terbaca */
.video-section::after {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,.5);
    z-index: 1;
}
.overlay-content {
    position: relative;
    z-index: 2;
}
```

### Atribut Video Wajib

```html
<video autoplay muted playsinline loop>
```

| Atribut | Wajib? | Alasan |
|---------|--------|--------|
| `autoplay` | ✅ | Mulai otomatis tanpa klik |
| `muted` | ✅ | Browser modern blokir autoplay video bersuara |
| `playsinline` | ✅ | iOS Safari — agar tidak fullscreen otomatis |
| `loop` | Direkomendasikan | Video pendek di-loop terus |

### Storage Path

Video tersimpan di:

```
storage/app/public/invitations/{invitation_id}/{uniqid}_video_{slot}.mp4
```

Contoh path di JSON content:
```json
{
  "media": {
    "video_cover":   "invitations/42/abc123_video_cover.mp4",
    "video_main":    "invitations/42/def456_video_main.mp4",
    "video_akad":    "invitations/42/ghi789_video_akad.mp4",
    "video_resepsi": "invitations/42/jkl012_video_resepsi.mp4"
  }
}
```

### Tips & Gotchas

- **Mobile data**: video autoplay+loop boros bandwidth — sediakan opsi disable di tema kalau perlu
- **iOS Safari** kadang tidak autoplay walau sudah `muted+playsinline` — pastikan `playsinline` ditulis pertama setelah `muted`
- **Background music** & video bisa berjalan bersamaan — video sudah `muted` jadi tidak bentrok
- **Fallback image**: selalu sediakan `cover_image` agar saat video belum loaded ada placeholder
- **CDN**: untuk video besar, simpan di CDN (Cloudflare R2, Bunny CDN, dsb) lalu pakai URL eksternal di JSON content — tidak perlu upload ke server

---

## 🛠️ Troubleshooting

| Masalah | Solusi |
|---------|--------|
| **"Folder tema dengan slug ini sudah ada"** saat tambah tema | Slug berbenturan dengan folder lama. Pilih slug lain ATAU hapus folder `resources/views/themes/{slug}/` lewat file manager |
| **Perubahan kode Blade tidak tampak setelah save** | Jalankan `php artisan view:clear` di terminal. Atau hard refresh browser (Ctrl+F5) |
| **Upload thumbnail/musik gagal** | Cek ukuran file (thumbnail max 4MB, musik max 10MB). Pastikan format sesuai (PNG/JPG/WebP untuk gambar, MP3 untuk audio) |
| **Tema tidak muncul di `/themes`** | Pastikan toggle **Tema aktif** dicentang di tab Metadata |
| **Code editor (Monaco) tidak loading** | Cek koneksi internet — Monaco di-load dari CDN jsdelivr. Clear browser cache jika perlu |
| **Validasi slug ditolak** | Slug HANYA boleh huruf kecil, angka, dan tanda hubung (`-`). Tidak boleh spasi, simbol lain, atau huruf besar |
| **Harga promo lebih besar dari harga normal** | Harga promo wajib lebih kecil. Jika harga normal kosong, akan dibandingkan dengan harga default global |
| **Video upload gagal / hilang setelah simpan** | Cek ukuran (max 30MB) & format (mp4/webm/mov). File yang gagal validasi otomatis di-skip tanpa error |
| **Video tidak autoplay di iOS** | Pastikan tema pakai atribut `muted playsinline loop autoplay`. iPhone blokir video bersuara sampai user interaksi pertama |
| **Video lambat loading di mobile** | Kompres ke 720p + bitrate rendah (~3-5MB). Atau host di CDN dan pakai URL eksternal di `content.media.video_*` |
| **Tema khitanan/aqiqah pakai data mempelai** | Pastikan kolom `event_type` di tabel `themes` sudah benar. Jalankan migration & seeder: `php artisan migrate && php artisan db:seed --class=ThemeSeeder` |
| **Form order tidak menampilkan tema sesuai jenis acara** | Clear browser cache. Setiap tema harus punya `event_type` yang sama dengan radio button "Jenis Acara" yang dipilih customer |

---

## 🔐 Catatan Keamanan

- **Hanya akun admin** yang bisa akses semua route ini (middleware `auth + admin`)
- File Blade dieksekusi sebagai PHP — **jangan paste kode dari sumber tidak terpercaya** ke Code Editor
- Slug divalidasi dengan regex `^[a-z0-9]+(?:-[a-z0-9]+)*$` untuk mencegah path traversal
- Semua aksi tercatat di tabel `activity_logs` untuk audit trail dengan event:
  - `theme.created` — tema baru dibuat
  - `theme.updated` — metadata tema diubah
  - `theme.content_updated` — konten Blade diedit
  - `theme.duplicated` — konten dicopy dari tema lain
  - `theme.deactivated` — tema dihapus (soft delete)
  - `theme.restored` — tema diaktifkan kembali
  - `theme.price_updated` — harga tema diubah

---

## ⚡ Workflow Cepat

### Skenario: Buat 3 variasi warna dari tema `barakah-love`

1. Klik **+ Tambah Tema** → nama: `Barakah Gold`, slug otomatis `barakah-gold` → pilih sumber duplikasi: `Barakah Love` → submit
2. Otomatis masuk ke halaman Edit Tema → buka tab **💻 Code Editor**
3. Ubah class warna Tailwind (mis. `bg-rose-500` → `bg-amber-500`)
4. Tekan **Ctrl+S** untuk simpan
5. Klik **👁 Preview** di pojok kanan atas untuk verifikasi
6. Ulangi langkah 1-5 untuk variasi `Barakah Emerald`, `Barakah Royal`

⏱️ **Selesai dalam < 5 menit per tema.**

---

## 📚 Referensi Teknis

### Struktur File Tema

```
resources/views/themes/{slug}/index.blade.php   ← konten template
public/assets/thumbnail/{slug}.png               ← thumbnail preview
public/assets/music/{slug}.mp3                   ← musik default
```

### Tabel `themes` (database)

| Kolom | Tipe | Keterangan |
|-------|------|-----------|
| `id` | bigint | Primary key |
| `name` | string | Nama tema |
| `slug` | string (unique) | URL & nama folder |
| `view_path` | string | Path Blade (`themes.{slug}.index`) |
| `thumbnail` | string\|null | Filename thumbnail |
| `is_active` | boolean | Status aktif |
| `price` | int\|null | Harga normal |
| `promo_price` | int\|null | Harga promo |

### Routes Admin Tema

| Method | URI | Aksi |
|--------|-----|------|
| GET | `/admin/themes` | List semua tema |
| GET | `/admin/themes/create` | Form tambah tema |
| POST | `/admin/themes` | Simpan tema baru |
| GET | `/admin/themes/{id}/edit` | Form edit tema |
| PUT | `/admin/themes/{id}` | Update metadata |
| PUT | `/admin/themes/{id}/content` | Update konten Blade |
| POST | `/admin/themes/{id}/duplicate` | Duplikasi dari tema lain |
| DELETE | `/admin/themes/{id}` | Soft delete (nonaktifkan) |
| POST | `/admin/themes/{id}/restore` | Aktifkan kembali |
| POST | `/admin/themes/{id}/price` | Update harga (legacy) |
| POST | `/admin/themes/default-price` | Update harga default global |

---

_Dokumen ini dibuat untuk platform Undangin._
