UI Development Guidelines
UI Development Guidelines for ERP BUMDes Bolulango (v2026.5)
Dokumen ini berisi standar teknis (Technical Standards) yang wajib dipatuhi oleh seluruh pengembang dan AI Agent ketika membuat atau memodifikasi antarmuka pengguna (UI) di ERP BUMDes Bolulango (berpasangan dengan DESIGN_SYSTEM.md dan rule .agents/rules/standar_desain_ui_ux_erp.md).
1. Sistem Notifikasi (Toasts)#
ERP BUMDes menggunakan pustaka sonner sebagai sistem standar untuk notifikasi (Toast), BUKAN pustaka useToast bawaan asli dari Shadcn UI.
❌ Jangan Gunakan (Incorrect):
tsx// JANGAN GUNAKAN INI import { useToast } from '@/components/ui/use-toast' export default function MyComponent() { const { toast } = useToast() const handleAction = () => { toast({ title: 'Berhasil', description: 'Data disimpan.' }) } }
Alasan: Karena komponen <Toaster /> dari Shadcn tidak di-mount di app/layout.tsx, sehingga toast tidak akan pernah muncul di layar (silent fail).
✅ Gunakan Ini (Correct):
tsx// GUNAKAN INI import { toast } from 'sonner' export default function MyComponent() { const handleAction = () => { toast.success('Berhasil', { description: 'Data disimpan.' }) // Atau untuk error: // toast.error('Gagal', { description: 'Terjadi kesalahan.' }) } }
Alasan: Komponen <Toaster /> dari sonner sudah di-mount secara global di app/layout.tsx sehingga notifikasi dijamin akan muncul di seluruh aplikasi.
2. Server Actions & Penanganan Error UI#
Setiap kali komponen klien (Client Component) memanggil Server Action, pastikan untuk:
- Membungkus panggilan menggunakan
try/catch. - Menyediakan state loading (seperti
isSavingatauisLoading) yang di-set kembali kefalsedi dalam blokfinally {}. - Menampilkan notifikasi rincian error (
toast.error) jika terjadi Exception agar UI tidak terkesan membeku (freeze).
3. Dual-Mode <Card> Architecture (v2026.5), Named-Group group/card, & Dark Mode Shadow Hygiene#
- Pisahkan Kartu Metrik Interaktif vs Kontainer Tabel/Dokumen Statis:
- Kartu Metrik / KPI (
<Card glowColor="emerald|blue|amber|rose|indigo|purple|sky">): Wajib menyertakan propglowColorsemantik yang cocok dengan warna ikon/kategori metriknya. Jangan membiarkan kartu KPI non-emerald jatuh ke defaultemerald. - Kontainer Tabel Data, Kertas Kerja Laporan (
id="*-report-doc"), & Filter Bar (<Card glowColor="none">): Wajib menyertakanglowColor="none"agar kontainer tabel tidak melompat (hover:-translate-y-0.5) dan tidak memancarkan gradasi hijau di seluruh tabel saat pengguna menggerakkan kursor di atas baris data.
- Isolasi Named-Group (
group/card) & Top Specular Glass Edge:
- Komponen
Card.tsxmenggunakangroup/card(group-hover/card:opacity-100) agar efek hover kartu tidak menginterferensigrouppada baris tabel (<tr>) di dalamnya, serta menyertakan kilau atasdark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.06)].
- Zero-Resting-Tint pada Kartu KPI & Anti-Black-Hole Table Headers:
- Dilarang membuat kartu KPI dengan latar hitam statis (
bg-slate-900 text-white), blok warna solid (bg-emerald-600 text-white), border tebal sepihak (border-l-4,border-b-4), atau gradasi statis (from-blue-50/50,dark:from-blue-950/20). - Dilarang menggunakan
bg-slate-900 dark:bg-black text-whitepada<thead>atau<tfoot>tabel laporan keuangan; gunakanbg-slate-50/90 dark:bg-white/[0.03]untuk<thead>danbg-slate-100/90 dark:bg-white/[0.04]untuk<tfoot>.
- Dilarang Menggunakan
border-none/border-0pada<Card>Interaktif:
- Menghapus border akan mematikan efek garis tepi tipis berwarna saat di-hover.
- Anti-Light-Shadow-Leak di Mode Gelap:
- Setiap penggunaan bayangan pastel terang (
shadow-slate-50|100|200|300,shadow-indigo-100|200,shadow-emerald-100|200, dll.) wajib menyertakandark:shadow-none(atau bayangan semantik/gelap eksplisit) agar tidak memunculkan bayangan putih silau di atas tema gelap.
- Pola Kontainer Bersarang Shared-Surface Unit Leaderboard (Anti-Bolong / Anti-Sunken Pits):
- Dilarang keras menggunakan kelas latar kanvas dasar (
dark:bg-[#202020]) pada elemen anak, baris item daftar, kotak siklus alur kerja, maupun kotak metrik yang berada di dalam kartu induk<Card>(berlatar#2B2B2B) atau modal. Penempatan#202020di dalam#2B2B2Bmenciptakan ilusi optik kartu yang bolong tembus pandang ke lantai halaman di bawahnya. - Kotak bersarang wajib menggunakan pola Shared Surface (mewarisi
#2B2B2Bsecara alami /rgba(0,0,0,0)), dibingkai garis tepi tipisborder border-slate-200/80 dark:border-white/10, dan berpendar lembut kedark:hover:bg-[#323232]saat interaksi (mengikuti desain Windows 11 Personalisation Settings & Unit LeaderboardPerformanceRanking.tsx). Wadah ikon/lencana anak menggunakanbg-slate-100 dark:bg-white/5 border border-slate-200/60 dark:border-white/10.
4. Standar Arsitektur App Shell (Sidebar, TopBar, WorkContextBar), Neutral Charcoal Dark Mode, & 2-Layer Leaderboard#
- Borderless Unified Canvas (
#F6F9F8Light /#202020Dark Neutral Charcoal) & Anti-Black-Forest:
- Jangan pernah menambahkan
border-rpada<aside>diSidebar.tsx,border-bpada<header>diTopBar.tsx, atauborder-bpadaWorkContextBar.tsx. - Seluruh halaman, kartu, tabel, drawer, dan modal popup di Mode Gelap wajib mengikuti 4-Lapisan Ritme Warna Neutral Charcoal: Kanvas
#202020(bg-background), Kartu/Tabel/Modal#2B2B2B(bg-card), Kontrol Input/Search Pill/Menu Aktif#2D2D2D–#323232, serta Border#383838(dark:border-white/10) & Scrollbar#484848. Dilarang keras menggunakan hex Black Forest (#070D0A,#13201B,#2d5c44). - Pastikan
<nav>diSidebar.tsxselalu memiliki kelas.no-scrollbardanapp/globals.cssmenjaga::-webkit-scrollbar-track { background: transparent; }.
- Mode Ciut Sidebar (
collapsed === true):
- Saat
collapsedbernilaitrue, jangan pernah memberikan stylepaddingLeftinline atau kelastruncatepada pembungkus ikon menu utama agar tombol ikonw-10 h-10 mx-auto justify-center p-0tetap berada presisi di tengah kolomw-16.
- TopBar Unified Glass Pill Pod (
h-9) & Zero-Duplicate Icons:
- Pertahankan tinggi seragam
h-9(36px) untuk Search Pill, Utility Pod, dan Profile Pill diTopBar.tsx. - Jangan menduplikasi switcher
Mode Latihandi TopBar (sudah ada di Footer Sidebar), dan jangan menggunakan<Sparkles />lebih dari satu kali diTopBar.tsx(gunakan<Maximize2 />untuk tombol Zen Mode).
- 2-Layer Stacked Leaderboard (
PerformanceRanking.tsx):
- Gunakan tata letak 2-lapis (Baris 1: Identitas Unit utuh 2 baris + Status; Baris 2: Grid 3 kolom metrik dengan format ringkas
Rp XX,XX Jt+ tooltiptitlenominal penuh) agar teks nama unit dan angka keuangan tidak pernah saling bertabrakan.
5. Pencegahan Infinite Re-render Loop pada useEffect (Maximum update depth exceeded)#
- Wajib Membungkus Pohon Navigasi / Array Konfigurasi Dinamis dengan
useMemo:
- Jika sebuah komponen membangun array konfigurasi bersarang (seperti
navigationdanunitDirectorySubmenusdiSidebar.tsx), wajib bungkus denganuseMemoagar tidak menciptakan referensi array baru pada setiap siklus render.
- Wajib Memeriksa Kesamaan Elemen Sebelum Memanggil
setStatedi DalamuseEffect:
- Dilarang memanggil
setExpandedMenus(prev => Array.from(new Set([...prev, item.name])))langsung di dalam perulangan.forEach()padauseEffect, karenaArray.from(...)selalu mengembalikan referensi array baru dan memicu render tanpa henti (infinite loop). - Gunakan pola aman berikut:
tsxuseEffect(() => { const toExpand = new Set<string>() // Kumpulkan semua item terlebih dahulu... if (toExpand.size === 0) return setExpandedMenus(prev => { const missing = Array.from(toExpand).filter(name => !prev.includes(name)) if (missing.length === 0) return prev // Kembalikan referensi prev yang sama! return [...prev, ...missing] }) }, [pathname, navigation])