From 980952252dc4cdf2ec9a2f642cf7a7e953c99a09 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:26:41 +0700 Subject: [PATCH] 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. --- .devcontainer/devcontainer.json | 7 +- apps/showcase/.eslintrc.cjs | 5 + apps/showcase/index.html | 13 + apps/showcase/package.json | 48 +++ apps/showcase/src/App.tsx | 49 +++ apps/showcase/src/core/constants/events.ts | 21 + .../src/core/hooks/use-electron-printer.ts | 110 +++++ .../src/core/hooks/use-electron-updater.ts | 140 ++++++ .../src/core/hooks/use-is-electron.ts | 7 + apps/showcase/src/core/lib/api-client.ts | 43 ++ apps/showcase/src/core/storage/local/index.ts | 44 ++ .../core/storage/pouch-db/entities/index.ts | 3 + .../pouch-db/entities/item.pouchdb.entity.ts | 27 ++ .../entities/new-item.pouchdb.entity.ts | 51 +++ .../pos-configuration.pouchdb.entity.ts | 16 + .../src/core/storage/pouch-db/index.ts | 45 ++ apps/showcase/src/core/stores/theme.store.ts | 30 ++ apps/showcase/src/layouts/ShowcaseLayout.tsx | 131 ++++++ apps/showcase/src/main.css | 3 + apps/showcase/src/main.tsx | 17 + .../showcase/src/pages/action-tools/index.tsx | 161 +++++++ apps/showcase/src/pages/ag-grid/index.tsx | 218 ++++++++++ apps/showcase/src/pages/auth/index.tsx | 14 + .../auth-sync/profile-settings.ui.tsx | 64 +++ .../auth-sync/storage-sync.listener.tsx | 36 ++ .../events/components/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 ++++ .../components/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 ++++ .../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/showcase/src/pages/events/index.tsx | 20 + .../form-demo/components/all-fields-demo.tsx | 408 ++++++++++++++++++ .../components/reactive-watch-demo.tsx | 368 ++++++++++++++++ .../components/validation-bank-demo.tsx | 254 +++++++++++ .../components/form-demo/form-demo-view.tsx | 31 ++ .../forms/components/form-demo/i18n/en.json | 117 +++++ .../forms/components/form-demo/i18n/id.json | 117 +++++ .../form-demo/i18n/useFormDemoTranslation.ts | 10 + .../pages/forms/components/form-demo/index.ts | 1 + apps/showcase/src/pages/forms/index.tsx | 12 + .../hardware/components/printer-list.tsx | 37 ++ apps/showcase/src/pages/hardware/index.tsx | 14 + apps/showcase/src/pages/rbac/index.tsx | 14 + apps/showcase/src/pages/shell-demo/index.tsx | 346 +++++++++++++++ .../pages/storage/components/pouch-sample.tsx | 324 ++++++++++++++ apps/showcase/src/pages/storage/index.tsx | 12 + .../src/pages/ui-components/index.tsx | 124 ++++++ apps/showcase/src/types/electron.d.ts | 98 +++++ apps/showcase/src/types/events.d.ts | 80 ++++ apps/showcase/src/vite-env.d.ts | 1 + apps/showcase/tsconfig.json | 7 + apps/showcase/vite.config.ts | 19 + package.json | 3 + pnpm-lock.yaml | 106 ++++- 65 files changed, 5749 insertions(+), 9 deletions(-) create mode 100644 apps/showcase/.eslintrc.cjs create mode 100644 apps/showcase/index.html create mode 100644 apps/showcase/package.json create mode 100644 apps/showcase/src/App.tsx create mode 100644 apps/showcase/src/core/constants/events.ts create mode 100644 apps/showcase/src/core/hooks/use-electron-printer.ts create mode 100644 apps/showcase/src/core/hooks/use-electron-updater.ts create mode 100644 apps/showcase/src/core/hooks/use-is-electron.ts create mode 100644 apps/showcase/src/core/lib/api-client.ts create mode 100644 apps/showcase/src/core/storage/local/index.ts create mode 100644 apps/showcase/src/core/storage/pouch-db/entities/index.ts create mode 100644 apps/showcase/src/core/storage/pouch-db/entities/item.pouchdb.entity.ts create mode 100644 apps/showcase/src/core/storage/pouch-db/entities/new-item.pouchdb.entity.ts create mode 100644 apps/showcase/src/core/storage/pouch-db/entities/pos-configuration.pouchdb.entity.ts create mode 100644 apps/showcase/src/core/storage/pouch-db/index.ts create mode 100644 apps/showcase/src/core/stores/theme.store.ts create mode 100644 apps/showcase/src/layouts/ShowcaseLayout.tsx create mode 100644 apps/showcase/src/main.css create mode 100644 apps/showcase/src/main.tsx create mode 100644 apps/showcase/src/pages/action-tools/index.tsx create mode 100644 apps/showcase/src/pages/ag-grid/index.tsx create mode 100644 apps/showcase/src/pages/auth/index.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/auth-sync/profile-settings.ui.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/auth-sync/storage-sync.listener.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/index.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/printer/cashier.ui.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/printer/printer.listener.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/stock-grid/live-stock-grid.ui.tsx create mode 100644 apps/showcase/src/pages/events/components/events-demo/stock-grid/mock-websocket.service.ts create mode 100644 apps/showcase/src/pages/events/components/events-demo/stock-grid/stock-row.ui.tsx create mode 100644 apps/showcase/src/pages/events/components/example/example.page.tsx create mode 100644 apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.data-services.ts create mode 100644 apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.transformer.ts create mode 100644 apps/showcase/src/pages/events/components/example/features/booking/data/booking.data-services.ts create mode 100644 apps/showcase/src/pages/events/components/example/features/booking/data/booking.transformer.ts create mode 100644 apps/showcase/src/pages/events/components/example/features/booking/presentation/BookingSample.tsx create mode 100644 apps/showcase/src/pages/events/components/example/features/i18n/languages/en/booking.json create mode 100644 apps/showcase/src/pages/events/components/example/features/i18n/languages/id/booking.json create mode 100644 apps/showcase/src/pages/events/components/example/features/i18n/presentation/I18nSample.tsx create mode 100644 apps/showcase/src/pages/events/components/example/features/storage/presentation/StorageSample.tsx create mode 100644 apps/showcase/src/pages/events/index.tsx create mode 100644 apps/showcase/src/pages/forms/components/form-demo/components/all-fields-demo.tsx create mode 100644 apps/showcase/src/pages/forms/components/form-demo/components/reactive-watch-demo.tsx create mode 100644 apps/showcase/src/pages/forms/components/form-demo/components/validation-bank-demo.tsx create mode 100644 apps/showcase/src/pages/forms/components/form-demo/form-demo-view.tsx create mode 100644 apps/showcase/src/pages/forms/components/form-demo/i18n/en.json create mode 100644 apps/showcase/src/pages/forms/components/form-demo/i18n/id.json create mode 100644 apps/showcase/src/pages/forms/components/form-demo/i18n/useFormDemoTranslation.ts create mode 100644 apps/showcase/src/pages/forms/components/form-demo/index.ts create mode 100644 apps/showcase/src/pages/forms/index.tsx create mode 100644 apps/showcase/src/pages/hardware/components/printer-list.tsx create mode 100644 apps/showcase/src/pages/hardware/index.tsx create mode 100644 apps/showcase/src/pages/rbac/index.tsx create mode 100644 apps/showcase/src/pages/shell-demo/index.tsx create mode 100644 apps/showcase/src/pages/storage/components/pouch-sample.tsx create mode 100644 apps/showcase/src/pages/storage/index.tsx create mode 100644 apps/showcase/src/pages/ui-components/index.tsx create mode 100644 apps/showcase/src/types/electron.d.ts create mode 100644 apps/showcase/src/types/events.d.ts create mode 100644 apps/showcase/src/vite-env.d.ts create mode 100644 apps/showcase/tsconfig.json create mode 100644 apps/showcase/vite.config.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f759ae2..5d1677a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,7 +18,8 @@ // 4173: VitePress Docs (Preview) // 5173: Web App (Must be strictly 5173 for Electron IPC compatibility) // 3000: Landing App (Isolated from Vite's default 517x blast radius) - "forwardPorts": [6060, 4173, 5173, 3000], + // 3005: Showcase App + "forwardPorts": [6060, 4173, 5173, 3000, 3005], "portsAttributes": { "6060": { @@ -33,6 +34,10 @@ "label": "Web App (Electron Target)", "onAutoForward": "notify" }, + "3005": { + "label": "Showcase App", + "onAutoForward": "notify" + }, "3000": { "label": "Landing App (Public)", "onAutoForward": "notify" diff --git a/apps/showcase/.eslintrc.cjs b/apps/showcase/.eslintrc.cjs new file mode 100644 index 0000000..a779c57 --- /dev/null +++ b/apps/showcase/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@repo/eslint-config/react.js'], +}; diff --git a/apps/showcase/index.html b/apps/showcase/index.html new file mode 100644 index 0000000..e09f575 --- /dev/null +++ b/apps/showcase/index.html @@ -0,0 +1,13 @@ + + +
+ + + +@repo/core-events — zero coupling, strict typing, high performance.
+ {DEVICE_EVENTS.PRINT_RECEIPT} event.
+ The PrinterListener listens for it and simulates interacting with a physical printer.
+ WS:STOCK_UPDATE every 20ms.
+ Each StockRow subscribes to the global event but only updates when{' '}
+ payload.id === row.id. The parent grid never re-renders.
+ @repo/core-storage
+ {AUTH_EVENTS.PROFILE_UPDATED}.
+ StorageSyncListener silently catches it in the background and saves to IndexedDB via secureIndexedDB.
+ | + Ticker + | ++ Price + | ++ Change + | ++ Volume + | ++ Renders + | +
|---|
+ Pipeline: Faro + Custom Span "booking.list.fetch" → Bearer Token → GET /bookings +
+ + + + {error && ( +
+ ❌ {error}
+
+ )}
+
+ {result && (
+
+ {JSON.stringify(result, null, 2)}
+
+ )}
+ + Current Active Language: {activeLang} +
+ + {/* ─── Admin Panel ──────────────────────────────────────────── */} ++ Simulate a backend CMS. Save the vocabulary overrides to IndexedDB. +
+ +
+ {dbPayloadStr}
+
+ + Change the language. The callback simulates a 1-second backend API request. +
+ +
+ common:save
+ {t('common:save')}
+
+ booking:select_date
+ {t('booking:select_date')}
+
+ Simulates a user logging in. It fetches the config directly from IndexedDB (mock database) and applies the + deep-merge override. +
+ +| Key | +Value | +
|---|---|
+ booking:module_name
+ |
+ {t('booking:module_name')} | +
+ booking:header.title
+ |
+ {t('booking:header.title')} | +
+ booking:header.subtitle
+ |
+ {t('booking:header.subtitle')} | +
+ Key: {LS_KEY} — stored encrypted at rest
+
+ Verify: DevTools → Application → Local Storage
+
{lsResult}
+
+ Key: {IDB_KEY} — plain JSON (not in ENCRYPTED_KEYS)
+
+ Verify: DevTools → Application → IndexedDB → app_db
+
{idbResult}
+ This is a highly important update. Please observe the following:
Thank you for your cooperation.
", + realPokeSelect: null, + multiRealPokeSelect: [] + } + }); + + const onSubmit = (data: any) => console.log('All Fields Submitted:', data); + const data = watch(); + + return ( +{JSON.stringify(data, null, 2)}
+ Start typing to see the live preview...
", + }, + }); + + // Micro-subscriptions via useWatch + const userType = useWatch({ control, name: 'userType' }); + const hasSpouse = useWatch({ control, name: 'hasSpouse' }); + const newsletter = useWatch({ control, name: 'newsletter' }); + const department = useWatch({ control, name: 'department' }); + const role = useWatch({ control, name: 'role' }); + const regions = useWatch({ control, name: 'regions' }); + const watchedRichTextLive = useWatch({ control, name: 'richTextLive' }); + + // Use the custom hook to cleanly unregister and reset fields when hidden + useConditionalField({ + condition: userType === 'CORPORATE', + name: 'corporateTaxId', + setValue, + unregister, + mode: 'unregister', + defaultValue: '', + }); + + useConditionalField({ + condition: hasSpouse === true, + name: 'spouseName', + setValue, + unregister, + mode: 'unregister', + defaultValue: '', + }); + + // Use the reset mode to clear values and errors without unregistering the field + useConditionalField({ + condition: newsletter === true, + name: 'newsletterEmail', + setValue, + clearErrors, + mode: 'reset', + defaultValue: '', + }); + + // Cascading Dropdown Logic: Department -> Role + const roleOptions: Record{JSON.stringify(allValues, null, 2)}
+ {JSON.stringify(data, null, 2)}
+ + Status Auto-Update: {status} +
+ + + +{JSON.stringify(liveConfig, null, 2)}
+