# UI COMPONENTS — GOVERNMENT SERVICE PORTAL
# Satu Mimpi City Administration
Version 2.0

---

# LAYOUT COMPONENTS

---

## AppShell (Admin Layout)

File: resources/views/layouts/app.blade.php

Structure:
```
<html class="h-full" x-data="appShell()">
  <body class="h-full bg-page dark:bg-dark-page font-sans">
    <AdminNavbar />              ← fixed top, z-50
    <div class="flex h-full pt-[72px]">
      <AdminSidebar />           ← sticky, collapsible
      <main class="flex-1 overflow-y-auto p-8 transition-all duration-400">
        <!-- @yield('content') -->
        <PageHeader />           ← included per page
        <slot />
      </main>
    </div>
    <ToastContainer />           ← fixed bottom-right
    <ModalContainer />           ← overlay layer
  </body>
</html>
```

Alpine.js state:
```js
function appShell() {
  return {
    sidebarOpen: localStorage.getItem('sidebar') !== 'collapsed',
    darkMode: localStorage.getItem('theme') === 'dark',
    toggleSidebar() {
      this.sidebarOpen = !this.sidebarOpen
      localStorage.setItem('sidebar', this.sidebarOpen ? 'expanded' : 'collapsed')
    },
    toggleDark() {
      this.darkMode = !this.darkMode
      localStorage.setItem('theme', this.darkMode ? 'dark' : 'light')
      document.documentElement.classList.toggle('dark', this.darkMode)
    }
  }
}
```

---

## AdminNavbar

File: resources/views/components/admin/navbar.blade.php
Height: 72px
Position: fixed top-0 left-0 right-0 z-50

Left side:
  - Hamburger toggle button (icon MenuIcon / XIcon based on sidebar state)
  - Logo kota (24px) + "Pemkot Satu Mimpi" text (hidden on mobile)
  - Breadcrumb (desktop only, auto-generated from route)

Right side (flex items-center gap-3):
  1. Dark Mode Toggle Button
     - Icon: SunIcon (light mode) / MoonIcon (dark mode)
     - Animated: rotate 180deg on toggle, 250ms
     - Tooltip: "Mode Gelap / Terang"
  2. Notification Bell
     - Icon: BellIcon
     - Badge: merah rounded-full dengan unread count (jika ada)
     - Click: dropdown notifikasi (max 5 terbaru + "Lihat Semua")
     - Dropdown: animate slideDown, shadow-dropdown
  3. User Avatar Menu
     - Avatar: foto profil bulat 36px / fallback initials dengan bg-gov-primary
     - Nama + jabatan (desktop only)
     - Dropdown:
       • Profil Saya → /admin/profile
       • Ubah Password → /admin/profile#password
       • ─────────────
       • Logout (red text, LogoutIcon)

Styling:
  bg-white dark:bg-dark-navbar
  border-b border-border dark:border-dark-border
  shadow-navbar
  backdrop-blur-sm (subtle)

---

## AdminSidebar

File: resources/views/components/admin/sidebar.blade.php

Dimensions:
  Expanded: w-[280px]
  Collapsed: w-[80px]
  Height: 100% (full viewport minus navbar)
  Position: sticky top-[72px]
  Overflow: hidden (clip text on collapse)
  Transition: width 400ms ease-decelerate

Styling:
  bg-white dark:bg-dark-sidebar
  border-r border-border dark:border-dark-border
  shadow-sidebar
  flex flex-col

Structure:
```
<aside :class="sidebarOpen ? 'w-[280px]' : 'w-[80px]'">
  <!-- Scrollable nav area -->
  <nav class="flex-1 overflow-y-auto overflow-x-hidden py-4 space-y-1">
    <!-- Navigation groups -->
    <SidebarGroup label="UMUM" :open="sidebarOpen">
      <SidebarItem icon="LayoutDashboard" label="Dashboard" route="/admin/dashboard" />
    </SidebarGroup>
    <SidebarGroup label="PELAYANAN PUBLIK" :open="sidebarOpen">
      <SidebarDropItem icon="IdentificationCard" label="KTP" ...subItems />
      <!-- ... 7 more services -->
    </SidebarGroup>
    <!-- ... more groups -->
  </nav>

  <!-- Bottom user card -->
  <div class="border-t p-4">
    <SidebarUserCard :open="sidebarOpen" />
  </div>
</aside>
```

### SidebarGroup
- Label: uppercase 10px text-secondary letter-spacing-widest (hidden when collapsed)
- Section separator with padding-top

