# REDD+SIS Dynamic Pages Documentation

## Overview

Dokumen ini menerangkan bagaimana semua page di REDD+SIS frontend (`resources/views/reddsis/`) ditukar dari **static content** kepada **dynamic content** yang boleh diurus melalui admin panel.

---

## 1. Sebelum (Static)

Setiap page adalah file `.blade.php` yang mengandungi HTML hardcode:

```
resources/views/reddsis/pages/
├── home.blade.php          ← Content hardcode dalam HTML
├── what-is-redd-plus.blade.php  ← Content hardcode dalam HTML
├── cancun-safeguards.blade.php  ← Content hardcode dalam HTML
└── ... (13 files)
```

Untuk tukar content, kena edit file code secara manual.

---

## 2. Selepas (Dynamic)

Semua content disimpan dalam **database** (table `content_article` + `content_article_translation`). Admin boleh create/edit/delete content melalui admin panel tanpa sentuh code.

```
resources/views/reddsis/pages/
├── home.blade.php          ← Fetch dari DB, ada fallback static
├── dynamic.blade.php       ← Template universal untuk semua pages
└── login.blade.php         ← Tetap static (form login)
```

---

## 3. Bagaimana Ia Berfungsi (Flow)

### 3.1 Request Flow

```
User buka URL: /what-is-redd-plus
         │
         ▼
routes/web.php → Route::get('/what-is-redd-plus', [HomeController::class, 'whatIsReddPlus'])
         │
         ▼
HomeController::whatIsReddPlus() → calls $this->page('what-is-redd-plus')
         │
         ▼
HomeController::page($slug)
         │
         ├── Query DB: ContentArticle::where('article_code', $slug)
         │                      ->where('article_status', 'Published')
         │                      ->with('translations')
         │                      ->first()
         │
         ├── JIKA ADA DATA → return view('reddsis.pages.dynamic', compact('article'))
         │
         └── JIKA TIADA DATA → abort(404)
```

### 3.2 Database Structure

```
content_article                    content_article_translation
┌──────────────────────────┐      ┌──────────────────────────────────┐
│ article_id (PK)          │◄─────┤ article_translation_parent_id    │
│ article_code             │      │ article_translation_title        │
│ article_category         │      │ article_translation_content      │
│ article_status           │      │ article_translation_main         │
│ article_sorting          │      │ article_translation_language     │
│ article_image            │      │ created_by, updated_by           │
│ created_by, updated_by   │      │ created_at, updated_at           │
│ created_at, updated_at   │      └──────────────────────────────────┘
└──────────────────────────┘
```

- **`content_article`** → Data utama (code, status, sorting, dll)
- **`content_article_translation`** → Title & content untuk setiap bahasa
- **`article_translation_main = 1`** → Bahasa utama yang akan display dulu

---

## 4. Files Yang Diubah / Dicipta

### 4.1 `app/Http/Controllers/HomeController.php`

**Apa yang dibuat:**
- Setiap method (`whatIsReddPlus()`, `cancunSafeguards()`, dll) panggil `$this->page('slug')`
- Method `page($slug)` → query database, return `dynamic.blade.php`
- Method `index()` → query `article_code = 'home'`, return `home.blade.php`

```php
public function page($slug)
{
    $article = ContentArticle::with('translations')
        ->where('article_code', $slug)
        ->where('article_status', 'Published')
        ->first();

    if (!$article) {
        abort(404);
    }

    return view('reddsis.pages.dynamic', compact('article'));
}
```

### 4.2 `resources/views/reddsis/pages/dynamic.blade.php` (NEW)

**Apa yang dibuat:**
- Template universal untuk SEMUA pages
- Ambil title dari translation yang `article_translation_main = 1`
- Render content menggunakan `{!! !!}` (unescaped HTML untuk WYSIWYG content)

```blade
@extends('reddsis.layouts.app')

@section('title', $article->translations->firstWhere('article_translation_main', 1)->article_translation_title)

@section('content')
<div class="container">
  <div class="breadcrumb">
    <a href="{{ route('reddsis.home') }}">Home</a>
    <span>&rsaquo;</span>
    {{ $article->translations->firstWhere('article_translation_main', 1)->article_translation_title }}
  </div>
  <h2 class="section-title" style="text-align:left;">
    {{ $article->translations->firstWhere('article_translation_main', 1)->article_translation_title }}
  </h2>
  <div class="content-section">
    {!! $article->translations->firstWhere('article_translation_main', 1)->article_translation_content !!}
  </div>
</div>
@endsection
```

