From e99aeb6def9d04abec7491ff1303ccfceb36ada6 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:23:05 +0700 Subject: [PATCH 1/4] refactor: remove unused showcase components and related files - Deleted PouchSample component and its associated logic for PouchDB data handling. - Removed PrinterList component and its integration for printer management. - Eliminated registry.ts for component registration, simplifying the showcase structure. - Cleaned up ShellDemo component, removing unnecessary layout controls and demo configurations. - Removed ShowcaseView component, consolidating the showcase functionality. --- apps/web/src/apps/index.tsx | 13 +- .../components/ActionToolsShowcase.tsx | 161 ------- .../showcase/components/AgGridShowcase.tsx | 218 --------- .../auth-sync/profile-settings.ui.tsx | 64 --- .../auth-sync/storage-sync.listener.tsx | 36 -- .../src/apps/showcase/events-demo/index.tsx | 167 ------- .../events-demo/printer/cashier.ui.tsx | 109 ----- .../events-demo/printer/printer.listener.tsx | 65 --- .../stock-grid/live-stock-grid.ui.tsx | 187 -------- .../stock-grid/mock-websocket.service.ts | 98 ---- .../events-demo/stock-grid/stock-row.ui.tsx | 91 ---- .../apps/showcase/example/example.page.tsx | 22 - .../data/advanced-booking.data-services.ts | 118 ----- .../data/advanced-booking.transformer.ts | 159 ------- .../booking/data/booking.data-services.ts | 58 --- .../booking/data/booking.transformer.ts | 123 ----- .../booking/presentation/BookingSample.tsx | 92 ---- .../form-demo/components/all-fields-demo.tsx | 408 ---------------- .../components/reactive-watch-demo.tsx | 368 --------------- .../components/validation-bank-demo.tsx | 254 ---------- .../features/form-demo/form-demo-view.tsx | 31 -- .../example/features/form-demo/i18n/en.json | 117 ----- .../example/features/form-demo/i18n/id.json | 117 ----- .../form-demo/i18n/useFormDemoTranslation.ts | 10 - .../example/features/form-demo/index.ts | 1 - .../features/i18n/languages/en/booking.json | 8 - .../features/i18n/languages/id/booking.json | 8 - .../features/i18n/presentation/I18nSample.tsx | 347 -------------- .../storage/presentation/StorageSample.tsx | 280 ----------- apps/web/src/apps/showcase/pouch-sample.tsx | 324 ------------- apps/web/src/apps/showcase/printer-list.tsx | 37 -- apps/web/src/apps/showcase/registry.ts | 20 - .../src/apps/showcase/shell-demo/index.tsx | 346 -------------- apps/web/src/apps/showcase/showcase-view.tsx | 437 ------------------ 34 files changed, 4 insertions(+), 4890 deletions(-) delete mode 100644 apps/web/src/apps/showcase/components/ActionToolsShowcase.tsx delete mode 100644 apps/web/src/apps/showcase/components/AgGridShowcase.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/auth-sync/profile-settings.ui.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/auth-sync/storage-sync.listener.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/index.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/printer/cashier.ui.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/printer/printer.listener.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/stock-grid/live-stock-grid.ui.tsx delete mode 100644 apps/web/src/apps/showcase/events-demo/stock-grid/mock-websocket.service.ts delete mode 100644 apps/web/src/apps/showcase/events-demo/stock-grid/stock-row.ui.tsx delete mode 100644 apps/web/src/apps/showcase/example/example.page.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/booking/data/advanced-booking.data-services.ts delete mode 100644 apps/web/src/apps/showcase/example/features/booking/data/advanced-booking.transformer.ts delete mode 100644 apps/web/src/apps/showcase/example/features/booking/data/booking.data-services.ts delete mode 100644 apps/web/src/apps/showcase/example/features/booking/data/booking.transformer.ts delete mode 100644 apps/web/src/apps/showcase/example/features/booking/presentation/BookingSample.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/components/all-fields-demo.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/components/reactive-watch-demo.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/components/validation-bank-demo.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/form-demo-view.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/i18n/en.json delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/i18n/id.json delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/i18n/useFormDemoTranslation.ts delete mode 100644 apps/web/src/apps/showcase/example/features/form-demo/index.ts delete mode 100644 apps/web/src/apps/showcase/example/features/i18n/languages/en/booking.json delete mode 100644 apps/web/src/apps/showcase/example/features/i18n/languages/id/booking.json delete mode 100644 apps/web/src/apps/showcase/example/features/i18n/presentation/I18nSample.tsx delete mode 100644 apps/web/src/apps/showcase/example/features/storage/presentation/StorageSample.tsx delete mode 100644 apps/web/src/apps/showcase/pouch-sample.tsx delete mode 100644 apps/web/src/apps/showcase/printer-list.tsx delete mode 100644 apps/web/src/apps/showcase/registry.ts delete mode 100644 apps/web/src/apps/showcase/shell-demo/index.tsx delete mode 100644 apps/web/src/apps/showcase/showcase-view.tsx diff --git a/apps/web/src/apps/index.tsx b/apps/web/src/apps/index.tsx index 7d0920d..bbf9c1d 100644 --- a/apps/web/src/apps/index.tsx +++ b/apps/web/src/apps/index.tsx @@ -1,6 +1,6 @@ -import { lazy, Suspense, useEffect, useState } from 'react'; +import { lazy, Suspense, useEffect } from 'react'; import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; -import { ThemeProvider, DensityType } from '@repo/ui/provider'; +import { ThemeProvider } from '@repo/ui/provider'; import { NotFound, Forbidden, Maintenance, ComingSoon, AgGridProvider } from '@repo/ui/components'; import { LoadingScreen } from '../core/components/loading-screen'; import { useThemeStore } from '../core/stores/theme.store'; @@ -8,12 +8,9 @@ import { initializeAndPurgeHistoryBackground } from './modules/layouts/hooks/use const AuthModule = lazy(() => import('./auth')); const AppModule = lazy(() => import('./modules')); -const ShowcaseView = lazy(() => import('./showcase/showcase-view')); -const ShellDemo = lazy(() => import('./showcase/shell-demo')); export default function App() { const colorScheme = useThemeStore((s) => s.colorScheme); - const [density, setDensity] = useState('compact'); useEffect(() => { // Execution runs purely in the background (fire and forget) @@ -22,20 +19,18 @@ export default function App() { }, []); return ( - + }> } /> } /> - } /> - } /> } /> } /> } /> } /> - } /> + } /> } /> diff --git a/apps/web/src/apps/showcase/components/ActionToolsShowcase.tsx b/apps/web/src/apps/showcase/components/ActionToolsShowcase.tsx deleted file mode 100644 index ace41c1..0000000 --- a/apps/web/src/apps/showcase/components/ActionToolsShowcase.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { Card, Title, Text, Table, Stack, Badge } from '@repo/ui/components'; -import { PageActions, RowActions, PageActionProps, RowActionProps } from '@repo/ui/components'; -import { Save, Printer, Trash, MoreVertical, Edit, FileText, CheckCircle, Check } from 'lucide-react'; - -export default function ActionToolsShowcase() { - const pageActions: PageActionProps[] = [ - { - key: 'save', - label: 'Save Changes', - icon: , - variant: 'filled', - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'save', - label: 'Save Changes', - icon: , - onClick: (key) => console.log('Clicked', key), - }, - { type: 'divider' }, - { - key: 'print', - label: 'Print', - icon: , - children: [ - { - key: 'print-original', - label: 'Print Original', - icon: , - onClick: (k) => console.log(k), - }, - // { type: 'divider' }, - { key: 'print-copy', label: 'Print Copy', icon: , onClick: (k) => console.log(k) }, - { key: 'print-copy', label: 'Print Copy', icon: , onClick: (k) => console.log(k) }, - { key: 'print-copy', label: 'Print Copy', icon: , onClick: (k) => console.log(k) }, - ], - }, - { - key: 'confirm', - label: 'Confirm', - icon: , - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'delete', - label: 'Delete', - icon: , - intent: 'destructive', - onClick: (key) => console.log('Clicked', key), - }, - - { - key: 'success', - label: 'Success', - icon: , - intent: 'success', - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'warning', - label: 'Warning', - icon: , - intent: 'warning', - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'Primary', - label: 'Primary', - icon: , - intent: 'primary', - onClick: (key) => console.log('Clicked', key), - }, - ]; - - const rowActions: RowActionProps[] = [ - { - key: 'edit', - tooltip: 'Edit Record', - label: 'Edit Record', - icon: , - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'approve', - tooltip: 'Approve', - label: 'Approve', - icon: , - intent: 'success', - onClick: (key) => console.log('Clicked', key), - }, - { - key: 'more', - label: 'More Options', - icon: , - children: [ - { - key: 'delete', - label: 'Delete Record', - icon: , - intent: 'destructive', - onClick: (k) => console.log(k), - }, - ], - }, - ]; - - const tableData = [ - { id: '1', name: 'Invoice #001', status: 'Pending' }, - { id: '2', name: 'Invoice #002', status: 'Approved' }, - ]; - - return ( - - - - Page Actions - - - Used in toolbars and page headers. - - - - - - - Row Actions - - - Used inside data grids or list items. - - - - - ID - Name - Status - Actions - - - - {tableData.map((row) => ( - - {row.id} - {row.name} - - {row.status} - - - - - - - - - ))} - -
-
-
- ); -} diff --git a/apps/web/src/apps/showcase/components/AgGridShowcase.tsx b/apps/web/src/apps/showcase/components/AgGridShowcase.tsx deleted file mode 100644 index a6a0f90..0000000 --- a/apps/web/src/apps/showcase/components/AgGridShowcase.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import { useState, useMemo } from 'react'; -import { Card, Title, Text, Stack, Badge, Group, Select, NumberInput } from '@repo/ui/components'; -import { AgGridProvider, DataGrid } from '@repo/ui/ag-grid'; -import type { ColDef, ValueFormatterParams } from '@repo/ui/ag-grid'; - -/* ─── Sample Data ──────────────────────────────────────────────── */ - -interface OrderRow { - id: string; - orderCode: string; - customer: string; - email: string; - product: string; - quantity: number; - unitPrice: number; - total: number; - status: 'Draft' | 'Pending' | 'Processing' | 'Shipped' | 'Delivered' | 'Cancelled'; - createdAt: string; - region: string; -} - -const SAMPLE_DATA: OrderRow[] = [ - { id: '1', orderCode: 'ORD-2026-001', customer: 'Acme Corp', email: 'acme@example.com', product: 'Widget A', quantity: 120, unitPrice: 24.5, total: 2940, status: 'Delivered', createdAt: '2026-01-15', region: 'North America' }, - { id: '2', orderCode: 'ORD-2026-002', customer: 'Globex Inc', email: 'info@globex.com', product: 'Widget B', quantity: 50, unitPrice: 89.99, total: 4499.5, status: 'Shipped', createdAt: '2026-02-03', region: 'Europe' }, - { id: '3', orderCode: 'ORD-2026-003', customer: 'Initech', email: 'orders@initech.com', product: 'Gadget Pro', quantity: 200, unitPrice: 15.0, total: 3000, status: 'Processing', createdAt: '2026-03-21', region: 'Asia Pacific' }, - { id: '4', orderCode: 'ORD-2026-004', customer: 'Umbrella Ltd', email: 'sales@umbrella.co', product: 'Widget A', quantity: 75, unitPrice: 24.5, total: 1837.5, status: 'Pending', createdAt: '2026-04-10', region: 'Europe' }, - { id: '5', orderCode: 'ORD-2026-005', customer: 'Stark Industries', email: 'tony@stark.io', product: 'Gadget Elite', quantity: 10, unitPrice: 499.0, total: 4990, status: 'Delivered', createdAt: '2026-04-18', region: 'North America' }, - { id: '6', orderCode: 'ORD-2026-006', customer: 'Wayne Enterprises', email: 'bruce@wayne.com', product: 'Widget C', quantity: 300, unitPrice: 12.75, total: 3825, status: 'Draft', createdAt: '2026-05-02', region: 'North America' }, - { id: '7', orderCode: 'ORD-2026-007', customer: 'Cyberdyne', email: 'info@cyberdyne.jp', product: 'Gadget Pro', quantity: 150, unitPrice: 15.0, total: 2250, status: 'Cancelled', createdAt: '2026-05-15', region: 'Asia Pacific' }, - { id: '8', orderCode: 'ORD-2026-008', customer: 'Oscorp', email: 'orders@oscorp.com', product: 'Widget B', quantity: 90, unitPrice: 89.99, total: 8099.1, status: 'Shipped', createdAt: '2026-06-01', region: 'North America' }, - { id: '9', orderCode: 'ORD-2026-009', customer: 'LexCorp', email: 'lex@lexcorp.com', product: 'Gadget Elite', quantity: 5, unitPrice: 499.0, total: 2495, status: 'Processing', createdAt: '2026-06-12', region: 'Europe' }, - { id: '10', orderCode: 'ORD-2026-010', customer: 'Pied Piper', email: 'richard@piedpiper.io', product: 'Widget A', quantity: 500, unitPrice: 24.5, total: 12250, status: 'Pending', createdAt: '2026-06-20', region: 'North America' }, - { id: '11', orderCode: 'ORD-2026-011', customer: 'Hooli', email: 'gavin@hooli.com', product: 'Gadget Pro', quantity: 1000, unitPrice: 15.0, total: 15000, status: 'Delivered', createdAt: '2026-07-01', region: 'North America' }, - { id: '12', orderCode: 'ORD-2026-012', customer: 'Massive Dynamic', email: 'nina@massive.com', product: 'Widget C', quantity: 60, unitPrice: 12.75, total: 765, status: 'Shipped', createdAt: '2026-07-08', region: 'Europe' }, -]; - -/* ─── Status Badge Renderer ─────────────────────────────────────── */ - -const STATUS_COLORS: Record = { - Draft: 'gray', - Pending: 'warning', - Processing: 'info', - Shipped: 'brand', - Delivered: 'success', - Cancelled: 'error', -}; - -function StatusCellRenderer({ value }: { value: OrderRow['status'] }) { - return ( - - {value} - - ); -} - -/* ─── Currency Formatter ────────────────────────────────────────── */ - -function currencyFormatter(params: ValueFormatterParams): string { - if (params.value == null) return '-'; - return new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', - minimumFractionDigits: 2, - }).format(params.value); -} - -/* ─── Showcase Component ────────────────────────────────────────── */ - -export default function AgGridShowcase() { - const [gridHeight, setGridHeight] = useState(460); - const [rowModelType, setRowModelType] = useState('clientSide'); - - const defaultColDef = useMemo( - () => ({ - flex: 1, - minWidth: 100, - sortable: true, - filter: true, - resizable: true, - }), - [], - ); - - const columnDefs = useMemo[]>( - () => [ - { - field: 'orderCode', - headerName: 'Order Code', - pinned: 'left', - minWidth: 140, - filter: 'agTextColumnFilter', - }, - { - field: 'status', - headerName: 'Status', - minWidth: 120, - cellRenderer: StatusCellRenderer, - filter: 'agSetColumnFilter', - }, - { - field: 'customer', - headerName: 'Customer', - minWidth: 160, - filter: 'agTextColumnFilter', - }, - { - field: 'email', - headerName: 'Email', - minWidth: 180, - filter: 'agTextColumnFilter', - }, - { - field: 'product', - headerName: 'Product', - minWidth: 130, - filter: 'agSetColumnFilter', - }, - { - field: 'region', - headerName: 'Region', - minWidth: 140, - filter: 'agSetColumnFilter', - enableRowGroup: true, - }, - { - field: 'quantity', - headerName: 'Qty', - minWidth: 80, - filter: 'agNumberColumnFilter', - type: 'numericColumn', - }, - { - field: 'unitPrice', - headerName: 'Unit Price', - minWidth: 110, - filter: 'agNumberColumnFilter', - type: 'numericColumn', - valueFormatter: currencyFormatter, - }, - { - field: 'total', - headerName: 'Total', - minWidth: 120, - filter: 'agNumberColumnFilter', - type: 'numericColumn', - valueFormatter: currencyFormatter, - }, - { - field: 'createdAt', - headerName: 'Created Date', - minWidth: 130, - filter: 'agDateColumnFilter', - }, - ], - [], - ); - - return ( - - - {/* ── Info Card ────────────────────────────────── */} - - - AG Grid Enterprise — Mantine Integration - - - This showcase demonstrates AG Grid integrated with Mantine's theme system. The grid - automatically adapts to dark/light mode changes, inherits brand colors, typography, and - border radius from the Mantine theme — all via AG Grid's built-in CSS variable system with - zero custom stylesheets. Toggle the color scheme in the Theme Controls above to see it in - action. - - - setGridHeight(Number(val) || 400)} - min={300} - max={800} - step={50} - w={180} - /> - setAdminModuleName(e.target.value)} - style={{ - padding: 6, - borderRadius: 4, - background: '#1e293b', - border: '1px solid #475569', - color: '#fff', - width: 250, - }} - /> - - - - - - -
-
Raw JSON in DB:
-
-            {dbPayloadStr}
-          
-
- - - {/* ─── Section A ────────────────────────────────────────────── */} -
-

A. Language Switcher & Backend Sync

-

- Change the language. The callback simulates a 1-second backend API request. -

- -
- - - -
- - {syncStatus && ( -
- {syncStatus} -
- )} - - {/* UI Result untuk Section A */} -
-

UI Result (Live Dictionary):

-

- common:save - {t('common:save')} -

-

- booking:select_date - {t('booking:select_date')} -

-
-
- - {/* ─── Section B ────────────────────────────────────────────── */} -
-

B. Dynamic Tenant Overrides (End-to-End)

-

- Simulates a user logging in. It fetches the config directly from IndexedDB (mock database) and applies the - deep-merge override. -

- -
- - - -
- - {isFetchingConfig && ( -
⏳ Fetching tenant config...
- )} - - {/* Display the localized strings */} -
-

UI Result (Tenant Overlay):

- - - - - - - - - - - - - - - - - - - -
KeyValue
- booking:module_name - {t('booking:module_name')}
- booking:header.title - {t('booking:header.title')}
- booking:header.subtitle - {t('booking:header.subtitle')}
-
-
- - ); -} diff --git a/apps/web/src/apps/showcase/example/features/storage/presentation/StorageSample.tsx b/apps/web/src/apps/showcase/example/features/storage/presentation/StorageSample.tsx deleted file mode 100644 index 58e9d7b..0000000 --- a/apps/web/src/apps/showcase/example/features/storage/presentation/StorageSample.tsx +++ /dev/null @@ -1,280 +0,0 @@ -import { useState, useCallback } from 'react'; -import { secureStorage, secureIndexedDB, AppStorageKey } from '../../../../../../core/storage/local'; - -// ─── Demo Data ────────────────────────────────────────────────── - -interface DemoUser { - id: string; - name: string; - role: string; -} - -interface DemoDraft { - id: number; - type: string; - content: string; -} - -const DEMO_USER: DemoUser = { - id: 'u-123', - name: 'Firman Ramdhani', - role: 'admin', -}; - -const DEMO_DRAFT: DemoDraft = { id: 101, type: 'offline_draft', content: 'Draft data saved offline' }; - -const LS_KEY = AppStorageKey.USER_PROFILE; // Encrypted at rest (in ENCRYPTED_KEYS) -const IDB_KEY = AppStorageKey.OFFLINE_DRAFT; // Plain key for IndexedDB demo - -// ─── Shared Styles ────────────────────────────────────────────── - -const btnStyle = (color: string) => ({ - padding: '8px 16px', - fontSize: 14, - fontWeight: 600 as const, - cursor: 'pointer' as const, - background: color, - color: '#fff', - border: 'none', - borderRadius: 6, -}); - -const preStyle = { - marginTop: 16, - background: '#1e1e2e', - color: '#a6e3a1', - padding: 16, - borderRadius: 8, - minHeight: 60, - overflow: 'auto' as const, - fontSize: 13, -}; - -const logContainerStyle = { - background: '#0f0f17', - color: '#94a3b8', - padding: 12, - borderRadius: 8, - maxHeight: 200, - overflow: 'auto' as const, - fontSize: 12, -}; - -// ─── Reusable CRUD Button Row ─────────────────────────────────── - -interface CRUDAction { - label: string; - handler: () => void; - color: string; -} - -function CRUDButtons({ actions }: { actions: CRUDAction[] }) { - return ( -
- {actions.map(({ label, handler, color }) => ( - - ))} -
- ); -} - -// ─── Component ────────────────────────────────────────────────── - -/** - * Interactive demo for `@repo/core-storage`. - * - * Demonstrates the full CRUD lifecycle for BOTH storage backends: - * - **localStorage** (encrypted via AES for sensitive keys) - * - **IndexedDB** (Promise-wrapped, suitable for large payloads) - * - * Open the browser's DevTools: - * - **Application → Local Storage** to see AES-encrypted payloads - * - **Application → IndexedDB → app_db → kv_store** to see IDB entries - */ -export default function StorageSample() { - const [lsResult, setLsResult] = useState('(no data read yet)'); - const [idbResult, setIdbResult] = useState('(no data read yet)'); - const [log, setLog] = useState([]); - - const pushLog = useCallback((msg: string) => { - setLog((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${msg}`]); - }, []); - - // ═══════════════════════════════════════════════════════════════ - // ── localStorage CRUD ───────────────────────────────────────── - // ═══════════════════════════════════════════════════════════════ - - const lsCreate = useCallback(async () => { - await secureStorage.setItem(LS_KEY, DEMO_USER); - pushLog(`[LS] CREATE → Stored encrypted: ${JSON.stringify(DEMO_USER)}`); - }, [pushLog]); - - const lsRead = useCallback(async () => { - const result = await secureStorage.getItem(LS_KEY); - if (result) { - setLsResult(JSON.stringify(result, null, 2)); - pushLog(`[LS] READ → Decrypted: ${JSON.stringify(result)}`); - } else { - setLsResult('(null — no data found)'); - pushLog('[LS] READ → null (key does not exist)'); - } - }, [pushLog]); - - const lsUpdate = useCallback(async () => { - const existing = await secureStorage.getItem(LS_KEY); - if (!existing) { - pushLog('[LS] UPDATE → Failed: key does not exist. Create first.'); - return; - } - const updated: DemoUser = { ...existing, role: 'superadmin', id: existing.id + 1 }; - await secureStorage.setItem(LS_KEY, updated); - pushLog(`[LS] UPDATE → Re-encrypted: ${JSON.stringify(updated)}`); - }, [pushLog]); - - const lsDelete = useCallback(async () => { - await secureStorage.removeItem(LS_KEY); - setLsResult('(deleted)'); - pushLog(`[LS] DELETE → Removed key "${LS_KEY}"`); - }, [pushLog]); - - const lsClear = useCallback(async () => { - await secureStorage.clear(); - setLsResult('(cleared)'); - pushLog('[LS] CLEAR → All localStorage keys removed'); - }, [pushLog]); - - // ═══════════════════════════════════════════════════════════════ - // ── IndexedDB CRUD ──────────────────────────────────────────── - // ═══════════════════════════════════════════════════════════════ - - const idbCreate = useCallback(async () => { - try { - await secureIndexedDB.setItem(IDB_KEY, DEMO_DRAFT); - pushLog(`[IDB] CREATE → Stored: ${JSON.stringify(DEMO_DRAFT)}`); - } catch (err) { - pushLog(`[IDB] CREATE → ERROR: ${err instanceof Error ? err.message : String(err)}`); - } - }, [pushLog]); - - const idbRead = useCallback(async () => { - try { - const result = await secureIndexedDB.getItem(IDB_KEY); - if (result) { - setIdbResult(JSON.stringify(result, null, 2)); - pushLog(`[IDB] READ → Retrieved: ${JSON.stringify(result)}`); - } else { - setIdbResult('(null — no data found)'); - pushLog('[IDB] READ → null (key does not exist)'); - } - } catch (err) { - pushLog(`[IDB] READ → ERROR: ${err instanceof Error ? err.message : String(err)}`); - } - }, [pushLog]); - - const idbUpdate = useCallback(async () => { - try { - const existing = await secureIndexedDB.getItem(IDB_KEY); - if (!existing) { - pushLog('[IDB] UPDATE → Failed: key does not exist. Create first.'); - return; - } - const updated: DemoDraft = { - ...existing, - id: existing.id + 1, - content: `Updated at ${new Date().toLocaleTimeString()}`, - }; - await secureIndexedDB.setItem(IDB_KEY, updated); - pushLog(`[IDB] UPDATE → Persisted: ${JSON.stringify(updated)}`); - } catch (err) { - pushLog(`[IDB] UPDATE → ERROR: ${err instanceof Error ? err.message : String(err)}`); - } - }, [pushLog]); - - const idbDelete = useCallback(async () => { - try { - await secureIndexedDB.removeItem(IDB_KEY); - setIdbResult('(deleted)'); - pushLog(`[IDB] DELETE → Removed key "${IDB_KEY}"`); - } catch (err) { - pushLog(`[IDB] DELETE → ERROR: ${err instanceof Error ? err.message : String(err)}`); - } - }, [pushLog]); - - const idbClear = useCallback(async () => { - try { - await secureIndexedDB.clear(); - setIdbResult('(cleared)'); - pushLog('[IDB] CLEAR → All IndexedDB entries removed'); - } catch (err) { - pushLog(`[IDB] CLEAR → ERROR: ${err instanceof Error ? err.message : String(err)}`); - } - }, [pushLog]); - - // ═══════════════════════════════════════════════════════════════ - // ── Render ──────────────────────────────────────────────────── - // ═══════════════════════════════════════════════════════════════ - - return ( -
-

