Fix New UI Kinerja Divisi

This commit is contained in:
2026-03-17 21:19:10 +07:00
parent 97e6caa332
commit 2d68d4dc06
8 changed files with 205 additions and 64 deletions

139
Kinerja-Divisi-New.md Normal file
View File

@@ -0,0 +1,139 @@
Create a modern admin dashboard UI for a village management system using React 19 + Vite + TailwindCSS + Mantine components + Recharts.
Design style:
- Clean, soft UI with rounded corners (2xl)
- Light gray background (#f5f6f8)
- Card-based layout with subtle shadow
- Smooth spacing and consistent padding
- Professional government-style but still modern
- Use Inter or system font
- Primary color: dark blue
- Accent color: orange for progress
- Success color: green
- Use Mantine components where possible
Layout:
- Responsive grid layout (desktop-first)
- 4 summary cards on top (horizontal)
- 2 columns main content below
- Left sidebar for division list
- Right content for charts and activity
---
## 🔹 TOP CARDS (4 ITEMS)
Each card contains:
- Title (e.g: "Rakor 2025")
- Progress bar (orange)
- Date (small text)
- Status badge "Selesai" (green)
Use:
- Mantine Card
- Mantine Progress
- Mantine Badge
---
## 🔹 LEFT PANEL - "Divisi teraktif"
Vertical list of divisions:
- Each item is clickable
- Show division name + number of activities
- Rounded container with hover effect
- Chevron icon on right
Example items:
- Kesejahteraan (37 kegiatan)
- Pemerintahan (26 kegiatan)
- Keuangan (17 kegiatan)
- etc
Use:
- Scrollable container
- Soft border + hover highlight
---
## 🔹 CENTER - BAR CHART (Jumlah Dokumen)
- Use Recharts
- Two bars:
- Gambar
- Dokumen
- Color:
- Yellow/orange
- Green
- Show Y axis scale (0400)
---
## 🔹 RIGHT - PIE CHART (Progres Kegiatan)
- Use Recharts PieChart
- Segments:
- Selesai (green ~83%)
- Dikerjakan (orange ~16%)
- Segera dikerjakan (blue)
- Dibatalkan (red)
- Include legend below
---
## 🔹 BOTTOM LEFT - Diskusi Panel
- List of discussion messages
- Each item:
- Title
- Sender name
- Date
- Styled like notification cards
- Compact and clean
---
## 🔹 BOTTOM RIGHT - "Acara Hari Ini"
- Empty state
- Show text: "Tidak ada acara hari ini"
- Centered, muted text
---
## ⚙️ TECH REQUIREMENTS
- Use React functional components
- Use TanStack Router (file-based or route config)
- Use Mantine for UI components
- Use Tailwind for layout and spacing
- Use Recharts for charts
- State management: Valtio (simple global state)
- Date formatting: dayjs
- Icons: lucide-react
---
## 📁 COMPONENT STRUCTURE
- components/
- DashboardCard.tsx
- DivisionList.tsx
- BarChartCard.tsx
- PieChartCard.tsx
- DiscussionList.tsx
- EmptyState.tsx
- routes/
- dashboard.tsx
---
## ✨ EXTRA (IMPORTANT FOR VIBE CODING)
- Add subtle hover animations (scale 1.02)
- Smooth transitions (150200ms)
- Keep spacing consistent (gap-4 / gap-6)
- Avoid clutter, prioritize readability
- Make it feel "calm and productive"
---
Output:
- Full React component code (modular, not monolithic)
- Clean, readable, production-ready
- No unnecessary comments

View File

@@ -1,5 +1,5 @@
import { Grid, Stack } from "@mantine/core"; import { Grid, Stack } from "@mantine/core";
import { ActivityCard, } from "./kinerja-divisi/activity-card"; import { ActivityCard } from "./kinerja-divisi/activity-card";
import { DivisionList } from "./kinerja-divisi/division-list"; import { DivisionList } from "./kinerja-divisi/division-list";
import { DocumentChart } from "./kinerja-divisi/document-chart"; import { DocumentChart } from "./kinerja-divisi/document-chart";
import { ProgressChart } from "./kinerja-divisi/progress-chart"; import { ProgressChart } from "./kinerja-divisi/progress-chart";
@@ -14,25 +14,25 @@ const programKegiatanData = [
title: "Rakor 2025", title: "Rakor 2025",
date: "3 Juli 2025", date: "3 Juli 2025",
progress: 90, progress: 90,
status: "selesai" as const, status: "Selesai" as const,
}, },
{ {
title: "Pemutakhiran Indeks Desa", title: "Pemutakhiran Indeks Desa",
date: "3 Juli 2025", date: "3 Juli 2025",
progress: 85, progress: 85,
status: "selesai" as const, status: "Selesai" as const,
}, },
{ {
title: "Mengurus Akta Cerai Warga", title: "Mengurus Akta Cerai Warga",
date: "3 Juli 2025", date: "3 Juli 2025",
progress: 80, progress: 80,
status: "selesai" as const, status: "Selesai" as const,
}, },
{ {
title: "Pasek 7 Desa Adat", title: "Pasek 7 Desa Adat",
date: "3 Juli 2025", date: "3 Juli 2025",
progress: 92, progress: 92,
status: "selesai" as const, status: "Selesai" as const,
}, },
]; ];