### SidebarItem (no submenu)
Structure: anchor tag
  - Left: icon (w-5 h-5, centered when collapsed)
  - Right: label text (hidden when collapsed, opacity-0 + w-0 transition)
  - Right end: active indicator dot (hidden when collapsed)
States:
  Default: text-gray-600 dark:text-gray-400 hover:bg-green-50 dark:hover:bg-green-950/30 hover:text-gov-primary
  Active: bg-gov-primary text-white font-medium rounded-lg shadow-sm

When collapsed → tooltip on hover (data-tooltip attribute, custom Alpine tooltip)

### SidebarDropItem (with submenu)
- Toggle chevron (rotate 90° when open, 250ms)
- Submenu: x-show with x-transition:enter slide-down animation
- Submenu items: smaller text, pl-10 when expanded / centered icon when collapsed
- Pending badge: numeric badge on icon when collapsed

### SidebarUserCard
Expanded state:
  - Avatar (32px) + nama + jabatan
  - 3 dots menu: Profil, Password, Logout

Collapsed state:
  - Only avatar (centered), click → mini dropdown

---

## PublicLayout

File: resources/views/layouts/public.blade.php

Structure:
```
<html>
  <body class="bg-white dark:bg-dark-page font-sans">
    <PublicHeader />           ← sticky top
    <main>@yield('content')</main>
    <PublicFooter />
    <FloatingContact />        ← fixed bottom-right
    <BackToTop />              ← fixed bottom-right (above floating)
    <CookieBanner />           ← fixed bottom (optional)
  </body>
</html>
```

### PublicHeader
Height: 72px sticky
Left: Logo + "Pemerintah Kota Satu Mimpi" (full name)
Center: Navigation links (Home, Tentang, Layanan, Berita, Kontak)
Right: Dark Mode toggle + "Portal LOKET" button (outline gov-primary)

Scroll behavior: adds shadow + blur backdrop when scrolled > 50px
Mobile: hamburger → slide-down mobile menu (full width)

### MegaMenu (for Layanan)
Hover on "Layanan" → mega menu drop:
  Grid 2×4 of service cards (icon + nama + deskripsi singkat)
  Bottom row: "Lihat Semua Layanan →"
  Animation: fadeIn + translateY(-8px) → 0, 200ms

### PublicFooter
4-column layout:
  Col 1: Logo + tagline + sosmed icons (placeholder)
  Col 2: Layanan (links ke 8 service pages)
  Col 3: Informasi (About, News, Contact, Tracking)
  Col 4: Kontak (alamat, telp, email, jam buka)
Bottom bar: copyright + "Made with ❤️ Pemerintah Kota Satu Mimpi"

### FloatingContact
Position: fixed bottom-6 right-6
Stack (bottom to top):
  - Main FAB button: ChatBubble icon, bg-gov-primary, shadow-lg
  - On click (Alpine toggle): expand to show options:
    • WhatsApp (green)
    • Telepon (blue)
    • Email (gray)
  Animation: scale + opacity staggered in/out

---

# NAVIGATION COMPONENTS

---

## Breadcrumb

File: resources/views/components/breadcrumb.blade.php
Auto-generated from current route name / passed $breadcrumbs array

Design:
  - Text-sm, text-secondary
  - Separator: "/" or ChevronRight icon (w-3 h-3)
  - Last item: text-primary font-medium (non-link)
  - First: HomeIcon + "Beranda"
  - Fade in animation on load

---

## PageHeader

File: resources/views/components/page-header.blade.php

Props: title, subtitle (optional), breadcrumbs, actions (slot for buttons)

Design:
  - mb-6
  - Left: H1 (text-2xl font-semibold) + subtitle (text-sm text-secondary)
  - Breadcrumb below title
  - Right: action buttons slot

---

# CARD COMPONENTS

---

## StatCard (Dashboard)

File: resources/views/components/cards/stat-card.blade.php

Props: icon, title, value, trend, trend_direction, color, link

