diff --git a/src/app/components/dashboard-card.tsx b/src/app/components/dashboard-card.tsx new file mode 100644 index 0000000..b60ddaa --- /dev/null +++ b/src/app/components/dashboard-card.tsx @@ -0,0 +1,43 @@ +import type { ReactNode } from "react"; +import { Card } from "./ui/card"; + +interface DashboardCardProps { + title: string; + value: string | number; + subtitle?: string; + change?: string; + icon: ReactNode; + badge?: string; +} + +export function DashboardCard({ + title, + value, + subtitle, + change, + icon, + badge, +}: DashboardCardProps) { + return ( + +
+
+

{title}

+
+

{value}

+ {badge && ( +
+ {badge} +
+ )} +
+ {subtitle &&

{subtitle}

} + {change &&

↗ {change}

} +
+
+ {icon} +
+
+
+ ); +} diff --git a/src/app/components/dashboard-content.tsx b/src/app/components/dashboard-content.tsx new file mode 100644 index 0000000..d65f14b --- /dev/null +++ b/src/app/components/dashboard-content.tsx @@ -0,0 +1,279 @@ +import { + Calendar, + CheckCircle, + FileText, + MessageCircle, + Users, +} from "lucide-react"; +import { + Bar, + BarChart, + CartesianGrid, + Cell, + Pie, + PieChart, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; +import { DashboardCard } from "./dashboard-card"; +import { Card } from "./ui/card"; + +const barChartData = [ + { month: "Jan", value: 145 }, + { month: "Feb", value: 165 }, + { month: "Mar", value: 195 }, + { month: "Apr", value: 155 }, + { month: "Mei", value: 205 }, + { month: "Jun", value: 185 }, +]; + +const pieChartData = [ + { name: "Puas", value: 25 }, + { name: "Cukup", value: 25 }, + { name: "Kurang", value: 25 }, + { name: "Sangat puas", value: 25 }, +]; + +const COLORS = ["#4E5BA6", "#F4C542", "#8CC63F", "#E57373"]; + +const divisiData = [ + { name: "Kesejahteraan", value: 37 }, + { name: "Pemerintahan", value: 26 }, + { name: "Keuangan", value: 17 }, + { name: "Sekretaris Desa", value: 15 }, +]; + +const eventData = [ + { date: "1 Oktober 2025", title: "Hari Kesaktian Pancasila" }, + { date: "15 Oktober 2025", title: "Davest" }, + { date: "19 Oktober 2025", title: "Rapat Koordinasi" }, +]; + +export function DashboardContent() { + return ( +
+ {/* Stats Cards */} +
+ } + /> + } + /> + } + /> + } + badge="87%" + /> +
+ + {/* Charts Section */} +
+ {/* Bar Chart */} + +
+
+

+ Statistik Pengajuan Surat +

+

+ Trend pengajuan surat 6 bulan terakhir +

+
+ +
+ + + + + + + + +
+ + {/* Pie Chart */} + +

Tingkat Kepuasan

+

Tingkat kepuasan layanan

+ + + + {pieChartData.map((_entry, index) => ( + + ))} + + + +
+
+
+ Sangat puas (0%) +
+
+
+ Puas (0%) +
+
+
+ Cukup (0%) +
+
+
+ Kurang (0%) +
+
+
+
+ + {/* Bottom Section */} +
+ {/* Divisi Teraktif */} + +
+ + + + + + +

Divisi Teraktif

+
+
+ {divisiData.map((divisi, index) => ( +
+
+ {divisi.name} + + {divisi.value} Kegiatan + +
+
+
+
+
+ ))} +
+ + + {/* Kalender */} + +
+ +

+ Kalender & Kegiatan Mendatang +

+
+
+ {eventData.map((event, index) => ( +
+

{event.date}

+

{event.title}

+
+ ))} +
+
+
+ + {/* APBDes Chart */} + +

Grafik APBDes

