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

View File

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

View File

@@ -28,6 +28,7 @@ export function ArchiveCard({ item, onClick }: ArchiveCardProps) {
cursor: "pointer",
transition: "transform 0.2s, box-shadow 0.2s",
}}
h="100%"
onClick={onClick}
>
<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)",
}}
h="100%"
>
<Group gap="xs" mb="md">
<MessageCircle size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} />

View File

@@ -3,6 +3,7 @@ import {
Bar,
BarChart,
CartesianGrid,
Cell,
ResponsiveContainer,
Tooltip,
XAxis,
@@ -10,8 +11,8 @@ import {
} from "recharts";
const documentData = [
{ name: "Gambar", value: 300 },
{ name: "Dokumen", value: 310 },
{ name: "Gambar", jumlah: 300, color: "#FACC15" },
{ name: "Dokumen", jumlah: 310, color: "#22C55E" },
];
export function DocumentChart() {
@@ -61,7 +62,11 @@ export function DocumentChart() {
}}
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>
</ResponsiveContainer>
</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)",
}}
h="100%"
>
<Group gap="xs" mb="md">
<Calendar size={20} color={dark ? "#E2E8F0" : "#1E3A5F"} />

View File

@@ -10,7 +10,7 @@ import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts";
const progressData = [
{ 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: "Dibatalkan", value: 0, color: "#EF4444" },
];