Design:
```
<div class="bg-white dark:bg-dark-card rounded-2xl p-6 shadow-card
            hover:shadow-card-hover hover:-translate-y-0.5
            transition-all duration-250 cursor-pointer group">
  <div class="flex items-start justify-between">
    <div>
      <p class="text-sm text-secondary">{{ $title }}</p>
      <p class="text-3xl font-bold mt-1 text-primary">{{ $value }}</p>
      <span class="text-xs" :class="trend >= 0 ? 'text-success' : 'text-danger'">
        {{ trend >= 0 ? '↑' : '↓' }} {{ abs(trend) }}% vs kemarin
      </span>
    </div>
    <div class="w-12 h-12 rounded-xl flex items-center justify-center
                bg-{{ color }}-100 dark:bg-{{ color }}-900/30
                group-hover:scale-110 transition-transform duration-250">
      <Icon :name="icon" class="w-6 h-6 text-{{ color }}-600" />
    </div>
  </div>
</div>
```

Animation: fadeIn on page load, staggered (delay-100 per card)
Counter animation: countUp from 0 to value over 1s on mount

---

## ServiceCard (Public)

File: resources/views/components/cards/service-card.blade.php

Props: icon, name, description, process_time, cost, link, color

Design:
  - Card with colored left border (4px, color per service)
  - Icon in colored circle top-right
  - Name: H5 bold
  - Description: 2 lines max (overflow-hidden)
  - Tags row: process time chip + cost chip
  - Hover: translateY(-4px) + border glow (shadow-[0_0_0_2px_theme(colors.gov.500)])
  - Footer: "Pelajari →" link

---

## InfoCard

File: resources/views/components/cards/info-card.blade.php

Props: title, description, icon, badge (optional), action_label, action_link

Generic card untuk berbagai keperluan.
Design: standard card with optional colored badge.

---

## ActivityCard (Dashboard)

File: resources/views/components/cards/activity-card.blade.php

Used in: Dashboard activity feed

Design: Timeline-style list
Each item:
  - Avatar (24px) left
  - Content: "[Nama Staff] menyetujui pengajuan KTP [No. Pengajuan]"
  - Timestamp: "2 menit lalu" (relative time)
  - Status icon on right (CheckCircle green / XCircle red)

---

# DATA TABLE COMPONENT

File: resources/views/components/data-table.blade.php

Features:
  1. Search bar — real-time filter by name/NIK
  2. Status filter — dropdown (All / Pending / Approved / Rejected)
  3. Date range picker — tanggal submit dari/sampai
  4. Per-page selector — 10 / 25 / 50 / 100
  5. Bulk checkbox — select all + individual row select
  6. Bulk actions bar — muncul saat ada yang diselect (Approve / Reject / Export terpilih)
  7. Column sorting — klik header untuk sort asc/desc
  8. Responsive — horizontal scroll on mobile
  9. Empty state — illustration + pesan "Tidak ada data"
  10. Loading skeleton — shimmer rows saat loading
  11. Export bar — PDF | Excel | CSV

Table Styling:
  - Header: bg-gray-50 dark:bg-gray-800, text-xs uppercase letter-spacing font-semibold
  - Rows: hover:bg-gray-50 dark:hover:bg-gray-800/50, transition background 150ms
  - Border: border-b border-border (subtle, not heavy grid)
  - Cell padding: px-4 py-3
  - Actions column: flex gap-2, right-aligned

Skeleton row (loading state):
  - 5 shimmer bars per row, gradient animated left-to-right
  - 5 skeleton rows displayed

Empty state:
  - Centered illustration (SVG)
  - "Tidak ada pengajuan ditemukan"
  - Sub text sesuai filter aktif
  - Reset filter button

---

# FORM COMPONENTS

---

## TextInput

File: resources/views/components/forms/text-input.blade.php
Props: name, label, type, placeholder, required, disabled, error, icon_left, hint

Design:
  Label: text-sm font-medium text-primary mb-1.5
  Input wrapper: relative
  Input: w-full px-4 py-2.5 border rounded-lg text-sm
         focus:border-gov-primary focus:ring-2 focus:ring-gov-primary/20
         dark:bg-dark-card dark:border-dark-border dark:text-dark-text
         transition-all duration-150
  Error state: border-danger focus:ring-danger/20 + error message below (text-xs text-danger)
  Hint: text-xs text-secondary below input
  Icon left: absolute left-3 top-1/2 -translate-y-1/2 text-secondary

---

## SelectInput

File: resources/views/components/forms/select-input.blade.php
Props: name, label, options, placeholder, required, error

Design: Same as TextInput with chevron-down icon right
Options rendered as <select> → styled to match design
Custom select wrapper to override browser default

---

## DatePicker

File: resources/views/components/forms/date-picker.blade.php
Props: name, label, required, min, max

Implementation: HTML5 <input type="date"> + custom styling
Or: Flatpickr (via CDN, no npm required)