🔐 @repo/core-storage — Dual Backend CRUD Demo

- -
- {/* ── Left: localStorage ─────────────────────────────────── */} -
-

📦 localStorage (AES Encrypted)

-

- Key: {LS_KEY} — stored encrypted at rest -
- Verify: DevTools → Application → Local Storage -

- - - -
{lsResult}
-
- - {/* ── Right: IndexedDB ───────────────────────────────────── */} -
-

🗃️ IndexedDB (app_db / kv_store)

-

- Key: {IDB_KEY} — plain JSON (not in ENCRYPTED_KEYS) -
- Verify: DevTools → Application → IndexedDB → app_db -

- - - -
{idbResult}
-
-
- - {/* ── Shared Action Log ────────────────────────────────────── */} -

📋 Action Log

-
- {log.length === 0 ? ( - (no actions yet) - ) : ( - log.map((entry, i) =>
{entry}
) - )} -
-
- ); -} diff --git a/apps/web/src/apps/showcase/pouch-sample.tsx b/apps/web/src/apps/showcase/pouch-sample.tsx deleted file mode 100644 index 07b4e1c..0000000 --- a/apps/web/src/apps/showcase/pouch-sample.tsx +++ /dev/null @@ -1,324 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import { Button, Card, Group, Stack, Title, Text, Table, Badge } from '@repo/ui/components'; - -import { itemDB, posConfigDB, newItemDB } from '../../core/storage/pouch-db'; -import type { ItemEntity, POSConfigurationEntity } from '../../core/storage/pouch-db/entities'; - -export default function PouchSample() { - const [configs, setConfigs] = useState([]); - const [items, setItems] = useState([]); - const [newItems, setNewItems] = useState([]); - - // Load initial data - const loadData = useCallback(async () => { - try { - const allConfigs = await posConfigDB.find({ selector: {} }); - setConfigs(allConfigs); - - const allItems = await itemDB.find({ selector: {} }); - setItems(allItems); - - // We can use getAll() or find() on Envelope DB. It's automatically scoped. - const allNewItems = await newItemDB.getAll(); - console.log('allNewItems', allNewItems); - setNewItems(allNewItems as ItemEntity[]); - - console.log({ allConfigs, allItems, allNewItems }); - } catch (err) { - console.error('Failed to load PouchDB data', err); - } - }, []); - - useEffect(() => { - // 1. Initial Data Load - loadData(); - - // 2. Setup Real-Time Listeners - const unsubscribeItems = itemDB.onChange(() => { - loadData(); - }); - - const unsubscribePos = posConfigDB.onChange(() => { - loadData(); - }); - - const unsubscribeNewItems = newItemDB.onChange(() => { - loadData(); - }); - - // 3. CRITICAL: Cleanup to prevent memory leaks on unmount - return () => { - unsubscribeItems(); - unsubscribePos(); - unsubscribeNewItems(); - }; - }, [loadData]); - - // ─── POS Configuration Handlers ───────────────────────────────── - - const handleSeedConfig = async () => { - try { - const id = `pos-${Date.now()}`; - await posConfigDB.create({ - _id: id, - pos_number: '1111111111666', - pos_name: 'Premium Test POS TESTING COUNCH', - items: items, // mapping current items - payment_methods: [{ id: 'cash', name: 'Cash' }], - }); - loadData(); - } catch (err) { - console.error('Failed to seed config', err); - } - }; - - const handleDeleteConfig = async (id: string) => { - try { - await posConfigDB.delete(id); - loadData(); - } catch (err) { - console.error('Failed to delete config', err); - } - }; - - // ─── Items Inventory Handlers ─────────────────────────────────── - - const handleAddItem = async () => { - try { - const id = `item-${Date.now()}`; - await itemDB.create({ - _id: id, - name: 'PLAYGROUND ALL DAY TESTING POUCH', - base_price: '75000', - item_type: 'wahana', - usage_type: 'ticket', - item_category: [{ name: 'Entertainment' }], - item_rates: [ - { season_period: 'weekday', price: 50000 }, - { season_period: 'weekend', price: 75000 }, - ], - }); - - loadData(); - } catch (err) { - console.error('Failed to add item', err); - } - }; - - const handleDeleteItem = async (id: string) => { - try { - await itemDB.delete(id); - loadData(); - } catch (err) { - console.error('Failed to delete item', err); - } - }; - - // ─── New Items Envelope Handlers ──────────────────────────────── - - const handleAddNewItem = async () => { - try { - const id = `new-item-${Date.now()}`; - await newItemDB.create({ - _id: id, - name: 'ENVELOPE ITEM TEST', - base_price: '100000', - item_type: 'souvenir', - usage_type: 'retail', - item_category: [{ name: 'Merchandise' }], - }); - - loadData(); - } catch (err) { - console.error('Failed to add new item', err); - } - }; - - const handleDeleteNewItem = async (id: string) => { - try { - await newItemDB.delete(id); - loadData(); - } catch (err) { - console.error('Failed to delete new item', err); - } - }; - - const handleClearAll = async () => { - try { - await posConfigDB.cleanAllData(); - await itemDB.cleanAllData(); - await newItemDB.cleanAllData(); - loadData(); - } catch (err) { - console.error('Failed to clear data', err); - } - }; - - return ( - - - Enterprise PouchDB Sync - - - - {/* Items Inventory Table */} - - - Items Database (Standard) - - - -
- - - - ID - Name - Type - Base Price - Rates Count - Actions - - - - {items.length > 0 ? ( - items.map((item) => ( - - {item._id} - {item.name} - - - {item.item_type} - - - ${Number(item.base_price).toFixed(2)} - {item.item_rates?.length || 0} - - - - - )) - ) : ( - - - No items found. - - - )} - -
-
-
- - {/* New Items Envelope Table */} - - - New Items Database (Envelope Pattern) - - - -
- - - - ID - Name - Type - Base Price - Rates Count - Actions - - - - {newItems.length > 0 ? ( - newItems.map((item) => ( - - {item._id} - {item.name} - - - {item.item_type} - - - ${Number(item.base_price).toFixed(2)} - {item.item_rates?.length || 0} - - - - - )) - ) : ( - - - No items found. - - - )} - -
-
-
- - {/* POS Configuration Table */} - - - POS Configurations - - - -
- - - - ID - POS Name - POS Number - Mapped Items - Actions - - - - {configs.length > 0 ? ( - configs.map((cfg) => ( - - {cfg._id} - {cfg.pos_name} - {cfg.pos_number} - - - {cfg.items?.length || 0} Items - - - - - - - )) - ) : ( - - - No configurations found. - - - )} - -
-
-
-
- ); -} diff --git a/apps/web/src/apps/showcase/printer-list.tsx b/apps/web/src/apps/showcase/printer-list.tsx deleted file mode 100644 index a03df5a..0000000 --- a/apps/web/src/apps/showcase/printer-list.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// 1. Import hooks yang baru saja dibuat Opus -import { Button } from '@repo/ui/components'; -import { useElectronPrinter } from '../../core/hooks/use-electron-printer'; -import { useElectronUpdater } from '../../core/hooks/use-electron-updater'; - -export default function App() { - // 2. Panggil hooks-nya - const { printers, refreshPrinters } = useElectronPrinter(); - const { status } = useElectronUpdater(); - - return ( -
-

