# Dokumentasi Proyek: Cluster Management System (CMS Multi-Domain & Multi-Template)

Dokumentasi ini mencakup arsitektur keseluruhan sistem **Admin Cluster Panel** (Backend CMS berbasis Laravel 13 + Filament v5) beserta tiga aplikasi **Frontend Multi-Domain** berbasis Next.js 16 (`cluster-homepage`, `cluster-b-homepage`, dan `cluster-c-homepage`).

---

## 1. Gambaran Umum Arsitektur (System Overview)

Proyek ini dirancang sebagai **Multi-Tenant / Multi-Domain Headless CMS** di mana satu panel admin pusat mengelola konten, tema warna, SEO metadata, navigasi menu, produk, brand, serta artikel/portofolio pemasangan untuk banyak domain sekaligus.

```mermaid
flowchart TD
    subgraph Admin["Backend CMS (/Volumes/Data/www/admin-cluster-panel)"]
        Filament["Filament v5 Admin Panel (/admin)"]
        API["REST API (FrontendController - /api/*)"]
        Storage["Public Storage (/storage/*)"]
        DB[("Database (w_domain, wp_post, dll.)")]
        Filament --> DB
        API --> DB
    end

    subgraph Frontends["Next.js 16 Frontends (/Volumes/Data/nextjs/*)"]
        ClusterA["Cluster A (cluster-homepage)<br/>Layout Minimalis / Grid Klasik"]
        ClusterB["Cluster B (cluster-b-homepage)<br/>Layout Full-Screen Hero + Marquee"]
        ClusterC["ClusterC (cluster-c-homepage)<br/>Layout Split Hero + Bento Grid + Sidebar"]
    end

    User(("Pengunjung Website")) -->|"HTTP Request (Header: Host)"| Frontends
    ClusterA & ClusterB & ClusterC -->|"SSR Fetch ?domain={host}"| API
    ClusterA & ClusterB & ClusterC -->|"Next.js Rewrite /storage/:path*"| Storage
```

### Lokasi Direktori Proyek
| Komponen | Path Direktori | Teknologi Utama | Peran |
| :--- | :--- | :--- | :--- |
| **Backend CMS & API** | `/Volumes/Data/www/admin-cluster-panel` | Laravel 13, PHP 8.3+, Filament v5.4, TinyEditor | Panel Admin terpusat, manajemen konten multi-domain, penyedia REST API & file media |
| **Frontend Cluster A** | `/Volumes/Data/nextjs/cluster-homepage` | Next.js 16.1, React 19, Tailwind CSS v4, Shadcn UI | Template Frontend Tipe A (Centered Hero, Grid 6-Kolom CallOut, Grid Statis Brand) |
| **Frontend Cluster B** | `/Volumes/Data/nextjs/cluster-b-homepage` | Next.js 16.1, React 19, Tailwind CSS v4, Shadcn UI | Template Frontend Tipe B (Full-Screen Background Hero, Circular Categories, Dual Marquee Brand) |
| **Frontend Cluster C** | `/Volumes/Data/nextjs/cluster-c-homepage` | Next.js 16.1, React 19, Tailwind CSS v4, Shadcn UI | Template Frontend Tipe C (2-Column Split Hero, Bento Grid Categories, Single Marquee Brand, Mobile Sidebar) |

---

## 2. Dokumentasi Backend (`admin-cluster-panel`)

### 2.1. Autentikasi & Hak Akses (RBAC)
Backend menggunakan model autentikasi kustom [`WUser`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WUser.php) (tabel `w_user`) yang mengimplementasikan `FilamentUser`.
- **Login & Profil**: Menggunakan halaman kustom [`CustomLogin`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Pages/Auth/CustomLogin.php) dan [`EditProfile`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Pages/Auth/EditProfile.php).
- **Syarat Akses Panel**: User harus memiliki `status === 1` (Aktif).
- **Level User**:
  - `1` = **Admin** (Hanya dapat mengelola **Post / Artikel & Pemasangan**)
  - `2` = **Manajemen** (Hanya dapat mengelola **Post / Artikel & Pemasangan**)
  - `3` = **Superadmin** (Akses penuh ke seluruh modul)
