feat: update components with Mantine UI and improve dark mode support
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
285
Dashboard-MD/BUMDES.md
Normal file
285
Dashboard-MD/BUMDES.md
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
# BUMDES & UMKM Desa — UI & Functional Specification
|
||||||
|
|
||||||
|
Dokumen ini menjelaskan spesifikasi **UI, tata letak, gaya visual, responsivitas, serta ekspektasi data & interaksi** untuk halaman **BUMDes & UMKM Desa (Jenna Analytic Module)**. Dokumen ini menjadi kontrak desain antara **UI/UX, Frontend, dan Backend**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Tujuan Halaman
|
||||||
|
|
||||||
|
Halaman **BUMDes & UMKM Desa** berfungsi sebagai dashboard monitoring:
|
||||||
|
|
||||||
|
* Aktivitas UMKM desa
|
||||||
|
* Performa penjualan produk unggulan
|
||||||
|
* Omzet BUMDes
|
||||||
|
* Stok & tren produk
|
||||||
|
|
||||||
|
Target pengguna:
|
||||||
|
|
||||||
|
* Kepala Desa
|
||||||
|
* Admin Desa
|
||||||
|
* Operator BUMDes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Tata Letak Keseluruhan (Layout)
|
||||||
|
|
||||||
|
### 2.1 Struktur Utama
|
||||||
|
|
||||||
|
Layout menggunakan pola **Dashboard Sidebar Layout**:
|
||||||
|
|
||||||
|
* **Sidebar kiri (fixed)**
|
||||||
|
* **Topbar/header (fixed)**
|
||||||
|
* **Main content (scrollable)**
|
||||||
|
|
||||||
|
Grid utama:
|
||||||
|
|
||||||
|
* Desktop: `12-column grid`
|
||||||
|
* Tablet: `8-column grid`
|
||||||
|
* Mobile: `4-column grid`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 Sidebar
|
||||||
|
|
||||||
|
Komponen:
|
||||||
|
|
||||||
|
* Logo DESA + tagline
|
||||||
|
* Search bar ("cari apa saja")
|
||||||
|
* Menu navigasi vertikal
|
||||||
|
|
||||||
|
Menu aktif:
|
||||||
|
|
||||||
|
* **Bumdes & UMKM Desa** (highlight biru muda)
|
||||||
|
|
||||||
|
Perilaku:
|
||||||
|
|
||||||
|
* Collapsible pada layar kecil
|
||||||
|
* Ikon + label (label hidden di mobile)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.3 Topbar
|
||||||
|
|
||||||
|
Komponen:
|
||||||
|
|
||||||
|
* Nama desa
|
||||||
|
* Nama & jabatan user
|
||||||
|
* Avatar user
|
||||||
|
* Ikon notifikasi (badge)
|
||||||
|
* Ikon pengaturan
|
||||||
|
|
||||||
|
Perilaku:
|
||||||
|
|
||||||
|
* Sticky
|
||||||
|
* Dropdown user menu
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Komponen UI Utama
|
||||||
|
|
||||||
|
### 3.1 KPI Cards (Ringkasan Atas)
|
||||||
|
|
||||||
|
Menampilkan ringkasan metrik utama:
|
||||||
|
|
||||||
|
| KPI | Deskripsi |
|
||||||
|
| -------------- | ---------------------- |
|
||||||
|
| UMKM Aktif | Jumlah UMKM beroperasi |
|
||||||
|
| UMKM Terdaftar | Total UMKM terdaftar |
|
||||||
|
| Omzet | Omzet BUMDes per bulan |
|
||||||
|
| Kategori UMKM | Jumlah kategori aktif |
|
||||||
|
|
||||||
|
Karakteristik:
|
||||||
|
|
||||||
|
* Card rounded
|
||||||
|
* Icon di kanan
|
||||||
|
* Angka besar (headline)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Update Penjualan Produk
|
||||||
|
|
||||||
|
Komponen:
|
||||||
|
|
||||||
|
* Section header berwarna biru gelap
|
||||||
|
* Button filter waktu:
|
||||||
|
|
||||||
|
* "Minggu ini"
|
||||||
|
* "Bulan ini"
|
||||||
|
|
||||||
|
Perilaku:
|
||||||
|
|
||||||
|
* Toggle data chart & tabel
|
||||||
|
* Default: Bulan ini
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Produk Unggulan (Left Column)
|
||||||
|
|
||||||
|
Sub-komponen:
|
||||||
|
|
||||||
|
1. **Total Penjualan**
|
||||||
|
|
||||||
|
* Nominal
|
||||||
|
* Persentase perubahan
|
||||||
|
|
||||||
|
2. **Produk Aktif**
|
||||||
|
|
||||||
|
* Jumlah produk
|
||||||
|
* Jumlah kategori
|
||||||
|
|
||||||
|
3. **Total Transaksi**
|
||||||
|
|
||||||
|
* Jumlah transaksi bulan berjalan
|
||||||
|
|
||||||
|
4. **Top 3 Produk Terlaris**
|
||||||
|
|
||||||
|
* Ranking
|
||||||
|
* Nama produk
|
||||||
|
* Pelaku UMKM
|
||||||
|
* Growth indicator
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 Detail Penjualan Produk (Right Column)
|
||||||
|
|
||||||
|
Bentuk:
|
||||||
|
|
||||||
|
* Tabel data
|
||||||
|
|
||||||
|
Kolom:
|
||||||
|
|
||||||
|
* Produk
|
||||||
|
* Penjualan bulan ini
|
||||||
|
* Bulan lalu
|
||||||
|
* Trend
|
||||||
|
* Volume
|
||||||
|
* Stok
|
||||||
|
* Aksi
|
||||||
|
|
||||||
|
Elemen khusus:
|
||||||
|
|
||||||
|
* Trend: arrow hijau/merah
|
||||||
|
* Stok: badge warna (aman / kritis)
|
||||||
|
* Aksi: tombol "Detail"
|
||||||
|
|
||||||
|
Filter:
|
||||||
|
|
||||||
|
* Dropdown filter kategori / stok
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gaya & Tema Visual
|
||||||
|
|
||||||
|
### 4.1 Warna
|
||||||
|
|
||||||
|
| Elemen | Warna |
|
||||||
|
| ---------- | ------------------------- |
|
||||||
|
| Primary | Biru tua (#183A63 approx) |
|
||||||
|
| Secondary | Biru muda (#E6F0FF) |
|
||||||
|
| Success | Hijau (#22C55E) |
|
||||||
|
| Danger | Merah (#EF4444) |
|
||||||
|
| Background | Abu muda / putih |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 Tipografi
|
||||||
|
|
||||||
|
* Font family: **Inter / Poppins / Sans-serif modern**
|
||||||
|
* Heading: Semi-bold
|
||||||
|
* Body text: Regular
|
||||||
|
* Angka KPI: Bold
|
||||||
|
|
||||||
|
Ukuran:
|
||||||
|
|
||||||
|
* H1: 24–28px
|
||||||
|
* H2: 18–20px
|
||||||
|
* Body: 14–16px
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.3 Spasi & Card
|
||||||
|
|
||||||
|
* Padding card: 16–20px
|
||||||
|
* Gap antar card: 16px
|
||||||
|
* Border radius: 12–16px
|
||||||
|
* Shadow ringan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Responsivitas
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
* Sidebar terbuka penuh
|
||||||
|
* Tabel full
|
||||||
|
|
||||||
|
### Tablet
|
||||||
|
|
||||||
|
* Sidebar collapse
|
||||||
|
* KPI card wrap
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* Sidebar drawer
|
||||||
|
* KPI card stack
|
||||||
|
* Tabel menjadi card list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Interaksi & UX Behavior
|
||||||
|
|
||||||
|
* Hover effect pada card & row tabel
|
||||||
|
* Tooltip pada ikon & trend
|
||||||
|
* Loading skeleton saat fetch data
|
||||||
|
* Empty state ("Belum ada data")
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Ekspektasi Data (Backend Contract)
|
||||||
|
|
||||||
|
### KPI API
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"umkmAktif": 45,
|
||||||
|
"umkmTerdaftar": 68,
|
||||||
|
"omzet": 48000000,
|
||||||
|
"kategori": 34
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Produk API
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"produk": "Beras Premium Organik",
|
||||||
|
"penjualanBulanIni": 8500000,
|
||||||
|
"bulanLalu": 8500000,
|
||||||
|
"trend": 10,
|
||||||
|
"volume": "650 Kg",
|
||||||
|
"stok": "850 Kg"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Catatan Implementasi Teknis
|
||||||
|
|
||||||
|
* Frontend: React + Vite
|
||||||
|
* State management: query-based (TanStack Query)
|
||||||
|
* Chart (jika ditambah): Recharts / Chart.js
|
||||||
|
* Format uang: Intl.NumberFormat("id-ID")
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Acceptance Checklist
|
||||||
|
|
||||||
|
* [ ] Semua KPI muncul & valid
|
||||||
|
* [ ] Filter waktu berfungsi
|
||||||
|
* [ ] Trend naik/turun akurat
|
||||||
|
* [ ] Responsif mobile
|
||||||
|
* [ ] Empty & loading state tersedia
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Dokumen ini bersifat **final reference** untuk implementasi halaman **BUMDes & UMKM Desa**.
|
||||||
224
Dashboard-MD/KEAMANAN.md
Normal file
224
Dashboard-MD/KEAMANAN.md
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# KEAMANAN
|
||||||
|
|
||||||
|
Dokumen ini mendeskripsikan spesifikasi UI/UX dan ekspektasi teknis untuk modul **Keamanan Lingkungan Desa** pada sistem dashboard DESA. Dokumen ini menjadi acuan bersama bagi tim desain, frontend, dan backend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Tujuan Halaman
|
||||||
|
|
||||||
|
Memberikan visibilitas real-time dan historis terhadap kondisi keamanan desa, meliputi:
|
||||||
|
|
||||||
|
* Status CCTV aktif
|
||||||
|
* Laporan kejadian keamanan
|
||||||
|
* Peta lokasi CCTV
|
||||||
|
* Monitoring tren dan respons kejadian
|
||||||
|
|
||||||
|
Target pengguna utama:
|
||||||
|
|
||||||
|
* Kepala Desa
|
||||||
|
* Perangkat Desa / Petugas Keamanan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Struktur Tata Letak (Layout)
|
||||||
|
|
||||||
|
### 2.1 Pola Umum
|
||||||
|
|
||||||
|
* **Layout dashboard dua kolom**
|
||||||
|
|
||||||
|
* Kolom kiri: monitoring visual (peta)
|
||||||
|
* Kolom kanan: daftar laporan keamanan
|
||||||
|
* Header KPI ringkas di bagian atas
|
||||||
|
* Semua konten dibungkus dalam **card container** dengan radius konsisten
|
||||||
|
|
||||||
|
Grid:
|
||||||
|
|
||||||
|
* Desktop: 12-column grid
|
||||||
|
* Tablet: 8-column grid
|
||||||
|
* Mobile: 1-column (stack vertikal)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Komponen UI
|
||||||
|
|
||||||
|
### 3.1 KPI Cards (Header)
|
||||||
|
|
||||||
|
1. **CCTV Aktif**
|
||||||
|
|
||||||
|
* Value: `20`
|
||||||
|
* Subtext: `Kamera Online`
|
||||||
|
* Ikon: Kamera CCTV
|
||||||
|
|
||||||
|
2. **Laporan Keamanan**
|
||||||
|
|
||||||
|
* Value: `15`
|
||||||
|
* Subtext: `Minggu ini`
|
||||||
|
* Ikon: Chat / laporan
|
||||||
|
|
||||||
|
Perilaku:
|
||||||
|
|
||||||
|
* Tooltip saat hover (penjelasan metrik)
|
||||||
|
* Klik opsional → navigasi ke halaman detail
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Peta Keamanan CCTV
|
||||||
|
|
||||||
|
**Judul Card:** Peta Keamanan CCTV
|
||||||
|
**Subjudul:** Titik Lokasi CCTV
|
||||||
|
|
||||||
|
Isi:
|
||||||
|
|
||||||
|
* Embedded map (Google Maps / Mapbox)
|
||||||
|
* Marker lokasi CCTV
|
||||||
|
|
||||||
|
* Hijau: aktif
|
||||||
|
* Abu-abu: offline (future)
|
||||||
|
|
||||||
|
Kontrol:
|
||||||
|
|
||||||
|
* Toggle tampilan (grid / fullscreen)
|
||||||
|
* Zoom in / out
|
||||||
|
|
||||||
|
Interaksi:
|
||||||
|
|
||||||
|
* Klik marker → popup info CCTV
|
||||||
|
|
||||||
|
* ID CCTV
|
||||||
|
* Lokasi
|
||||||
|
* Status
|
||||||
|
* Waktu terakhir aktif
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Daftar Laporan Keamanan
|
||||||
|
|
||||||
|
**Judul:** Laporan Keamanan Lingkungan
|
||||||
|
|
||||||
|
Item laporan (card list):
|
||||||
|
|
||||||
|
* Judul kejadian (contoh: *Pencurian Motor*)
|
||||||
|
* Timestamp relatif ("2 jam yang lalu")
|
||||||
|
* Tanggal & jam
|
||||||
|
* Lokasi kejadian
|
||||||
|
* Badge status: `Baru`
|
||||||
|
|
||||||
|
Perilaku:
|
||||||
|
|
||||||
|
* Scrollable container
|
||||||
|
* Klik item → detail laporan (modal / halaman baru)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gaya & Tema Visual
|
||||||
|
|
||||||
|
### 4.1 Warna
|
||||||
|
|
||||||
|
* Primary: Navy Blue (`#1F3A5F` – perkiraan)
|
||||||
|
* Background: Light Blue / Off-white (`#F5F9FC`)
|
||||||
|
* Card: Putih
|
||||||
|
* Success: Hijau (status aktif)
|
||||||
|
* Alert: Merah muda / merah untuk laporan baru
|
||||||
|
|
||||||
|
### 4.2 Tipografi
|
||||||
|
|
||||||
|
* Font utama: Sans-serif modern (Inter / Poppins)
|
||||||
|
* Heading: Semi-bold
|
||||||
|
* Body text: Regular
|
||||||
|
* Angka KPI: Bold
|
||||||
|
|
||||||
|
Ukuran relatif:
|
||||||
|
|
||||||
|
* Heading: 16–18px
|
||||||
|
* Body: 13–14px
|
||||||
|
* KPI Value: 22–28px
|
||||||
|
|
||||||
|
### 4.3 Spasi & Elevasi
|
||||||
|
|
||||||
|
* Padding card: 16–20px
|
||||||
|
* Gap antar card: 16px
|
||||||
|
* Border radius: 12–16px
|
||||||
|
* Shadow ringan untuk card
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Responsivitas
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
* Peta dan laporan tampil berdampingan
|
||||||
|
|
||||||
|
### Tablet
|
||||||
|
|
||||||
|
* KPI tetap horizontal
|
||||||
|
* Peta dan laporan bisa stack 2 baris
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* Semua komponen stack vertikal
|
||||||
|
* Peta dalam mode scroll / fullscreen
|
||||||
|
* List laporan menjadi full-width
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Ekspektasi Interaksi & UX
|
||||||
|
|
||||||
|
* Hover state pada card & list item
|
||||||
|
* Tooltip pada ikon KPI
|
||||||
|
* Loading skeleton saat data fetch
|
||||||
|
* Empty state bila tidak ada laporan
|
||||||
|
* Badge status real-time (baru / diproses / selesai – future)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Ekspektasi Data (Backend Contract)
|
||||||
|
|
||||||
|
### 7.1 KPI Summary
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cctvActive": 20,
|
||||||
|
"securityReportsWeekly": 15
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 CCTV Locations
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "CCTV-01",
|
||||||
|
"lat": -8.5,
|
||||||
|
"lng": 115.2,
|
||||||
|
"status": "active",
|
||||||
|
"lastSeen": "2025-10-06T14:30:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 Security Reports
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "REP-001",
|
||||||
|
"title": "Pencurian Motor",
|
||||||
|
"reportedAt": "2025-10-06T15:30:00Z",
|
||||||
|
"location": "Jl. Kecubung 20",
|
||||||
|
"status": "new"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Catatan Pengembangan Lanjutan
|
||||||
|
|
||||||
|
* Filter laporan berdasarkan jenis & waktu
|
||||||
|
* Integrasi snapshot CCTV
|
||||||
|
* Heatmap area rawan
|
||||||
|
* Role-based access (admin / petugas)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Dokumen ini wajib diperbarui bila terdapat perubahan UI atau kebutuhan data pada modul **Keamanan**.
|
||||||
239
Dashboard-MD/KINERJA-DIVISI-2.md
Normal file
239
Dashboard-MD/KINERJA-DIVISI-2.md
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# KINERJA-DIVISI-2
|
||||||
|
|
||||||
|
## 1. Tujuan Halaman
|
||||||
|
|
||||||
|
Halaman **Kinerja Divisi** berfungsi sebagai dashboard operasional untuk memantau progres pekerjaan lintas divisi perangkat desa secara real-time, mencakup status tugas, arsip digital, kegiatan, diskusi internal, dan statistik pendukung.
|
||||||
|
|
||||||
|
Halaman ini dipakai oleh:
|
||||||
|
|
||||||
|
* Kepala Desa
|
||||||
|
* Sekretaris Desa
|
||||||
|
* Kepala Divisi
|
||||||
|
* Admin Operasional
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Struktur Tata Letak (Overall Layout)
|
||||||
|
|
||||||
|
### Pola Layout
|
||||||
|
|
||||||
|
* **Dashboard berbasis Card + Grid**
|
||||||
|
* Scroll vertikal satu halaman
|
||||||
|
* Tidak ada sidebar khusus (diasumsikan sudah ada global sidebar)
|
||||||
|
|
||||||
|
### Urutan Section (Top → Bottom)
|
||||||
|
|
||||||
|
1. Grafik Progres Tugas per Divisi
|
||||||
|
2. Ringkasan Tugas per Divisi (Sekretariat, Keuangan, Sosial, Humas)
|
||||||
|
3. Arsip Digital Perangkat Desa
|
||||||
|
4. Kartu Progres Kegiatan (Event / Program)
|
||||||
|
5. Statistik Dokumen & Progres Kegiatan
|
||||||
|
6. Diskusi Internal
|
||||||
|
7. Agenda / Acara Hari Ini
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Komponen UI
|
||||||
|
|
||||||
|
### 3.1 Grafik Progres Tugas per Divisi
|
||||||
|
|
||||||
|
**Tipe:** Grouped Bar Chart
|
||||||
|
|
||||||
|
**Data:**
|
||||||
|
|
||||||
|
* Divisi: Sekretariat, Keuangan, Sosial, Humas
|
||||||
|
* Status:
|
||||||
|
|
||||||
|
* Selesai
|
||||||
|
* Berjalan
|
||||||
|
* Tertunda
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Hover tooltip menampilkan jumlah tugas
|
||||||
|
* Legend clickable (toggle series)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Card Ringkasan Divisi
|
||||||
|
|
||||||
|
Setiap divisi memiliki satu card berisi daftar tugas.
|
||||||
|
|
||||||
|
**Struktur Card:**
|
||||||
|
|
||||||
|
* Judul divisi
|
||||||
|
* List tugas:
|
||||||
|
|
||||||
|
* Nama tugas
|
||||||
|
* Badge status: `selesai | proses | tertunda`
|
||||||
|
|
||||||
|
**Status Badge:**
|
||||||
|
|
||||||
|
* Hijau: Selesai
|
||||||
|
* Kuning: Proses
|
||||||
|
* Merah: Tertunda
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Arsip Digital Perangkat Desa
|
||||||
|
|
||||||
|
**Tipe:** Action Card / Button Card
|
||||||
|
|
||||||
|
**Item:**
|
||||||
|
|
||||||
|
* Surat Keputusan
|
||||||
|
* Laporan Keuangan
|
||||||
|
* Dokumentasi
|
||||||
|
* Notulensi Rapat
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Klik membuka modul arsip
|
||||||
|
* Role-based access (view / upload)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 Progres Kegiatan / Program
|
||||||
|
|
||||||
|
**Tipe:** Progress Card
|
||||||
|
|
||||||
|
**Isi:**
|
||||||
|
|
||||||
|
* Nama kegiatan
|
||||||
|
* Progress bar horizontal
|
||||||
|
* Tanggal
|
||||||
|
* Badge status (selesai)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 Statistik Dokumen
|
||||||
|
|
||||||
|
#### a. Jumlah Dokumen
|
||||||
|
|
||||||
|
**Tipe:** Bar Chart
|
||||||
|
|
||||||
|
* Kategori: Gambar, Dokumen
|
||||||
|
|
||||||
|
#### b. Progres Kegiatan
|
||||||
|
|
||||||
|
**Tipe:** Pie Chart
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
|
||||||
|
* Selesai
|
||||||
|
* Dikerjakan
|
||||||
|
* Segera Dikerjakan
|
||||||
|
* Dibatalkan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.6 Diskusi Internal
|
||||||
|
|
||||||
|
**Tipe:** List Card
|
||||||
|
|
||||||
|
**Isi:**
|
||||||
|
|
||||||
|
* Judul diskusi
|
||||||
|
* Pengirim
|
||||||
|
* Timestamp
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Klik membuka thread diskusi
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.7 Agenda / Acara Hari Ini
|
||||||
|
|
||||||
|
**Tipe:** Informational Card
|
||||||
|
|
||||||
|
**State:**
|
||||||
|
|
||||||
|
* Empty state: "Tidak ada acara hari ini"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gaya & Tema Visual
|
||||||
|
|
||||||
|
### 4.1 Warna
|
||||||
|
|
||||||
|
* Background utama: `#0F172A` / `#111827`
|
||||||
|
* Card: `#1E293B`
|
||||||
|
* Primary accent: Biru tua
|
||||||
|
* Success: Hijau
|
||||||
|
* Warning: Kuning
|
||||||
|
* Danger: Merah
|
||||||
|
|
||||||
|
### 4.2 Tipografi
|
||||||
|
|
||||||
|
* Font utama: **Inter / Poppins**
|
||||||
|
* Heading: Semi-bold
|
||||||
|
* Body: Regular
|
||||||
|
* Angka & statistik: Medium
|
||||||
|
|
||||||
|
### 4.3 Spasi & Radius
|
||||||
|
|
||||||
|
* Padding card: 16–24px
|
||||||
|
* Gap grid: 16–20px
|
||||||
|
* Border radius: 12–16px
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Responsivitas
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
* Multi-column grid (2–4 kolom)
|
||||||
|
|
||||||
|
### Tablet
|
||||||
|
|
||||||
|
* Grid 2 kolom
|
||||||
|
* Grafik full-width
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* Semua card 1 kolom
|
||||||
|
* Chart di-scroll horizontal bila perlu
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Ekspektasi Interaksi & Data
|
||||||
|
|
||||||
|
### 6.1 State Management
|
||||||
|
|
||||||
|
* Filter status tugas
|
||||||
|
* Sinkronisasi real-time (polling / websocket)
|
||||||
|
|
||||||
|
### 6.2 Contoh Struktur Data
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"division": "Keuangan",
|
||||||
|
"tasks": [
|
||||||
|
{ "title": "Laporan APBDes", "status": "selesai" },
|
||||||
|
{ "title": "Verifikasi Dana", "status": "tertunda" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Acceptance Criteria
|
||||||
|
|
||||||
|
* Semua status warna konsisten
|
||||||
|
* Chart sesuai data backend
|
||||||
|
* Empty state ditampilkan bila data kosong
|
||||||
|
* Mobile friendly tanpa overflow
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Catatan Implementasi
|
||||||
|
|
||||||
|
* Chart library: Chart.js / Recharts / ECharts
|
||||||
|
* Dark mode default
|
||||||
|
* Akses berbasis role (viewer, editor, admin)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status Dokumen:** Final Draft
|
||||||
|
**Digunakan untuk:** Implementasi Frontend & Kontrak API
|
||||||
225
Dashboard-MD/PENGAUDAN.md
Normal file
225
Dashboard-MD/PENGAUDAN.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
# PENGADUAN-2.md
|
||||||
|
|
||||||
|
## 1. Tujuan Halaman
|
||||||
|
|
||||||
|
Dashboard **Pengaduan Masyarakat** berfungsi untuk memantau, menganalisis, dan menindaklanjuti pengaduan warga secara real-time. Halaman ini mendukung **Light Mode** dan **Dark Mode** dengan struktur UI dan interaksi yang konsisten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Struktur Komponen UI
|
||||||
|
|
||||||
|
### 2.1 Summary Cards (Statistik Utama)
|
||||||
|
|
||||||
|
Menampilkan ringkasan status pengaduan bulan berjalan.
|
||||||
|
|
||||||
|
**Komponen:**
|
||||||
|
|
||||||
|
* Total Pengaduan
|
||||||
|
* Baru (Belum diproses)
|
||||||
|
* Diproses (Sedang ditangani)
|
||||||
|
* Selesai (Terselesaikan)
|
||||||
|
|
||||||
|
**Elemen UI:**
|
||||||
|
|
||||||
|
* Angka utama (bold, besar)
|
||||||
|
* Label deskriptif
|
||||||
|
* Ikon status (chat, alert, clock, check)
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Hover: efek elevasi + highlight border
|
||||||
|
* Click (opsional): filter dashboard berdasarkan status
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 Grafik Tren Pengaduan (Line Chart)
|
||||||
|
|
||||||
|
Menampilkan jumlah pengaduan per bulan.
|
||||||
|
|
||||||
|
**Komponen:**
|
||||||
|
|
||||||
|
* Sumbu X: Bulan
|
||||||
|
* Sumbu Y: Jumlah pengaduan
|
||||||
|
* Garis tren + titik data
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Tooltip saat hover (bulan & jumlah)
|
||||||
|
* Toggle range waktu (bulanan/tahunan – opsional)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.3 Surat Terbanyak (Horizontal Bar Chart)
|
||||||
|
|
||||||
|
Menunjukkan jenis surat/pengaduan yang paling sering diajukan.
|
||||||
|
|
||||||
|
**Contoh Data:**
|
||||||
|
|
||||||
|
* KTP
|
||||||
|
* KK
|
||||||
|
* Domisili
|
||||||
|
* Usaha
|
||||||
|
* Lainnya
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Hover tooltip
|
||||||
|
* Click bar → filter daftar pengajuan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.4 Pengajuan Terbaru (List)
|
||||||
|
|
||||||
|
Daftar pengaduan terbaru dari warga.
|
||||||
|
|
||||||
|
**Isi Item:**
|
||||||
|
|
||||||
|
* Nama pengaju
|
||||||
|
* Jenis pengaduan
|
||||||
|
* Waktu pengajuan
|
||||||
|
* Badge status: Baru / Diproses / Selesai
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Click item → halaman detail
|
||||||
|
* Scrollable list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.5 Ajuan Ide Inovatif (List)
|
||||||
|
|
||||||
|
Menampilkan ide/inisiatif warga untuk desa.
|
||||||
|
|
||||||
|
**Isi Item:**
|
||||||
|
|
||||||
|
* Nama pengaju
|
||||||
|
* Judul ide
|
||||||
|
* Kategori
|
||||||
|
* Tombol "Detail"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Tata Letak Keseluruhan (Layout)
|
||||||
|
|
||||||
|
**Desktop:**
|
||||||
|
|
||||||
|
* Grid 12 kolom
|
||||||
|
* Baris 1: 4 Summary Cards
|
||||||
|
* Baris 2: Line Chart (full width)
|
||||||
|
* Baris 3: 3 kolom (Bar Chart, Pengajuan Terbaru, Ide Inovatif)
|
||||||
|
|
||||||
|
**Tablet:**
|
||||||
|
|
||||||
|
* Summary card 2x2
|
||||||
|
* Chart full width
|
||||||
|
* List stack vertikal
|
||||||
|
|
||||||
|
**Mobile:**
|
||||||
|
|
||||||
|
* Semua komponen stacked
|
||||||
|
* Chart scroll horizontal
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gaya & Tema Visual
|
||||||
|
|
||||||
|
### 4.1 Light Mode
|
||||||
|
|
||||||
|
**Warna:**
|
||||||
|
|
||||||
|
* Background utama: #F6F9FC
|
||||||
|
* Card: #FFFFFF
|
||||||
|
* Primary: #2563EB (Blue)
|
||||||
|
* Text utama: #0F172A
|
||||||
|
* Text sekunder: #64748B
|
||||||
|
* Border: #E2E8F0
|
||||||
|
|
||||||
|
**Chart:**
|
||||||
|
|
||||||
|
* Line: Biru
|
||||||
|
* Bar: Biru tua
|
||||||
|
* Grid: Abu muda
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 Dark Mode
|
||||||
|
|
||||||
|
**Warna:**
|
||||||
|
|
||||||
|
* Background utama: #0B1220
|
||||||
|
* Card: #111827 / #0F172A
|
||||||
|
* Primary: #3B82F6
|
||||||
|
* Text utama: #E5E7EB
|
||||||
|
* Text sekunder: #9CA3AF
|
||||||
|
* Border: #1F2937
|
||||||
|
|
||||||
|
**Chart:**
|
||||||
|
|
||||||
|
* Line: Biru terang
|
||||||
|
* Bar: Biru medium
|
||||||
|
* Grid: Abu gelap
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Tipografi
|
||||||
|
|
||||||
|
* Font utama: Inter / Poppins
|
||||||
|
* Heading: 600–700
|
||||||
|
* Body: 400–500
|
||||||
|
* Angka statistik: 700
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Spasi & Radius
|
||||||
|
|
||||||
|
* Padding card: 16–24px
|
||||||
|
* Gap grid: 16–24px
|
||||||
|
* Border radius card: 12–16px
|
||||||
|
* Icon container: circle / rounded-full
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Responsivitas
|
||||||
|
|
||||||
|
* Mobile-first
|
||||||
|
* Breakpoint umum: 640px, 768px, 1024px
|
||||||
|
* Chart auto-resize
|
||||||
|
* List menjadi full-width
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Ekspektasi Data & Interaksi Backend
|
||||||
|
|
||||||
|
### 8.1 Contoh Data Summary
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"total": 42,
|
||||||
|
"baru": 14,
|
||||||
|
"diproses": 14,
|
||||||
|
"selesai": 14
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Contoh Data Tren
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "bulan": "Apr", "jumlah": 30 },
|
||||||
|
{ "bulan": "Mei", "jumlah": 50 },
|
||||||
|
{ "bulan": "Jun", "jumlah": 42 }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Catatan Teknis
|
||||||
|
|
||||||
|
* Chart: Recharts / Chart.js / ECharts
|
||||||
|
* State: loading, empty, error
|
||||||
|
* Theme switch: CSS variable / Tailwind dark mode
|
||||||
|
* Konsistensi warna status di seluruh modul
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Dokumen ini menjadi **UI Spec + UX Contract** untuk modul Pengaduan dan siap diturunkan ke desain Figma maupun implementasi Frontend.
|
||||||
37
Dashboard-MD/SIDEBAR.md
Normal file
37
Dashboard-MD/SIDEBAR.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# SIDEBAR.md
|
||||||
|
## Spesifikasi Sidebar Navigasi – DESA+
|
||||||
|
|
||||||
|
### Tujuan
|
||||||
|
Membuat sidebar navigasi dengan **indikator menu aktif (isActive)** seperti pada desain:
|
||||||
|
- Menu aktif memiliki **background biru muda**
|
||||||
|
- Ada **indikator garis vertikal di sisi kiri**
|
||||||
|
- Status aktif mengikuti **route/path yang sedang dibuka**
|
||||||
|
|
||||||
|
---
|
||||||
|
## Perilaku isActive
|
||||||
|
|
||||||
|
### Definisi isActive
|
||||||
|
Sebuah menu dianggap **aktif** jika:
|
||||||
|
- `currentPath === menu.path`
|
||||||
|
- atau (opsional) `currentPath.startsWith(menu.path)` untuk nested route
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tampilan Menu Aktif
|
||||||
|
|
||||||
|
Jika menu **AKTIF**, maka:
|
||||||
|
- Background: `#E6F0FF` (biru muda)
|
||||||
|
- Text: bold / semibold
|
||||||
|
- Border kiri:
|
||||||
|
- Lebar: `4px`
|
||||||
|
- Warna: `#1E40AF` (biru tua)
|
||||||
|
- Border radius: `8px`
|
||||||
|
- Transition halus (`150–200ms`)
|
||||||
|
|
||||||
|
Jika menu **TIDAK AKTIF**:
|
||||||
|
- Background transparan
|
||||||
|
- Text normal
|
||||||
|
- Hover state:
|
||||||
|
- Background `#F1F5F9`
|
||||||
|
|
||||||
|
---
|
||||||
230
Dashboard-MD/SOSIAL.md
Normal file
230
Dashboard-MD/SOSIAL.md
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
# SOSIAL.md
|
||||||
|
|
||||||
|
Dokumen ini mendeskripsikan spesifikasi UI/UX, tata letak, gaya visual, serta ekspektasi data & interaksi untuk **Halaman Sosial Desa**. Digunakan sebagai acuan bersama antara UI/UX Designer, Frontend Developer, dan Backend/API.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Tujuan Halaman
|
||||||
|
|
||||||
|
Halaman **Sosial** berfungsi sebagai dashboard monitoring:
|
||||||
|
|
||||||
|
* Kesehatan masyarakat (ibu hamil, balita, stunting)
|
||||||
|
* Aktivitas Posyandu
|
||||||
|
* Pendidikan desa
|
||||||
|
* Beasiswa
|
||||||
|
* Event sosial & budaya
|
||||||
|
|
||||||
|
Fokus utama: **ringkas, informatif, real-time friendly**, dan mudah dipahami perangkat desa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Tata Letak Keseluruhan
|
||||||
|
|
||||||
|
### Struktur Global
|
||||||
|
|
||||||
|
* **Sidebar kiri (persisten)**
|
||||||
|
|
||||||
|
* Navigasi utama aplikasi desa
|
||||||
|
* Menu aktif: `Sosial`
|
||||||
|
|
||||||
|
* **Topbar**
|
||||||
|
|
||||||
|
* Nama desa
|
||||||
|
* Profil user (Kepala Desa)
|
||||||
|
* Notifikasi
|
||||||
|
* Toggle tema (opsional)
|
||||||
|
|
||||||
|
* **Main Content Area (scrollable)**
|
||||||
|
|
||||||
|
* Grid berbasis card
|
||||||
|
* Layout desktop: 2–3 kolom
|
||||||
|
* Mobile: 1 kolom (stack)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Komponen UI
|
||||||
|
|
||||||
|
### 3.1 Statistik Kesehatan (Summary Cards)
|
||||||
|
|
||||||
|
**Tipe:** KPI Cards
|
||||||
|
|
||||||
|
| Komponen | Deskripsi |
|
||||||
|
| ---------------- | -------------------------------- |
|
||||||
|
| Ibu Hamil Aktif | Total ibu hamil aktif saat ini |
|
||||||
|
| Balita Terdaftar | Total balita tercatat |
|
||||||
|
| Alert Stunting | Jumlah kasus/peringatan stunting |
|
||||||
|
| Posyandu Aktif | Jumlah posyandu yang aktif |
|
||||||
|
|
||||||
|
**Elemen UI:**
|
||||||
|
|
||||||
|
* Angka besar (headline)
|
||||||
|
* Label kecil
|
||||||
|
* Ikon kontekstual
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Statistik Kesehatan (Progress Bar)
|
||||||
|
|
||||||
|
Menampilkan capaian dalam bentuk **horizontal progress bar**:
|
||||||
|
|
||||||
|
* Imunisasi Lengkap
|
||||||
|
* Pemeriksaan Rutin
|
||||||
|
* Gizi Baik
|
||||||
|
* Target Stunting
|
||||||
|
|
||||||
|
**Behavior:**
|
||||||
|
|
||||||
|
* Persentase dinamis
|
||||||
|
* Warna bar menyesuaikan status (normal / warning)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Jadwal Posyandu
|
||||||
|
|
||||||
|
**Tipe:** List Card
|
||||||
|
|
||||||
|
Setiap item menampilkan:
|
||||||
|
|
||||||
|
* Nama Posyandu
|
||||||
|
* Tanggal
|
||||||
|
* Jam kegiatan
|
||||||
|
|
||||||
|
**Interaksi (opsional):**
|
||||||
|
|
||||||
|
* Klik item → detail jadwal
|
||||||
|
* Filter berdasarkan wilayah / tanggal
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 Pendidikan
|
||||||
|
|
||||||
|
**Tipe:** Informational List
|
||||||
|
|
||||||
|
#### Data Siswa
|
||||||
|
|
||||||
|
* TK / PAUD
|
||||||
|
* SD
|
||||||
|
* SMP
|
||||||
|
* SMA
|
||||||
|
|
||||||
|
#### Info Sekolah
|
||||||
|
|
||||||
|
* Jumlah lembaga pendidikan
|
||||||
|
* Jumlah tenaga pengajar
|
||||||
|
|
||||||
|
Layout sederhana, fokus ke angka.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 Beasiswa Desa
|
||||||
|
|
||||||
|
**Tipe:** Highlight Card
|
||||||
|
|
||||||
|
* Jumlah penerima beasiswa
|
||||||
|
* Total dana tersalurkan
|
||||||
|
* Tahun ajaran
|
||||||
|
|
||||||
|
Digunakan sebagai **headline informasi bantuan pendidikan**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.6 Kalender Event Budaya
|
||||||
|
|
||||||
|
**Tipe:** Event List
|
||||||
|
|
||||||
|
Setiap event:
|
||||||
|
|
||||||
|
* Nama kegiatan
|
||||||
|
* Tanggal
|
||||||
|
* Lokasi
|
||||||
|
|
||||||
|
**Interaksi:**
|
||||||
|
|
||||||
|
* Klik → detail event
|
||||||
|
* Potensi integrasi kalender
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gaya & Tema Visual
|
||||||
|
|
||||||
|
### Warna
|
||||||
|
|
||||||
|
* **Primary:** Biru navy (#1F3A5F – estimasi)
|
||||||
|
* **Secondary:** Biru muda / abu terang
|
||||||
|
* **Success:** Hijau
|
||||||
|
* **Warning:** Oranye / Merah (stunting alert)
|
||||||
|
|
||||||
|
### Font
|
||||||
|
|
||||||
|
* Sans-serif modern (Inter / Poppins)
|
||||||
|
* Hierarki jelas:
|
||||||
|
|
||||||
|
* Judul card
|
||||||
|
* Angka utama
|
||||||
|
* Label kecil
|
||||||
|
|
||||||
|
### Spasi & Bentuk
|
||||||
|
|
||||||
|
* Padding card: 16–24px
|
||||||
|
* Border radius: 12–16px
|
||||||
|
* Shadow ringan (soft elevation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Responsivitas
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
* Grid 2–3 kolom
|
||||||
|
* Semua card terlihat tanpa overflow horizontal
|
||||||
|
|
||||||
|
### Tablet
|
||||||
|
|
||||||
|
* Grid 2 kolom
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* 1 kolom (stack)
|
||||||
|
* KPI cards jadi swipe / vertical list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Ekspektasi Interaksi & Data
|
||||||
|
|
||||||
|
### Interaksi Umum
|
||||||
|
|
||||||
|
* Hover state pada card
|
||||||
|
* Click-through ke halaman detail
|
||||||
|
* Data auto-refresh (optional)
|
||||||
|
|
||||||
|
### Ekspektasi API (contoh)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ibu_hamil": 87,
|
||||||
|
"balita": 342,
|
||||||
|
"alert_stunting": 12,
|
||||||
|
"posyandu_aktif": 8,
|
||||||
|
"kesehatan": {
|
||||||
|
"imunisasi": 92,
|
||||||
|
"pemeriksaan": 88,
|
||||||
|
"gizi": 86,
|
||||||
|
"stunting": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Acceptance Checklist
|
||||||
|
|
||||||
|
* [ ] Semua data tampil konsisten
|
||||||
|
* [ ] Tidak ada overflow di mobile
|
||||||
|
* [ ] Progress bar sesuai persentase
|
||||||
|
* [ ] Event & jadwal dapat diklik
|
||||||
|
* [ ] Alert stunting terlihat jelas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Catatan:**
|
||||||
|
Dokumen ini bersifat living document dan dapat diperluas ke modul detail (kesehatan, pendidikan, bantuan sosial).
|
||||||
303
src/components/bumdes-page.tsx
Normal file
303
src/components/bumdes-page.tsx
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
Button,
|
||||||
|
Badge,
|
||||||
|
Table,
|
||||||
|
Stack,
|
||||||
|
Select,
|
||||||
|
useMantineColorScheme
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { IconBuildingStore, IconCategory, IconCurrency, IconUsers } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
const BumdesPage = () => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === 'dark';
|
||||||
|
|
||||||
|
const [timeFilter, setTimeFilter] = useState<string>("bulan");
|
||||||
|
|
||||||
|
// Sample data for KPI cards
|
||||||
|
const kpiData = [
|
||||||
|
{
|
||||||
|
title: "UMKM Aktif",
|
||||||
|
value: 45,
|
||||||
|
icon: <IconUsers size={24} />,
|
||||||
|
color: "darmasaba-blue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "UMKM Terdaftar",
|
||||||
|
value: 68,
|
||||||
|
icon: <IconBuildingStore size={24} />,
|
||||||
|
color: "darmasaba-success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Omzet",
|
||||||
|
value: "Rp 48.000.000",
|
||||||
|
icon: <IconCurrency size={24} />,
|
||||||
|
color: "darmasaba-warning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Kategori UMKM",
|
||||||
|
value: 34,
|
||||||
|
icon: <IconCategory size={24} />,
|
||||||
|
color: "darmasaba-danger",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for top products
|
||||||
|
const topProducts = [
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
name: "Beras Premium Organik",
|
||||||
|
umkmOwner: "Warung Pak Joko",
|
||||||
|
growth: "+12%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rank: 2,
|
||||||
|
name: "Keripik Singkong",
|
||||||
|
umkmOwner: "Ibu Sari Snack",
|
||||||
|
growth: "+8%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rank: 3,
|
||||||
|
name: "Madu Alami",
|
||||||
|
umkmOwner: "Peternakan Lebah",
|
||||||
|
growth: "+5%",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for product sales
|
||||||
|
const productSales = [
|
||||||
|
{
|
||||||
|
produk: "Beras Premium Organik",
|
||||||
|
penjualanBulanIni: "Rp 8.500.000",
|
||||||
|
bulanLalu: "Rp 8.500.000",
|
||||||
|
trend: 10,
|
||||||
|
volume: "650 Kg",
|
||||||
|
stok: "850 Kg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
produk: "Keripik Singkong",
|
||||||
|
penjualanBulanIni: "Rp 4.200.000",
|
||||||
|
bulanLalu: "Rp 3.800.000",
|
||||||
|
trend: 10,
|
||||||
|
volume: "320 Kg",
|
||||||
|
stok: "120 Kg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
produk: "Madu Alami",
|
||||||
|
penjualanBulanIni: "Rp 3.750.000",
|
||||||
|
bulanLalu: "Rp 4.100.000",
|
||||||
|
trend: -8,
|
||||||
|
volume: "150 Liter",
|
||||||
|
stok: "45 Liter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
produk: "Kecap Tradisional",
|
||||||
|
penjualanBulanIni: "Rp 2.800.000",
|
||||||
|
bulanLalu: "Rp 2.500.000",
|
||||||
|
trend: 12,
|
||||||
|
volume: "280 Botol",
|
||||||
|
stok: "95 Botol",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Page Header */}
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Title order={2} c={dark ? "dark.0" : "black"}>
|
||||||
|
BUMDes & UMKM Desa
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* KPI Cards */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
{kpiData.map((kpi, index) => (
|
||||||
|
<GridCol key={index} span={{ base: 12, sm: 6, md: 3 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
{kpi.title}
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{typeof kpi.value === 'number' ? kpi.value.toLocaleString() : kpi.value}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color={kpi.color}
|
||||||
|
p={8}
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
{kpi.icon}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Update Penjualan Produk Header */}
|
||||||
|
<Card withBorder radius="md" bg={dark ? "dark.8" : "darmasaba-blue.0"}>
|
||||||
|
<Group justify="space-between" align="center" px="md" py="xs">
|
||||||
|
<Title order={3} c={dark ? "dark.0" : "black"}>
|
||||||
|
Update Penjualan Produk
|
||||||
|
</Title>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
variant={timeFilter === "minggu" ? "filled" : "light"}
|
||||||
|
onClick={() => setTimeFilter("minggu")}
|
||||||
|
color="darmasaba-blue"
|
||||||
|
>
|
||||||
|
Minggu ini
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={timeFilter === "bulan" ? "filled" : "light"}
|
||||||
|
onClick={() => setTimeFilter("bulan")}
|
||||||
|
color="darmasaba-blue"
|
||||||
|
>
|
||||||
|
Bulan ini
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Produk Unggulan (Left Column) */}
|
||||||
|
<GridCol span={{ base: 12, lg: 4 }}>
|
||||||
|
<Stack gap="md">
|
||||||
|
{/* Total Penjualan, Produk Aktif, Total Transaksi */}
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Stack gap="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Total Penjualan</Text>
|
||||||
|
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>Rp 28.500.000</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Produk Aktif</Text>
|
||||||
|
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>124 Produk</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Total Transaksi</Text>
|
||||||
|
<Text size="lg" fw={700} c={dark ? "dark.0" : "black"}>1.240 Transaksi</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Top 3 Produk Terlaris */}
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>Top 3 Produk Terlaris</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{topProducts.map((product) => (
|
||||||
|
<Group key={product.rank} justify="space-between" align="center">
|
||||||
|
<Group gap="sm">
|
||||||
|
<Badge
|
||||||
|
variant="filled"
|
||||||
|
color={product.rank === 1 ? "gold" : product.rank === 2 ? "gray" : "bronze"}
|
||||||
|
radius="xl"
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
{product.rank}
|
||||||
|
</Badge>
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{product.name}</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{product.umkmOwner}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color={product.growth.startsWith('+') ? "green" : "red"}
|
||||||
|
>
|
||||||
|
{product.growth}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Detail Penjualan Produk (Right Column) */}
|
||||||
|
<GridCol span={{ base: 12, lg: 8 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Group justify="space-between" mb="md">
|
||||||
|
<Title order={4} c={dark ? "dark.0" : "black"}>Detail Penjualan Produk</Title>
|
||||||
|
<Select
|
||||||
|
placeholder="Filter kategori"
|
||||||
|
data={[
|
||||||
|
{ value: 'semua', label: 'Semua Kategori' },
|
||||||
|
{ value: 'makanan', label: 'Makanan' },
|
||||||
|
{ value: 'minuman', label: 'Minuman' },
|
||||||
|
{ value: 'kerajinan', label: 'Kerajinan' },
|
||||||
|
]}
|
||||||
|
defaultValue="semua"
|
||||||
|
w={200}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Table striped highlightOnHover withColumnBorders>
|
||||||
|
<Table.Thead>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Produk</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Penjualan Bulan Ini</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Bulan Lalu</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Trend</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Volume</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Stok</Text></Table.Th>
|
||||||
|
<Table.Th><Text c={dark ? "dark.3" : "dimmed"}>Aksi</Text></Table.Th>
|
||||||
|
</Table.Tr>
|
||||||
|
</Table.Thead>
|
||||||
|
<Table.Tbody>
|
||||||
|
{productSales.map((product, index) => (
|
||||||
|
<Table.Tr key={index}>
|
||||||
|
<Table.Td>
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{product.produk}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Text fz={"sm"} c={dark ? "dark.0" : "black"}>{product.penjualanBulanIni}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Text fz={"sm"} c={dark ? "dark.3" : "dimmed"}>{product.bulanLalu}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Group gap="xs">
|
||||||
|
<Text c={product.trend >= 0 ? "green" : "red"}>
|
||||||
|
{product.trend >= 0 ? '↑' : '↓'} {Math.abs(product.trend)}%
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Text fz={"sm"} c={dark ? "dark.0" : "black"}>{product.volume}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color={parseInt(product.stok) > 200 ? "green" : "yellow"}
|
||||||
|
>
|
||||||
|
{product.stok}
|
||||||
|
</Badge>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Button variant="subtle" size="compact-sm" color="darmasaba-blue">
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
))}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BumdesPage;
|
||||||
@@ -74,7 +74,7 @@ export function DashboardContent() {
|
|||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<Grid gutter="md">
|
<Grid gutter="md">
|
||||||
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<Card p="md" radius="md" h="100%" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group justify="space-between" align="flex-start" w="100%">
|
<Group justify="space-between" align="flex-start" w="100%">
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<Text size="sm" c="dimmed" mb="xs">
|
<Text size="sm" c="dimmed" mb="xs">
|
||||||
@@ -99,7 +99,7 @@ export function DashboardContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<Card p="md" radius="md" h="100%" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group justify="space-between" align="flex-start" w="100%">
|
<Group justify="space-between" align="flex-start" w="100%">
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<Text size="sm" c="dimmed" mb="xs">
|
<Text size="sm" c="dimmed" mb="xs">
|
||||||
@@ -121,7 +121,7 @@ export function DashboardContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<Card p="md" radius="md" h="100%" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group justify="space-between" align="flex-start" w="100%">
|
<Group justify="space-between" align="flex-start" w="100%">
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<Text size="sm" c="dimmed" mb="xs">
|
<Text size="sm" c="dimmed" mb="xs">
|
||||||
@@ -146,7 +146,7 @@ export function DashboardContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
<Grid.Col span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<Card p="md" radius="md" h="100%" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" h="100%" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group justify="space-between" align="flex-start" w="100%">
|
<Group justify="space-between" align="flex-start" w="100%">
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<Text size="sm" c="dimmed" mb="xs">
|
<Text size="sm" c="dimmed" mb="xs">
|
||||||
@@ -171,7 +171,7 @@ export function DashboardContent() {
|
|||||||
<Grid gutter="lg">
|
<Grid gutter="lg">
|
||||||
{/* Bar Chart */}
|
{/* Bar Chart */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Card p="md" radius="md" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group justify="space-between" mb="md">
|
<Group justify="space-between" mb="md">
|
||||||
<Box>
|
<Box>
|
||||||
<Title order={4} mb={5}>
|
<Title order={4} mb={5}>
|
||||||
@@ -232,7 +232,7 @@ export function DashboardContent() {
|
|||||||
|
|
||||||
{/* Pie Chart */}
|
{/* Pie Chart */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Card p="md" radius="md" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Title order={4} mb={5}>
|
<Title order={4} mb={5}>
|
||||||
Tingkat Kepuasan
|
Tingkat Kepuasan
|
||||||
</Title>
|
</Title>
|
||||||
@@ -283,7 +283,7 @@ export function DashboardContent() {
|
|||||||
<Grid gutter="lg">
|
<Grid gutter="lg">
|
||||||
{/* Divisi Teraktif */}
|
{/* Divisi Teraktif */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Card p="md" radius="md" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group gap="xs" mb="lg">
|
<Group gap="xs" mb="lg">
|
||||||
<Box>
|
<Box>
|
||||||
{/* Original SVG icon */}
|
{/* Original SVG icon */}
|
||||||
@@ -355,7 +355,7 @@ export function DashboardContent() {
|
|||||||
|
|
||||||
{/* Kalender */}
|
{/* Kalender */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Card p="md" radius="md" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Group gap="xs" mb="lg">
|
<Group gap="xs" mb="lg">
|
||||||
<Calendar style={{ width: 20, height: 20 }} />
|
<Calendar style={{ width: 20, height: 20 }} />
|
||||||
<Title order={4}>Kalender & Kegiatan Mendatang</Title>
|
<Title order={4}>Kalender & Kegiatan Mendatang</Title>
|
||||||
@@ -378,7 +378,7 @@ export function DashboardContent() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* APBDes Chart */}
|
{/* APBDes Chart */}
|
||||||
<Card p="md" radius="md" withBorder>
|
<Card p="md" style={{borderColor: dark ? "#141D34" : "white"}} radius="md" withBorder bg={dark ? "#141D34" : "white"}>
|
||||||
<Title order={4} mb="lg">
|
<Title order={4} mb="lg">
|
||||||
Grafik APBDes
|
Grafik APBDes
|
||||||
</Title>
|
</Title>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ const DemografiPekerjaan = () => {
|
|||||||
<Box className="space-y-6">
|
<Box className="space-y-6">
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<Title order={1} fw={700}>
|
<Title order={2} fw={700}>
|
||||||
Demografi & Kependudukan
|
Demografi & Kependudukan
|
||||||
</Title>
|
</Title>
|
||||||
<Button variant="filled">Export Data</Button>
|
<Button variant="filled">Export Data</Button>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function Header() {
|
|||||||
const getPageTitle = () => {
|
const getPageTitle = () => {
|
||||||
switch (location.pathname) {
|
switch (location.pathname) {
|
||||||
case "/":
|
case "/":
|
||||||
return "Dashboard";
|
return "Desa Darmasaba";
|
||||||
case "/kinerja-divisi":
|
case "/kinerja-divisi":
|
||||||
return "Kinerja Divisi";
|
return "Kinerja Divisi";
|
||||||
case "/pengaduan":
|
case "/pengaduan":
|
||||||
@@ -43,14 +43,14 @@ export function Header() {
|
|||||||
case "/pengaturan":
|
case "/pengaturan":
|
||||||
return "Pengaturan";
|
return "Pengaturan";
|
||||||
default:
|
default:
|
||||||
return "Dashboard";
|
return "Desa Darmasaba";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="space-between" w="100%">
|
<Group justify="space-between" w="100%">
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<Title order={2}>{getPageTitle()}</Title>
|
<Title order={3} c={"white"}>{getPageTitle()}</Title>
|
||||||
|
|
||||||
{/* Right Section */}
|
{/* Right Section */}
|
||||||
<Group gap="md">
|
<Group gap="md">
|
||||||
@@ -59,15 +59,15 @@ export function Header() {
|
|||||||
{/* User Info */}
|
{/* User Info */}
|
||||||
<Group gap="sm">
|
<Group gap="sm">
|
||||||
<Box ta="right">
|
<Box ta="right">
|
||||||
<Text size="sm" fw={500}>
|
<Text c={"white"} size="sm" fw={500}>
|
||||||
I. B. Surya Prabhawa M...
|
I. B. Surya Prabhawa M...
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed">
|
<Text c={"white"} size="xs">
|
||||||
Kepala Desa
|
Kepala Desa
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Avatar color="blue" radius="xl">
|
<Avatar color="blue" radius="xl">
|
||||||
<UserIcon style={{ width: "70%", height: "70%" }} />
|
<UserIcon color="white" style={{ width: "70%", height: "70%" }} />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
@@ -84,13 +84,13 @@ export function Header() {
|
|||||||
aria-label="Toggle color scheme"
|
aria-label="Toggle color scheme"
|
||||||
>
|
>
|
||||||
{dark ? (
|
{dark ? (
|
||||||
<Sun style={{ width: "70%", height: "70%" }} />
|
<Sun color="white" style={{ width: "70%", height: "70%" }} />
|
||||||
) : (
|
) : (
|
||||||
<Moon style={{ width: "70%", height: "70%" }} />
|
<Moon color="white" style={{ width: "70%", height: "70%" }} />
|
||||||
)}
|
)}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
<ActionIcon variant="subtle" size="lg" radius="xl" pos="relative">
|
<ActionIcon variant="subtle" size="lg" radius="xl" pos="relative">
|
||||||
<Bell style={{ width: "70%", height: "70%" }} />
|
<Bell color="white" style={{ width: "70%", height: "70%" }} />
|
||||||
<Badge
|
<Badge
|
||||||
size="xs"
|
size="xs"
|
||||||
color="red"
|
color="red"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Grid,
|
Grid,
|
||||||
Box,
|
Box,
|
||||||
|
useMantineColorScheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { BarChart } from "@mantine/charts";
|
import { BarChart } from "@mantine/charts";
|
||||||
|
|
||||||
@@ -133,21 +134,17 @@ const busyHours = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const JennaAnalytic = () => {
|
const JennaAnalytic = () => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === "dark";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="space-y-6">
|
<Box className="space-y-6">
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Group justify="space-between" align="center">
|
|
||||||
<Title order={1} fw={700}>
|
|
||||||
Jenna Analytic
|
|
||||||
</Title>
|
|
||||||
<Button variant="filled">Export Data</Button>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
{/* KPI Cards */}
|
{/* KPI Cards */}
|
||||||
<Grid gutter="lg">
|
<Grid gutter="lg">
|
||||||
{kpiData.map((kpi) => (
|
{kpiData.map((kpi) => (
|
||||||
<Grid.Col key={kpi.id} span={{ base: 12, md: 6, lg: 3 }}>
|
<Grid.Col key={kpi.id} span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
<Group justify="space-between" align="flex-start" mb="xs">
|
<Group justify="space-between" align="flex-start" mb="xs">
|
||||||
<Text size="sm" fw={500} c="dimmed">
|
<Text size="sm" fw={500} c="dimmed">
|
||||||
{kpi.title}
|
{kpi.title}
|
||||||
@@ -185,11 +182,7 @@ const JennaAnalytic = () => {
|
|||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Charts and Lists Section */}
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
<Grid gutter="lg">
|
|
||||||
{/* Grafik Interaksi Chatbot (now Bar Chart) */}
|
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
|
||||||
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
|
||||||
<Title order={3} fw={500} mb="md">
|
<Title order={3} fw={500} mb="md">
|
||||||
Interaksi Chatbot
|
Interaksi Chatbot
|
||||||
</Title>
|
</Title>
|
||||||
@@ -201,13 +194,38 @@ const JennaAnalytic = () => {
|
|||||||
withLegend
|
withLegend
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Charts and Lists Section */}
|
||||||
|
<Grid gutter="lg">
|
||||||
|
{/* Grafik Interaksi Chatbot (now Bar Chart) */}
|
||||||
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Title order={3} fw={500} mb="md">
|
||||||
|
Jam Tersibuk
|
||||||
|
</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{busyHours.map((item, index) => (
|
||||||
|
<Box key={index}>
|
||||||
|
<Text size="sm">
|
||||||
|
{item.period}
|
||||||
|
</Text>
|
||||||
|
<Group align="center">
|
||||||
|
<Progress value={item.percentage} flex={1} />
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{item.percentage}%
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
|
|
||||||
{/* Topik Pertanyaan Terbanyak & Jam Tersibuk */}
|
{/* Topik Pertanyaan Terbanyak & Jam Tersibuk */}
|
||||||
<Grid.Col span={{ base: 12, lg: 6 }}>
|
<Grid.Col span={{ base: 12, lg: 6 }}>
|
||||||
<Stack gap="lg">
|
<Stack gap="lg">
|
||||||
{/* Topik Pertanyaan Terbanyak */}
|
{/* Topik Pertanyaan Terbanyak */}
|
||||||
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
<Title order={3} fw={500} mb="md">
|
<Title order={3} fw={500} mb="md">
|
||||||
Topik Pertanyaan Terbanyak
|
Topik Pertanyaan Terbanyak
|
||||||
</Title>
|
</Title>
|
||||||
@@ -231,29 +249,12 @@ const JennaAnalytic = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Jam Tersibuk */}
|
{/* Jam Tersibuk */}
|
||||||
<Card shadow="sm" padding="lg" radius="md" withBorder>
|
|
||||||
<Title order={3} fw={500} mb="md">
|
|
||||||
Jam Tersibuk
|
|
||||||
</Title>
|
|
||||||
<Stack gap="sm">
|
|
||||||
{busyHours.map((item, index) => (
|
|
||||||
<Group key={index} align="center">
|
|
||||||
<Text w={80} size="sm">
|
|
||||||
{item.period}
|
|
||||||
</Text>
|
|
||||||
<Progress value={item.percentage} flex={1} />
|
|
||||||
<Text size="sm" fw={500}>
|
|
||||||
{item.percentage}%
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid >
|
||||||
</Stack>
|
|
||||||
</Box>
|
</Stack >
|
||||||
|
</Box >
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
225
src/components/keamanan-page.tsx
Normal file
225
src/components/keamanan-page.tsx
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
Stack,
|
||||||
|
useMantineColorScheme,
|
||||||
|
Badge,
|
||||||
|
List,
|
||||||
|
ThemeIcon,
|
||||||
|
Box
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
IconCamera,
|
||||||
|
IconAlertTriangle,
|
||||||
|
IconMapPin,
|
||||||
|
IconClock,
|
||||||
|
IconEye,
|
||||||
|
IconShieldLock
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
|
||||||
|
const KeamananPage = () => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === 'dark';
|
||||||
|
|
||||||
|
// Sample data for KPI cards
|
||||||
|
const kpiData = [
|
||||||
|
{
|
||||||
|
title: "CCTV Aktif",
|
||||||
|
value: 20,
|
||||||
|
subtitle: "Kamera Online",
|
||||||
|
icon: <IconCamera size={24} />,
|
||||||
|
color: "darmasaba-success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Laporan Keamanan",
|
||||||
|
value: 15,
|
||||||
|
subtitle: "Minggu ini",
|
||||||
|
icon: <IconAlertTriangle size={24} />,
|
||||||
|
color: "darmasaba-danger",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for CCTV locations
|
||||||
|
const cctvLocations = [
|
||||||
|
{ id: "CCTV-01", lat: -8.5, lng: 115.2, status: "active", lastSeen: "2 jam yang lalu", location: "Balai Desa" },
|
||||||
|
{ id: "CCTV-02", lat: -8.6, lng: 115.3, status: "active", lastSeen: "1 jam yang lalu", location: "Pintu Masuk Desa" },
|
||||||
|
{ id: "CCTV-03", lat: -8.4, lng: 115.1, status: "offline", lastSeen: "1 hari yang lalu", location: "Taman Desa" },
|
||||||
|
{ id: "CCTV-04", lat: -8.7, lng: 115.4, status: "active", lastSeen: "30 menit yang lalu", location: "Pasar Desa" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for security reports
|
||||||
|
const securityReports = [
|
||||||
|
{
|
||||||
|
id: "REP-001",
|
||||||
|
title: "Pencurian Motor",
|
||||||
|
reportedAt: "2 jam yang lalu",
|
||||||
|
date: "12 Feb 2026, 14:30",
|
||||||
|
location: "Jl. Kecubung 20",
|
||||||
|
status: "baru",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "REP-002",
|
||||||
|
title: "Kerusuhan Antar Warga",
|
||||||
|
reportedAt: "4 jam yang lalu",
|
||||||
|
date: "12 Feb 2026, 12:15",
|
||||||
|
location: "RT 05 RW 02",
|
||||||
|
status: "baru",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "REP-003",
|
||||||
|
title: "Kebakaran Rumah",
|
||||||
|
reportedAt: "1 hari yang lalu",
|
||||||
|
date: "11 Feb 2026, 08:45",
|
||||||
|
location: "Jl. Flamboyan 15",
|
||||||
|
status: "diproses",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "REP-004",
|
||||||
|
title: "Kehilangan Barang",
|
||||||
|
reportedAt: "2 hari yang lalu",
|
||||||
|
date: "10 Feb 2026, 16:20",
|
||||||
|
location: "Taman Desa",
|
||||||
|
status: "selesai",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Page Header */}
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Title order={2} c={dark ? "dark.0" : "black"}>
|
||||||
|
Keamanan Lingkungan Desa
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* KPI Cards */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
{kpiData.map((kpi, index) => (
|
||||||
|
<GridCol key={index} span={{ base: 12, sm: 6, md: 6 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
{kpi.subtitle}
|
||||||
|
</Text>
|
||||||
|
<Group gap="xs" align="center">
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{kpi.value}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
{kpi.title}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color={kpi.color} size="xl" radius="xl">
|
||||||
|
{kpi.icon}
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Peta Keamanan CCTV */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Peta Keamanan CCTV</Title>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mb="md">Titik Lokasi CCTV</Text>
|
||||||
|
|
||||||
|
{/* Placeholder for map */}
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
backgroundColor: dark ? '#2d3748' : '#e2e8f0',
|
||||||
|
borderRadius: '8px',
|
||||||
|
height: '400px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack align="center">
|
||||||
|
<IconMapPin size={48} stroke={1.5} color={dark ? '#94a3b8' : '#64748b'} />
|
||||||
|
<Text c={dark ? "dark.3" : "dimmed"}>Peta Lokasi CCTV</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} ta="center">Integrasi dengan Google Maps atau Mapbox akan ditampilkan di sini</Text>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* CCTV Locations List */}
|
||||||
|
<Stack mt="md" gap="sm">
|
||||||
|
<Title order={4} c={dark ? "dark.0" : "black"}>Daftar CCTV</Title>
|
||||||
|
{cctvLocations.map((cctv, index) => (
|
||||||
|
<Card key={index} withBorder radius="md" p="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Group gap="xs">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{cctv.id}</Text>
|
||||||
|
<Badge
|
||||||
|
variant="dot"
|
||||||
|
color={cctv.status === "active" ? "green" : "gray"}
|
||||||
|
>
|
||||||
|
{cctv.status === "active" ? "Online" : "Offline"}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{cctv.location}</Text>
|
||||||
|
</Stack>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconClock size={16} stroke={1.5} />
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{cctv.lastSeen}</Text>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Daftar Laporan Keamanan */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Laporan Keamanan Lingkungan</Title>
|
||||||
|
|
||||||
|
<Stack gap="sm">
|
||||||
|
{securityReports.map((report, index) => (
|
||||||
|
<Card key={index} withBorder radius="md" p="md">
|
||||||
|
<Group justify="space-between" mb="sm">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{report.title}</Text>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color={
|
||||||
|
report.status === "baru" ? "red" :
|
||||||
|
report.status === "diproses" ? "yellow" : "green"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{report.status}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconMapPin size={16} stroke={1.5} />
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{report.location}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconClock size={16} stroke={1.5} />
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{report.reportedAt}</Text>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"} mt="sm">{report.date}</Text>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeamananPage;
|
||||||
@@ -116,10 +116,10 @@ const apbdReport = {
|
|||||||
|
|
||||||
const KeuanganAnggaran = () => {
|
const KeuanganAnggaran = () => {
|
||||||
return (
|
return (
|
||||||
<Box p="md">
|
<Box>
|
||||||
<Stack gap="xl">
|
<Stack gap="xl">
|
||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<Title order={1} fw={700}>
|
<Title order={2} fw={700}>
|
||||||
Keuangan & Anggaran
|
Keuangan & Anggaran
|
||||||
</Title>
|
</Title>
|
||||||
<Button variant="filled">Export Laporan</Button>
|
<Button variant="filled">Export Laporan</Button>
|
||||||
|
|||||||
@@ -1,264 +1,342 @@
|
|||||||
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { Button } from "@/components/ui/button"; // Correct import for Button
|
|
||||||
import {
|
import {
|
||||||
|
Stack,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
ActionIcon,
|
||||||
|
Progress as MantineProgress,
|
||||||
|
Box,
|
||||||
|
Badge as MantineBadge,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
useMantineColorScheme,
|
||||||
CardHeader,
|
ThemeIcon,
|
||||||
CardTitle,
|
List,
|
||||||
} from "@/components/ui/card";
|
Divider,
|
||||||
import { Progress } from "@/components/ui/progress";
|
Skeleton
|
||||||
import {
|
} from "@mantine/core";
|
||||||
Table,
|
import { Button } from "@/components/ui/button";
|
||||||
TableBody,
|
import { Bar, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from "recharts";
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
|
|
||||||
const KinerjaDivisi = () => {
|
const KinerjaDivisi = () => {
|
||||||
// Sample data for division performance
|
const { colorScheme } = useMantineColorScheme();
|
||||||
const divisions = [
|
const dark = colorScheme === 'dark';
|
||||||
|
|
||||||
|
// Data for division progress chart
|
||||||
|
const divisionProgressData = [
|
||||||
|
{ name: "Sekretariat", selesai: 12, berjalan: 5, tertunda: 2 },
|
||||||
|
{ name: "Keuangan", selesai: 8, berjalan: 7, tertunda: 1 },
|
||||||
|
{ name: "Sosial", selesai: 10, berjalan: 3, tertunda: 4 },
|
||||||
|
{ name: "Humas", selesai: 6, berjalan: 9, tertunda: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Division task summaries
|
||||||
|
const divisionTasks = [
|
||||||
{
|
{
|
||||||
id: 1,
|
name: "Sekretariat",
|
||||||
name: "Divisi Teknologi",
|
tasks: [
|
||||||
target: 95,
|
{ title: "Laporan Bulanan", status: "selesai" },
|
||||||
achievement: 87,
|
{ title: "Arsip Dokumen", status: "berjalan" },
|
||||||
status: "On Track",
|
{ title: "Undangan Rapat", status: "tertunda" },
|
||||||
projects: 12,
|
]
|
||||||
budget: "Rp 2.5M",
|
|
||||||
lastUpdate: "2 days ago",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
name: "Keuangan",
|
||||||
name: "Divisi Keuangan",
|
tasks: [
|
||||||
target: 90,
|
{ title: "Laporan APBDes", status: "selesai" },
|
||||||
achievement: 92,
|
{ title: "Verifikasi Dana", status: "tertunda" },
|
||||||
status: "Above Target",
|
{ title: "Pengeluaran Harian", status: "berjalan" },
|
||||||
projects: 8,
|
]
|
||||||
budget: "Rp 1.8M",
|
|
||||||
lastUpdate: "1 day ago",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
name: "Sosial",
|
||||||
name: "Divisi SDM",
|
tasks: [
|
||||||
target: 85,
|
{ title: "Program Bantuan", status: "selesai" },
|
||||||
achievement: 78,
|
{ title: "Kegiatan Posyandu", status: "berjalan" },
|
||||||
status: "Needs Attention",
|
{ title: "Monitoring Stunting", status: "tertunda" },
|
||||||
projects: 6,
|
]
|
||||||
budget: "Rp 1.2M",
|
|
||||||
lastUpdate: "3 days ago",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
name: "Humas",
|
||||||
name: "Divisi Operasional",
|
tasks: [
|
||||||
target: 92,
|
{ title: "Publikasi Kegiatan", status: "selesai" },
|
||||||
achievement: 89,
|
{ title: "Koordinasi Media", status: "berjalan" },
|
||||||
status: "On Track",
|
{ title: "Laporan Kegiatan", status: "tertunda" },
|
||||||
projects: 15,
|
]
|
||||||
budget: "Rp 3.2M",
|
|
||||||
lastUpdate: "5 hours ago",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: "Divisi Pemasaran",
|
|
||||||
target: 88,
|
|
||||||
achievement: 91,
|
|
||||||
status: "Above Target",
|
|
||||||
projects: 10,
|
|
||||||
budget: "Rp 2.1M",
|
|
||||||
lastUpdate: "1 day ago",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Archive items
|
||||||
|
const archiveItems = [
|
||||||
|
{ name: "Surat Keputusan", count: 12 },
|
||||||
|
{ name: "Laporan Keuangan", count: 8 },
|
||||||
|
{ name: "Dokumentasi", count: 24 },
|
||||||
|
{ name: "Notulensi Rapat", count: 15 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Activity progress
|
||||||
|
const activityProgress = [
|
||||||
|
{ name: "Pembangunan Jalan", progress: 75, date: "15 Feb 2026", status: "berjalan" },
|
||||||
|
{ name: "Posyandu Bulanan", progress: 100, date: "10 Feb 2026", status: "selesai" },
|
||||||
|
{ name: "Vaksinasi Massal", progress: 45, date: "20 Feb 2026", status: "berjalan" },
|
||||||
|
{ name: "Festival Budaya", progress: 20, date: "5 Mar 2026", status: "berjalan" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Document statistics
|
||||||
|
const documentStats = [
|
||||||
|
{ name: "Gambar", value: 42 },
|
||||||
|
{ name: "Dokumen", value: 87 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Activity progress statistics
|
||||||
|
const activityProgressStats = [
|
||||||
|
{ name: "Selesai", value: 12 },
|
||||||
|
{ name: "Dikerjakan", value: 8 },
|
||||||
|
{ name: "Segera Dikerjakan", value: 5 },
|
||||||
|
{ name: "Dibatalkan", value: 2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const COLORS = ['#10B981', '#F59E0B', '#EF4444', '#6B7280'];
|
||||||
|
const STATUS_COLORS: Record<string, string> = {
|
||||||
|
selesai: 'green',
|
||||||
|
berjalan: 'blue',
|
||||||
|
tertunda: 'red',
|
||||||
|
proses: 'yellow'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Discussion data
|
||||||
|
const discussions = [
|
||||||
|
{ title: "Pembahasan APBDes 2026", sender: "Kepala Desa", timestamp: "2 jam yang lalu" },
|
||||||
|
{ title: "Kegiatan Posyandu", sender: "Divisi Sosial", timestamp: "5 jam yang lalu" },
|
||||||
|
{ title: "Festival Budaya", sender: "Divisi Humas", timestamp: "1 hari yang lalu" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Today's agenda
|
||||||
|
const todayAgenda = [
|
||||||
|
{ time: "09:00", event: "Rapat Evaluasi Bulanan" },
|
||||||
|
{ time: "14:00", event: "Koordinasi Program Bantuan" },
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<Stack gap="lg">
|
||||||
<div className="flex justify-between items-center">
|
{/* Grafik Progres Tugas per Divisi */}
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} >
|
||||||
Kinerja Divisi
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
</h1>
|
Grafik Progres Tugas per Divisi
|
||||||
<div className="flex space-x-4">
|
</Title>
|
||||||
<Button variant="default">Export Data</Button>
|
<ResponsiveContainer width="100%" height={300}>
|
||||||
<Button variant="outline">Filter</Button>
|
<BarChart data={divisionProgressData}>
|
||||||
</div>
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={dark ? "#141D34" : "white"} />
|
||||||
</div>
|
<XAxis
|
||||||
|
dataKey="name"
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
axisLine={false}
|
||||||
<Card>
|
tickLine={false}
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
<CardTitle className="text-sm font-medium dark:text-gray-100">Total Divisi</CardTitle>
|
|
||||||
<div className="h-6 w-6 text-muted-foreground">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
className="h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
<YAxis
|
||||||
</div>
|
axisLine={false}
|
||||||
</CardHeader>
|
tickLine={false}
|
||||||
<CardContent>
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
<div className="text-2xl font-bold">5</div>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">Jumlah divisi aktif</p>
|
<Tooltip
|
||||||
</CardContent>
|
contentStyle={dark
|
||||||
|
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
|
||||||
|
: {}}
|
||||||
|
/>
|
||||||
|
<Bar dataKey="selesai" stackId="a" fill="#10B981" name="Selesai" radius={[4, 4, 0, 0]} />
|
||||||
|
<Bar dataKey="berjalan" stackId="a" fill="#3B82F6" name="Berjalan" radius={[4, 4, 0, 0]} />
|
||||||
|
<Bar dataKey="tertunda" stackId="a" fill="#EF4444" name="Tertunda" radius={[4, 4, 0, 0]} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
{/* Ringkasan Tugas per Divisi */}
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<Grid gutter="md">
|
||||||
<CardTitle className="text-sm font-medium dark:text-gray-100">
|
{divisionTasks.map((division, index) => (
|
||||||
Rata-rata Pencapaian
|
<GridCol key={index} span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
</CardTitle>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
<div className="h-6 w-6 text-muted-foreground">
|
<Title order={4} mb="sm" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
className="h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="text-2xl font-bold">87.4%</div>
|
|
||||||
<p className="text-xs text-muted-foreground">Target tercapai</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-sm font-medium dark:text-gray-100">
|
|
||||||
Divisi Melebihi Target
|
|
||||||
</CardTitle>
|
|
||||||
<div className="h-6 w-6 text-muted-foreground">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
className="h-6 w-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="text-2xl font-bold">2</div>
|
|
||||||
<p className="text-xs text-muted-foreground">Dari total 5 divisi</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="dark:text-gray-100">Detail Kinerja Divisi</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="dark:text-white">Nama Divisi</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Target (%)</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Pencapaian (%)</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Status</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Proyek Aktif</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Anggaran</TableHead>
|
|
||||||
<TableHead className="dark:text-white">Terakhir Diperbarui</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{divisions.map((division) => (
|
|
||||||
<TableRow key={division.id}>
|
|
||||||
<TableCell className="font-medium dark:text-white">
|
|
||||||
{division.name}
|
{division.name}
|
||||||
</TableCell>
|
</Title>
|
||||||
<TableCell className="dark:text-white">
|
<Stack gap="sm">
|
||||||
{division.target}%
|
{division.tasks.map((task, taskIndex) => (
|
||||||
</TableCell>
|
<Box key={taskIndex}>
|
||||||
<TableCell className="dark:text-white">
|
<Group justify="space-between">
|
||||||
{division.achievement}%
|
<Text size="sm" c={dark ? 'white' : 'darmasaba-navy'}>{task.title}</Text>
|
||||||
</TableCell>
|
<MantineBadge
|
||||||
<TableCell>
|
color={STATUS_COLORS[task.status] || 'gray'}
|
||||||
<div className="flex items-center">
|
variant="light"
|
||||||
<Progress
|
|
||||||
value={division.achievement}
|
|
||||||
max={100}
|
|
||||||
className="w-24 mr-2"
|
|
||||||
/>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
division.status === "Above Target"
|
|
||||||
? "success"
|
|
||||||
: division.status === "On Track"
|
|
||||||
? "secondary"
|
|
||||||
: "destructive"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{division.status}
|
{task.status}
|
||||||
</Badge>
|
</MantineBadge>
|
||||||
</div>
|
</Group>
|
||||||
</TableCell>
|
</Box>
|
||||||
<TableCell className="dark:text-white">
|
|
||||||
{division.projects}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="dark:text-white">
|
|
||||||
{division.budget}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="dark:text-white">
|
|
||||||
{division.lastUpdate}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</Stack>
|
||||||
</Table>
|
</Card>
|
||||||
</CardContent>
|
</GridCol>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Arsip Digital Perangkat Desa */}
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
|
Arsip Digital Perangkat Desa
|
||||||
|
</Title>
|
||||||
|
<Grid gutter="md">
|
||||||
|
{archiveItems.map((item, index) => (
|
||||||
|
<GridCol key={index} span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{item.name}</Text>
|
||||||
|
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={700}>{item.count}</Text>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
{/* Kartu Progres Kegiatan */}
|
||||||
<Card>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
<CardHeader>
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
<CardTitle className="dark:text-gray-100">Grafik Pencapaian Divisi</CardTitle>
|
Progres Kegiatan / Program
|
||||||
</CardHeader> <CardContent>
|
</Title>
|
||||||
<div className="h-80 flex items-center justify-center bg-gray-50 dark:bg-gray-700 rounded-lg">
|
<Stack gap="md">
|
||||||
<p className="text-gray-500 dark:text-gray-300">
|
{activityProgress.map((activity, index) => (
|
||||||
Grafik pencapaian akan ditampilkan di sini
|
<Card key={index} p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
|
||||||
</p>
|
<Group justify="space-between" mb="sm">
|
||||||
</div>
|
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{activity.name}</Text>
|
||||||
</CardContent>
|
<MantineBadge
|
||||||
|
color={STATUS_COLORS[activity.status] || 'gray'}
|
||||||
|
variant="light"
|
||||||
|
>
|
||||||
|
{activity.status}
|
||||||
|
</MantineBadge>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<MantineProgress
|
||||||
|
value={activity.progress}
|
||||||
|
size="sm"
|
||||||
|
radius="xl"
|
||||||
|
color={activity.progress === 100 ? "green" : "blue"}
|
||||||
|
w="calc(100% - 80px)"
|
||||||
|
/>
|
||||||
|
<Text size="sm" c={dark ? 'white' : 'darmasaba-navy'}>{activity.progress}%</Text>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" c="dimmed" mt="sm">{activity.date}</Text>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
{/* Statistik Dokumen & Progres Kegiatan */}
|
||||||
<CardHeader>
|
<Grid gutter="md">
|
||||||
<CardTitle className="dark:text-gray-100">Distribusi Anggaran Divisi</CardTitle>
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
</CardHeader> <CardContent>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
<div className="h-80 flex items-center justify-center bg-gray-50 dark:bg-gray-700 rounded-lg">
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
<p className="text-gray-500 dark:text-gray-300">
|
Jumlah Dokumen
|
||||||
Diagram distribusi anggaran akan ditampilkan di sini
|
</Title>
|
||||||
</p>
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
</div>
|
<BarChart data={documentStats}>
|
||||||
</CardContent>
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke={dark ? "#141D34" : "white"} />
|
||||||
|
<XAxis
|
||||||
|
dataKey="name"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={dark
|
||||||
|
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
|
||||||
|
: {}}
|
||||||
|
/>
|
||||||
|
<Bar dataKey="value" fill={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"} radius={[4, 4, 0, 0]} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</GridCol>
|
||||||
</div>
|
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
|
Progres Kegiatan
|
||||||
|
</Title>
|
||||||
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
|
<PieChart>
|
||||||
|
<Pie
|
||||||
|
data={activityProgressStats}
|
||||||
|
cx="50%"
|
||||||
|
cy="50%"
|
||||||
|
labelLine={false}
|
||||||
|
outerRadius={80}
|
||||||
|
fill="#8884d8"
|
||||||
|
dataKey="value"
|
||||||
|
label={({ name, percent }) => `${name}: ${percent ? (percent * 100).toFixed(0) : '0'}%`}
|
||||||
|
>
|
||||||
|
{activityProgressStats.map((entry, index) => (
|
||||||
|
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
|
||||||
|
))}
|
||||||
|
</Pie>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={dark
|
||||||
|
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
|
||||||
|
: {}}
|
||||||
|
/>
|
||||||
|
</PieChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Diskusi Internal */}
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
|
Diskusi Internal
|
||||||
|
</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{discussions.map((discussion, index) => (
|
||||||
|
<Card key={index} p="md" radius="md" withBorder bg={dark ? "#263852ff" : "#F1F5F9"} style={{ borderColor: dark ? "#263852ff" : "#F1F5F9" }}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text c={dark ? 'white' : 'darmasaba-navy'} fw={500}>{discussion.title}</Text>
|
||||||
|
<Text size="sm" c="dimmed">{discussion.timestamp}</Text>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" c="dimmed">{discussion.sender}</Text>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Agenda / Acara Hari Ini */}
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
|
<Title order={4} mb="md" c={dark ? 'white' : 'darmasaba-navy'}>
|
||||||
|
Agenda / Acara Hari Ini
|
||||||
|
</Title>
|
||||||
|
{todayAgenda.length > 0 ? (
|
||||||
|
<Stack gap="sm">
|
||||||
|
{todayAgenda.map((agenda, index) => (
|
||||||
|
<Group key={index} align="flex-start">
|
||||||
|
<Box w={60}>
|
||||||
|
<Text c="dimmed">{agenda.time}</Text>
|
||||||
|
</Box>
|
||||||
|
<Divider orientation="vertical" mx="sm" />
|
||||||
|
<Text c={dark ? 'white' : 'darmasaba-navy'}>{agenda.event}</Text>
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed" ta="center" py="md">
|
||||||
|
Tidak ada acara hari ini
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,76 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
Grid,
|
||||||
CardHeader,
|
GridCol,
|
||||||
CardTitle,
|
Group,
|
||||||
} from "@/components/ui/card";
|
Text,
|
||||||
import { Input } from "@/components/ui/input";
|
Title,
|
||||||
import { Select } from "@/components/ui/select";
|
TextInput,
|
||||||
import {
|
Textarea,
|
||||||
|
Select,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
Badge,
|
||||||
TableCell,
|
Stack,
|
||||||
TableHead,
|
useMantineColorScheme,
|
||||||
TableHeader,
|
List,
|
||||||
TableRow,
|
Divider,
|
||||||
} from "@/components/ui/table";
|
ActionIcon,
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
Box
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { IconMessage, IconAlertTriangle, IconClock, IconCheck, IconChevronRight } from "@tabler/icons-react";
|
||||||
|
import { Line, LineChart, Bar, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts";
|
||||||
|
|
||||||
const PengaduanLayananPublik = () => {
|
const PengaduanLayananPublik = () => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === 'dark';
|
||||||
|
|
||||||
|
// Summary data
|
||||||
|
const summaryData = {
|
||||||
|
total: 42,
|
||||||
|
baru: 14,
|
||||||
|
diproses: 14,
|
||||||
|
selesai: 14
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tren pengaduan data
|
||||||
|
const trenData = [
|
||||||
|
{ bulan: "Jan", jumlah: 30 },
|
||||||
|
{ bulan: "Feb", jumlah: 50 },
|
||||||
|
{ bulan: "Mar", jumlah: 42 },
|
||||||
|
{ bulan: "Apr", jumlah: 38 },
|
||||||
|
{ bulan: "Mei", jumlah: 45 },
|
||||||
|
{ bulan: "Jun", jumlah: 42 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Surat terbanyak data
|
||||||
|
const suratData = [
|
||||||
|
{ jenis: "KTP", jumlah: 24 },
|
||||||
|
{ jenis: "KK", jumlah: 18 },
|
||||||
|
{ jenis: "Domisili", jumlah: 15 },
|
||||||
|
{ jenis: "Usaha", jumlah: 12 },
|
||||||
|
{ jenis: "Lainnya", jumlah: 8 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pengajuan terbaru data
|
||||||
|
const pengajuanTerbaru = [
|
||||||
|
{ nama: "Budi Santoso", jenis: "Ketertiban Umum", waktu: "2 jam yang lalu", status: "baru" },
|
||||||
|
{ nama: "Siti Rahayu", jenis: "Pelayanan Kesehatan", waktu: "5 jam yang lalu", status: "diproses" },
|
||||||
|
{ nama: "Ahmad Fauzi", jenis: "Infrastruktur", waktu: "1 hari yang lalu", status: "selesai" },
|
||||||
|
{ nama: "Dewi Lestari", jenis: "Administrasi", waktu: "1 hari yang lalu", status: "baru" },
|
||||||
|
{ nama: "Joko Widodo", jenis: "Keamanan", waktu: "2 hari yang lalu", status: "diproses" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Ide inovatif data
|
||||||
|
const ideInovatif = [
|
||||||
|
{ nama: "Andi Prasetyo", judul: "Penerapan Smart Village", kategori: "Teknologi" },
|
||||||
|
{ nama: "Rina Kusuma", judul: "Program Ekowisata Desa", kategori: "Ekonomi" },
|
||||||
|
{ nama: "Bambang Suryono", judul: "Peningkatan Sanitasi", kategori: "Kesehatan" },
|
||||||
|
{ nama: "Lina Marlina", judul: "Pusat Kreatif Anak Muda", kategori: "Pendidikan" }
|
||||||
|
];
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<"complaints" | "services">(
|
const [activeTab, setActiveTab] = useState<"complaints" | "services">(
|
||||||
"complaints",
|
"complaints",
|
||||||
);
|
);
|
||||||
@@ -133,68 +183,321 @@ const PengaduanLayananPublik = () => {
|
|||||||
setNewComplaint({ title: "", category: "", description: "" });
|
setNewComplaint({ title: "", category: "", description: "" });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
// Render complaint table rows
|
||||||
<div className="space-y-6">
|
const complaintRows = complaints.map((complaint) => (
|
||||||
<div className="flex justify-between items-center">
|
<Table.Tr key={complaint.id}>
|
||||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
<Table.Td className="font-medium">
|
||||||
Pengaduan & Layanan Publik
|
<Text c={dark ? "white" : "dark.3"}>{complaint.title}</Text>
|
||||||
</h1>
|
</Table.Td>
|
||||||
<div className="flex space-x-4">
|
<Table.Td>
|
||||||
<Button
|
<Text c={dark ? "white" : "dark.3"}>{complaint.category}</Text>
|
||||||
variant={activeTab === "complaints" ? "default" : "outline"}
|
</Table.Td>
|
||||||
onClick={() => setActiveTab("complaints")}
|
<Table.Td>
|
||||||
className="dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white"
|
<Badge
|
||||||
|
variant="filled"
|
||||||
|
color={
|
||||||
|
complaint.status === "Resolved"
|
||||||
|
? "green"
|
||||||
|
: complaint.status === "In Progress"
|
||||||
|
? "yellow"
|
||||||
|
: "red"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Pengaduan
|
{complaint.status}
|
||||||
</Button>
|
</Badge>
|
||||||
<Button
|
</Table.Td>
|
||||||
variant={activeTab === "services" ? "default" : "outline"}
|
<Table.Td>
|
||||||
onClick={() => setActiveTab("services")}
|
<Badge
|
||||||
className="dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white"
|
variant="filled"
|
||||||
|
color={
|
||||||
|
complaint.priority === "High"
|
||||||
|
? "red"
|
||||||
|
: complaint.priority === "Medium"
|
||||||
|
? "yellow"
|
||||||
|
: "blue"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Layanan Publik
|
{complaint.priority}
|
||||||
</Button>
|
</Badge>
|
||||||
</div>
|
</Table.Td>
|
||||||
</div>
|
<Table.Td>
|
||||||
|
<Text c={dark ? "white" : "dark.3"}>{complaint.date}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
));
|
||||||
|
|
||||||
|
// Status badge color mapping
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'baru': return 'red';
|
||||||
|
case 'diproses': return 'yellow';
|
||||||
|
case 'selesai': return 'green';
|
||||||
|
default: return 'gray';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="lg">
|
||||||
{activeTab === "complaints" ? (
|
{activeTab === "complaints" ? (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<>
|
||||||
{/* Complaint Submission Form */}
|
{/* Summary Cards */}
|
||||||
<div className="lg:col-span-1">
|
<Grid gutter="md">
|
||||||
<Card className="dark:bg-gray-800 dark:border-gray-700">
|
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
<CardHeader>
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
<CardTitle className="dark:text-white">
|
<Group justify="space-between" align="center">
|
||||||
Ajukan Pengaduan
|
<Stack gap={0}>
|
||||||
</CardTitle>
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
</CardHeader>
|
Total Pengaduan
|
||||||
<CardContent>
|
</Text>
|
||||||
<form onSubmit={handleSubmitComplaint} className="space-y-4">
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
<div>
|
{summaryData.total}
|
||||||
<label
|
</Text>
|
||||||
htmlFor="title"
|
</Stack>
|
||||||
className="block text-sm font-medium mb-1 dark:text-gray-300"
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color="darmasaba-blue"
|
||||||
|
p={8}
|
||||||
|
radius="md"
|
||||||
>
|
>
|
||||||
Judul Pengaduan
|
<IconMessage size={20} />
|
||||||
</label>
|
</Badge>
|
||||||
<Input
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Baru
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{summaryData.baru}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
p={8}
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconAlertTriangle size={20} />
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Diproses
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{summaryData.diproses}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color="yellow"
|
||||||
|
p={8}
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconClock size={20} />
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Selesai
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{summaryData.selesai}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
p={8}
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconCheck size={20} />
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Grafik Tren Pengaduan */}
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} >
|
||||||
|
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||||
|
Grafik Tren Pengaduan
|
||||||
|
</Title>
|
||||||
|
<ResponsiveContainer width="100%" height={300}>
|
||||||
|
<LineChart data={trenData}>
|
||||||
|
<CartesianGrid
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
vertical={false}
|
||||||
|
stroke={dark ? "var(--mantine-color-gray-7)" : "var(--mantine-color-gray-3)"}
|
||||||
|
/>
|
||||||
|
<XAxis
|
||||||
|
dataKey="bulan"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={dark
|
||||||
|
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
|
||||||
|
: {}}
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="jumlah"
|
||||||
|
stroke={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"}
|
||||||
|
strokeWidth={2}
|
||||||
|
dot={{ stroke: dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)", strokeWidth: 2, r: 4 }}
|
||||||
|
activeDot={{ r: 6, stroke: '#fff', strokeWidth: 2 }}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Surat Terbanyak & Pengajuan Terbaru & Ide Inovatif */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Surat Terbanyak */}
|
||||||
|
<GridCol span={{ base: 12, lg: 4 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||||
|
Surat Terbanyak
|
||||||
|
</Title>
|
||||||
|
<ResponsiveContainer width="100%" height={250}>
|
||||||
|
<BarChart data={suratData} layout="horizontal">
|
||||||
|
<CartesianGrid
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
horizontal={false}
|
||||||
|
stroke={dark ? "var(--mantine-color-gray-7)" : "var(--mantine-color-gray-3)"}
|
||||||
|
/>
|
||||||
|
<XAxis
|
||||||
|
dataKey="jumlah"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
dataKey="jenis"
|
||||||
|
type="category"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{ fill: dark ? "var(--mantine-color-text)" : "var(--mantine-color-text)" }}
|
||||||
|
width={80}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={dark
|
||||||
|
? { backgroundColor: 'var(--mantine-color-dark-7)', borderColor: 'var(--mantine-color-dark-6)' }
|
||||||
|
: {}}
|
||||||
|
/>
|
||||||
|
<Bar
|
||||||
|
dataKey="jumlah"
|
||||||
|
fill={dark ? "var(--mantine-color-blue-6)" : "var(--mantine-color-blue-filled)"}
|
||||||
|
radius={[0, 4, 4, 0]}
|
||||||
|
/>
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Pengajuan Terbaru */}
|
||||||
|
<GridCol span={{ base: 12, lg: 4 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||||
|
Pengajuan Terbaru
|
||||||
|
</Title>
|
||||||
|
{pengajuanTerbaru.map((item, index) => (
|
||||||
|
<Box key={index}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.nama}</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.jenis}</Text>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={0} align="flex-end">
|
||||||
|
<Badge color={getStatusColor(item.status)} variant="light">
|
||||||
|
{item.status}
|
||||||
|
</Badge>
|
||||||
|
<Text size="xs" c={dark ? "dark.4" : "dimmed"}>{item.waktu}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
<Divider my="sm" />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Ajuan Ide Inovatif */}
|
||||||
|
<GridCol span={{ base: 12, lg: 4 }}>
|
||||||
|
<Card p="md" radius="md" withBorder bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }} h="100%">
|
||||||
|
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||||
|
Ajuan Ide Inovatif
|
||||||
|
</Title>
|
||||||
|
{ideInovatif.map((item, index) => (
|
||||||
|
<Box key={index}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.judul}</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.nama}</Text>
|
||||||
|
</Stack>
|
||||||
|
<Group>
|
||||||
|
<Badge color="blue" variant="light">
|
||||||
|
{item.kategori}
|
||||||
|
</Badge>
|
||||||
|
<ActionIcon variant="subtle" color="darmasaba-blue">
|
||||||
|
<IconChevronRight size={16} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<Divider my="sm" />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Complaint Submission Form and List */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Complaint Submission Form */}
|
||||||
|
<GridCol span={{ base: 12, lg: 4 }}>
|
||||||
|
<Card p="md" withBorder radius="md" h="100%" bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
|
<Card.Section withBorder inheritPadding py="xs">
|
||||||
|
<Title order={3} py="xs">Ajukan Pengaduan</Title>
|
||||||
|
</Card.Section>
|
||||||
|
<Card.Section>
|
||||||
|
<form onSubmit={handleSubmitComplaint}>
|
||||||
|
<Stack gap="md" p={"sm"}>
|
||||||
|
<TextInput
|
||||||
|
label="Judul Pengaduan"
|
||||||
id="title"
|
id="title"
|
||||||
name="title"
|
name="title"
|
||||||
value={newComplaint.title}
|
value={newComplaint.title}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Masukkan judul pengaduan"
|
placeholder="Masukkan judul pengaduan"
|
||||||
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
||||||
required
|
required
|
||||||
|
withAsterisk
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor="category"
|
|
||||||
className="block text-sm font-medium mb-1 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
Kategori
|
|
||||||
</label>
|
|
||||||
<Select
|
<Select
|
||||||
|
label="Kategori"
|
||||||
id="category"
|
id="category"
|
||||||
name="category"
|
name="category"
|
||||||
value={newComplaint.category}
|
value={newComplaint.category}
|
||||||
@@ -208,204 +511,134 @@ const PengaduanLayananPublik = () => {
|
|||||||
{ value: "kesehatan", label: "Kesehatan" },
|
{ value: "kesehatan", label: "Kesehatan" },
|
||||||
{ value: "pendidikan", label: "Pendidikan" },
|
{ value: "pendidikan", label: "Pendidikan" },
|
||||||
]}
|
]}
|
||||||
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor="description"
|
|
||||||
className="block text-sm font-medium mb-1 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
Deskripsi
|
|
||||||
</label>
|
|
||||||
<Textarea
|
<Textarea
|
||||||
|
label="Deskripsi"
|
||||||
id="description"
|
id="description"
|
||||||
name="description"
|
name="description"
|
||||||
value={newComplaint.description}
|
value={newComplaint.description}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Jelaskan pengaduan Anda secara detail..."
|
placeholder="Jelaskan pengaduan Anda secara detail..."
|
||||||
rows={4}
|
minRows={4}
|
||||||
className="dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
|
||||||
required
|
required
|
||||||
|
withAsterisk
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
<Button type="submit" mt="md" color="darmasaba-blue">
|
||||||
type="submit"
|
|
||||||
className="w-full dark:bg-blue-600 dark:hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
Kirim Pengaduan
|
Kirim Pengaduan
|
||||||
</Button>
|
</Button>
|
||||||
|
</Stack>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</Card.Section>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</GridCol>
|
||||||
|
|
||||||
{/* Complaints List */}
|
{/* Complaints List */}
|
||||||
<div className="lg:col-span-2">
|
<GridCol span={{ base: 12, lg: 8 }}>
|
||||||
<Card className="dark:bg-gray-800 dark:border-gray-700">
|
<Card withBorder radius="md" bg={dark ? "#141D34" : "white"} style={{ borderColor: dark ? "#141D34" : "white" }}>
|
||||||
<CardHeader>
|
<Card.Section withBorder inheritPadding py="xs">
|
||||||
<CardTitle className="dark:text-white">
|
<Title order={3} py="xs">Daftar Pengaduan</Title>
|
||||||
Daftar Pengaduan
|
</Card.Section>
|
||||||
</CardTitle>
|
<Card.Section py="md" px="xs">
|
||||||
</CardHeader>
|
<Table withColumnBorders>
|
||||||
<CardContent>
|
<Table.Thead>
|
||||||
<Table>
|
<Table.Tr>
|
||||||
<TableHeader>
|
<Table.Th><Text c={dark ? "white" : "dark.3" }>Judul</Text></Table.Th>
|
||||||
<TableRow>
|
<Table.Th><Text c={dark ? "white" : "dark.3" }>Kategori</Text></Table.Th>
|
||||||
<TableHead className="dark:text-gray-300">
|
<Table.Th><Text c={dark ? "white" : "dark.3" }>Status</Text></Table.Th>
|
||||||
Judul
|
<Table.Th><Text c={dark ? "white" : "dark.3" }>Prioritas</Text></Table.Th>
|
||||||
</TableHead>
|
<Table.Th><Text c={dark ? "white" : "dark.3" }>Tanggal</Text></Table.Th>
|
||||||
<TableHead className="dark:text-gray-300">
|
</Table.Tr>
|
||||||
Kategori
|
</Table.Thead>
|
||||||
</TableHead>
|
<Table.Tbody>
|
||||||
<TableHead className="dark:text-gray-300">
|
{complaintRows}
|
||||||
Status
|
</Table.Tbody>
|
||||||
</TableHead>
|
|
||||||
<TableHead className="dark:text-gray-300">
|
|
||||||
Prioritas
|
|
||||||
</TableHead>
|
|
||||||
<TableHead className="dark:text-gray-300">
|
|
||||||
Tanggal
|
|
||||||
</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{complaints.map((complaint) => (
|
|
||||||
<TableRow key={complaint.id}>
|
|
||||||
<TableCell className="font-medium dark:text-white">
|
|
||||||
{complaint.title}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="dark:text-gray-300">
|
|
||||||
{complaint.category}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
complaint.status === "Resolved"
|
|
||||||
? "success"
|
|
||||||
: complaint.status === "In Progress"
|
|
||||||
? "secondary"
|
|
||||||
: "destructive"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{complaint.status}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
complaint.priority === "High"
|
|
||||||
? "destructive"
|
|
||||||
: complaint.priority === "Medium"
|
|
||||||
? "secondary"
|
|
||||||
: "default"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{complaint.priority}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="dark:text-gray-300">
|
|
||||||
{complaint.date}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
</Table>
|
||||||
</CardContent>
|
</Card.Section>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</GridCol>
|
||||||
</div>
|
</Grid>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<Stack gap="lg">
|
||||||
<Card className="dark:bg-gray-800 dark:border-gray-700">
|
<Card withBorder radius="md">
|
||||||
<CardHeader>
|
<Card.Section withBorder inheritPadding py="xs">
|
||||||
<CardTitle className="dark:text-white">
|
<Title order={3} py="xs">Layanan Publik Tersedia</Title>
|
||||||
Layanan Publik Tersedia
|
</Card.Section>
|
||||||
</CardTitle>
|
<Card.Section pt="md">
|
||||||
</CardHeader>
|
<Grid gutter="md">
|
||||||
<CardContent>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{services.map((service) => (
|
{services.map((service) => (
|
||||||
<Card
|
<GridCol key={service.id} span={{ base: 12, md: 6, lg: 4 }}>
|
||||||
key={service.id}
|
<Card withBorder radius="md" h="100%">
|
||||||
className="dark:bg-gray-700 dark:border-gray-600"
|
<Title order={4} mb="sm">{service.name}</Title>
|
||||||
>
|
<Text size="sm" c={dark ? "white" : "dark.3" } mb="md">
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-lg dark:text-white">
|
|
||||||
{service.name}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
|
|
||||||
{service.description}
|
{service.description}
|
||||||
</p>
|
</Text>
|
||||||
<div className="flex justify-between items-center">
|
<Group justify="space-between">
|
||||||
<Badge
|
<Badge
|
||||||
variant={
|
variant="filled"
|
||||||
|
color={
|
||||||
service.status === "Available"
|
service.status === "Available"
|
||||||
? "success"
|
? "green"
|
||||||
: service.status === "Limited"
|
: service.status === "Limited"
|
||||||
? "secondary"
|
? "yellow"
|
||||||
: "destructive"
|
: "red"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{service.status}
|
{service.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
<Text size="sm" c={dark ? "white" : "dark.3" }>
|
||||||
{service.category}
|
{service.category}
|
||||||
</span>
|
</Text>
|
||||||
</div>
|
</Group>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
<Text size="xs" c={dark ? "white" : "dark.3" } mt="sm">
|
||||||
Terakhir diperbarui: {service.lastUpdated}
|
Terakhir diperbarui: {service.lastUpdated}
|
||||||
</p>
|
</Text>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
</GridCol>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Grid>
|
||||||
</CardContent>
|
</Card.Section>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="mt-6 dark:bg-gray-800 dark:border-gray-700">
|
<Card withBorder radius="md">
|
||||||
<CardHeader>
|
<Card.Section withBorder inheritPadding py="xs">
|
||||||
<CardTitle className="dark:text-white">
|
<Title order={3} py="xs">Statistik Layanan</Title>
|
||||||
Statistik Layanan
|
</Card.Section>
|
||||||
</CardTitle>
|
<Card.Section pt="md">
|
||||||
</CardHeader>
|
<Grid gutter="md">
|
||||||
<CardContent>
|
<GridCol span={{ base: 12, md: 4 }}>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
|
||||||
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
|
<Title order={4} mb="xs">Jumlah Layanan Tersedia</Title>
|
||||||
<h3 className="text-lg font-semibold dark:text-white">
|
<Text size="xl" fw={700} c="darmasaba-blue">
|
||||||
Jumlah Layanan Tersedia
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-blue-600 dark:text-blue-400">
|
|
||||||
12
|
12
|
||||||
</p>
|
</Text>
|
||||||
</div>
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
|
|
||||||
<h3 className="text-lg font-semibold dark:text-white">
|
|
||||||
Layanan Terpopuler
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-green-600 dark:text-green-400">
|
|
||||||
4
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg">
|
|
||||||
<h3 className="text-lg font-semibold dark:text-white">
|
|
||||||
Permintaan Baru
|
|
||||||
</h3>
|
|
||||||
<p className="text-3xl font-bold text-purple-600 dark:text-purple-400">
|
|
||||||
23
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 4 }}>
|
||||||
|
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
|
||||||
|
<Title order={4} mb="xs">Layanan Terpopuler</Title>
|
||||||
|
<Text size="xl" fw={700} c="darmasaba-success">
|
||||||
|
4
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 4 }}>
|
||||||
|
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
|
||||||
|
<Title order={4} mb="xs">Permintaan Baru</Title>
|
||||||
|
<Text size="xl" fw={700} c="darmasaba-warning">
|
||||||
|
23
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
</Card.Section>
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
NavLink as MantineNavLink,
|
NavLink as MantineNavLink,
|
||||||
Box,
|
Box,
|
||||||
|
useMantineColorScheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
@@ -17,6 +18,9 @@ interface SidebarProps {
|
|||||||
export function Sidebar({ className }: SidebarProps) {
|
export function Sidebar({ className }: SidebarProps) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const isActiveBg = colorScheme === 'dark' ? "#182949" : "#E6F0FF";
|
||||||
|
const isActiveBorder = colorScheme === 'dark' ? "#00398D" : "#1F41AE";
|
||||||
|
|
||||||
// Define menu items with their paths
|
// Define menu items with their paths
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
@@ -75,16 +79,34 @@ export function Sidebar({ className }: SidebarProps) {
|
|||||||
|
|
||||||
{/* Menu Items */}
|
{/* Menu Items */}
|
||||||
<Stack gap={0} px="xs" flex={1} style={{ overflowY: "auto" }}>
|
<Stack gap={0} px="xs" flex={1} style={{ overflowY: "auto" }}>
|
||||||
{menuItems.map((item, index) => (
|
{menuItems.map((item, index) => {
|
||||||
|
const isActive = location.pathname === item.path;
|
||||||
|
return (
|
||||||
<MantineNavLink
|
<MantineNavLink
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => navigate({ to: item.path })}
|
onClick={() => navigate({ to: item.path })}
|
||||||
label={item.name}
|
label={item.name}
|
||||||
active={location.pathname === item.path}
|
active={isActive}
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
style={{
|
||||||
|
background: isActive ? isActiveBg : "transparent",
|
||||||
|
fontWeight: isActive ? "bold" : "normal",
|
||||||
|
borderLeft: isActive ? `4px solid ${isActiveBorder}` : "4px solid transparent",
|
||||||
|
borderRadius: "8px",
|
||||||
|
transition: "all 200ms ease",
|
||||||
|
margin: "2px 0",
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
body: {
|
||||||
|
"&:hover": {
|
||||||
|
background: "#F1F5F9",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
296
src/components/sosial-page.tsx
Normal file
296
src/components/sosial-page.tsx
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
|
Group,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
Progress,
|
||||||
|
Stack,
|
||||||
|
useMantineColorScheme,
|
||||||
|
Badge,
|
||||||
|
List,
|
||||||
|
ThemeIcon
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
IconHeartbeat,
|
||||||
|
IconBabyCarriage,
|
||||||
|
IconStethoscope,
|
||||||
|
IconMedicalCross,
|
||||||
|
IconSchool,
|
||||||
|
IconBook,
|
||||||
|
IconCalendarEvent,
|
||||||
|
IconAward
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
|
||||||
|
const SosialPage = () => {
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const dark = colorScheme === 'dark';
|
||||||
|
|
||||||
|
// Sample data for health statistics
|
||||||
|
const healthStats = {
|
||||||
|
ibuHamil: 87,
|
||||||
|
balita: 342,
|
||||||
|
alertStunting: 12,
|
||||||
|
posyanduAktif: 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample data for health progress
|
||||||
|
const healthProgress = [
|
||||||
|
{ label: "Imunisasi Lengkap", value: 92, color: "green" },
|
||||||
|
{ label: "Pemeriksaan Rutin", value: 88, color: "blue" },
|
||||||
|
{ label: "Gizi Baik", value: 86, color: "teal" },
|
||||||
|
{ label: "Target Stunting", value: 14, color: "red" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for posyandu schedule
|
||||||
|
const posyanduSchedule = [
|
||||||
|
{ nama: "Posyandu Mawar", tanggal: "Senin, 15 Feb 2026", jam: "08:00 - 11:00" },
|
||||||
|
{ nama: "Posyandu Melati", tanggal: "Selasa, 16 Feb 2026", jam: "08:00 - 11:00" },
|
||||||
|
{ nama: "Posyandu Dahlia", tanggal: "Rabu, 17 Feb 2026", jam: "08:00 - 11:00" },
|
||||||
|
{ nama: "Posyandu Anggrek", tanggal: "Kamis, 18 Feb 2026", jam: "08:00 - 11:00" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sample data for education stats
|
||||||
|
const educationStats = {
|
||||||
|
siswa: {
|
||||||
|
tk: 125,
|
||||||
|
sd: 480,
|
||||||
|
smp: 210,
|
||||||
|
sma: 150,
|
||||||
|
},
|
||||||
|
sekolah: {
|
||||||
|
jumlah: 8,
|
||||||
|
guru: 42,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample data for scholarships
|
||||||
|
const scholarshipData = {
|
||||||
|
penerima: 45,
|
||||||
|
dana: "Rp 1.200.000.000",
|
||||||
|
tahunAjaran: "2025/2026",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sample data for cultural events
|
||||||
|
const culturalEvents = [
|
||||||
|
{ nama: "Hari Kesaktian Pancasila", tanggal: "1 Oktober 2025", lokasi: "Balai Desa" },
|
||||||
|
{ nama: "Festival Budaya Desa", tanggal: "20 Mei 2026", lokasi: "Lapangan Desa" },
|
||||||
|
{ nama: "Perayaan HUT Desa", tanggal: "17 Agustus 2026", lokasi: "Balai Desa" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Page Header */}
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Title order={2} c={dark ? "dark.0" : "black"}>
|
||||||
|
Sosial Desa
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Health Statistics Cards */}
|
||||||
|
<Grid gutter="md">
|
||||||
|
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Ibu Hamil Aktif
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{healthStats.ibuHamil}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color="darmasaba-blue" size="xl" radius="xl">
|
||||||
|
<IconHeartbeat size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Balita Terdaftar
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{healthStats.balita}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color="darmasaba-success" size="xl" radius="xl">
|
||||||
|
<IconBabyCarriage size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Alert Stunting
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c="red">
|
||||||
|
{healthStats.alertStunting}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color="red" size="xl" radius="xl">
|
||||||
|
<IconStethoscope size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
<GridCol span={{ base: 12, sm: 6, md: 3 }}>
|
||||||
|
<Card withBorder radius="md" padding="lg">
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||||
|
Posyandu Aktif
|
||||||
|
</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||||
|
{healthStats.posyanduAktif}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color="darmasaba-warning" size="xl" radius="xl">
|
||||||
|
<IconMedicalCross size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Health Progress Bars */}
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Statistik Kesehatan</Title>
|
||||||
|
<Stack gap="md">
|
||||||
|
{healthProgress.map((item, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Group justify="space-between" mb={5}>
|
||||||
|
<Text size="sm" fw={500} c={dark ? "dark.0" : "black"}>
|
||||||
|
{item.label}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" fw={600} c={dark ? "dark.0" : "black"}>
|
||||||
|
{item.value}%
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
<Progress
|
||||||
|
value={item.value}
|
||||||
|
size="lg"
|
||||||
|
radius="xl"
|
||||||
|
color={item.color}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Jadwal Posyandu */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Jadwal Posyandu</Title>
|
||||||
|
<Stack gap="sm">
|
||||||
|
{posyanduSchedule.map((item, index) => (
|
||||||
|
<Card key={index} withBorder radius="md" p="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{item.nama}</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{item.tanggal}</Text>
|
||||||
|
</Stack>
|
||||||
|
<Badge variant="light" color="darmasaba-blue">
|
||||||
|
{item.jam}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Pendidikan */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Pendidikan</Title>
|
||||||
|
<Stack gap="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>TK / PAUD</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.tk}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>SD</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.sd}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>SMP</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.smp}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>SMA</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.siswa.sma}</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Card withBorder radius="md" p="md" mt="md">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>Jumlah Lembaga Pendidikan</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.sekolah.jumlah}</Text>
|
||||||
|
</Group>
|
||||||
|
<Group justify="space-between" mt="sm">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>Jumlah Tenaga Pengajar</Text>
|
||||||
|
<Text fw={700} c={dark ? "dark.0" : "black"}>{educationStats.sekolah.guru}</Text>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid gutter="md">
|
||||||
|
{/* Beasiswa Desa */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg" bg={dark ? "dark.8" : "darmasaba-blue.0"}>
|
||||||
|
<Group justify="space-between" align="center">
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>Beasiswa Desa</Text>
|
||||||
|
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>Penerima: {scholarshipData.penerima}</Text>
|
||||||
|
</Stack>
|
||||||
|
<ThemeIcon variant="light" color="darmasaba-success" size="xl" radius="xl">
|
||||||
|
<IconAward size={24} />
|
||||||
|
</ThemeIcon>
|
||||||
|
</Group>
|
||||||
|
<Text mt="md" c={dark ? "dark.0" : "black"}>Dana Tersalurkan: <Text span fw={700}>{scholarshipData.dana}</Text></Text>
|
||||||
|
<Text mt="sm" c={dark ? "dark.3" : "dimmed"}>Tahun Ajaran: {scholarshipData.tahunAjaran}</Text>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
|
||||||
|
{/* Kalender Event Budaya */}
|
||||||
|
<GridCol span={{ base: 12, lg: 6 }}>
|
||||||
|
<Card withBorder radius="md" p="lg">
|
||||||
|
<Title order={3} mb="md" c={dark ? "dark.0" : "black"}>Kalender Event Budaya</Title>
|
||||||
|
<List spacing="sm">
|
||||||
|
{culturalEvents.map((event, index) => (
|
||||||
|
<List.Item key={index} icon={
|
||||||
|
<ThemeIcon color="darmasaba-blue" size={24} radius="xl">
|
||||||
|
<IconCalendarEvent size={12} />
|
||||||
|
</ThemeIcon>
|
||||||
|
}>
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Text fw={500} c={dark ? "dark.0" : "black"}>{event.nama}</Text>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{event.lokasi}</Text>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>{event.tanggal}</Text>
|
||||||
|
</List.Item>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Card>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SosialPage;
|
||||||
@@ -20,11 +20,14 @@ import { Route as DashboardIndexRouteImport } from './routes/dashboard/index'
|
|||||||
import { Route as AdminIndexRouteImport } from './routes/admin/index'
|
import { Route as AdminIndexRouteImport } from './routes/admin/index'
|
||||||
import { Route as UsersIdRouteImport } from './routes/users/$id'
|
import { Route as UsersIdRouteImport } from './routes/users/$id'
|
||||||
import { Route as ProfileEditRouteImport } from './routes/profile/edit'
|
import { Route as ProfileEditRouteImport } from './routes/profile/edit'
|
||||||
|
import { Route as DashboardSosialRouteImport } from './routes/dashboard/sosial'
|
||||||
import { Route as DashboardPengaduanLayananPublikRouteImport } from './routes/dashboard/pengaduan-layanan-publik'
|
import { Route as DashboardPengaduanLayananPublikRouteImport } from './routes/dashboard/pengaduan-layanan-publik'
|
||||||
import { Route as DashboardKinerjaDivisiRouteImport } from './routes/dashboard/kinerja-divisi'
|
import { Route as DashboardKinerjaDivisiRouteImport } from './routes/dashboard/kinerja-divisi'
|
||||||
import { Route as DashboardKeuanganAnggaranRouteImport } from './routes/dashboard/keuangan-anggaran'
|
import { Route as DashboardKeuanganAnggaranRouteImport } from './routes/dashboard/keuangan-anggaran'
|
||||||
|
import { Route as DashboardKeamananRouteImport } from './routes/dashboard/keamanan'
|
||||||
import { Route as DashboardJennaAnalyticRouteImport } from './routes/dashboard/jenna-analytic'
|
import { Route as DashboardJennaAnalyticRouteImport } from './routes/dashboard/jenna-analytic'
|
||||||
import { Route as DashboardDemografiPekerjaanRouteImport } from './routes/dashboard/demografi-pekerjaan'
|
import { Route as DashboardDemografiPekerjaanRouteImport } from './routes/dashboard/demografi-pekerjaan'
|
||||||
|
import { Route as DashboardBumdesRouteImport } from './routes/dashboard/bumdes'
|
||||||
import { Route as AdminUsersRouteImport } from './routes/admin/users'
|
import { Route as AdminUsersRouteImport } from './routes/admin/users'
|
||||||
import { Route as AdminSettingsRouteImport } from './routes/admin/settings'
|
import { Route as AdminSettingsRouteImport } from './routes/admin/settings'
|
||||||
import { Route as AdminApikeyRouteImport } from './routes/admin/apikey'
|
import { Route as AdminApikeyRouteImport } from './routes/admin/apikey'
|
||||||
@@ -84,6 +87,11 @@ const ProfileEditRoute = ProfileEditRouteImport.update({
|
|||||||
path: '/profile/edit',
|
path: '/profile/edit',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const DashboardSosialRoute = DashboardSosialRouteImport.update({
|
||||||
|
id: '/sosial',
|
||||||
|
path: '/sosial',
|
||||||
|
getParentRoute: () => DashboardRouteRoute,
|
||||||
|
} as any)
|
||||||
const DashboardPengaduanLayananPublikRoute =
|
const DashboardPengaduanLayananPublikRoute =
|
||||||
DashboardPengaduanLayananPublikRouteImport.update({
|
DashboardPengaduanLayananPublikRouteImport.update({
|
||||||
id: '/pengaduan-layanan-publik',
|
id: '/pengaduan-layanan-publik',
|
||||||
@@ -101,6 +109,11 @@ const DashboardKeuanganAnggaranRoute =
|
|||||||
path: '/keuangan-anggaran',
|
path: '/keuangan-anggaran',
|
||||||
getParentRoute: () => DashboardRouteRoute,
|
getParentRoute: () => DashboardRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const DashboardKeamananRoute = DashboardKeamananRouteImport.update({
|
||||||
|
id: '/keamanan',
|
||||||
|
path: '/keamanan',
|
||||||
|
getParentRoute: () => DashboardRouteRoute,
|
||||||
|
} as any)
|
||||||
const DashboardJennaAnalyticRoute = DashboardJennaAnalyticRouteImport.update({
|
const DashboardJennaAnalyticRoute = DashboardJennaAnalyticRouteImport.update({
|
||||||
id: '/jenna-analytic',
|
id: '/jenna-analytic',
|
||||||
path: '/jenna-analytic',
|
path: '/jenna-analytic',
|
||||||
@@ -112,6 +125,11 @@ const DashboardDemografiPekerjaanRoute =
|
|||||||
path: '/demografi-pekerjaan',
|
path: '/demografi-pekerjaan',
|
||||||
getParentRoute: () => DashboardRouteRoute,
|
getParentRoute: () => DashboardRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const DashboardBumdesRoute = DashboardBumdesRouteImport.update({
|
||||||
|
id: '/bumdes',
|
||||||
|
path: '/bumdes',
|
||||||
|
getParentRoute: () => DashboardRouteRoute,
|
||||||
|
} as any)
|
||||||
const AdminUsersRoute = AdminUsersRouteImport.update({
|
const AdminUsersRoute = AdminUsersRouteImport.update({
|
||||||
id: '/users',
|
id: '/users',
|
||||||
path: '/users',
|
path: '/users',
|
||||||
@@ -137,11 +155,14 @@ export interface FileRoutesByFullPath {
|
|||||||
'/admin/apikey': typeof AdminApikeyRoute
|
'/admin/apikey': typeof AdminApikeyRoute
|
||||||
'/admin/settings': typeof AdminSettingsRoute
|
'/admin/settings': typeof AdminSettingsRoute
|
||||||
'/admin/users': typeof AdminUsersRoute
|
'/admin/users': typeof AdminUsersRoute
|
||||||
|
'/dashboard/bumdes': typeof DashboardBumdesRoute
|
||||||
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
||||||
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
||||||
|
'/dashboard/keamanan': typeof DashboardKeamananRoute
|
||||||
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
||||||
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
||||||
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
||||||
|
'/dashboard/sosial': typeof DashboardSosialRoute
|
||||||
'/profile/edit': typeof ProfileEditRoute
|
'/profile/edit': typeof ProfileEditRoute
|
||||||
'/users/$id': typeof UsersIdRoute
|
'/users/$id': typeof UsersIdRoute
|
||||||
'/admin/': typeof AdminIndexRoute
|
'/admin/': typeof AdminIndexRoute
|
||||||
@@ -156,11 +177,14 @@ export interface FileRoutesByTo {
|
|||||||
'/admin/apikey': typeof AdminApikeyRoute
|
'/admin/apikey': typeof AdminApikeyRoute
|
||||||
'/admin/settings': typeof AdminSettingsRoute
|
'/admin/settings': typeof AdminSettingsRoute
|
||||||
'/admin/users': typeof AdminUsersRoute
|
'/admin/users': typeof AdminUsersRoute
|
||||||
|
'/dashboard/bumdes': typeof DashboardBumdesRoute
|
||||||
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
||||||
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
||||||
|
'/dashboard/keamanan': typeof DashboardKeamananRoute
|
||||||
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
||||||
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
||||||
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
||||||
|
'/dashboard/sosial': typeof DashboardSosialRoute
|
||||||
'/profile/edit': typeof ProfileEditRoute
|
'/profile/edit': typeof ProfileEditRoute
|
||||||
'/users/$id': typeof UsersIdRoute
|
'/users/$id': typeof UsersIdRoute
|
||||||
'/admin': typeof AdminIndexRoute
|
'/admin': typeof AdminIndexRoute
|
||||||
@@ -178,11 +202,14 @@ export interface FileRoutesById {
|
|||||||
'/admin/apikey': typeof AdminApikeyRoute
|
'/admin/apikey': typeof AdminApikeyRoute
|
||||||
'/admin/settings': typeof AdminSettingsRoute
|
'/admin/settings': typeof AdminSettingsRoute
|
||||||
'/admin/users': typeof AdminUsersRoute
|
'/admin/users': typeof AdminUsersRoute
|
||||||
|
'/dashboard/bumdes': typeof DashboardBumdesRoute
|
||||||
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
'/dashboard/demografi-pekerjaan': typeof DashboardDemografiPekerjaanRoute
|
||||||
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
'/dashboard/jenna-analytic': typeof DashboardJennaAnalyticRoute
|
||||||
|
'/dashboard/keamanan': typeof DashboardKeamananRoute
|
||||||
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
'/dashboard/keuangan-anggaran': typeof DashboardKeuanganAnggaranRoute
|
||||||
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
'/dashboard/kinerja-divisi': typeof DashboardKinerjaDivisiRoute
|
||||||
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
'/dashboard/pengaduan-layanan-publik': typeof DashboardPengaduanLayananPublikRoute
|
||||||
|
'/dashboard/sosial': typeof DashboardSosialRoute
|
||||||
'/profile/edit': typeof ProfileEditRoute
|
'/profile/edit': typeof ProfileEditRoute
|
||||||
'/users/$id': typeof UsersIdRoute
|
'/users/$id': typeof UsersIdRoute
|
||||||
'/admin/': typeof AdminIndexRoute
|
'/admin/': typeof AdminIndexRoute
|
||||||
@@ -201,11 +228,14 @@ export interface FileRouteTypes {
|
|||||||
| '/admin/apikey'
|
| '/admin/apikey'
|
||||||
| '/admin/settings'
|
| '/admin/settings'
|
||||||
| '/admin/users'
|
| '/admin/users'
|
||||||
|
| '/dashboard/bumdes'
|
||||||
| '/dashboard/demografi-pekerjaan'
|
| '/dashboard/demografi-pekerjaan'
|
||||||
| '/dashboard/jenna-analytic'
|
| '/dashboard/jenna-analytic'
|
||||||
|
| '/dashboard/keamanan'
|
||||||
| '/dashboard/keuangan-anggaran'
|
| '/dashboard/keuangan-anggaran'
|
||||||
| '/dashboard/kinerja-divisi'
|
| '/dashboard/kinerja-divisi'
|
||||||
| '/dashboard/pengaduan-layanan-publik'
|
| '/dashboard/pengaduan-layanan-publik'
|
||||||
|
| '/dashboard/sosial'
|
||||||
| '/profile/edit'
|
| '/profile/edit'
|
||||||
| '/users/$id'
|
| '/users/$id'
|
||||||
| '/admin/'
|
| '/admin/'
|
||||||
@@ -220,11 +250,14 @@ export interface FileRouteTypes {
|
|||||||
| '/admin/apikey'
|
| '/admin/apikey'
|
||||||
| '/admin/settings'
|
| '/admin/settings'
|
||||||
| '/admin/users'
|
| '/admin/users'
|
||||||
|
| '/dashboard/bumdes'
|
||||||
| '/dashboard/demografi-pekerjaan'
|
| '/dashboard/demografi-pekerjaan'
|
||||||
| '/dashboard/jenna-analytic'
|
| '/dashboard/jenna-analytic'
|
||||||
|
| '/dashboard/keamanan'
|
||||||
| '/dashboard/keuangan-anggaran'
|
| '/dashboard/keuangan-anggaran'
|
||||||
| '/dashboard/kinerja-divisi'
|
| '/dashboard/kinerja-divisi'
|
||||||
| '/dashboard/pengaduan-layanan-publik'
|
| '/dashboard/pengaduan-layanan-publik'
|
||||||
|
| '/dashboard/sosial'
|
||||||
| '/profile/edit'
|
| '/profile/edit'
|
||||||
| '/users/$id'
|
| '/users/$id'
|
||||||
| '/admin'
|
| '/admin'
|
||||||
@@ -241,11 +274,14 @@ export interface FileRouteTypes {
|
|||||||
| '/admin/apikey'
|
| '/admin/apikey'
|
||||||
| '/admin/settings'
|
| '/admin/settings'
|
||||||
| '/admin/users'
|
| '/admin/users'
|
||||||
|
| '/dashboard/bumdes'
|
||||||
| '/dashboard/demografi-pekerjaan'
|
| '/dashboard/demografi-pekerjaan'
|
||||||
| '/dashboard/jenna-analytic'
|
| '/dashboard/jenna-analytic'
|
||||||
|
| '/dashboard/keamanan'
|
||||||
| '/dashboard/keuangan-anggaran'
|
| '/dashboard/keuangan-anggaran'
|
||||||
| '/dashboard/kinerja-divisi'
|
| '/dashboard/kinerja-divisi'
|
||||||
| '/dashboard/pengaduan-layanan-publik'
|
| '/dashboard/pengaduan-layanan-publik'
|
||||||
|
| '/dashboard/sosial'
|
||||||
| '/profile/edit'
|
| '/profile/edit'
|
||||||
| '/users/$id'
|
| '/users/$id'
|
||||||
| '/admin/'
|
| '/admin/'
|
||||||
@@ -345,6 +381,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof ProfileEditRouteImport
|
preLoaderRoute: typeof ProfileEditRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/dashboard/sosial': {
|
||||||
|
id: '/dashboard/sosial'
|
||||||
|
path: '/sosial'
|
||||||
|
fullPath: '/dashboard/sosial'
|
||||||
|
preLoaderRoute: typeof DashboardSosialRouteImport
|
||||||
|
parentRoute: typeof DashboardRouteRoute
|
||||||
|
}
|
||||||
'/dashboard/pengaduan-layanan-publik': {
|
'/dashboard/pengaduan-layanan-publik': {
|
||||||
id: '/dashboard/pengaduan-layanan-publik'
|
id: '/dashboard/pengaduan-layanan-publik'
|
||||||
path: '/pengaduan-layanan-publik'
|
path: '/pengaduan-layanan-publik'
|
||||||
@@ -366,6 +409,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof DashboardKeuanganAnggaranRouteImport
|
preLoaderRoute: typeof DashboardKeuanganAnggaranRouteImport
|
||||||
parentRoute: typeof DashboardRouteRoute
|
parentRoute: typeof DashboardRouteRoute
|
||||||
}
|
}
|
||||||
|
'/dashboard/keamanan': {
|
||||||
|
id: '/dashboard/keamanan'
|
||||||
|
path: '/keamanan'
|
||||||
|
fullPath: '/dashboard/keamanan'
|
||||||
|
preLoaderRoute: typeof DashboardKeamananRouteImport
|
||||||
|
parentRoute: typeof DashboardRouteRoute
|
||||||
|
}
|
||||||
'/dashboard/jenna-analytic': {
|
'/dashboard/jenna-analytic': {
|
||||||
id: '/dashboard/jenna-analytic'
|
id: '/dashboard/jenna-analytic'
|
||||||
path: '/jenna-analytic'
|
path: '/jenna-analytic'
|
||||||
@@ -380,6 +430,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof DashboardDemografiPekerjaanRouteImport
|
preLoaderRoute: typeof DashboardDemografiPekerjaanRouteImport
|
||||||
parentRoute: typeof DashboardRouteRoute
|
parentRoute: typeof DashboardRouteRoute
|
||||||
}
|
}
|
||||||
|
'/dashboard/bumdes': {
|
||||||
|
id: '/dashboard/bumdes'
|
||||||
|
path: '/bumdes'
|
||||||
|
fullPath: '/dashboard/bumdes'
|
||||||
|
preLoaderRoute: typeof DashboardBumdesRouteImport
|
||||||
|
parentRoute: typeof DashboardRouteRoute
|
||||||
|
}
|
||||||
'/admin/users': {
|
'/admin/users': {
|
||||||
id: '/admin/users'
|
id: '/admin/users'
|
||||||
path: '/users'
|
path: '/users'
|
||||||
@@ -423,20 +480,26 @@ const AdminRouteRouteWithChildren = AdminRouteRoute._addFileChildren(
|
|||||||
)
|
)
|
||||||
|
|
||||||
interface DashboardRouteRouteChildren {
|
interface DashboardRouteRouteChildren {
|
||||||
|
DashboardBumdesRoute: typeof DashboardBumdesRoute
|
||||||
DashboardDemografiPekerjaanRoute: typeof DashboardDemografiPekerjaanRoute
|
DashboardDemografiPekerjaanRoute: typeof DashboardDemografiPekerjaanRoute
|
||||||
DashboardJennaAnalyticRoute: typeof DashboardJennaAnalyticRoute
|
DashboardJennaAnalyticRoute: typeof DashboardJennaAnalyticRoute
|
||||||
|
DashboardKeamananRoute: typeof DashboardKeamananRoute
|
||||||
DashboardKeuanganAnggaranRoute: typeof DashboardKeuanganAnggaranRoute
|
DashboardKeuanganAnggaranRoute: typeof DashboardKeuanganAnggaranRoute
|
||||||
DashboardKinerjaDivisiRoute: typeof DashboardKinerjaDivisiRoute
|
DashboardKinerjaDivisiRoute: typeof DashboardKinerjaDivisiRoute
|
||||||
DashboardPengaduanLayananPublikRoute: typeof DashboardPengaduanLayananPublikRoute
|
DashboardPengaduanLayananPublikRoute: typeof DashboardPengaduanLayananPublikRoute
|
||||||
|
DashboardSosialRoute: typeof DashboardSosialRoute
|
||||||
DashboardIndexRoute: typeof DashboardIndexRoute
|
DashboardIndexRoute: typeof DashboardIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const DashboardRouteRouteChildren: DashboardRouteRouteChildren = {
|
const DashboardRouteRouteChildren: DashboardRouteRouteChildren = {
|
||||||
|
DashboardBumdesRoute: DashboardBumdesRoute,
|
||||||
DashboardDemografiPekerjaanRoute: DashboardDemografiPekerjaanRoute,
|
DashboardDemografiPekerjaanRoute: DashboardDemografiPekerjaanRoute,
|
||||||
DashboardJennaAnalyticRoute: DashboardJennaAnalyticRoute,
|
DashboardJennaAnalyticRoute: DashboardJennaAnalyticRoute,
|
||||||
|
DashboardKeamananRoute: DashboardKeamananRoute,
|
||||||
DashboardKeuanganAnggaranRoute: DashboardKeuanganAnggaranRoute,
|
DashboardKeuanganAnggaranRoute: DashboardKeuanganAnggaranRoute,
|
||||||
DashboardKinerjaDivisiRoute: DashboardKinerjaDivisiRoute,
|
DashboardKinerjaDivisiRoute: DashboardKinerjaDivisiRoute,
|
||||||
DashboardPengaduanLayananPublikRoute: DashboardPengaduanLayananPublikRoute,
|
DashboardPengaduanLayananPublikRoute: DashboardPengaduanLayananPublikRoute,
|
||||||
|
DashboardSosialRoute: DashboardSosialRoute,
|
||||||
DashboardIndexRoute: DashboardIndexRoute,
|
DashboardIndexRoute: DashboardIndexRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
src/routes/dashboard/bumdes.ts
Normal file
7
src/routes/dashboard/bumdes.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import BumdesPage from '@/components/bumdes-page'
|
||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/dashboard/bumdes')({
|
||||||
|
component: BumdesPage,
|
||||||
|
})
|
||||||
|
|
||||||
7
src/routes/dashboard/keamanan.ts
Normal file
7
src/routes/dashboard/keamanan.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import KeamananPage from '@/components/keamanan-page'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/dashboard/keamanan')({
|
||||||
|
component: KeamananPage,
|
||||||
|
})
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||||
import { Header } from "@/components/header";
|
import { Header } from "@/components/header";
|
||||||
import { Sidebar } from "@/components/sidebar";
|
import { Sidebar } from "@/components/sidebar";
|
||||||
import { AppShell, Burger, Group } from "@mantine/core";
|
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
|
||||||
import { useDisclosure } from "@mantine/hooks";
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
|
|
||||||
export const Route = createFileRoute("/dashboard")({
|
export const Route = createFileRoute("/dashboard")({
|
||||||
@@ -10,7 +10,10 @@ export const Route = createFileRoute("/dashboard")({
|
|||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const [opened, { toggle }] = useDisclosure();
|
const [opened, { toggle }] = useDisclosure();
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
const headerBgColor = colorScheme === 'dark' ? "#11192D" : "#19355E";
|
||||||
|
const navbarBgColor = colorScheme === 'dark' ? "#11192D" : "white";
|
||||||
|
const mainBgColor = colorScheme === 'dark' ? "#11192D" : "#edf3f8ff";
|
||||||
return (
|
return (
|
||||||
<AppShell
|
<AppShell
|
||||||
header={{ height: 60 }}
|
header={{ height: 60 }}
|
||||||
@@ -21,18 +24,18 @@ function RouteComponent() {
|
|||||||
}}
|
}}
|
||||||
padding="md"
|
padding="md"
|
||||||
>
|
>
|
||||||
<AppShell.Header>
|
<AppShell.Header bg={headerBgColor}>
|
||||||
<Group h="100%" px="md">
|
<Group h="100%" px="md">
|
||||||
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
|
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
|
||||||
<Header />
|
<Header />
|
||||||
</Group>
|
</Group>
|
||||||
</AppShell.Header>
|
</AppShell.Header>
|
||||||
|
|
||||||
<AppShell.Navbar p="md">
|
<AppShell.Navbar p="md" bg={navbarBgColor}>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
</AppShell.Navbar>
|
</AppShell.Navbar>
|
||||||
|
|
||||||
<AppShell.Main>
|
<AppShell.Main bg={mainBgColor}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
|
|||||||
8
src/routes/dashboard/sosial.ts
Normal file
8
src/routes/dashboard/sosial.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import SocialPage from '@/components/sosial-page'
|
||||||
|
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/dashboard/sosial')({
|
||||||
|
component: SocialPage,
|
||||||
|
})
|
||||||
|
|
||||||
Reference in New Issue
Block a user