View File

@@ -1,17 +1,10 @@
import { import { Card, Text, Progress, Group, Box } from "@mantine/core";
Box,
Card,
Group,
Progress,
Text,
useMantineColorScheme,
} from "@mantine/core";
interface ActivityCardProps { interface ActivityCardProps {
title: string; title: string;
date: string; date: string;
progress: number; progress: number;
status: "selesai" | "berjalan" | "tertunda"; status: "Selesai" | "Berjalan" | "Tertunda";
} }
export function ActivityCard({ export function ActivityCard({
@@ -20,16 +13,13 @@ export function ActivityCard({
progress, progress,
status, status,
}: ActivityCardProps) { }: ActivityCardProps) {
const { colorScheme } = useMantineColorScheme(); const getStatusColor = () => {
const dark = colorScheme === "dark"; switch (status) {
case "Selesai":
const getStatusColor = (s: string) => {
switch (s) {
case "selesai":
return "#22C55E"; return "#22C55E";
case "berjalan": case "Berjalan":
return "#3B82F6"; return "#3B82F6";
case "tertunda": case "Tertunda":
return "#EF4444"; return "#EF4444";
default: default:
return "#9CA3AF"; return "#9CA3AF";
@@ -38,58 +28,62 @@ export function ActivityCard({
return ( return (
<Card <Card
p="md"
radius="xl" radius="xl"
withBorder p={0}
bg={dark ? "#1E293B" : "white"} withBorder={false}
style={{ style={{
borderColor: dark ? "#334155" : "white", backgroundColor: "#F3F4F6",
boxShadow: dark overflow: "hidden",
? "0 1px 3px 0 rgb(0 0 0 / 0.1)"
: "0 1px 3px 0 rgb(0 0 0 / 0.1)",
}} }}
> >
{/* 🔵 HEADER */}
<Box <Box
style={{ style={{
borderLeft: `4px solid #3B82F6`, backgroundColor: "#1E3A5F",
paddingLeft: 12, padding: "16px",
marginBottom: 12, textAlign: "center",
}} }}
> >
<Text size="sm" fw={600} c={dark ? "white" : "#1E3A5F"}> <Text c="white" fw={700} size="md">
{title} {title}
</Text> </Text>
</Box> </Box>
<Group justify="space-between" mb="xs"> {/* CONTENT */}
<Text size="xs" c="dimmed"> <Box p="md">
{/* PROGRESS */}
<Progress
value={progress}
radius="xl"
size="lg"
color="orange"
styles={{
root: {
height: 16,
},
}}
/>
{/* FOOTER */}
<Group justify="space-between" mt="md">
<Text size="sm" fw={500}>
{date} {date}
</Text> </Text>
<Box <Box
style={{ style={{
backgroundColor: getStatusColor(status), backgroundColor: getStatusColor(),
color: "white", color: "white",
padding: "2px 8px", padding: "4px 12px",
borderRadius: 4, borderRadius: 999,
fontSize: 11, fontSize: 12,
fontWeight: 600, fontWeight: 600,
}} }}
> >
{status.toUpperCase()} {status}
</Box> </Box>
</Group> </Group>
</Box>
<Progress
value={progress}
size="sm"
radius="xl"
color={progress === 100 ? "green" : "yellow"}
animated={progress < 100}
/>
<Text size="xs" c="dimmed" mt="xs" ta="right">
{progress}%
</Text>
</Card> </Card>
); );
} }

View File

@@ -28,6 +28,7 @@ export function ArchiveCard({ item, onClick }: ArchiveCardProps) {
cursor: "pointer", cursor: "pointer",
transition: "transform 0.2s, box-shadow 0.2s", transition: "transform 0.2s, box-shadow 0.2s",
}} }}
h="100%"
onClick={onClick} onClick={onClick}
> >
<Group gap="md"> <Group gap="md">

View File

@@ -48,6 +48,7 @@ export function DiscussionPanel() {
? "0 1px 3px 0 rgb(0 0 0 / 0.1)" ? "0 1px 3px 0 rgb(0 0 0 / 0.1)"
: "0 1px 3px 0 rgb(0 0 0 / 0.1)", : "0 1px 3px 0 rgb(0 0 0 / 0.1)",
}} }}
h="100%"
> >
<Group gap="xs" mb="md"> <Group gap="xs" mb="md">
<MessageCircle size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} /> <MessageCircle size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} />

