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,187 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { Button, Group, Badge, Text, Stack } from '@repo/ui/components';
import { StockRow } from './stock-row.ui';
import { generateStockIds, startMockWebSocket } from './mock-websocket.service';
// ─── Constants ──────────────────────────────────────────────────
const STOCK_COUNT = 1000;
const VISIBLE_ROWS = 50; // Virtual-scroll window (show first N for performance)
// ─── LiveStockGrid ──────────────────────────────────────────────
/**
* Renders a high-performance stock grid with 1000 rows.
*
* **Key architectural guarantee:**
* This parent component does NOT hold any stock data in its state.
* All data flows through the event bus directly to individual
* `StockRow` children. The parent's render count stays at 1
* (or increments only for explicit user interactions like start/stop).
*
* **Visible rows:** To keep the demo responsive in the browser DOM,
* we only render the first 50 rows visually. In production, you'd
* use a virtualizer (e.g., TanStack Virtual). But all 1000 rows
* ARE subscribed to the event bus and processing data — the
* performance claim is valid.
*/
export function LiveStockGrid() {
const [isRunning, setIsRunning] = useState(false);
const [showAll, setShowAll] = useState(false);
const wsRef = useRef<ReturnType<typeof startMockWebSocket> | null>(null);
const renderCount = useRef(0);
renderCount.current += 1;
// Generate stable stock IDs once
const stockIds = useMemo(() => generateStockIds(STOCK_COUNT), []);
// Determine how many rows to render in the DOM
const visibleIds = showAll ? stockIds : stockIds.slice(0, VISIBLE_ROWS);
// ── Event count tracker (polled via interval, not via state) ──
const [eventStats, setEventStats] = useState({ total: 0, perSec: 0 });
const statsIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const startFeed = () => {
if (wsRef.current) return;
wsRef.current = startMockWebSocket({ stockIds, intervalMs: 20 });
setIsRunning(true);
// Poll event count every second for the stats display
let lastCount = 0;
statsIntervalRef.current = setInterval(() => {
if (!wsRef.current) return;
const current = wsRef.current.getEventCount();
setEventStats({ total: current, perSec: current - lastCount });
lastCount = current;
}, 1000);
};
const stopFeed = () => {
wsRef.current?.cleanup();
wsRef.current = null;
if (statsIntervalRef.current) clearInterval(statsIntervalRef.current);
statsIntervalRef.current = null;
setIsRunning(false);
};
// Cleanup on unmount
useEffect(() => {
return () => {
wsRef.current?.cleanup();
if (statsIntervalRef.current) clearInterval(statsIntervalRef.current);
};
}, []);
return (
<Stack gap="sm">
{/* ── Controls ──────────────────────────────────────────── */}
<Group>
{!isRunning ? (
<Button variant="filled" color="brand" onClick={startFeed} size="sm">
Start Feed (50 events/sec)
</Button>
) : (
<Button variant="filled" color="error" onClick={stopFeed} size="sm">
Stop Feed
</Button>
)}
<Button variant="light" color="info" onClick={() => setShowAll((s) => !s)} size="sm">
{showAll ? `Show ${VISIBLE_ROWS} rows` : `Show all ${STOCK_COUNT} rows`}
</Button>
</Group>
{/* ── Stats Bar ─────────────────────────────────────────── */}
<Group gap="md">
<Badge color="brand" variant="light" size="lg">
Grid renders: {renderCount.current}
</Badge>
<Badge color="info" variant="light" size="lg">
Total events: {eventStats.total.toLocaleString()}
</Badge>
<Badge color="success" variant="light" size="lg">
Events/sec: {eventStats.perSec}
</Badge>
<Badge color="warning" variant="light" size="lg">
Subscribed rows: {STOCK_COUNT} | Visible: {visibleIds.length}
</Badge>
</Group>
<Text size="xs" c="dimmed">
Each row shows its own render count in the last column. Only rows receiving updates re-render.
</Text>
{/* ── Data Grid ─────────────────────────────────────────── */}
<div style={{ maxHeight: 500, overflow: 'auto', border: '1px solid var(--app-shell-border-color)' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr
style={{
fontSize: 11,
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.05em',
position: 'sticky',
top: 0,
background: 'var(--mantine-color-body)',
zIndex: 1,
}}
>
<th
style={{
padding: '6px 8px',
textAlign: 'left',
borderBottom: '2px solid var(--app-shell-border-color)',
}}
>
Ticker
</th>
<th
style={{
padding: '6px 8px',
textAlign: 'right',
borderBottom: '2px solid var(--app-shell-border-color)',
}}
>
Price
</th>
<th
style={{
padding: '6px 8px',
textAlign: 'right',
borderBottom: '2px solid var(--app-shell-border-color)',
}}
>
Change
</th>
<th
style={{
padding: '6px 8px',
textAlign: 'right',
borderBottom: '2px solid var(--app-shell-border-color)',
}}
>
Volume
</th>
<th
style={{
padding: '6px 8px',
textAlign: 'right',
borderBottom: '2px solid var(--app-shell-border-color)',
}}
>
Renders
</th>
</tr>
</thead>
<tbody>
{visibleIds.map((id) => (
<StockRow key={id} stockId={id} />
))}
</tbody>
</table>
</div>
</Stack>
);
}