Fix New UI Pengaduan
This commit is contained in:
168
Pengaduan-New.md
Normal file
168
Pengaduan-New.md
Normal file
@@ -0,0 +1,168 @@
|
||||
Create a modern analytics dashboard UI for a village complaint system (Pengaduan Dashboard).
|
||||
|
||||
Tech stack:
|
||||
- React 19 + Vite (Bun runtime)
|
||||
- Mantine UI (core components)
|
||||
- TailwindCSS (layout & spacing only)
|
||||
- Recharts (charts)
|
||||
- TanStack Router
|
||||
- Icons: lucide-react
|
||||
- State: Valtio
|
||||
- Date: dayjs
|
||||
|
||||
---
|
||||
|
||||
## 🎨 DESIGN STYLE
|
||||
|
||||
- Clean, minimal, and soft dashboard
|
||||
- Background: light gray (#f3f4f6)
|
||||
- Card: white with subtle shadow
|
||||
- Border radius: 16px–24px (rounded-2xl)
|
||||
- Typography: medium contrast (not too bold)
|
||||
- Primary color: navy blue (#1E3A5F)
|
||||
- Accent: soft blue + neutral gray
|
||||
- Icons inside circular solid background
|
||||
|
||||
Spacing:
|
||||
- Use gap-6 consistently
|
||||
- Internal padding: p-5 or p-6
|
||||
- Layout must feel breathable (no clutter)
|
||||
|
||||
---
|
||||
|
||||
## 🧱 LAYOUT STRUCTURE
|
||||
|
||||
### 🔹 TOP SECTION (4 STAT CARDS - GRID)
|
||||
Grid: 4 columns (responsive → 2 / 1)
|
||||
|
||||
Each card contains:
|
||||
- Title (small, muted)
|
||||
- Big number (bold, large)
|
||||
- Subtitle (small gray text)
|
||||
- Right side: circular icon container
|
||||
|
||||
Example:
|
||||
- Total Pengaduan → 42 → "Bulan ini"
|
||||
- Baru → 14 → "Belum diproses"
|
||||
- Diproses → 14 → "Sedang ditangani"
|
||||
- Selesai → 14 → "Terselesaikan"
|
||||
|
||||
Use:
|
||||
- Mantine Card
|
||||
- Group justify="space-between"
|
||||
- Icon inside circle (bg navy, icon white)
|
||||
|
||||
---
|
||||
|
||||
## 📈 MAIN CHART (FULL WIDTH)
|
||||
Title: "Tren Pengaduan"
|
||||
|
||||
- Use Recharts LineChart
|
||||
- Smooth line (monotone)
|
||||
- Show dots on each point
|
||||
- Data: Apr → Okt
|
||||
- Value range: 30–60
|
||||
|
||||
Style:
|
||||
- Minimal grid (light dashed)
|
||||
- No heavy colors (use gray/blue line)
|
||||
- Rounded container card
|
||||
- Add small top-right icon (expand)
|
||||
|
||||
---
|
||||
|
||||
## 📊 BOTTOM SECTION (3 COLUMN GRID)
|
||||
|
||||
### 🔹 LEFT: "Surat Terbanyak"
|
||||
- Horizontal bar chart (Recharts)
|
||||
- Categories:
|
||||
- KTP
|
||||
- KK
|
||||
- Domisili
|
||||
- Usaha
|
||||
- Lainnya
|
||||
|
||||
Style:
|
||||
- Dark blue bars
|
||||
- Rounded edges
|
||||
- Clean axis
|
||||
|
||||
---
|
||||
|
||||
### 🔹 CENTER: "Pengajuan Terbaru"
|
||||
List of activity cards:
|
||||
|
||||
Each item:
|
||||
- Name (bold)
|
||||
- Subtitle (jenis surat)
|
||||
- Time (small text)
|
||||
- Status badge (kanan)
|
||||
|
||||
Status:
|
||||
- baru → red
|
||||
- proses → blue
|
||||
- selesai → green
|
||||
|
||||
Style:
|
||||
- Card per item
|
||||
- Soft border
|
||||
- Rounded
|
||||
- Compact spacing
|
||||
|
||||
---
|
||||
|
||||
### 🔹 RIGHT: "Ajuan Ide Inovatif"
|
||||
List mirip dengan pengajuan terbaru:
|
||||
|
||||
Each item:
|
||||
- Nama
|
||||
- Judul ide
|
||||
- Waktu
|
||||
- Button kecil "Detail"
|
||||
|
||||
Style:
|
||||
- Right-aligned action button
|
||||
- Light border
|
||||
- Clean spacing
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ COMPONENT STRUCTURE
|
||||
|
||||
components/
|
||||
- StatCard.tsx
|
||||
- LineChartCard.tsx
|
||||
- BarChartCard.tsx
|
||||
- ActivityList.tsx
|
||||
- IdeaList.tsx
|
||||
|
||||
routes/
|
||||
- dashboard.tsx
|
||||
|
||||
---
|
||||
|
||||
## ✨ INTERACTIONS (IMPORTANT)
|
||||
|
||||
- Hover card → scale(1.02)
|
||||
- Transition: 150ms ease
|
||||
- Icon circle slightly pop on hover
|
||||
- List item hover → subtle bg change
|
||||
|
||||
---
|
||||
|
||||
## 🎯 UX DETAILS
|
||||
|
||||
- Numbers must be visually dominant
|
||||
- Icons must balance layout (not too big)
|
||||
- Avoid heavy borders
|
||||
- Keep everything aligned perfectly
|
||||
- No clutter
|
||||
|
||||
---
|
||||
|
||||
## 🚀 OUTPUT
|
||||
|
||||
- Modular React components (NOT one file)
|
||||
- Clean code (production-ready)
|
||||
- Use Mantine properly (no hacky inline styles unless needed)
|
||||
- Use Tailwind only for layout/grid/spacing
|
||||
@@ -1,32 +1,16 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
List,
|
||||
Select,
|
||||
Stack,
|
||||
Table,
|
||||
Text,
|
||||
Textarea,
|
||||
TextInput,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
useMantineColorScheme,
|
||||
useMantineColorScheme
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconCheck,
|
||||
IconChevronRight,
|
||||
IconClock,
|
||||
IconMessage,
|
||||
} from "@tabler/icons-react";
|
||||
import type React from "react";
|
||||
import { useState } from "react";
|
||||
import { CheckCircle, Clock, FileText, MessageCircle } from "lucide-react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
@@ -39,26 +23,47 @@ import {
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
|
||||
const PengaduanLayananPublik = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
// Summary data
|
||||
const summaryData = {
|
||||
total: 42,
|
||||
baru: 14,
|
||||
diproses: 14,
|
||||
selesai: 14,
|
||||
};
|
||||
const summaryData = [
|
||||
{
|
||||
title: "Total Pengaduan",
|
||||
value: 42,
|
||||
subtitle: "Bulan ini",
|
||||
icon: MessageCircle,
|
||||
color: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "Baru",
|
||||
value: 14,
|
||||
subtitle: "Belum diproses",
|
||||
icon: FileText,
|
||||
color: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "Diproses",
|
||||
value: 14,
|
||||
subtitle: "Sedang ditangani",
|
||||
icon: Clock,
|
||||
color: "#1E3A5F",
|
||||
},
|
||||
{
|
||||
title: "Selesai",
|
||||
value: 14,
|
||||
subtitle: "Terselesaikan",
|
||||
icon: CheckCircle,
|
||||
color: "#1E3A5F",
|
||||
},
|
||||
];
|
||||
|
||||
// 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: "Apr", jumlah: 35 },
|
||||
{ bulan: "Mei", jumlah: 48 },
|
||||
{ bulan: "Jun", jumlah: 42 },
|
||||
{ bulan: "Jul", jumlah: 55 },
|
||||
{ bulan: "Agu", jumlah: 50 },
|
||||
{ bulan: "Sep", jumlah: 58 },
|
||||
{ bulan: "Okt", jumlah: 52 },
|
||||
];
|
||||
|
||||
// Surat terbanyak data
|
||||
@@ -82,7 +87,7 @@ const PengaduanLayananPublik = () => {
|
||||
nama: "Siti Rahayu",
|
||||
jenis: "Pelayanan Kesehatan",
|
||||
waktu: "5 jam yang lalu",
|
||||
status: "diproses",
|
||||
status: "proses",
|
||||
},
|
||||
{
|
||||
nama: "Ahmad Fauzi",
|
||||
@@ -100,7 +105,7 @@ const PengaduanLayananPublik = () => {
|
||||
nama: "Joko Widodo",
|
||||
jenis: "Keamanan",
|
||||
waktu: "2 hari yang lalu",
|
||||
status: "diproses",
|
||||
status: "proses",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -109,187 +114,35 @@ const PengaduanLayananPublik = () => {
|
||||
{
|
||||
nama: "Andi Prasetyo",
|
||||
judul: "Penerapan Smart Village",
|
||||
waktu: "3 hari yang lalu",
|
||||
kategori: "Teknologi",
|
||||
},
|
||||
{
|
||||
nama: "Rina Kusuma",
|
||||
judul: "Program Ekowisata Desa",
|
||||
waktu: "5 hari yang lalu",
|
||||
kategori: "Ekonomi",
|
||||
},
|
||||
{
|
||||
nama: "Bambang Suryono",
|
||||
judul: "Peningkatan Sanitasi",
|
||||
waktu: "1 minggu yang lalu",
|
||||
kategori: "Kesehatan",
|
||||
},
|
||||
{
|
||||
nama: "Lina Marlina",
|
||||
judul: "Pusat Kreatif Anak Muda",
|
||||
waktu: "2 minggu yang lalu",
|
||||
kategori: "Pendidikan",
|
||||
},
|
||||
];
|
||||
|
||||
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<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
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: "" });
|
||||
};
|
||||
|
||||
// Render complaint table rows
|
||||
const complaintRows = complaints.map((complaint) => (
|
||||
<Table.Tr key={complaint.id}>
|
||||
<Table.Td className="font-medium">
|
||||
<Text c={dark ? "white" : "dark.3"}>{complaint.title}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text c={dark ? "white" : "dark.3"}>{complaint.category}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
variant="filled"
|
||||
color={
|
||||
complaint.status === "Resolved"
|
||||
? "green"
|
||||
: complaint.status === "In Progress"
|
||||
? "yellow"
|
||||
: "red"
|
||||
}
|
||||
>
|
||||
{complaint.status}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
variant="filled"
|
||||
color={
|
||||
complaint.priority === "High"
|
||||
? "red"
|
||||
: complaint.priority === "Medium"
|
||||
? "yellow"
|
||||
: "blue"
|
||||
}
|
||||
>
|
||||
{complaint.priority}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<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 "proses":
|
||||
return "blue";
|
||||
case "selesai":
|
||||
return "green";
|
||||
default:
|
||||
@@ -297,284 +150,202 @@ const PengaduanLayananPublik = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const PengaduanLayananPublik = () => {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const dark = colorScheme === "dark";
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
{activeTab === "complaints" ? (
|
||||
<>
|
||||
{/* Summary Cards */}
|
||||
{/* TOP SECTION - 4 STAT CARDS */}
|
||||
<Grid gutter="md">
|
||||
<GridCol span={{ base: 12, md: 6, lg: 3 }}>
|
||||
{summaryData.map((item, index) => (
|
||||
<Grid.Col key={index} span={{ base: 12, sm: 6, lg: 3 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
radius="xl"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
||||
}}
|
||||
h="100%"
|
||||
>
|
||||
<Group justify="space-between" align="center">
|
||||
<Stack gap={0}>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
Total Pengaduan
|
||||
<Group justify="space-between" align="center" w="100%">
|
||||
<Stack gap={2}>
|
||||
<Text size="sm" c="dimmed">
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text size="xl" fw={700} c={dark ? "dark.0" : "black"}>
|
||||
{summaryData.total}
|
||||
<Text size="xl" fw={700} c={dark ? "white" : "gray.9"}>
|
||||
{item.value}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{item.subtitle}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Badge
|
||||
variant="light"
|
||||
color="darmasaba-blue"
|
||||
p={8}
|
||||
radius="md"
|
||||
<ThemeIcon
|
||||
color={item.color}
|
||||
variant="filled"
|
||||
size="lg"
|
||||
radius="xl"
|
||||
style={{
|
||||
transition: "transform 0.15s ease",
|
||||
}}
|
||||
>
|
||||
<IconMessage size={20} />
|
||||
</Badge>
|
||||
<item.icon style={{ width: "60%", height: "60%" }} />
|
||||
</ThemeIcon>
|
||||
</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.Col>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{/* Grafik Tren Pengaduan */}
|
||||
{/* MAIN CHART - TREN PENGADUAN */}
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
radius="xl"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
}}
|
||||
>
|
||||
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
Grafik Tren Pengaduan
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4} c={dark ? "white" : "gray.9"}>
|
||||
Tren Pengaduan
|
||||
</Title>
|
||||
</Group>
|
||||
<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)"
|
||||
}
|
||||
stroke={dark ? "#334155" : "#e5e7eb"}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="bulan"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{
|
||||
fill: dark
|
||||
? "var(--mantine-color-text)"
|
||||
: "var(--mantine-color-text)",
|
||||
}}
|
||||
tick={{ fill: dark ? "#E2E8F0" : "#374151" }}
|
||||
/>
|
||||
<YAxis
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{
|
||||
fill: dark
|
||||
? "var(--mantine-color-text)"
|
||||
: "var(--mantine-color-text)",
|
||||
}}
|
||||
tick={{ fill: dark ? "#E2E8F0" : "#374151" }}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={
|
||||
dark
|
||||
? {
|
||||
backgroundColor: "var(--mantine-color-dark-7)",
|
||||
borderColor: "var(--mantine-color-dark-6)",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1E293B" : "white",
|
||||
borderColor: dark ? "#334155" : "#e5e7eb",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
labelStyle={{ color: dark ? "#E2E8F0" : "#374151" }}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="jumlah"
|
||||
stroke={
|
||||
dark
|
||||
? "var(--mantine-color-blue-6)"
|
||||
: "var(--mantine-color-blue-filled)"
|
||||
}
|
||||
stroke="#1E3A5F"
|
||||
strokeWidth={2}
|
||||
dot={{
|
||||
stroke: dark
|
||||
? "var(--mantine-color-blue-6)"
|
||||
: "var(--mantine-color-blue-filled)",
|
||||
fill: "#1E3A5F",
|
||||
strokeWidth: 2,
|
||||
r: 4,
|
||||
}}
|
||||
activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }}
|
||||
activeDot={{ r: 6 }}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
|
||||
{/* Surat Terbanyak & Pengajuan Terbaru & Ide Inovatif */}
|
||||
{/* BOTTOM SECTION - 3 COLUMNS */}
|
||||
<Grid gutter="md">
|
||||
{/* Surat Terbanyak */}
|
||||
<GridCol span={{ base: 12, lg: 4 }}>
|
||||
{/* LEFT: SURAT TERBANYAK */}
|
||||
<Grid.Col span={{ base: 12, lg: 4 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
radius="xl"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
}}
|
||||
h="100%"
|
||||
>
|
||||
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
<Title order={4} c={dark ? "white" : "gray.9"} mb="md">
|
||||
Surat Terbanyak
|
||||
</Title>
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
<BarChart data={suratData} layout="horizontal">
|
||||
<BarChart data={suratData} layout="vertical">
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
horizontal={false}
|
||||
stroke={
|
||||
dark
|
||||
? "var(--mantine-color-gray-7)"
|
||||
: "var(--mantine-color-gray-3)"
|
||||
}
|
||||
stroke={dark ? "#334155" : "#e5e7eb"}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="jumlah"
|
||||
type="number"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{
|
||||
fill: dark
|
||||
? "var(--mantine-color-text)"
|
||||
: "var(--mantine-color-text)",
|
||||
}}
|
||||
tick={{ fill: dark ? "#E2E8F0" : "#374151" }}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey="jenis"
|
||||
type="category"
|
||||
dataKey="jenis"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{
|
||||
fill: dark
|
||||
? "var(--mantine-color-text)"
|
||||
: "var(--mantine-color-text)",
|
||||
}}
|
||||
tick={{ fill: dark ? "#E2E8F0" : "#374151" }}
|
||||
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]}
|
||||
contentStyle={{
|
||||
backgroundColor: dark ? "#1E293B" : "white",
|
||||
borderColor: dark ? "#334155" : "#e5e7eb",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
/>
|
||||
<Bar dataKey="jumlah" fill="#1E3A5F" radius={[0, 4, 4, 0]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</Grid.Col>
|
||||
|
||||
{/* Pengajuan Terbaru */}
|
||||
<GridCol span={{ base: 12, lg: 4 }}>
|
||||
{/* CENTER: PENGAJUAN TERBARU */}
|
||||
<Grid.Col span={{ base: 12, lg: 4 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
radius="xl"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
}}
|
||||
h="100%"
|
||||
>
|
||||
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
<Title order={4} c={dark ? "white" : "gray.9"} mb="md">
|
||||
Pengajuan Terbaru
|
||||
</Title>
|
||||
<Stack gap="sm">
|
||||
{pengajuanTerbaru.map((item, index) => (
|
||||
<Box key={index}>
|
||||
<Card
|
||||
key={index}
|
||||
p="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#334155" : "#F1F5F9"}
|
||||
style={{
|
||||
borderColor: "transparent",
|
||||
transition: "background-color 0.15s ease",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between">
|
||||
<Stack gap={0}>
|
||||
<Text fw={500} c={dark ? "dark.0" : "black"}>
|
||||
<Text fw={600} c={dark ? "white" : "gray.9"}>
|
||||
{item.nama}
|
||||
</Text>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c="dimmed">
|
||||
{item.jenis}
|
||||
</Text>
|
||||
</Stack>
|
||||
@@ -582,260 +353,77 @@ const PengaduanLayananPublik = () => {
|
||||
<Badge
|
||||
color={getStatusColor(item.status)}
|
||||
variant="light"
|
||||
radius="sm"
|
||||
>
|
||||
{item.status}
|
||||
</Badge>
|
||||
<Text size="xs" c={dark ? "dark.4" : "dimmed"}>
|
||||
<Text size="xs" c="dimmed">
|
||||
{item.waktu}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
<Divider my="sm" />
|
||||
</Box>
|
||||
))}
|
||||
</Card>
|
||||
</GridCol>
|
||||
))}
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
{/* Ajuan Ide Inovatif */}
|
||||
<GridCol span={{ base: 12, lg: 4 }}>
|
||||
{/* RIGHT: AJUAN IDE INOVATIF */}
|
||||
<Grid.Col span={{ base: 12, lg: 4 }}>
|
||||
<Card
|
||||
p="md"
|
||||
radius="md"
|
||||
radius="xl"
|
||||
withBorder
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
bg={dark ? "#1E293B" : "white"}
|
||||
style={{
|
||||
borderColor: dark ? "#334155" : "white",
|
||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
|
||||
}}
|
||||
h="100%"
|
||||
>
|
||||
<Title order={4} mb="md" c={dark ? "dark.0" : "black"}>
|
||||
<Title order={4} c={dark ? "white" : "gray.9"} mb="md">
|
||||
Ajuan Ide Inovatif
|
||||
</Title>
|
||||
<Stack gap="sm">
|
||||
{ideInovatif.map((item, index) => (
|
||||
<Box key={index}>
|
||||
<Card
|
||||
key={index}
|
||||
p="sm"
|
||||
radius="md"
|
||||
withBorder
|
||||
bg={dark ? "#334155" : "#F1F5F9"}
|
||||
style={{
|
||||
borderColor: "transparent",
|
||||
transition: "background-color 0.15s ease",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between">
|
||||
<Stack gap={0}>
|
||||
<Text fw={500} c={dark ? "dark.0" : "black"}>
|
||||
<Text fw={600} c={dark ? "white" : "gray.9"}>
|
||||
{item.judul}
|
||||
</Text>
|
||||
<Text size="sm" c={dark ? "dark.3" : "dimmed"}>
|
||||
<Text size="sm" c="dimmed">
|
||||
{item.nama}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{item.waktu}
|
||||
</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
|
||||
<Button
|
||||
size="xs"
|
||||
variant="light"
|
||||
color="darmasaba-blue"
|
||||
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"
|
||||
name="title"
|
||||
value={newComplaint.title}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Masukkan judul pengaduan"
|
||||
required
|
||||
withAsterisk
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="Kategori"
|
||||
id="category"
|
||||
name="category"
|
||||
value={newComplaint.category}
|
||||
onChange={handleSelectChange}
|
||||
placeholder="Pilih kategori"
|
||||
data={[
|
||||
{ value: "infrastruktur", label: "Infrastruktur" },
|
||||
{ value: "administrasi", label: "Administrasi" },
|
||||
{ value: "utilitas", label: "Utilitas" },
|
||||
{ value: "sanitasi", label: "Sanitasi" },
|
||||
{ value: "kesehatan", label: "Kesehatan" },
|
||||
{ value: "pendidikan", label: "Pendidikan" },
|
||||
]}
|
||||
clearable
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
label="Deskripsi"
|
||||
id="description"
|
||||
name="description"
|
||||
value={newComplaint.description}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Jelaskan pengaduan Anda secara detail..."
|
||||
minRows={4}
|
||||
required
|
||||
withAsterisk
|
||||
/>
|
||||
|
||||
<Button type="submit" mt="md" color="darmasaba-blue">
|
||||
Kirim Pengaduan
|
||||
Detail
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Card.Section>
|
||||
</Card>
|
||||
</GridCol>
|
||||
|
||||
{/* Complaints List */}
|
||||
<GridCol span={{ base: 12, lg: 8 }}>
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
bg={dark ? "#141D34" : "white"}
|
||||
style={{ borderColor: dark ? "#141D34" : "white" }}
|
||||
>
|
||||
<Card.Section withBorder inheritPadding py="xs">
|
||||
<Title order={3} py="xs">
|
||||
Daftar Pengaduan
|
||||
</Title>
|
||||
</Card.Section>
|
||||
<Card.Section py="md" px="xs">
|
||||
<Table withColumnBorders>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "white" : "dark.3"}>Judul</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "white" : "dark.3"}>Kategori</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "white" : "dark.3"}>Status</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "white" : "dark.3"}>Prioritas</Text>
|
||||
</Table.Th>
|
||||
<Table.Th>
|
||||
<Text c={dark ? "white" : "dark.3"}>Tanggal</Text>
|
||||
</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>{complaintRows}</Table.Tbody>
|
||||
</Table>
|
||||
</Card.Section>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
<Stack gap="lg">
|
||||
<Card withBorder radius="md">
|
||||
<Card.Section withBorder inheritPadding py="xs">
|
||||
<Title order={3} py="xs">
|
||||
Layanan Publik Tersedia
|
||||
</Title>
|
||||
</Card.Section>
|
||||
<Card.Section pt="md">
|
||||
<Grid gutter="md">
|
||||
{services.map((service) => (
|
||||
<GridCol key={service.id} span={{ base: 12, md: 6, lg: 4 }}>
|
||||
<Card withBorder radius="md" h="100%">
|
||||
<Title order={4} mb="sm">
|
||||
{service.name}
|
||||
</Title>
|
||||
<Text size="sm" c={dark ? "white" : "dark.3"} mb="md">
|
||||
{service.description}
|
||||
</Text>
|
||||
<Group justify="space-between">
|
||||
<Badge
|
||||
variant="filled"
|
||||
color={
|
||||
service.status === "Available"
|
||||
? "green"
|
||||
: service.status === "Limited"
|
||||
? "yellow"
|
||||
: "red"
|
||||
}
|
||||
>
|
||||
{service.status}
|
||||
</Badge>
|
||||
<Text size="sm" c={dark ? "white" : "dark.3"}>
|
||||
{service.category}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="xs" c={dark ? "white" : "dark.3"} mt="sm">
|
||||
Terakhir diperbarui: {service.lastUpdated}
|
||||
</Text>
|
||||
</Card>
|
||||
</GridCol>
|
||||
))}
|
||||
</Grid>
|
||||
</Card.Section>
|
||||
</Card>
|
||||
|
||||
<Card withBorder radius="md">
|
||||
<Card.Section withBorder inheritPadding py="xs">
|
||||
<Title order={3} py="xs">
|
||||
Statistik Layanan
|
||||
</Title>
|
||||
</Card.Section>
|
||||
<Card.Section pt="md">
|
||||
<Grid gutter="md">
|
||||
<GridCol span={{ base: 12, md: 4 }}>
|
||||
<Card p="md" bg={dark ? "dark.7" : "gray.0"} radius="md">
|
||||
<Title order={4} mb="xs">
|
||||
Jumlah Layanan Tersedia
|
||||
</Title>
|
||||
<Text size="xl" fw={700} c="darmasaba-blue">
|
||||
12
|
||||
</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">
|
||||
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>
|
||||
)}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user