### 4.3 `resources/views/reddsis/pages/home.blade.php`

**Apa yang dibuat:**
- Support **dual mode**:
  - **Ada data DB** → render content dari database
  - **Tiada data (`null`)** → fallback ke static HTML sedia ada

```blade
@if($home && $home->translations->isNotEmpty())
  <div class="content-section">
    {!! $home->translations->firstWhere('article_translation_main', 1)->article_translation_content !!}
  </div>
@else
  <!-- Fallback: static HTML content -->
  <div class="two-col">
    <div><img src="{{ asset('reddsis/img/forest-main.jpg') }}" alt="Malaysia Forest"></div>
    <div><p>Malaysia became a Non-Annex I Party...</p></div>
  </div>
@endif
```

### 4.4 `routes/web.php`

**Apa yang dibuat:**
- Setiap page ada route sendiri yang map ke method dalam `HomeController`

```php
Route::prefix('/')->group(function () {
    Route::get('/', [HomeController::class, 'index'])->name('reddsis.home');
    Route::get('/what-is-redd-plus', [HomeController::class, 'whatIsReddPlus'])->name('reddsis.whatIsReddPlus');
    Route::get('/benefits-redd-plus', [HomeController::class, 'benefitsReddPlus'])->name('reddsis.benefitsReddPlus');
    Route::get('/redd-plus-malaysia', [HomeController::class, 'reddPlusMalaysia'])->name('reddsis.reddPlusMalaysia');
    Route::get('/cancun-safeguards', [HomeController::class, 'cancunSafeguards'])->name('reddsis.cancunSafeguards');
    Route::get('/malaysia-interpretation', [HomeController::class, 'malaysiaInterpretation'])->name('reddsis.malaysiaInterpretation');
    Route::get('/what-is-sis', [HomeController::class, 'whatIsSis'])->name('reddsis.whatIsSis');
    Route::get('/about-my-sis', [HomeController::class, 'aboutMySis'])->name('reddsis.aboutMySis');
    Route::get('/pci-status', [HomeController::class, 'pciStatus'])->name('reddsis.pciStatus');
    Route::get('/soi', [HomeController::class, 'soi'])->name('reddsis.soi');
    Route::get('/publications', [HomeController::class, 'publications'])->name('reddsis.publications');
    Route::get('/contact', [HomeController::class, 'contact'])->name('reddsis.contact');
    Route::get('/login', [HomeController::class, 'login'])->name('reddsis.login');
});
```

### 4.5 `app/Providers/AppServiceProvider.php`

**Apa yang dibuat:**
- Share visitor stats (`$visits`) secara global ke semua views
- Footer counter display data real-time dari table `visits`

```php
View::share('visits', [
    'total' => Visit::totalVisits(),
    'today' => Visit::todayVisits(),
    'unique' => Visit::uniqueIps(),
]);
```

### 4.6 `resources/views/reddsis/layouts/app.blade.php`

**Apa yang dibuat:**
- Semua nav links guna `{{ route('reddsis.*') }}`
- Active state check guna `request()->routeIs('reddsis.*')`
- Top bar: conditional link (Dashboard jika dah login, Login jika belum)
- Footer visitor counter guna `$visits` dari AppServiceProvider

---

## 5. Mapping Article Code ke Pages

| URL | Route Name | `article_code` |
|---|---|---|
| `/` | `reddsis.home` | `home` |
| `/what-is-redd-plus` | `reddsis.whatIsReddPlus` | `what-is-redd-plus` |
| `/benefits-redd-plus` | `reddsis.benefitsReddPlus` | `benefits-redd-plus` |
| `/redd-plus-malaysia` | `reddsis.reddPlusMalaysia` | `redd-plus-malaysia` |
| `/cancun-safeguards` | `reddsis.cancunSafeguards` | `cancun-safeguards` |
| `/malaysia-interpretation` | `reddsis.malaysiaInterpretation` | `malaysia-interpretation` |
| `/what-is-sis` | `reddsis.whatIsSis` | `what-is-sis` |
| `/about-my-sis` | `reddsis.aboutMySis` | `about-my-sis` |
| `/pci-status` | `reddsis.pciStatus` | `pci-status` |
| `/soi` | `reddsis.soi` | `soi` |
| `/publications` | `reddsis.publications` | `publications` |
| `/contact` | `reddsis.contact` | `contact` |

