feat: add example components and system pages for UI showcase

- Introduced HeaderExample component for header display.
- Created StyleGuideOne, StyleGuideTwo, and StyleGuideThree components showcasing various UI elements and layouts.
- Added system pages: ComingSoon, Forbidden, Maintenance, and NotFound for better user experience during different states.
- Updated index.ts to export new components for easier access.
This commit is contained in:
Firman Ramdhani
2026-01-19 16:04:07 +07:00
parent 2313284aab
commit 23c869e574
15 changed files with 838 additions and 14 deletions
+5 -2
View File
@@ -1,5 +1,6 @@
import { lazy, Suspense } from 'react';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import { NotFound, Forbidden, Maintenance, ComingSoon } from '@repo/ui';
const AuthModule = lazy(() => import('./auth'));
const AppModule = lazy(() => import('./modules'));
@@ -11,8 +12,10 @@ export default function App() {
<Routes>
<Route path="/auth/*" element={<AuthModule />} />
<Route path="/app/*" element={<AppModule />} />
{/* <Route path="/404" element={<NotFoundPage />} />
<Route path="/403" element={<ForbiddenPage />} /> */}
<Route path="/404" element={<NotFound />} />
<Route path="/403" element={<Forbidden />} />
<Route path="/maintenance" element={<Maintenance />} />
<Route path="/coming-soon" element={<ComingSoon />} />
<Route path="*" element={<Navigate to="/app" />} />
</Routes>
</Suspense>
+5 -2
View File
@@ -1,10 +1,13 @@
import { DateServiceComponent } from '@repo/ui';
import { DateServiceExample, StyleGuideOne, StyleGuideTree, StyleGuideTwo } from '@repo/ui';
import { Route, Routes } from 'react-router-dom';
export default function AppModule() {
return (
<Routes>
<Route path="/example/*" element={<DateServiceComponent />} />
<Route path="/example/*" element={<DateServiceExample />} />
<Route path="/guide-1/*" element={<StyleGuideOne />} />
<Route path="/guide-2/*" element={<StyleGuideTwo />} />
<Route path="/guide-3/*" element={<StyleGuideTree />} />
</Routes>
);
}
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
export const Counter: React.FC = () => {
export const CounterExample: React.FC = () => {
const [count, setCount] = useState(0);
return (
@@ -1,7 +1,7 @@
import { DateService } from '@repo/utils';
import React, { useState, useEffect, useMemo } from 'react';
export const DateServiceComponent: React.FC = () => {
export const DateServiceExample: React.FC = () => {
// --- 0. TIMEZONE CONFIGURATION (ROOT CONTROL) ---
const [currentZone, setCurrentZone] = useState<string>(DateService.getGlobalTimezone());
@@ -1,7 +1,7 @@
import { EncryptionService } from '@repo/utils';
import React, { useState } from 'react';
export const EncryptionDemo: React.FC = () => {
export const EncryptionExample: React.FC = () => {
// --- 1. SETUP INSTANCE ---
// Kita gunakan Singleton agar hemat memory
const cryptoService = EncryptionService.getInstance();
@@ -1,10 +1,10 @@
import React from 'react';
interface HeaderProps {
interface HeaderExampleProps {
title: string;
}
export const Header: React.FC<HeaderProps> = ({ title }) => {
export const HeaderExample: React.FC<HeaderExampleProps> = ({ title }) => {
return (
<header id="header">
<h1>{title}</h1>
@@ -0,0 +1,208 @@
import React from 'react';
// --- SHARED SUB-COMPONENTS ---
const Card = ({
title,
children,
className = '',
}: {
title?: string;
children: React.ReactNode;
className?: string;
}) => (
<div className={`bg-white border border-gray-200 rounded-lg shadow-sm ${className}`}>
{title && (
<div className="px-4 py-3 border-b border-gray-100 bg-gray-50/50">
<h3 className="text-lg font-semibold text-gray-800">{title}</h3>
</div>
)}
<div className="p-5">{children}</div>
</div>
);
const Badge = ({
children,
variant = 'info',
}: {
children: React.ReactNode;
variant?: 'success' | 'error' | 'warning' | 'info';
}) => {
const styles = {
success: 'bg-green-50 text-green-600 border-green-100',
error: 'bg-red-50 text-red-600 border-red-100',
warning: 'bg-amber-50 text-amber-600 border-amber-100',
info: 'bg-brand-50 text-brand-600 border-brand-100',
};
return (
<span className={`px-2 py-0.5 rounded-sm text-xs font-bold border ${styles[variant]} uppercase tracking-wider`}>
{children}
</span>
);
};
// --- MAIN PAGE COMPONENT ---
export function StyleGuideOne() {
return (
<div className="min-h-screen bg-gray-50 text-gray-700 font-sans">
{/* 1. OBJECT HEADER (SAP Pattern) */}
<div className="bg-white border-b border-gray-200 px-8 py-6 sticky top-0 z-10 shadow-sm">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<nav className="text-xs font-bold text-brand-600 uppercase tracking-widest mb-1">
Procurement / Purchase Orders
</nav>
<h1 className="text-xl font-bold text-gray-900 flex items-center gap-3">
PO #4500019283
<Badge variant="success">Released</Badge>
</h1>
</div>
<div className="flex items-center gap-2">
<button className="px-4 py-1.5 border border-gray-300 rounded-md bg-white hover:bg-gray-50 font-medium transition-all shadow-sm">
Reject
</button>
<button className="px-4 py-1.5 bg-brand-500 text-white rounded-md hover:bg-brand-600 font-medium transition-all shadow-sm">
Approve Order
</button>
</div>
</div>
</div>
<div className="max-w-7xl mx-auto p-6 space-y-8">
{/* 2. ANALYTICS / KPI SECTION */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{[
{ label: 'Total Value', val: '$12,450.00', color: 'text-gray-900' },
{ label: 'Items Count', val: '24 SKUs', color: 'text-gray-900' },
{ label: 'Days Overdue', val: '5 Days', color: 'text-red-500' },
{ label: 'Vendor Rating', val: '4.8 / 5.0', color: 'text-green-600' },
].map((kpi, i) => (
<div key={i} className="bg-white p-4 rounded-lg border border-gray-200 shadow-sm">
<p className="text-xs font-bold text-gray-500 uppercase tracking-wider">{kpi.label}</p>
<p className={`text-xl font-bold mt-1 ${kpi.color}`}>{kpi.val}</p>
</div>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* LEFT: FORM & CONTROLS */}
<div className="lg:col-span-1 space-y-6">
<Card title="General Information">
<div className="space-y-4">
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-500">Vendor Name</label>
<input
type="text"
readOnly
className="bg-gray-50 border border-gray-200 rounded-md px-3 py-1.5 text-gray-600 outline-none cursor-not-allowed"
defaultValue="Steel Global Corp"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-500">Shipping Method</label>
<select className="border border-gray-300 rounded-md px-3 py-1.5 bg-white focus:ring-1 focus:ring-brand-500 outline-none">
<option>Sea Freight - Standard</option>
<option>Air Freight - Express</option>
</select>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-medium text-gray-500">Order Priority</label>
<div className="flex gap-4 mt-1">
<label className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="prio" className="accent-brand-500" />{' '}
<span className="text-base">Normal</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="prio" className="accent-brand-500" defaultChecked />{' '}
<span className="text-base">High</span>
</label>
</div>
</div>
</div>
</Card>
<Card title="Order Status Logs">
<ul className="space-y-4">
{[
{ time: '10:00 AM', msg: 'Order Created', user: 'System' },
{ time: '11:30 AM', msg: 'Manager Approved', user: 'Budi Santoso' },
].map((log, i) => (
<li key={i} className="flex gap-3 border-l-2 border-brand-200 pl-4 relative">
<div className="absolute w-2.5 h-2.5 bg-brand-500 rounded-full -left-[6px] top-1"></div>
<div>
<p className="text-base font-medium text-gray-800">{log.msg}</p>
<p className="text-xs text-gray-500">
{log.time} By {log.user}
</p>
</div>
</li>
))}
</ul>
</Card>
</div>
{/* RIGHT: DATA TABLE (The Core of ERP) */}
<div className="lg:col-span-2">
<Card className="!p-0 overflow-hidden">
<div className="p-4 border-b border-gray-100 flex justify-between items-center">
<h3 className="text-lg font-semibold text-gray-800">Line Items</h3>
<div className="flex gap-2">
<button className="text-xs font-bold text-brand-600 hover:bg-brand-50 px-2 py-1 rounded">
EXPORT CSV
</button>
<button className="text-xs font-bold text-brand-600 hover:bg-brand-50 px-2 py-1 rounded">
PRINT PO
</button>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100/80">
<th className="px-4 py-2 text-xs font-bold text-gray-500 uppercase">Material</th>
<th className="px-4 py-2 text-xs font-bold text-gray-500 uppercase">Description</th>
<th className="px-4 py-2 text-xs font-bold text-gray-500 uppercase text-right">Qty</th>
<th className="px-4 py-2 text-xs font-bold text-gray-500 uppercase text-right">Price</th>
<th className="px-4 py-2 text-xs font-bold text-gray-500 uppercase text-right">Total</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 text-base">
{[1, 2, 3, 4, 5].map((item) => (
<tr key={item} className="hover:bg-brand-50/20 transition-all cursor-pointer group">
<td className="px-4 py-2 font-mono text-brand-600 group-hover:underline">M-1000{item}</td>
<td className="px-4 py-2 text-gray-800">
Industrial Steel Pipe 2.5" - Grade {item === 2 ? 'B' : 'A'}
</td>
<td className="px-4 py-2 text-right">100.00</td>
<td className="px-4 py-2 text-right">150.00</td>
<td className="px-4 py-2 text-right font-semibold text-gray-900">15,000.00</td>
</tr>
))}
</tbody>
<tfoot className="bg-gray-50/80 font-bold border-t-2 border-gray-200">
<tr>
<td colSpan={4} className="px-4 py-3 text-right text-gray-500 uppercase text-xs tracking-widest">
Grand Total (Net)
</td>
<td className="px-4 py-3 text-right text-brand-600 text-lg">$ 75,000.00</td>
</tr>
</tfoot>
</table>
</div>
</Card>
<div className="mt-4 bg-amber-50 border border-amber-100 rounded-lg p-4 flex gap-3">
<span className="text-xl">⚠️</span>
<div>
<p className="text-base font-bold text-amber-800">Budget Warning</p>
<p className="text-sm text-amber-700">
This purchase order exceeds the quarterly budget for Department "Steel Fabrication" by 12%.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,257 @@
import React, { useState } from 'react';
// ==========================================================
// 1. ATOMIC COMPONENTS (Berdasar Config 13px & Burgundy)
// ==========================================================
const Card = ({ title, children, className = '', footer }: any) => (
<div className={`bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden ${className}`}>
{title && (
<div className="px-4 py-3 border-b border-gray-100 bg-gray-50/50 flex justify-between items-center">
<h3 className="text-lg font-semibold text-gray-800 tracking-tight">{title}</h3>
</div>
)}
<div className="p-5 text-base">{children}</div>
{footer && <div className="px-4 py-3 bg-gray-50 border-t border-gray-100">{footer}</div>}
</div>
);
const Button = ({ children, variant = 'primary', className = '', ...props }: any) => {
const styles: any = {
primary: 'bg-brand-500 text-white hover:bg-brand-600 shadow-sm border border-brand-600',
secondary: 'bg-white border border-gray-300 text-gray-700 hover:bg-gray-50 shadow-xs',
ghost: 'text-gray-600 hover:bg-gray-100',
danger: 'bg-red-500 text-white hover:bg-red-600',
};
return (
<button
className={`px-3 py-1.5 rounded-md font-medium text-base transition-all active:scale-95 disabled:opacity-50 ${styles[variant]} ${className}`}
{...props}
>
{children}
</button>
);
};
const Badge = ({ children, variant = 'info' }: any) => {
const styles: any = {
success: 'bg-green-50 text-green-600 border-green-100',
error: 'bg-red-50 text-red-600 border-red-100',
warning: 'bg-amber-50 text-amber-600 border-amber-100',
info: 'bg-brand-50 text-brand-700 border-brand-100',
};
return (
<span className={`px-2 py-0.5 rounded-sm text-xs font-bold border ${styles[variant]} uppercase tracking-wider`}>
{children}
</span>
);
};
// ==========================================================
// 2. MAIN PAGE: ADVANCED STYLE GUIDE
// ==========================================================
export function StyleGuideTwo() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div className="flex min-h-screen bg-gray-50 font-sans text-gray-800">
{/* SIDE NAVIGATION */}
<aside className="w-64 bg-brand-950 text-white flex flex-col sticky top-0 h-screen shadow-xl shrink-0">
<div className="p-6 border-b border-brand-900 flex items-center gap-3">
<div className="w-8 h-8 bg-brand-500 rounded-lg flex items-center justify-center font-bold text-xl shadow-inner">
B
</div>
<span className="font-bold text-lg tracking-tight">BURGUNDY_ERP</span>
</div>
<nav className="flex-1 p-4 space-y-1">
<p className="text-brand-300 text-[10px] font-bold uppercase tracking-widest px-2 mb-2 opacity-60">
Finance Modules
</p>
<div className="flex items-center gap-3 px-3 py-2 rounded-md bg-brand-500 text-white shadow-lg cursor-pointer">
<span>📓</span> <span className="text-base font-medium">General Ledger</span>
</div>
<div className="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-brand-900 text-brand-100 cursor-pointer transition-colors">
<span>📊</span> <span className="text-base font-medium">Cost Analysis</span>
</div>
<div className="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-brand-900 text-brand-100 cursor-pointer transition-colors">
<span>🏦</span> <span className="text-base font-medium">Bank Reconciliation</span>
</div>
</nav>
</aside>
{/* MAIN CONTENT AREA */}
<main className="flex-1 flex flex-col min-w-0">
{/* TOP BAR */}
<header className="h-14 bg-white border-b border-gray-200 flex items-center justify-between px-8 shrink-0 shadow-sm">
<div className="text-sm text-gray-500 font-medium">
Master Data <span className="mx-2">/</span>{' '}
<span className="text-gray-900 font-bold">Chart of Accounts</span>
</div>
<div className="flex items-center gap-4">
<div className="w-8 h-8 rounded-full bg-brand-100 border border-brand-200 flex items-center justify-center text-brand-700 font-bold text-xs">
JD
</div>
</div>
</header>
{/* CONTENT */}
<div className="p-8 space-y-8 overflow-y-auto">
{/* HEADER SECTION */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h2 className="text-2xl font-bold text-gray-900">Account Overview: 110200</h2>
<p className="text-base text-gray-500 mt-1">
Domestic Trade Accounts Receivable {' '}
<span className="text-brand-600 font-semibold italic">Local Currency (IDR)</span>
</p>
</div>
<div className="flex gap-2">
<Button variant="secondary" onClick={() => window.print()}>
Print Statement
</Button>
<Button onClick={() => setIsModalOpen(true)}>Create Entry</Button>
</div>
</div>
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
{/* LEFT: MAIN DATA TABLE */}
<div className="xl:col-span-2 space-y-6">
<Card title="Recent Transactions">
<div className="overflow-x-auto -mx-5 -my-5">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-gray-50 border-b border-gray-100">
<th className="px-5 py-3 text-xs font-bold text-gray-500 uppercase">Document</th>
<th className="px-5 py-3 text-xs font-bold text-gray-500 uppercase">Partner</th>
<th className="px-5 py-3 text-xs font-bold text-gray-500 uppercase text-right">Debit</th>
<th className="px-5 py-3 text-xs font-bold text-gray-500 uppercase text-right">Credit</th>
<th className="px-5 py-3 text-xs font-bold text-gray-500 uppercase text-center">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-50">
{[1, 2, 3, 4].map((i) => (
<tr key={i} className="hover:bg-brand-50/20 text-base transition-colors group">
<td className="px-5 py-3 font-mono text-brand-700 font-semibold">INV-2026-00{i}</td>
<td className="px-5 py-3">
<p className="font-medium text-gray-800">Global Tech Solutions</p>
<p className="text-xs text-gray-400">Vendor ID: V-9920</p>
</td>
<td className="px-5 py-3 text-right font-semibold">$ {i * 125}.00</td>
<td className="px-5 py-3 text-right text-gray-300">0.00</td>
<td className="px-5 py-3 text-center">
<Badge variant={i % 2 === 0 ? 'success' : 'warning'}>
{i % 2 === 0 ? 'Cleared' : 'Pending'}
</Badge>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card title="Budget Utilization">
<div className="space-y-4">
<div className="w-full bg-gray-100 rounded-full h-3">
<div className="bg-brand-500 h-3 rounded-full" style={{ width: '75%' }}></div>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-500 font-medium italic underline decoration-brand-200 underline-offset-4">
Spent: $75,000.00
</span>
<span className="text-gray-900 font-bold">Total: $100,000.00</span>
</div>
</div>
</Card>
<Card title="Quick Insight">
<p className="text-gray-600 leading-relaxed italic">
"Anomali terdeteksi pada baris INV-2026-003. Nilai transaksi melebihi rata-rata bulanan sebesar
40%."
</p>
</Card>
</div>
</div>
{/* RIGHT: CONFIGURATION & ACTIONS */}
<div className="xl:col-span-1 space-y-6">
<Card title="Account Settings" footer={<Button className="w-full">Update Control</Button>}>
<div className="space-y-4">
<div className="flex flex-col gap-1">
<label className="text-sm font-bold text-gray-500 uppercase tracking-tighter italic">
Reconciliation Type
</label>
<select className="w-full border border-gray-300 rounded-md px-3 py-1.5 bg-white focus:ring-1 focus:ring-brand-500 outline-none">
<option>Automatic Match</option>
<option>Manual Review</option>
</select>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-bold text-gray-500 uppercase tracking-tighter italic">
Tax Code
</label>
<input
className="w-full border border-gray-300 rounded-md px-3 py-1.5 outline-none focus:border-brand-500 transition-colors"
placeholder="e.g. VAT_11"
/>
</div>
<div className="flex items-center gap-3 py-2 border-t border-gray-50">
<input type="checkbox" className="w-4 h-4 accent-brand-500" id="lock" />
<label htmlFor="lock" className="text-sm font-semibold text-gray-700">
Lock account for posting
</label>
</div>
</div>
</Card>
<div className="bg-brand-900 text-white p-5 rounded-lg shadow-lg relative overflow-hidden">
<div className="relative z-10">
<h4 className="text-lg font-bold mb-1 italic">Premium Support</h4>
<p className="text-sm text-brand-100 opacity-80">
Butuh bantuan rekonsiliasi? Hubungi konsultan finansial kami.
</p>
<button className="mt-4 text-xs font-bold bg-white text-brand-900 px-3 py-1.5 rounded uppercase tracking-widest shadow-lg">
Chat Konsultan
</button>
</div>
<div className="absolute -right-4 -bottom-4 text-6xl opacity-10 font-bold">ERP</div>
</div>
</div>
</div>
</div>
</main>
{/* 3. SAMPLE MODAL (Overlay) */}
{isModalOpen && (
<div className="fixed inset-0 bg-brand-950/40 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div className="bg-white rounded-xl shadow-2xl w-full max-w-lg overflow-hidden border border-gray-200">
<div className="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50/50">
<h3 className="text-xl font-bold text-gray-900">New Journal Entry</h3>
<button onClick={() => setIsModalOpen(false)} className="text-gray-400 hover:text-gray-600 text-2xl">
×
</button>
</div>
<div className="p-6 space-y-4">
<p className="text-base text-gray-600">
Silakan masukkan detail entri jurnal baru untuk tahun fiskal 2026.
</p>
<div className="grid grid-cols-2 gap-4">
<div className="col-span-2 border border-brand-100 bg-brand-50/50 p-3 rounded-md italic text-xs text-brand-800">
Info: Pastikan periode pembukuan Januari sudah dibuka sebelum menekan "Post".
</div>
</div>
</div>
<div className="px-6 py-4 bg-gray-50 border-t border-gray-100 flex justify-end gap-2">
<Button variant="secondary" onClick={() => setIsModalOpen(false)}>
Discard
</Button>
<Button onClick={() => setIsModalOpen(false)}>Post Entry</Button>
</div>
</div>
</div>
)}
</div>
);
}
@@ -0,0 +1,232 @@
import React from 'react';
// ==========================================================
// COMPACT UI COMPONENTS
// ==========================================================
const SectionTitle = ({ children }: { children: React.ReactNode }) => (
<h3 className="text-[11px] font-bold text-gray-500 uppercase tracking-widest mb-2 border-b border-gray-200 pb-1 italic">
{children}
</h3>
);
const CompactCard = ({ title, children, footer }: any) => (
<div className="bg-white border border-gray-200 rounded shadow-sm flex flex-col">
{title && (
<div className="px-3 py-1.5 border-b border-gray-100 bg-gray-50/50 flex justify-between items-center">
<span className="text-sm font-bold text-gray-700 tracking-tight">{title}</span>
</div>
)}
<div className="p-3 flex-1">{children}</div>
{footer && <div className="px-3 py-2 bg-gray-50/80 border-t border-gray-100">{footer}</div>}
</div>
);
const Badge = ({ children, variant = 'info' }: any) => {
const styles: any = {
success: 'bg-green-50 text-green-700 border-green-200',
error: 'bg-red-50 text-red-700 border-red-200',
warning: 'bg-amber-50 text-amber-700 border-amber-200',
info: 'bg-brand-50 text-brand-700 border-brand-200',
};
return (
<span
className={`px-1.5 py-0.5 rounded-sm text-[10px] font-bold border ${styles[variant]} uppercase tracking-tighter`}
>
{children}
</span>
);
};
// ==========================================================
// MAIN STYLE SHOWCASE
// ==========================================================
export function StyleGuideTree() {
return (
<div className="min-h-screen bg-gray-100 p-3 space-y-3 font-sans text-gray-800">
{/* GLOBAL ACTION BAR */}
<div className="bg-brand-900 text-white p-3 rounded flex justify-between items-center shadow-md">
<div className="flex items-center gap-3">
<div className="bg-white p-1 rounded text-brand-900 font-bold text-xs uppercase">ERP-SYSTEM</div>
<h1 className="text-base font-bold tracking-tight">Financial Postings & Asset Management</h1>
</div>
<div className="flex gap-2">
<button className="bg-brand-700 hover:bg-brand-600 px-3 py-1 rounded text-base font-medium border border-brand-600 transition-all">
Simulate
</button>
<button className="bg-white text-brand-900 hover:bg-gray-100 px-3 py-1 rounded text-base font-bold shadow-sm transition-all">
Post Document
</button>
</div>
</div>
<div className="grid grid-cols-12 gap-3">
{/* ROW 1: ANALYTICS & STATS (Top Row) */}
{[
{ label: 'Total Debit', val: 'IDR 4.500.000.000', sub: '24 Entries Today', color: 'text-gray-900' },
{ label: 'Total Credit', val: 'IDR 4.500.000.000', sub: 'Balanced', color: 'text-green-600' },
{ label: 'Unposted Items', val: '12 Documents', sub: 'High Priority', color: 'text-amber-600' },
{ label: 'System Health', val: '99.9%', sub: 'No Latency', color: 'text-brand-600' },
].map((stat, i) => (
<div key={i} className="col-span-12 md:col-span-3 bg-white p-3 rounded border border-gray-200 shadow-sm">
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest">{stat.label}</p>
<p className={`text-lg font-bold ${stat.color} tracking-tight`}>{stat.val}</p>
<p className="text-[11px] text-gray-400 font-medium italic">{stat.sub}</p>
</div>
))}
{/* ROW 2: FORMS & CONTROLS (Left Panel) */}
<div className="col-span-12 lg:col-span-4 space-y-3">
<CompactCard title="Document Header">
<div className="grid grid-cols-2 gap-3">
<div className="col-span-1 flex flex-col">
<label className="text-xs font-bold text-gray-400 uppercase italic mb-1 tracking-tight">
Posting Date
</label>
<input
type="date"
className="border border-gray-300 rounded px-2 py-1 text-base outline-none focus:border-brand-500 bg-gray-50"
defaultValue="2026-01-19"
/>
</div>
<div className="col-span-1 flex flex-col">
<label className="text-xs font-bold text-gray-400 uppercase italic mb-1 tracking-tight">Period</label>
<input
type="text"
readOnly
className="border border-gray-200 rounded px-2 py-1 text-base bg-gray-100 text-gray-500"
defaultValue="01 / 2026"
/>
</div>
<div className="col-span-2 flex flex-col">
<label className="text-xs font-bold text-gray-400 uppercase italic mb-1 tracking-tight">
Document Type
</label>
<select className="border border-gray-300 rounded px-2 py-1 text-base outline-none bg-white">
<option>SA - GL Account Document</option>
<option>DZ - Customer Payment</option>
</select>
</div>
<div className="col-span-2 flex flex-col">
<label className="text-xs font-bold text-gray-400 uppercase italic mb-1 tracking-tight">
Header Text
</label>
<textarea
className="border border-gray-300 rounded px-2 py-1 text-base h-16 outline-none focus:border-brand-500"
placeholder="Monthly reconciliation..."
></textarea>
</div>
</div>
</CompactCard>
<CompactCard title="Operational Controls">
<div className="space-y-2">
<label className="flex items-center gap-3 p-2 border border-gray-100 rounded hover:bg-gray-50 cursor-pointer transition-colors">
<input type="checkbox" className="accent-brand-600 w-4 h-4" defaultChecked />
<span className="text-base font-medium">Automatic Tax Calculation</span>
</label>
<label className="flex items-center gap-3 p-2 border border-gray-100 rounded hover:bg-gray-50 cursor-pointer transition-colors">
<input type="checkbox" className="accent-brand-600 w-4 h-4" />
<span className="text-base font-medium">Post to All Ledgers</span>
</label>
</div>
</CompactCard>
</div>
{/* ROW 3: COMPLEX DATA TABLE (Center Panel) */}
<div className="col-span-12 lg:col-span-8 space-y-3">
<CompactCard className="!p-0 overflow-hidden">
<div className="bg-gray-50/50 px-4 py-2 border-b border-gray-200 flex justify-between items-center">
<SectionTitle>Line Item Entries (001 - 005)</SectionTitle>
<div className="flex gap-2 mb-2">
<button className="text-[10px] font-bold text-brand-700 bg-brand-50 px-2 py-0.5 rounded border border-brand-100">
ADD ROW
</button>
<button className="text-[10px] font-bold text-gray-500 bg-white px-2 py-0.5 rounded border border-gray-200">
CLEAR ALL
</button>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100 border-b border-gray-200">
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase">Item</th>
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase">GL Account</th>
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase">Cost Center</th>
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase text-right">
Amount (LC)
</th>
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase text-center">Ind.</th>
<th className="px-3 py-1.5 text-[10px] font-bold text-gray-500 uppercase text-center">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100">
{[10, 20, 30, 40, 50].map((item, idx) => (
<tr key={item} className="hover:bg-brand-50/30 transition-colors group">
<td className="px-3 py-1.5 text-center font-mono text-gray-400">{item}</td>
<td className="px-3 py-1.5">
<div className="font-bold text-brand-700 text-base leading-none">11000{idx}</div>
<div className="text-[10px] text-gray-400 italic">Petty Cash - Main Office</div>
</td>
<td className="px-3 py-1.5">
<div className="font-medium text-gray-700">CC-HEAD-01</div>
</td>
<td className="px-3 py-1.5 text-right font-mono font-bold text-gray-900">
{(item * 1500000).toLocaleString('id-ID')}
</td>
<td className="px-3 py-1.5 text-center font-bold text-brand-600">{idx % 2 === 0 ? 'S' : 'H'}</td>
<td className="px-3 py-1.5 text-center">
<Badge variant={idx === 2 ? 'warning' : 'success'}>{idx === 2 ? 'Review' : 'Valid'}</Badge>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="bg-gray-50 p-3 flex justify-end gap-10 border-t border-gray-200">
<div className="text-right">
<p className="text-[10px] font-bold text-gray-400 uppercase">Total Debit</p>
<p className="text-lg font-bold text-gray-900 tracking-tight">IDR 450.000.000</p>
</div>
<div className="text-right">
<p className="text-[10px] font-bold text-gray-400 uppercase">Total Credit</p>
<p className="text-lg font-bold text-brand-600 tracking-tight">IDR 450.000.000</p>
</div>
</div>
</CompactCard>
{/* MESSAGE LOGS / ACTIVITY */}
<div className="grid grid-cols-2 gap-3">
<CompactCard title="System Activity">
<div className="space-y-2">
{[
{ t: '09:41', u: 'System', m: 'Auto-balancing completed successfully.' },
{ t: '10:05', u: 'Admin', m: 'Modified Cost Center on Item 20.' },
].map((log, i) => (
<div key={i} className="text-base flex gap-2 border-b border-gray-50 pb-1">
<span className="font-mono text-brand-600 text-xs">{log.t}</span>
<span className="text-gray-600 leading-tight">
<strong>{log.u}:</strong> {log.m}
</span>
</div>
))}
</div>
</CompactCard>
<CompactCard title="Quick Attachments">
<div className="flex flex-wrap gap-2">
<div className="border border-dashed border-gray-300 rounded p-2 text-center w-full hover:bg-gray-50 cursor-pointer">
<span className="text-xs font-bold text-gray-400">+ DROP FILES HERE</span>
</div>
<div className="bg-gray-100 px-2 py-1 rounded text-[11px] font-medium flex items-center gap-2">
📄 invoice_001.pdf <span className="text-red-500 font-bold cursor-pointer">×</span>
</div>
</div>
</CompactCard>
</div>
</div>
</div>
</div>
);
}
+14 -5
View File
@@ -1,5 +1,14 @@
export * from './header';
export * from './counter';
export * from './date-example';
export * from './encryption-demo';
export * from './button';
export * from './examples/header-example';
export * from './examples/counter-example';
export * from './examples/date-example';
export * from './examples/encryption-example';
export * from './examples/style-guide-1';
export * from './examples/style-guide-2';
export * from './examples/style-guide-3';
export * from './system-pages/coming-soon';
export * from './system-pages/forbidden';
export * from './system-pages/maintenance';
export * from './system-pages/not-found';
export * from './button/button';
@@ -0,0 +1,25 @@
export function ComingSoon() {
return (
<div className="flex min-h-screen items-center justify-center bg-white px-4">
<div className="w-full max-w-md text-center">
{/* Code */}
<p className="text-sm font-medium uppercase tracking-widest text-gray-400">Coming Soon</p>
{/* Title */}
<h1 className="mt-3 text-6xl font-extrabold text-gray-900">Feature in Progress</h1>
{/* Description */}
<p className="mt-4 text-base text-gray-500">
This feature is currently under development. Stay tuned! We'll have it ready for you very soon.
</p>
{/* Actions */}
<div className="mt-8 flex justify-center gap-3">
<a href="/" className="rounded-md bg-gray-900 px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-800">
Back to Home
</a>
</div>
</div>
</div>
);
}
@@ -0,0 +1,33 @@
export function Forbidden() {
return (
<div className="flex min-h-screen items-center justify-center bg-white px-4">
<div className="w-full max-w-md text-center">
{/* Code */}
<p className="text-sm font-medium uppercase tracking-widest text-gray-400">403 Error</p>
{/* Title */}
<h1 className="mt-3 text-6xl font-extrabold text-gray-900">Access Denied</h1>
{/* Description */}
<p className="mt-4 text-base text-gray-500">
Sorry, you dont have permission to access this page. Please contact your administrator if you believe this is
a mistake.
</p>
{/* Actions */}
<div className="mt-8 flex justify-center gap-3">
<button
onClick={() => window.history.back()}
className="rounded-md border border-gray-300 px-5 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-100"
>
Go Back
</button>
<a href="/" className="rounded-md bg-gray-900 px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-800">
Back to Home
</a>
</div>
</div>
</div>
);
}
@@ -0,0 +1,26 @@
export function Maintenance() {
return (
<div className="flex min-h-screen items-center justify-center bg-white px-4">
<div className="w-full max-w-md text-center">
{/* Code */}
<p className="text-sm font-medium uppercase tracking-widest text-gray-400">Maintenance</p>
{/* Title */}
<h1 className="mt-3 text-6xl font-extrabold text-gray-900">We'll Be Back Soon</h1>
{/* Description */}
<p className="mt-4 text-base text-gray-500">
Our system is currently undergoing scheduled maintenance. We are working hard to bring it back online as soon
as possible.
</p>
{/* Optional Actions */}
<div className="mt-8 flex justify-center gap-3">
<a href="/" className="rounded-md bg-gray-900 px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-800">
Back to Home
</a>
</div>
</div>
</div>
);
}
@@ -0,0 +1,28 @@
export function NotFound() {
return (
<div className="flex min-h-screen items-center justify-center bg-white px-4">
<div className="w-full max-w-md text-center">
<p className="text-sm font-medium uppercase tracking-widest text-gray-400">404 Error</p>
<h1 className="mt-3 text-6xl font-extrabold text-gray-900">Page Not Found</h1>
<p className="mt-4 text-base text-gray-500">
Sorry, the page youre looking for doesnt exist or has been moved.
</p>
<div className="mt-8 flex justify-center gap-3">
<button
onClick={() => window.history.back()}
className="rounded-md border border-gray-300 px-5 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-100"
>
Go Back
</button>
<a href="/" className="rounded-md bg-gray-900 px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-800">
Back to Home
</a>
</div>
</div>
</div>
);
}