feat(showcase): add PouchDB sample component and storage page

- Implemented PouchSample component for managing POS configurations and item inventories using PouchDB.
- Created StoragePage to encapsulate the PouchSample component.
- Added UI components page with various UI elements including buttons, forms, and data grids.
- Defined Electron type declarations for printing and auto-update functionalities.
- Extended event registry with custom application events for printing and stock updates.
- Configured Vite for the showcase application with React and Tailwind CSS support.
- Updated package.json and pnpm-lock.yaml to include necessary dependencies for the showcase app.
This commit is contained in:
Firman Ramdhani
2026-07-23 17:26:41 +07:00
parent e99aeb6def
commit 980952252d
65 changed files with 5749 additions and 9 deletions
@@ -0,0 +1,131 @@
import { AppShell, NavLink, Group, Title, Box, Select, Text, Paper } from '@repo/ui/components';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useTranslation } from '@repo/core-i18n';
import { Layout, FileText, Database, ShieldCheck, Lock, Activity, Printer, Globe, Layers, Table2 } from 'lucide-react';
import { DensityType } from '@repo/ui/provider';
import { useThemeStore } from '../core/stores/theme.store';
import { ColorSchemeType } from '@repo/ui/provider';
interface ShowcaseLayoutProps {
density: DensityType;
setDensity: (val: DensityType) => void;
}
export default function ShowcaseLayout({ density, setDensity }: ShowcaseLayoutProps) {
const navigate = useNavigate();
const location = useLocation();
const { i18n } = useTranslation();
const { colorScheme, setColorScheme } = useThemeStore();
const navItems = [
{ label: 'UI Components', path: '/ui-components', icon: Layout },
{ label: 'Layout Engine', path: '/shell-demo', icon: Layout },
{ label: 'Form Engine', path: '/forms', icon: FileText },
{ label: 'Offline Storage', path: '/storage', icon: Database },
{ label: 'RBAC Engine', path: '/rbac', icon: ShieldCheck },
{ label: 'Auth & Security', path: '/auth', icon: Lock },
{ label: 'Events', path: '/events', icon: Activity },
{ label: 'Hardware', path: '/hardware', icon: Printer },
{ label: 'Action Tools', path: '/action-tools', icon: Layers },
{ label: 'AG Grid', path: '/ag-grid', icon: Table2 },
];
const getSubtitle = () => {
const currentPath = location.pathname.replace('/', '');
switch (currentPath) {
case 'rbac': return 'Role-Based Access Control and Permissions';
case 'storage': return 'Offline-First PouchDB Synchronization';
case 'auth': return 'Authentication & Security Layers';
case 'ui-components': return 'Theme, Typography, Forms & Data Grids';
case 'forms': return 'Enterprise Form Engine & Zod Validation';
case 'events': return 'Global Event Bus Synchronization';
case 'hardware': return 'Hardware Integration & Printers';
case 'layout-engine': return 'Core Layout Engine & Variants';
case 'action-tools': return 'Showcase for PageActions and RowActions components';
case 'ag-grid': return 'Enterprise Data Grid with Mantine Theme Integration';
default: return 'Architecture Showcase';
}
};
return (
<AppShell
navbar={{
width: 260,
breakpoint: 'sm',
}}
padding="0"
>
<AppShell.Navbar p="md">
<Box mb="xl">
<Title order={3} c="brand.7">Eigen ERP</Title>
<Text size="xs" c="dimmed">Architecture Showcase</Text>
</Box>
<Box style={{ flex: 1 }}>
{navItems.map((item) => (
<NavLink
key={item.path}
active={location.pathname === item.path}
label={item.label}
leftSection={<item.icon size={18} />}
onClick={() => navigate(item.path)}
variant="filled"
style={{ borderRadius: 'var(--mantine-radius-md)', marginBottom: 4 }}
/>
))}
</Box>
<Box mt="auto" pt="md" style={{ borderTop: '1px solid var(--mantine-color-default-border)' }}>
<Select
label="Color Scheme"
size="xs"
mb="xs"
value={colorScheme}
onChange={(val) => setColorScheme((val as ColorSchemeType) || 'light')}
data={[
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
]}
/>
<Select
label="Density"
size="xs"
value={density}
onChange={(val) => setDensity((val as DensityType) || 'standard')}
data={[
{ value: 'compact', label: 'Compact' },
{ value: 'standard', label: 'Standard' },
]}
/>
</Box>
</AppShell.Navbar>
<AppShell.Main style={{ display: 'flex', flexDirection: 'column', height: '100vh', overflow: 'hidden' }}>
<Paper p="md" radius={0} withBorder style={{ borderTop: 0, borderLeft: 0, borderRight: 0, zIndex: 10, flexShrink: 0 }}>
<Group justify="space-between">
<Box>
<Title order={3}>Architecture Showcase</Title>
<Text size="sm" c="dimmed">{getSubtitle()}</Text>
</Box>
<Select
w={180}
size="sm"
variant="filled"
leftSection={<Globe size={16} />}
data={[
{ value: 'en', label: 'English' },
{ value: 'id', label: 'Bahasa Indonesia' },
]}
value={i18n.resolvedLanguage || i18n.language}
onChange={(val) => val && i18n.changeLanguage(val)}
/>
</Group>
</Paper>
<Box className="flex-1 overflow-y-auto p-6" style={{ height: 'calc(100vh - 80px)' }}>
<Outlet />
</Box>
</AppShell.Main>
</AppShell>
);
}