- **Pembatasan Resource**: Trait [`RestrictsToSuperadmin`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Traits/RestrictsToSuperadmin.php) membatasi akses `canViewAny`, `canCreate`, `canEdit`, dan `canDelete` hanya untuk user dengan `level == 3` pada resource:
  - [`WDomainResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/WDomains/WDomainResource.php) (Domain)
  - [`CategoryProductResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/CategoryProducts/CategoryProductResource.php) (Kategori Produk)
  - [`CallOutResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/CallOuts/CallOutResource.php) (Call Out / Keunggulan)
  - [`WBrandResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/Brands/WBrandResource.php) (Brand / Mitra)
  - [`MenuResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/Menus/MenuResource.php) (Menu Header & Footer)
  - [`WUserResource`](file:///Volumes/Data/www/admin-cluster-panel/app/Filament/Resources/WUsers/WUserResource.php) (Daftar User)

---

### 2.2. Skema Database & Model Eloquent

```mermaid
erDiagram
    WDomain ||--o{ WpPost : "domain_id"
    WDomain ||--o{ WCategoryProduct : "domain_id"
    WDomain ||--o{ WCallOut : "domain_id"
    WDomain ||--o{ WBrand : "domain_id"
    WDomain ||--o{ WMenu : "domain_id"
    WDomain }o--|| WLookup : "theme -> code (theme)"
    WpPost ||--o{ WPostMedia : "post_id"
    WpPost }o--|| WLookup : "category -> code (pocat)"
    WpPost }o--|| MProvince : "province_id"
    WpPost }o--|| MCity : "city_id"
    WpPost }o--|| MProduct : "product_master_id"
    MProvince ||--o{ MCity : "province_id"
```

1. **[`WDomain`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WDomain.php) (`w_domain`)**
   Menyimpan konfigurasi per domain:
   - Identitas & Visual: `domain`, `name`, `logo`, `favicon`, `banner`, `theme`, `tag_line`, `short_description`, `long_description`, `short_profile`, `address`, `google_map`, `footer`.
   - Kustomisasi Judul Panel Homepage: `call_out_title`, `call_out_description`, `category_title`, `category_description`, `post_title`, `post_description`, `brand_title`, `brand_description`.
   - Call to Action & Kontak: `cta_title`, `cta_description`, `whatsapp`, `instagram`, `youtube`, `tiktok`.
   - SEO & Sitemap: `meta_title`, `meta_description`, `meta_keyword`, `file_sitemap`.

2. **[`WpPost`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WpPost.php) (`wp_post`) & [`WPostMedia`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WPostMedia.php) (`w_post_media`)**
   Menyimpan artikel blog maupun portofolio pemasangan untuk masing-masing domain:
   - Konten & SEO: `domain_id`, `title`, `slug`, `description` (HTML dari TinyEditor), `media` (Thumbnail), `meta_title`, `meta_description`, `meta_keyword`, `status`.
   - Kategori (`category` merujuk ke `w_lookup` kategori `pocat`):
     - `1` = Halaman
     - `2` = **Pemasangan** (Memunculkan field khusus spesifikasi proyek di Filament & Frontend)
     - `3` = Panduan
     - `4` = Informasi
   - Field Khusus Pemasangan (`category == 2`): `installation_date`, `product_master_id` (relasi ke [`MProduct`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/MProduct.php)), `material_name`, `quantity`, `province_id` ([`MProvince`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/MProvince.php)), `city_id` ([`MCity`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/MCity.php)).
   - Galeri: Relasi `hasMany` ke `WPostMedia` (`w_post_media.image_url`) yang ditampilkan menggunakan Lightbox di frontend.

3. **[`WCategoryProduct`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WCategoryProduct.php) (`w_category_product`)**
   Menyimpan daftar kategori produk per domain (`domain_id`, `name`, `description`, `media`, `position`, `status`).

4. **[`WCallOut`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WCallOut.php) (`w_call_out`)**
   Menyimpan poin keunggulan / fitur per domain (`domain_id`, `title`, `description`, `media_type` [1 = Image, 2 = Iconify Icon Code], `media`, `position`, `status`).

5. **[`WBrand`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WBrand.php) (`w_brand`)**
   Menyimpan daftar brand/merk beserta halaman detailnya (`domain_id`, `name`, `slug`, `url_identifier` [`brand` atau `merk`], `description`, `media`, `position`, `status`, `meta_title`, `meta_description`, `meta_keyword`).

6. **[`WMenu`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WMenu.php) (`w_menu`)**
   Menyimpan item navigasi dinamis per domain (`domain_id`, `type` [`1` = Header, `2` = Footer], `name`, `url`, `position`, `status`).

7. **[`WLookup`](file:///Volumes/Data/www/admin-cluster-panel/app/Models/WLookup.php) (`w_lookup`)**
   Tabel referensi key-value untuk kategori post (`pocat`) dan daftar pilihan tema warna (`theme`: `default`, `marshmallow`, `art_deco`, `vs_code`, `spotify`, `summer`, `material_design`, `marvel`, `velorant`, `ghibli_studio`, `modern_minimal`, `nature`, `neo_brutalism`, `pastel_dreams`, `midnight_bloom`, `sunset_horizon`, `slack`, `perplexity`, `caffeine`, `claude`, `clean_slate`, `corporate`).

---

### 2.3. Spesifikasi Endpoint REST API
Seluruh endpoint publik didefinisikan di [`routes/web.php`](file:///Volumes/Data/www/admin-cluster-panel/routes/web.php) dengan prefix `/api` dan ditangani oleh [`FrontendController`](file:///Volumes/Data/www/admin-cluster-panel/app/Http/Controllers/FrontendController.php):

| Method | Endpoint | Query Params | Deskripsi & Response |
| :--- | :--- | :--- | :--- |
| `GET` | `/api/homepage` | `domain` (wajib) | Mengembalikan objek JSON `{ domain, categories, call_outs, brands, latest_posts, menu }` untuk domain aktif (`status = 1`). `latest_posts` berisi maksimal 10 post terbaru berkategori `2` (Pemasangan). |
| `GET` | `/api/posts` | `domain` (wajib), `category` (opsional), `page` (opsional) | Mengembalikan daftar post terpaginasi (12 item/halaman). Jika `category` diisi (misal `category=2`), memfilter kategori tersebut. Jika tidak diisi, mengembalikan post selain kategori `2` (artikel umum). |
| `GET` | `/api/posts/{slug}` | `domain` (wajib) | Mengembalikan detail lengkap satu post beserta relasi `creator`, `category`, `postMedia`, `province`, `city`, `product`, dan data `domain`. |
| `GET` | `/api/brands/{urlIdentifier}/{slug}` | `domain` (wajib) | Mengembalikan detail halaman brand yang cocok dengan `domain_id`, `url_identifier` (`brand`/`merk`), dan `slug`. |

---

## 3. Dokumentasi Frontend Next.js (3 Cluster)

Ketiga project frontend menggunakan **Next.js 16 (App Router)**, **React 19**, **Tailwind CSS v4**, dan **Shadcn UI**, serta berbagi fondasi arsitektur yang sama di `src/lib/api.ts` dan `next.config.ts`.

### 3.1. Cara Kerja Multi-Domain & Dynamic Theming di Frontend
1. **Deteksi Domain Otomatis via HTTP Host Header**:
   Setiap Server Component membaca header `host` dari request masuk menggunakan `await headers()` dari `next/headers`, lalu memanggil API backend (`fetchHomepageData(host)`).
2. **Dynamic CSS Theme (`data-domain`)**:
   Di [`src/app/layout.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/app/layout.tsx#L274), atribut `<html data-domain={domain?.theme}>` diinjeksi secara dinamis dari database (`w_domain.theme`). File `src/app/globals.css` mendefinisikan variabel warna CSS (`--background`, `--primary`, `--secondary`, font families, radius, dll.) untuk masing-masing kode tema (`marshmallow`, `art_deco`, `spotify`, `claude`, `corporate`, dll.), ditambah mode terang/gelap (`next-themes`).
3. **Proxy File Media & Sitemap (`next.config.ts`)**:
   Melalui fitur `rewrites()` di [`next.config.ts`](file:///Volumes/Data/nextjs/cluster-homepage/next.config.ts#L19-L30):
   - Request ke `/storage/:path*` di-rewrite langsung ke `${NEXT_PUBLIC_API_URL}/storage/:path*` pada backend Laravel.
   - Request ke `/:path*.xml` di-rewrite ke `${NEXT_PUBLIC_API_URL}/storage/:path*.xml` untuk melayani file sitemap XML per domain.

---

### 3.2. Struktur Routing Halaman (Berlaku di Ketiga Frontend)

| Route Path | File Komponen | Fungsi Halaman |
| :--- | :--- | :--- |
| `/` | `src/app/(pages)/page.tsx` | **Homepage Dinamis**: Menampilkan `HeroSection`, `CallOutPanel2`, `CategoryPanel`, `BrandPanel`, dan `PostPanel` beserta SEO Metadata & OpenGraph dari `w_domain`. |
| `/pemasangan` | `src/app/(pages)/pemasangan/page.tsx` | **Daftar Portofolio Pemasangan**: Menampilkan daftar post dengan `category=2` (12 per halaman) lengkap dengan lokasi provinsi/kota dan ringkasan spesifikasi (Produk, Bahan, Jumlah). |
| `/post-list` | `src/app/(pages)/post-list/page.tsx` | **Daftar Artikel / Blog**: Menampilkan daftar artikel umum (`category != 2`) dengan paginasi. |
| `/post/[slug]` | `src/app/(pages)/post/[slug]/page.tsx` | **Detail Post / Pemasangan**: Menampilkan konten artikel. Jika post berkategori `2` (Pemasangan), otomatis menampilkan blok lokasi, tanggal pemasangan, kartu spesifikasi (Produk, Material, Jumlah), serta galeri foto interaktif ([`PostGallery`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/PostGallery.tsx) dengan `yet-another-react-lightbox`). |
| `/[url_identifier]/[slug]` | `src/app/(pages)/[url_identifier]/[slug]/page.tsx` | **Detail Brand / Merk**: Menangani URL dinamis seperti `/brand/sharp` atau `/merk/onna` sesuai `url_identifier` yang diatur di panel admin. |
| `/contact-us` | `src/app/(pages)/contact-us/page.tsx` | **Halaman Kontak**: Menampilkan informasi kontak, alamat, dan integrasi Google Maps (`domain.google_map`). |

---

### 3.3. Perbedaan Desain & Komponen Antar 3 Frontend Cluster

Walaupun menggunakan API dan struktur route yang identik, ketiga folder frontend menyediakan **3 variasi tata letak (layout template)** yang berbeda untuk memberikan variasi visual antar grup domain:

| Aspek / Komponen | Cluster A (`cluster-homepage`) | Cluster B (`cluster-b-homepage`) | Cluster C (`cluster-c-homepage`) |
| :--- | :--- | :--- | :--- |
| **Path Direktori** | `/Volumes/Data/nextjs/cluster-homepage` | `/Volumes/Data/nextjs/cluster-b-homepage` | `/Volumes/Data/nextjs/cluster-c-homepage` |
| **Hero Section** ([`hero-section.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/blocks/hero-section/hero-section.tsx)) | **Centered Minimalist Hero**: Teks rata tengah dengan latar `bg-muted`, badge nama domain, tagline, deskripsi singkat, dan tombol WhatsApp. | **Full-Screen Background Hero**: Tinggi `min-h-[110vh]` menggunakan gambar `domain.banner` (dengan fallback Unsplash) berlapis gradient gelap, animasi `fadeInUp`, dan teks di kiri bawah. | **Split 2-Column Hero**: Grid 2 kolom modern; teks & badge beranimasi ping di kiri, foto `domain.banner` dalam bingkai `rounded-[2.5rem]` berbayang di kanan. |
| **Call Out Panel** ([`dynamic-panels.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/blocks/dynamic-panels.tsx)) | Grid kompak 2–6 kolom (`lg:grid-cols-6`) dengan ikon/gambar kecil di tengah. | Grid kartu 4 kolom (`lg:grid-cols-4`) bergaya bersih (`bg-white rounded-2xl shadow-sm`) dengan ikon di pojok kiri atas. | Grid kartu 3 kolom (`lg:grid-cols-3`) bergaya premium (`rounded-[2rem] p-10`) dengan watermark ikon besar transparan di latar kartu. |
| **Category Panel** ([`dynamic-panels.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/blocks/dynamic-panels.tsx)) | Grid 5 kolom kartu persegi panjang dengan gambar `aspect-video` di atas dan teks di bawah. | Grid 3 kolom dengan gambar berbentuk lingkaran penuh (`rounded-full w-28 h-28`) dan teks rata tengah. | **Bento Grid Layout**: Item pertama berukuran besar (`col-span-2 row-span-2`), kartu memiliki overlay gradient gelap dengan teks putih di bagian bawah. |
| **Brand Panel** ([`dynamic-panels.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/blocks/dynamic-panels.tsx)) | Grid statis 6 kolom (`lg:grid-cols-6`) dengan efek *grayscale-to-color* saat di-hover. | **Dual Infinite Marquee**: 2 baris kartu brand yang bergerak otomatis berlawanan arah (baris atas ke kiri, baris bawah ke kanan). | **Single-Row Horizontal Marquee**: Judul "Trusted By / Mitra Utama Kami" di sisi kiri dan marquee bergerak ke kiri di sisi kanan. |
| **Navigasi & Header** ([`header.tsx`](file:///Volumes/Data/nextjs/cluster-homepage/src/components/layout/header.tsx)) | Header sticky dengan `MenuNavigation` dan dropdown mobile (`MenuDropdown`). | Header transparan/glassmorphism menyesuaikan hero full-screen. | Dilengkapi komponen [`sidebar.tsx`](file:///Volumes/Data/nextjs/cluster-c-homepage/src/components/layout/sidebar.tsx) khusus untuk navigasi drawer yang lebih modern. |

---

## 4. Panduan Instalasi & Menjalankan Proyek (Setup & Development)

### 4.1. Menjalankan Backend (`admin-cluster-panel`)
1. Masuk ke direktori backend:
   ```bash
   cd /Volumes/Data/www/admin-cluster-panel
   ```
2. Instal dependensi PHP & JS serta jalankan setup:
   ```bash
   composer install
   cp .env.example .env
   php artisan key:generate
   ```
3. Konfigurasi database di `.env` (SQLite atau MySQL), lalu jalankan migrasi & seeder:
   ```bash
   php artisan migrate
   php artisan db:seed --class=WLookupSeeder
   php artisan db:seed --class=ThemeLookUpSeeder
   php artisan db:seed --class=MasterDataSeeder
   ```
   > **Catatan**: `MasterDataSeeder` akan mengambil data Provinsi & Kota seluruh Indonesia dari API wilayah serta mengisi master produk pemasangan. Untuk data dummy pengujian (3 domain), Anda dapat menjalankan `php artisan db:seed --class=DummyDataSeeder`.
4. Buat symbolic link untuk storage publik agar gambar dapat diakses oleh frontend:
   ```bash
   php artisan storage:link
   ```
5. Jalankan server development (default berjalan di port `8000` atau `8001`):
   ```bash
   php artisan serve --port=8001
   ```
   Akses panel admin di: `http://127.0.0.1:8001/admin`

---

### 4.2. Menjalankan Frontend (`cluster-homepage`, `cluster-b-homepage`, `cluster-c-homepage`)
Langkah ini berlaku sama untuk ketiga direktori frontend:
1. Masuk ke salah satu direktori frontend:
   ```bash
   cd /Volumes/Data/nextjs/cluster-homepage
   # atau /Volumes/Data/nextjs/cluster-b-homepage
   # atau /Volumes/Data/nextjs/cluster-c-homepage
   ```
2. Salin konfigurasi `.env` dan sesuaikan URL API Backend:
   ```bash
   cp .env.example .env
   ```
   Pastikan isi `.env` memuat:
   ```env
   BASEPATH=
   NEXT_PUBLIC_APP_URL=http://localhost:3000
   NEXT_PUBLIC_API_URL=http://127.0.0.1:8001
   NEXT_PUBLIC_DOMIN_EXTENSION=in
   ```
3. Instal dependensi dan jalankan server Next.js:
   ```bash
   npm install
   npm run dev
   ```
4. **Pengujian Multi-Domain Lokal**:
   Karena frontend mendeteksi konfigurasi berdasarkan HTTP `Host` header (`request->query('domain')`), pastikan nilai kolom **Domain** di menu **Master Data -> Domain** pada Filament Admin cocok dengan host yang Anda akses di browser (misalnya `localhost:3000` untuk pengujian lokal, atau gunakan `/etc/hosts` seperti `rollerblindjakarta.in` / `rollerblindbekasi.in`).