---

## 6. Cara Guna (Admin Panel)

### 6.1 Create New Page Content

1. Login admin → pergi **Content Article**
2. Klik **Create**
3. Isi maklumat:
   - **Article Code**: `what-is-redd-plus` (MESTI match dengan route slug)
   - **Article Category**: `REDD_PLUS_PAGE`
   - **Article Status**: `Published`
   - **Article Sorting**: `1`
4. Isi **Translation**:
   - **Language**: `en` (atau `BM` untuk Bahasa Melayu)
   - **Title**: `What is REDD Plus?`
   - **Content**: Isi content guna WYSIWYG editor
   - **Main**: ✅ Check/tick untuk bahasa utama
5. Klik **Save**

### 6.2 Edit Existing Content

1. Pergi **Content Article**
2. Cari article yang nak edit
3. Klik **Edit**
4. Update title/content
5. Klik **Update**

### 6.3 Unpublish Page

1. Edit article
2. Tukar **Article Status** dari `Published` ke `Draft`
3. Page akan return **404** di frontend

---

## 7. Kenapa Ia Jadi Dynamic?

### Sebelum (Static):
```
Page = File .blade.php → HTML hardcode → Edit code untuk tukar content
```

### Selepas (Dynamic):
```
Page = Route → Controller → Query DB → Render dari content_article
```

**Key concept:**
- Controller **tidak** return static view (`return view('page-name')`)
- Controller **query database** untuk cari content yang match dengan `article_code`
- View (`dynamic.blade.php`) **render content dari variable** yang passed dari controller
- Admin panel **Content Article** → CRUD untuk table `content_article` + `content_article_translation`

---

## 8. Visitor Counter

Visitor counter di footer display data real-time dari table `visits`.

### Bagaimana Ia Diimplementasikan

**Step 1: `app/Providers/AppServiceProvider.php`**
- Share visitor stats (`$visits`) secara global ke semua views menggunakan `View::share()`
- Data auto-fetch setiap kali app boot, jadi tak perlu pass dari controller

```php
use App\Models\Backend\Visit;
use Illuminate\Support\Facades\View;

public function boot(): void
{
    Activity::observe(ActivityObserver::class);

    View::share('visits', [
        'total' => Visit::totalVisits(),
        'today' => Visit::todayVisits(),
        'unique' => Visit::uniqueIps(),
    ]);
}
```

**Step 2: `app/Models/Backend/Visit.php`**
- Model `Visit` ada static methods untuk kira stats:
  - `Visit::totalVisits()` → `count()` semua rows dalam table `visits`
  - `Visit::todayVisits()` → `whereDate('created_at', today())->count()`
  - `Visit::uniqueIps()` → `distinct('ip')->count('ip')`

**Step 3: `resources/views/reddsis/layouts/app.blade.php`**
- Footer counter display data dari variable `$visits` yang dah di-share global

```blade
<div class="visitor-counter">
  <span>Visits {{ $visits['unique'] ?? 0 }}</span>
  <span>Today {{ $visits['today'] ?? 0 }}</span>
  <span>Total {{ $visits['total'] ?? 0 }}</span>
</div>
```

### Kenapa Guna `View::share()`?
- **Tak perlu pass dari setiap controller** → data auto-available di semua views
- **Consistent** → satu source of truth untuk visitor stats
- **Clean code** → controller jadi lebih simple, tak perlu load stats setiap method

### Cara Visitor Data Dicatat
Setiap kali user buka page, module visitor logging (jika aktif) akan auto-record:
- IP address, browser, platform, device
- URL, referer, user agent
- Timestamp (`created_at`)

Data ni disimpan dalam table `visits` dan digunakan untuk kira stats di atas.

---

## 9. Troubleshooting

### Content tak keluar di frontend?
1. Check `article_status` = `Published` (bukan `Draft`)
2. Check `article_code` match dengan route slug
3. Check ada translation dengan `article_translation_main = 1`
4. Clear cache: `php artisan view:clear`

### Nak debug data dari DB?
Tambah `@dump($article)` atau `@dump($home)` di view untuk inspect data.

### Route tidak ditemui?
Check `routes/web.php` → pastikan route untuk page tu ada dan map ke method yang betul.