+
+
+ Belanja +
+
+
+
+
+ ); +} diff --git a/src/app/components/figma/ImageWithFallback.tsx b/src/app/components/figma/ImageWithFallback.tsx new file mode 100644 index 0000000..617f7eb --- /dev/null +++ b/src/app/components/figma/ImageWithFallback.tsx @@ -0,0 +1,42 @@ +import type React from "react"; +import { useState } from "react"; + +const ERROR_IMG_SRC = + "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg=="; + +export function ImageWithFallback( + props: React.ImgHTMLAttributes, +) { + const [didError, setDidError] = useState(false); + + const handleError = () => { + setDidError(true); + }; + + const { src, alt, style, className, ...rest } = props; + + return didError ? ( +
+
+ Error loading image +
+
+ ) : ( + {alt} + ); +} diff --git a/src/app/components/header.tsx b/src/app/components/header.tsx new file mode 100644 index 0000000..8d74c99 --- /dev/null +++ b/src/app/components/header.tsx @@ -0,0 +1,89 @@ +import { useLocation } from "@tanstack/react-router"; +import { Bell, Moon, Sun, User } from "lucide-react"; +import { useTheme } from "next-themes"; + +export function Header() { + const location = useLocation(); + const { theme, setTheme } = useTheme(); + + // Define page titles based on route + const getPageTitle = () => { + switch (location.pathname) { + case "/": + return "Dashboard"; + case "/kinerja-divisi": + return "Kinerja Divisi"; + case "/pengaduan": + return "Pengaduan & Layanan Publik"; + case "/analytic": + return "Jenna Analytic"; + case "/demografi": + return "Demografi & Kependudukan"; + case "/keuangan": + return "Keuangan & Anggaran"; + case "/bumdes": + return "Bumdes & UMKM Desa"; + case "/sosial": + return "Sosial"; + case "/keamanan": + return "Keamanan"; + case "/bantuan": + return "Bantuan"; + case "/pengaturan": + return "Pengaturan"; + default: + return "Dashboard"; + } + }; + + return ( +
+
+ {/* Title */} +

{getPageTitle()}

+ + {/* Right Section */} +
+ {/* Dark Mode Toggle */} + + + {/* User Info */} +
+
+

I. B. Surya Prabhawa M...

+

+ Kepala Desa +

+
+
+ +
+
+ + {/* Divider */} +
+ + {/* Icons */} +
+ +
+
+
+
+ ); +} diff --git a/src/app/components/kinerja-divisi.tsx b/src/app/components/kinerja-divisi.tsx new file mode 100644 index 0000000..25a2386 --- /dev/null +++ b/src/app/components/kinerja-divisi.tsx @@ -0,0 +1,267 @@ + +import { Badge } from "@/app/components/ui/badge"; +import { Button } from "@/app/components/ui/button"; // Correct import for Button +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/app/components/ui/card"; +import { Progress } from "@/app/components/ui/progress"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/app/components/ui/table"; + +const KinerjaDivisi = () => { + // Sample data for division performance + const divisions = [ + { + id: 1, + name: "Divisi Teknologi", + target: 95, + achievement: 87, + status: "On Track", + projects: 12, + budget: "Rp 2.5M", + lastUpdate: "2 days ago", + }, + { + id: 2, + name: "Divisi Keuangan", + target: 90, + achievement: 92, + status: "Above Target", + projects: 8, + budget: "Rp 1.8M", + lastUpdate: "1 day ago", + }, + { + id: 3, + name: "Divisi SDM", + target: 85, + achievement: 78, + status: "Needs Attention", + projects: 6, + budget: "Rp 1.2M", + lastUpdate: "3 days ago", + }, + { + id: 4, + name: "Divisi Operasional", + target: 92, + achievement: 89, + status: "On Track", + 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", + }, + ]; + + return ( +
+
+

+ Kinerja Divisi +

+
+ + +
+
+ +
+ + + Total Divisi +
+ + + +
+
+ +
5
+

Jumlah divisi aktif

+
+
+ + + + + Rata-rata Pencapaian + +
+ + + +
+
+ +
87.4%
+

Target tercapai

+
+
+ + + + + Divisi Melebihi Target + +
+ + + +
+
+ +
2
+

Dari total 5 divisi

+
+
+
+ + + + Detail Kinerja Divisi + + + + + + Nama Divisi + Target (%) + Pencapaian (%) + Status + Proyek Aktif + Anggaran + Terakhir Diperbarui + + + + {divisions.map((division) => ( + + + {division.name} + + + {division.target}% + + + {division.achievement}% + + +
+ + + {division.status} + +
+
+ + {division.projects} + + + {division.budget} + + + {division.lastUpdate} + +
+ ))} +
+
+
+
+ +
+ + + Grafik Pencapaian Divisi + + +
+

