- Standardized import statements and removed unnecessary line breaks for better readability in various components. - Enhanced error handling and logging in the useElectronPrinter hook. - Updated sample data formatting in AgGridShowcase for improved clarity. - Refactored JSX elements for consistent indentation and structure in LandingSample, AuthPage, and EventsPage components. - Consolidated and simplified conditional rendering logic in several components. These changes aim to enhance code maintainability and readability throughout the project.
160 lines
6.8 KiB
TypeScript
160 lines
6.8 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
import { Card, Title, Text, Stack, Badge, Divider } from '@repo/ui/components';
|
|
|
|
// ── Showcase Components ──────────────────────────────────────────
|
|
import { CashierUI } from './printer/cashier.ui';
|
|
import { PrinterListener } from './printer/printer.listener';
|
|
import { LiveStockGrid } from './stock-grid/live-stock-grid.ui';
|
|
import { ProfileSettingsUI } from './auth-sync/profile-settings.ui';
|
|
import { StorageSyncListener } from './auth-sync/storage-sync.listener';
|
|
import { DEVICE_EVENTS, AUTH_EVENTS } from '../../../../core/constants/events';
|
|
|
|
// ─── Events Demo Page ───────────────────────────────────────────
|
|
|
|
/**
|
|
* Orchestrator page for all three Event Bus showcase demos.
|
|
*
|
|
* This component is completely self-contained within the
|
|
* `events-demo/` folder and does not leak state or side-effects
|
|
* into the rest of the application.
|
|
*/
|
|
export default function EventsDemoPage() {
|
|
// ── Showcase 1: Printer status feedback ──────────────────────
|
|
const [printerLog, setPrinterLog] = useState<string[]>([]);
|
|
|
|
// ── Showcase 3: Storage sync status feedback ─────────────────
|
|
const [syncLog, setSyncLog] = useState<string[]>([]);
|
|
|
|
// ── Render counter to prove this parent is stable ────────────
|
|
const renderCount = useRef(0);
|
|
renderCount.current += 1;
|
|
|
|
// Keep log sizes bounded
|
|
useEffect(() => {
|
|
if (printerLog.length > 20) setPrinterLog((prev) => prev.slice(-20));
|
|
}, [printerLog.length]);
|
|
|
|
useEffect(() => {
|
|
if (syncLog.length > 20) setSyncLog((prev) => prev.slice(-20));
|
|
}, [syncLog.length]);
|
|
|
|
return (
|
|
<Stack gap="xl">
|
|
<div>
|
|
<Title order={2}>🔌 Event Bus Showcase</Title>
|
|
<Text size="sm" c="dimmed">
|
|
Three real-world demos of <code>@repo/core-events</code> — zero coupling, strict typing, high performance.
|
|
</Text>
|
|
<Badge color="brand" variant="light" mt="xs">
|
|
Parent render count: {renderCount.current}
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* ═══════════════════════════════════════════════════════════
|
|
SHOWCASE 1: Cross-Platform Printer Abstraction
|
|
═══════════════════════════════════════════════════════════ */}
|
|
<Card withBorder shadow="sm" radius="md" p="md">
|
|
<Title order={4} mb="xs">
|
|
🖨️ Showcase 1: Cross-Platform Printer Abstraction
|
|
</Title>
|
|
<Text size="sm" c="dimmed" mb="md">
|
|
The CashierUI publishes a <code>{DEVICE_EVENTS.PRINT_RECEIPT}</code> event. The PrinterListener listens for it
|
|
and simulates interacting with a physical printer.
|
|
</Text>
|
|
|
|
{/* Headless listener — renders nothing visible */}
|
|
<PrinterListener
|
|
onLog={(msg) => setPrinterLog((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${msg}`])}
|
|
/>
|
|
|
|
<CashierUI />
|
|
|
|
{printerLog.length > 0 && (
|
|
<>
|
|
<Divider my="sm" />
|
|
<Text size="sm" fw={600}>
|
|
📋 Printer Log:
|
|
</Text>
|
|
<div
|
|
style={{
|
|
maxHeight: 120,
|
|
overflow: 'auto',
|
|
fontSize: 12,
|
|
fontFamily: 'monospace',
|
|
background: 'var(--mantine-color-dark-7, #1a1b1e)',
|
|
color: 'var(--mantine-color-green-4, #69db7c)',
|
|
padding: 8,
|
|
borderRadius: 6,
|
|
}}
|
|
>
|
|
{printerLog.map((line, i) => (
|
|
<div key={i}>{line}</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</Card>
|
|
|
|
{/* ═══════════════════════════════════════════════════════════
|
|
SHOWCASE 2: Extreme Performance — Live Stock Grid
|
|
═══════════════════════════════════════════════════════════ */}
|
|
<Card withBorder shadow="sm" radius="md" p="md">
|
|
<Title order={4} mb="xs">
|
|
📈 Showcase 2: High-Frequency Real-Time Data (50 updates/sec)
|
|
</Title>
|
|
<Text size="sm" c="dimmed" mb="md">
|
|
A mock WebSocket fires <code>WS:STOCK_UPDATE</code> every 20ms. Each StockRow subscribes to the global event
|
|
but only updates when <code>payload.id === row.id</code>. The parent grid never re-renders.
|
|
</Text>
|
|
|
|
<LiveStockGrid />
|
|
</Card>
|
|
|
|
{/* ═══════════════════════════════════════════════════════════
|
|
SHOWCASE 3: Auth/Profile → IndexedDB Sync
|
|
═══════════════════════════════════════════════════════════ */}
|
|
<Card withBorder shadow="sm" radius="md" p="md">
|
|
<Title order={4} mb="xs">
|
|
💾 Showcase 3: Auth/Profile Sync with <code>@repo/core-storage</code>
|
|
</Title>
|
|
<Text size="sm" c="dimmed" mb="md">
|
|
ProfileSettingsUI publishes <code>{AUTH_EVENTS.PROFILE_UPDATED}</code>. StorageSyncListener silently catches
|
|
it in the background and saves to IndexedDB via <code>secureIndexedDB</code>.
|
|
</Text>
|
|
|
|
{/* Headless listener — renders nothing visible */}
|
|
<StorageSyncListener
|
|
onLog={(msg) => setSyncLog((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${msg}`])}
|
|
/>
|
|
|
|
<ProfileSettingsUI />
|
|
|
|
{syncLog.length > 0 && (
|
|
<>
|
|
<Divider my="sm" />
|
|
<Text size="sm" fw={600}>
|
|
📋 Storage Sync Log:
|
|
</Text>
|
|
<div
|
|
style={{
|
|
maxHeight: 120,
|
|
overflow: 'auto',
|
|
fontSize: 12,
|
|
fontFamily: 'monospace',
|
|
background: 'var(--mantine-color-dark-7, #1a1b1e)',
|
|
color: 'var(--mantine-color-blue-4, #4dabf7)',
|
|
padding: 8,
|
|
borderRadius: 6,
|
|
}}
|
|
>
|
|
{syncLog.map((line, i) => (
|
|
<div key={i}>{line}</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</Card>
|
|
</Stack>
|
|
);
|
|
}
|