Styling consistent with TextInput
Calendar popup: shadow-dropdown, rounded-xl, gov-primary highlight

---

## FileUpload (Single)

File: resources/views/components/forms/file-upload.blade.php
Props: name, label, accept, max_size, hint

Design:
  - Dashed border dropzone: border-2 border-dashed border-border rounded-xl p-6
  - Center: UploadIcon + "Klik untuk upload atau drag & drop"
  - Below: accepted formats + max size
  - After upload: show preview thumbnail (image) or file icon (PDF)
               + filename + size + remove button (×)
  - Hover state: border-gov-primary bg-gov-50
  - Drag-over state: border-gov-primary bg-gov-50 scale-[1.01]
  - Animation: fadeIn preview after upload

---

## MultipleFileUpload

File: resources/views/components/forms/multi-upload.blade.php
Props: name, label, accept, max_files, max_size

Design: Same dropzone
After upload: grid thumbnails (3 col)
Each thumbnail: preview + overlay with filename + remove ×
Add more: "+ Tambah" card at the end
Max file indicator: "3 dari 5 file dipilih"

---

## RichTextEditor

File: resources/views/components/forms/rich-text.blade.php
Library: Quill.js (CDN)
Toolbar: Bold, Italic, Underline, H2, H3, OrderedList, BulletList, Link, Image, Undo, Redo
Theme: Matches design system (rounded, gov-primary accent on focus)
Dark mode: custom CSS override for Quill dark mode

---

## SignaturePad

File: resources/views/components/forms/signature-pad.blade.php
Library: Signature_pad.js (CDN)
Canvas: 400×150px, border rounded-xl, bg-white
Tools:
  - Clear button (hapus tanda tangan)
  - Undo stroke button
Output: save as base64 PNG → hidden input
Mobile: touch support enabled

---

# MODAL COMPONENTS

Base modal wrapper:
  Overlay: fixed inset-0 bg-black/50 backdrop-blur-[2px] z-50
  Container: fixed inset-0 flex items-center justify-center p-4
  Panel: bg-white dark:bg-dark-card rounded-2xl shadow-modal
  Animation: overlay fadeIn (200ms) + panel scaleIn (250ms)
  Close: Esc key + × button + overlay click
  Trap focus: tabindex management for accessibility

---

## ConfirmModal (Approve / Reject)

Props: type (approve|reject), service_name, submission_number, applicant_name

### Approve Modal
  Header: CheckCircleIcon (green, 48px) + "Konfirmasi Persetujuan"
  Body:
    - Info pengajuan (no, nama, jenis layanan)
    - Warning: "Tindakan ini tidak dapat dibatalkan"
    - Textarea: catatan persetujuan (optional)
  Footer: Cancel (ghost) + "Ya, Setujui" (gov-primary)
  Animation: icon pulse-green on appear

### Reject Modal
  Header: XCircleIcon (red, 48px) + "Konfirmasi Penolakan"
  Body:
    - Info pengajuan
    - Textarea: alasan penolakan (WAJIB DIISI, min 10 karakter)
    - Validation: tombol reject disabled sampai alasan terisi
  Footer: Cancel (ghost) + "Ya, Tolak" (danger)

---

## PreviewModal (Attachment)

Props: url, file_type (image|pdf), filename

Design:
  Max-width: 900px, max-height: 90vh
  Header: filename + "Buka di Tab Baru" link + close ×
  Body:
    - Image: <img> dengan object-contain, zoom on click
    - PDF: <iframe> embed atau link download
  Footer (image only): Download button

---

## DeleteModal

Standard confirmation untuk delete item (staff, news, FAQ, dll)
Header: TrashIcon (red) + "Hapus [Item Name]?"
Body: Warning text tentang permanent delete
Footer: Cancel + "Ya, Hapus" (danger)

---

## CreateEditModal

Generic modal untuk form singkat (bukan service detail)
Untuk: tambah FAQ, tambah kategori, dll
Props: title, fields (slot), action URL, method

---

# STATUS BADGE COMPONENT

File: resources/views/components/status-badge.blade.php
Props: status (string)

Design: inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium

| Status | Classes |
|---|---|
| pending | bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400 |
| processing | bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400 |
| approved | bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 |
| rejected | bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 |
| completed | bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-400 |
| printed | bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400 |

Icon: 12px solid icon matching status (Clock / Refresh / Check / X / CheckBadge / Printer)

---

# TIMELINE COMPONENT