View File

@@ -3,6 +3,7 @@ import {
Bar, Bar,
BarChart, BarChart,
CartesianGrid, CartesianGrid,
Cell,
ResponsiveContainer, ResponsiveContainer,
Tooltip, Tooltip,
XAxis, XAxis,
@@ -10,8 +11,8 @@ import {
} from "recharts"; } from "recharts";
const documentData = [ const documentData = [
{ name: "Gambar", value: 300 }, { name: "Gambar", jumlah: 300, color: "#FACC15" },
{ name: "Dokumen", value: 310 }, { name: "Dokumen", jumlah: 310, color: "#22C55E" },
]; ];
export function DocumentChart() { export function DocumentChart() {
@@ -61,7 +62,11 @@ export function DocumentChart() {
}} }}
labelStyle={{ color: dark ? "#E2E8F0" : "#374151" }} labelStyle={{ color: dark ? "#E2E8F0" : "#374151" }}
/> />
<Bar dataKey="value" fill="#3B82F6" radius={[4, 4, 0, 0]} /> <Bar dataKey="jumlah" radius={[4, 4, 0, 0]}>
{documentData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Bar>
</BarChart> </BarChart>
</ResponsiveContainer> </ResponsiveContainer>
</Card> </Card>

View File

@@ -33,6 +33,7 @@ export function EventCard({ agendas = [] }: EventCardProps) {
? "0 1px 3px 0 rgb(0 0 0 / 0.1)" ? "0 1px 3px 0 rgb(0 0 0 / 0.1)"
: "0 1px 3px 0 rgb(0 0 0 / 0.1)", : "0 1px 3px 0 rgb(0 0 0 / 0.1)",
}} }}
h="100%"
> >
<Group gap="xs" mb="md"> <Group gap="xs" mb="md">
<Calendar size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} /> <Calendar size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} />

View File

@@ -10,7 +10,7 @@ import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts";
const progressData = [ const progressData = [
{ name: "Selesai", value: 83.33, color: "#22C55E" }, { name: "Selesai", value: 83.33, color: "#22C55E" },
{ name: "Dikerjakan", value: 16.67, color: "#FACC15" }, { name: "Dikerjakan", value: 16.67, color: "#F59E0B" },
{ name: "Segera Dikerjakan", value: 0, color: "#3B82F6" }, { name: "Segera Dikerjakan", value: 0, color: "#3B82F6" },
{ name: "Dibatalkan", value: 0, color: "#EF4444" }, { name: "Dibatalkan", value: 0, color: "#EF4444" },
]; ];