🧪 Test Integrasi Electron

- -

- Status Auto-Update: {status} -

- - - -

🖨️ Daftar Printer di Komputer Ini:

-
    - {printers.length === 0 ? ( -
  • Mencari printer... (Atau tidak ada printer terdeteksi)
  • - ) : ( - printers.map((printer, index) => ( -
  • - {printer.name} {printer.isDefault ? '(Default)' : ''} -
  • - )) - )} -
-
- ); -} diff --git a/apps/web/src/apps/showcase/registry.ts b/apps/web/src/apps/showcase/registry.ts deleted file mode 100644 index c342c72..0000000 --- a/apps/web/src/apps/showcase/registry.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { lazy } from 'react'; -import { Layers, Table2 } from 'lucide-react'; - -export const COMPONENTS_REGISTRY = { - actionTools: { - id: 'action-tools', - name: 'Action Tools', - description: 'Showcase for PageActions and RowActions components', - icon: Layers, - component: lazy(() => import('./components/ActionToolsShowcase')), - }, - agGrid: { - id: 'ag-grid', - name: 'AG Grid', - description: 'Enterprise Data Grid with Mantine Theme Integration', - icon: Table2, - component: lazy(() => import('./components/AgGridShowcase')), - }, -}; - diff --git a/apps/web/src/apps/showcase/shell-demo/index.tsx b/apps/web/src/apps/showcase/shell-demo/index.tsx deleted file mode 100644 index b744f99..0000000 --- a/apps/web/src/apps/showcase/shell-demo/index.tsx +++ /dev/null @@ -1,346 +0,0 @@ -import { useState, useMemo, ReactNode } from 'react'; -import { Link } from 'react-router-dom'; -import { ArrowLeft } from 'lucide-react'; -import { Box, Button, Group, SegmentedControl, Stack, Text, Paper, Switch, Select, Burger } from '@repo/ui/components'; -import { - CoreAppShell, - CorePageContainer, - CoreAppShellConfig, - LayoutVariant, - DesktopCollapseVariant, - useCoreAppShell, -} from '@repo/ui/components'; -import { Home, BarChart2, Settings as SettingsIcon } from 'lucide-react'; - -// Sub-component to test hook methods -function LayoutControls() { - const { toggleDesktop, toggleMobile, sidebarVariant, setSidebarVariant, toggleAside, toggleNavbarPanel } = - useCoreAppShell(); - - return ( - - - - - - - - ); -} - -function MockHeader() { - const { mobileOpened, toggleMobile } = useCoreAppShell(); - return ( - - - - - Mock Header (bg="green.1") - - - - ); -} - -function MockMobileDrawer() { - return ( - - - Mock Mobile Drawer (bg="yellow.1") - - - - - - - ); -} - -function SettingRow({ title, description, control }: { title: string; description: string; control: ReactNode }) { - return ( - - - - {title} - - {description} - - - {control} - - - ); -} - -export default function ShellDemo() { - const [layoutVariant, setLayoutVariant] = useState('header-first'); - const [collapseVariant, setCollapseVariant] = useState('hide'); - const [withUtilityBar, setWithUtilityBar] = useState(true); - const [withAside, setWithAside] = useState(true); - const [withFooter, setWithFooter] = useState(true); - const [withDoubleSidebar, setWithDoubleSidebar] = useState(true); - const [copied, setCopied] = useState(false); - - const liveConfig = useMemo(() => { - return { - variant: layoutVariant, - features: { - desktopCollapseVariant: collapseVariant, - withUtilityBar: withUtilityBar ? undefined : false, - withAside: withAside ? undefined : false, - withFooter: withFooter ? undefined : false, - withDoubleSidebar, - }, - }; - }, [layoutVariant, collapseVariant, withUtilityBar, withAside, withFooter, withDoubleSidebar]); - - const handleCopy = () => { - navigator.clipboard.writeText(JSON.stringify(liveConfig, null, 2)); - setCopied(true); - setTimeout(() => setCopied(false), 2500); - }; - - const config: CoreAppShellConfig = { - variant: layoutVariant, - features: { - desktopCollapseVariant: collapseVariant, - withUtilityBar: withUtilityBar ? undefined : false, - withAside: withAside ? undefined : false, - withFooter: withFooter ? undefined : false, - withDoubleSidebar, - persistState: false, // Don't persist for the demo to avoid confusing other showcases - disabled: false, // Allow nested render overrides if needed - zIndex: 100, - }, - }; - - return ( - - - Mock Utility Bar (bg="blue.1") - -
- ), - header: , - sidebarMobile: , - sidebar: !withDoubleSidebar ? ( - - - Mock Standard Navbar (bg="grape.1") - - - - - - - - - ) : undefined, - sidebarRail: withDoubleSidebar ? ( - - - Mock Rail (bg="orange.1") - - - - - - ) : undefined, - sidebarPanel: withDoubleSidebar ? ( - - - Mock Panel (bg="grape.1") - - - - - - - - - ) : undefined, - aside: ( - - - Mock Aside - - This area could be used for notifications, help text, or contextual settings. - - ), - footer: ( - - - Mock Footer (bg="gray.1") - - - ), - }} - > - - - Layout Engine Interactive Demo - - - - - - } - stickyHeader - > - - - - - - setLayoutVariant((value as LayoutVariant) || 'sidebar-first')} - data={[ - { label: 'Sidebar First (Alt)', value: 'sidebar-first' }, - { label: 'Header First (Default)', value: 'header-first' }, - { label: 'Top Nav (Hidden Sidebar)', value: 'top-nav' }, - ]} - /> - } - /> - - setCollapseVariant(value as DesktopCollapseVariant)} - data={[ - { label: 'Hide (Slide Out)', value: 'hide' }, - { label: 'Mini (Shrink)', value: 'mini' }, - ]} - /> - } - /> - - setWithDoubleSidebar(event.currentTarget.checked)} - /> - } - /> - - setWithUtilityBar(event.currentTarget.checked)} /> - } - /> - - setWithAside(event.currentTarget.checked)} />} - /> - - setWithFooter(event.currentTarget.checked)} />} - /> - - - - - Configuration Preview - - - - -
{JSON.stringify(liveConfig, null, 2)}
-
-
-
-
- - ); -} diff --git a/apps/web/src/apps/showcase/showcase-view.tsx b/apps/web/src/apps/showcase/showcase-view.tsx deleted file mode 100644 index d8cfe3c..0000000 --- a/apps/web/src/apps/showcase/showcase-view.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import { useState, Suspense } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { ColorSchemeType, DensityType } from '@repo/ui/provider'; -import { useThemeStore } from '../../core/stores/theme.store'; -import { - Button, - Card, - Container, - Select, - Group, - Stack, - Text, - Title, - TextInput, - Checkbox, - PasswordInput, - NumberInput, - Textarea, - Switch, - Table, - Badge, - Divider, - Tabs, - Box, - Paper, - StatusBadge, - STATUS_DATA, -} from '@repo/ui/components'; -import { ShieldCheck, Database, Lock, Layout, Activity, Printer, FileText, LayoutDashboard } from 'lucide-react'; -import { Globe } from 'lucide-react'; -import { useTranslation } from '@repo/core-i18n'; -import PrinterList from './printer-list'; -import ExamplePage from './example/example.page'; -import EventsDemoPage from './events-demo'; -import PouchSample from './pouch-sample'; -import FormDemoView from './example/features/form-demo'; -import { COMPONENTS_REGISTRY } from './registry'; - -interface ShowcaseViewProps { - density: DensityType; - setDensity: (val: DensityType) => void; -} - -export default function ShowcaseView({ density, setDensity }: ShowcaseViewProps) { - const { colorScheme, setColorScheme } = useThemeStore(); - const [activeTab, setActiveTab] = useState('ui-components'); - const { i18n } = useTranslation(); - const navigate = useNavigate(); - - // Mock data for the table - const tableData = [ - { id: 'ORD-001', customer: 'John Doe', status: 'Shipped', total: '$120.00' }, - { id: 'ORD-002', customer: 'Jane Smith', status: 'Pending', total: '$85.50' }, - { id: 'ORD-003', customer: 'Acme Corp', status: 'Delivered', total: '$1,250.00' }, - ]; - - const getSubtitle = () => { - const registryItem = Object.values(COMPONENTS_REGISTRY).find((item) => item.id === activeTab); - if (registryItem) { - return registryItem.description; - } - - switch (activeTab) { - 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'; - default: - return 'Architecture Showcase'; - } - }; - - return ( - - { - if (val === 'layout-engine') { - navigate('/shell-demo'); - } else if (val === 'apps') { - navigate('/app'); - } else { - setActiveTab(val); - } - }} - variant="pills" - radius="md" - className="h-screen" - styles={{ - root: { display: 'flex', height: '100vh', overflow: 'hidden' }, - list: { - minWidth: 260, - padding: '1rem', - borderRight: '1px solid var(--app-shell-border-color)', - backgroundColor: 'var(--mantine-color-default-element-bg)', - }, - panel: { flex: 1, display: 'flex', flexDirection: 'column', height: '100vh', overflow: 'hidden' }, - tab: { justifyContent: 'flex-start' }, // Ensure the entire tab content aligns left - tabLabel: { textAlign: 'left', flex: 1 }, // Ensure the text pushes to fill and aligns left - }} - > - - - - Eigen ERP - - - Architecture Showcase - - - - }> - UI Components - - }> - Layout Engine - - }> - Form Engine - - }> - Offline Storage - - }> - RBAC Engine - - }> - Auth & Security - - }> - Events - - }> - Hardware - - }> - Apps - - - {Object.values(COMPONENTS_REGISTRY).map((registryItem) => { - const Icon = registryItem.icon; - return ( - }> - {registryItem.name} - - ); - })} - - - - {/* Header */} - - - - Architecture Showcase - - {getSubtitle()} - - - setColorScheme((val as ColorSchemeType) || 'light')} - data={[ - { value: 'light', label: 'Light' }, - { value: 'dark', label: 'Dark' }, - ]} - /> -