+ Grafik pencapaian akan ditampilkan di sini +

+
+
+
+ + + + Distribusi Anggaran Divisi + + +
+

+ Diagram distribusi anggaran akan ditampilkan di sini +

+
+
+
+
+
+ ); +}; + +export default KinerjaDivisi; diff --git a/src/app/components/pengaduan-layanan-publik.tsx b/src/app/components/pengaduan-layanan-publik.tsx new file mode 100644 index 0000000..287a002 --- /dev/null +++ b/src/app/components/pengaduan-layanan-publik.tsx @@ -0,0 +1,412 @@ +import type React from "react"; +import { useState } from "react"; +import { Badge } from "@/app/components/ui/badge"; +import { Button } from "@/app/components/ui/button"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/app/components/ui/card"; +import { Input } from "@/app/components/ui/input"; +import { Select } from "@/app/components/ui/select"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/app/components/ui/table"; +import { Textarea } from "@/app/components/ui/textarea"; + +const PengaduanLayananPublik = () => { + const [activeTab, setActiveTab] = useState<"complaints" | "services">( + "complaints", + ); + const [newComplaint, setNewComplaint] = useState({ + title: "", + category: "", + description: "", + }); + + // Sample data for complaints + const complaints = [ + { + id: 1, + title: "Jalan Rusak di Jalan Raya", + category: "Infrastruktur", + status: "Pending", + priority: "High", + date: "2024-02-01", + reporter: "Bapak Ahmad", + }, + { + id: 2, + title: "Pemadaman Listrik Berkelanjutan", + category: "Utilitas", + status: "In Progress", + priority: "Medium", + date: "2024-02-03", + reporter: "Ibu Sari", + }, + { + id: 3, + title: "Pelayanan Administrasi Lambat", + category: "Administrasi", + status: "Resolved", + priority: "Low", + date: "2024-01-28", + reporter: "Pak Joko", + }, + { + id: 4, + title: "Kebersihan Lingkungan", + category: "Sanitasi", + status: "Pending", + priority: "Medium", + date: "2024-02-05", + reporter: "Bu Dewi", + }, + ]; + + // Sample data for public services + const services = [ + { + id: 1, + name: "Pembuatan KTP", + description: + "Pelayanan pembuatan Kartu Tanda Penduduk baru atau perpanjangan", + status: "Available", + category: "Administrasi", + lastUpdated: "2024-02-01", + }, + { + id: 2, + name: "Pembuatan Surat Keterangan Usaha", + description: "Surat keterangan untuk keperluan usaha atau perizinan", + status: "Available", + category: "Administrasi", + lastUpdated: "2024-02-02", + }, + { + id: 3, + name: "Pelayanan Kesehatan", + description: "Pelayanan kesehatan dasar di puskesmas desa", + status: "Available", + category: "Kesehatan", + lastUpdated: "2024-01-30", + }, + { + id: 4, + name: "Program Bantuan Sosial", + description: + "Informasi dan pendaftaran program bantuan sosial dari pemerintah", + status: "Limited", + category: "Sosial", + lastUpdated: "2024-02-04", + }, + ]; + + const handleInputChange = ( + e: React.ChangeEvent, + ) => { + const { name, value } = e.target; + setNewComplaint((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSelectChange = (value: string | null) => { + setNewComplaint((prev) => ({ + ...prev, + category: value || "", // Ensure category is always a string + })); + }; + + const handleSubmitComplaint = (e: React.FormEvent) => { + e.preventDefault(); + console.log("Submitting complaint:", newComplaint); + // Here you would typically send the complaint to your backend + alert("Pengaduan berhasil dikirim!"); + setNewComplaint({ title: "", category: "", description: "" }); + }; + + return ( +
+
+

+ Pengaduan & Layanan Publik +

+
+ + +
+
+ + {activeTab === "complaints" ? ( +
+ {/* Complaint Submission Form */} +
+ + + + Ajukan Pengaduan + + + +
+
+ + +
+ +
+ +