File: resources/views/components/timeline.blade.php

Props: steps (array of {title, description, timestamp, status: done|current|pending})

Design: Vertical timeline
  - Left: connector line (2px) + circle dot (12px)
  - Done: line = gov-primary, dot = gov-primary filled + CheckIcon white
  - Current: dot = gov-primary filled animated pulse-green
  - Pending: line = gray-200, dot = gray-300 outline
  - Content right: title (bold), description (secondary), timestamp (caption)
  - Gap between steps: 32px

---

# TOAST NOTIFICATIONS

File: resources/views/components/toast-container.blade.php
Alpine.js component: window.toast() global function

Position: fixed bottom-6 right-6, z-[100]
Stack: multiple toasts stack with gap-2
Max: 5 simultaneous toasts (FIFO)

### Toast Types
  Success: bg-white border-l-4 border-success icon CheckCircleIcon (green)
  Error: border-danger icon XCircleIcon (red)
  Warning: border-warning icon ExclamationTriangleIcon (yellow)
  Info: border-info icon InformationCircleIcon (blue)

Design per toast:
  - bg-white dark:bg-dark-card shadow-dropdown rounded-xl p-4
  - border-l-4 (colored)
  - Left: colored icon (20px)
  - Center: title (font-medium) + message (text-sm text-secondary)
  - Right: close ×
  - Progress bar: bottom of toast, shrinks over duration

Animation:
  In: slideInRight + fadeIn (300ms)
  Out: fadeOut + slideRight (200ms)
  Auto-dismiss: after 4s (success/info) / 6s (error/warning)

Usage (Alpine.js):
```js
$dispatch('toast', { type: 'success', title: 'Berhasil', message: 'Pengajuan disetujui.' })
```

Triggered automatically from Laravel session flash messages:
  session('success') → success toast
  session('error') → error toast

---

# SKELETON LOADER

File: resources/views/components/skeleton.blade.php
Props: type (card|table-row|text|avatar)

Skeleton gradient:
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)
  background-size: 200% 100%
  animation: shimmer 1.5s infinite

Types:
  - skeleton-text: rounded bar, various widths
  - skeleton-avatar: circle
  - skeleton-card: full card outline
  - skeleton-table-row: 5 inline bars

---

# CHART.JS COMPONENTS

---

## LineChart (Dashboard)

File: resources/views/components/charts/line-chart.blade.php
Props: data (JSON), labels, height

Config:
  Type: line
  Datasets: Total (gov-primary) / Disetujui (success) / Ditolak (danger)
  Grid: light gray, no border
  Tension: 0.4 (smooth curve)
  Fill: subtle gradient under line (alpha 0.15)
  Points: 5px dots, hover: 7px
  Tooltip: custom styled (rounded-xl, shadow)
  Animation: data draws in from left (duration: 1000ms)

---

## DoughnutChart (Dashboard)

File: resources/views/components/charts/doughnut-chart.blade.php
Props: data (JSON), labels

Config:
  cutout: 70%
  Center text plugin: total number + "Pengajuan"
  Legend: right side, custom styled
  Animation: rotate in (1000ms)
  Hover: segment enlarges 5px

---

## BarChart (Reports)

File: resources/views/components/charts/bar-chart.blade.php
Config:
  Horizontal bars for service comparison
  Color: gradient per bar (gov-500 → gov-300)
  Border radius: 6px on bars

---

# SPECIAL GOVERNMENT COMPONENTS

---

## CitizenLookup (NIK Auto-Complete)

File: resources/views/components/gov/citizen-lookup.blade.php
Usage: marriage service (cari suami/istri by NIK)

Design:
  - Input NIK dengan inline icon SearchIcon
  - On input (debounce 300ms): AJAX ke /api/citizens/search?nik=xxx
  - Dropdown hasil: foto + nama + NIK + pekerjaan
  - Select → auto-fill form fields terkait
  - "Warga tidak ditemukan" empty state

---

## DocumentPreviewPanel

File: resources/views/components/gov/document-preview-panel.blade.php
Usage: service detail page, lampiran section

Design:
  Grid 3 kolom (desktop) / 2 (tablet) / 1 (mobile)
  Each file card:
    - Thumbnail: image preview (jika gambar) atau PDF icon
    - Filename (truncate overflow)
    - File size + type badge
    - Tombol: Preview (eye icon) + Download (download icon)
  Click Preview → buka PreviewModal
  
---

## ApprovalActionCard

File: resources/views/components/gov/approval-action-card.blade.php
Usage: service detail page RIGHT column, sticky

Design (sticky top-6):
  Card rounded-2xl shadow-card p-6
  
  Status section:
    - Current status badge (large)
    - "Menunggu keputusan" / "Sudah diproses oleh [nama]"
  
  Textarea "Catatan" (label: optional untuk approve, required untuk reject):
    - placeholder: "Tambahkan catatan atau alasan..."
    - min-height: 80px
    - character counter below
  
  Action buttons (full width, stacked):
    Jika pending:
      [✓ Setujui Pengajuan] — bg-gov-primary, hover:bg-gov-600
      [✗ Tolak Pengajuan]   — bg-danger, hover:bg-red-600
    Jika sudah approved:
      [🖨 Cetak Dokumen]    — border border-gov-primary text-gov-primary
      [Status: Disetujui]   — disabled, success badge
    Jika sudah rejected:
      [Status: Ditolak]     — disabled, danger badge
      [Catatan Penolakan]   — read-only text
  
  Confirm: klik button → buka ConfirmModal

---

## RequestTimeline

File: resources/views/components/gov/request-timeline.blade.php
Usage: service detail page RIGHT column

Steps untuk semua layanan:
  1. Pengajuan Diterima
  2. Verifikasi Berkas
  3. Diproses
  4. Keputusan (Approved / Rejected)
  5. Selesai / Dicetak

Each step shows:
  - Timestamp (tanggal + jam)
  - Nama staff yang memproses (jika ada)
  - Catatan (jika ada, collapsed by default)

---

## ActivityLog

File: resources/views/components/gov/activity-log.blade.php
Usage: service detail page, dashboard widget

Shows chronological log of changes.
Each entry: avatar + action text + timestamp

Action templates:
  "[Nama] membuat pengajuan baru"
  "[Nama Staff] mengubah status ke Approved"
  "[Nama Staff] menolak dengan alasan: '...'"
  "[Nama Staff] mencetak dokumen"

---

## PrintTemplate

File: resources/views/print/{service}.blade.php

Extends: layouts/print.blade.php (bare HTML, no sidebar/navbar)

Structure:
  Header:
    - Logo kota (64px)
    - Nama instansi: "PEMERINTAH KOTA SATU MIMPI"
    - Divisi: "DINAS KEPENDUDUKAN DAN CATATAN SIPIL"
    - Garis bawah (double line, gov-primary)
  
  Body:
    - Judul dokumen (H1 center, uppercase)
    - Nomor dokumen: "No: GOV-[YEAR]-[ID]"
    - Tabel data pemohon + data pengajuan (styled, no Tailwind CDN for print)
    - Tabel lampiran references
  
  Footer:
    - Kota + tanggal approval
    - Kolom tanda tangan (nama + jabatan + signature image)
    - Watermark diagonal "SALINAN RESMI" (jika enabled)
    - QR code placeholder (v2)
  
  Print CSS: @media print { body { zoom: 0.9; } }

---

## DiscordNotificationBadge

File: resources/views/components/gov/discord-badge.blade.php
Usage: settings page + notification center

Design:
  - Status indicator: bulat (online=green/offline=red) + "Discord Bot: Terhubung/Tidak"
  - Queue count: "X notifikasi dalam antrian"

---

# EMPTY STATES

File: resources/views/components/empty-state.blade.php
Props: icon, title, description, action_label, action_link

Design:
  - Centered in container
  - SVG illustration (contextual per page)
  - Title: text-xl font-semibold
  - Description: text-secondary text-sm max-w-xs mx-auto
  - Optional CTA button

Illustrations (inline SVG, gov-primary colors):
  - empty-inbox: untuk service list kosong
  - empty-search: untuk search no results
  - empty-chart: untuk report kosong

---

# PAGINATION COMPONENT

File: resources/views/components/pagination.blade.php
Extends Laravel's default pagination with custom styling.

Design:
  - Rounded buttons, 36px size
  - Active: bg-gov-primary text-white
  - Hover: bg-gov-50 text-gov-primary
  - Prev/Next: icon + text (desktop) / icon only (mobile)
  - "Showing 1-10 of 47 results" text left

---

# ROLE GUARD COMPONENT

Blade directive: @admin, @operator
File: app/Providers/AppServiceProvider.php (Blade::if)

Usage:
```blade
@admin
  <!-- Content only for Admin -->
@endadmin

@operator
  <!-- Content for Admin + Operator -->
@endoperator
```
