From 67ae5b6c11282f7ad4a0faaeaac9964164f6b4e2 Mon Sep 17 00:00:00 2001 From: shancheas Date: Tue, 25 Aug 2026 17:50:48 +0700 Subject: [PATCH] refactor: improve code formatting and consistency across multiple files - 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. --- apps/desktop/scripts/copy-web-dist.ts | 3 +- apps/desktop/src/main/index.ts | 17 +- apps/docs-dev/src/.vitepress/theme/index.ts | 6 +- .../presentation/LandingSample.tsx | 19 +- apps/showcase/src/App.tsx | 42 +++-- .../src/core/hooks/use-electron-printer.ts | 41 ++-- .../src/core/hooks/use-electron-updater.ts | 9 +- apps/showcase/src/layouts/ShowcaseLayout.tsx | 54 ++++-- apps/showcase/src/pages/ag-grid/index.tsx | 177 ++++++++++++++++-- apps/showcase/src/pages/auth/index.tsx | 4 +- .../events/components/events-demo/index.tsx | 22 +-- .../events-demo/printer/cashier.ui.tsx | 10 +- .../events-demo/printer/printer.listener.tsx | 8 +- .../stock-grid/mock-websocket.service.ts | 23 ++- .../events-demo/stock-grid/stock-row.ui.tsx | 18 +- .../data/advanced-booking.transformer.ts | 4 +- .../booking/data/booking.data-services.ts | 1 - .../booking/presentation/BookingSample.tsx | 21 ++- apps/showcase/src/pages/events/index.tsx | 4 +- .../form-demo/components/all-fields-demo.tsx | 168 ++++++++++------- .../components/reactive-watch-demo.tsx | 145 ++++++++------ apps/showcase/src/pages/rbac/index.tsx | 4 +- .../src/pages/ui-components/index.tsx | 83 ++++++-- apps/showcase/src/types/events.d.ts | 1 - apps/web/.env.example | 2 +- .../auth/data/auth.remote.service.test.ts | 81 ++++++++ .../src/apps/auth/data/auth.remote.service.ts | 44 +++++ .../apps/auth/domain/entities/auth.entity.ts | 7 + .../src/apps/auth/domain/factories/index.ts | 4 + .../domain/validators/login.validator.test.ts | 47 +++++ .../auth/domain/validators/login.validator.ts | 14 ++ .../auth/login/get-login-error-key.test.ts | 17 ++ .../apps/auth/login/get-login-error-key.ts | 10 + apps/web/src/apps/auth/login/index.tsx | 27 +-- .../apps/auth/login/languages/en/login.json | 5 +- .../apps/auth/login/languages/id/login.json | 5 +- .../auth/login/validators/login.validator.ts | 9 - .../main/layouts/components/header.layout.tsx | 24 ++- .../pages/full-page.page.detail.tsx | 3 +- .../components/notification-setting.tsx | 19 +- apps/web/src/core/constants/api-url.ts | 8 +- .../src/core/hooks/use-electron-printer.ts | 41 ++-- .../src/core/hooks/use-electron-updater.ts | 9 +- apps/web/src/core/lib/api-client.ts | 22 ++- apps/web/src/core/lib/auth-guard.tsx | 61 +++--- apps/web/src/core/lib/auth.helper.test.ts | 147 +++++++++++++++ apps/web/src/core/lib/auth.helper.ts | 144 ++++++++------ apps/web/src/core/lib/auth.types.ts | 31 +++ .../lib/enterprise-storage-adapter.test.ts | 58 ++++++ .../core/lib/enterprise-storage-adapter.ts | 13 +- .../web/src/core/lib/get-http-error-status.ts | 8 + .../src/core/lib/handle-unauthorized.test.ts | 73 ++++++++ apps/web/src/core/lib/handle-unauthorized.ts | 36 ++++ .../src/core/lib/map-user-privileges.test.ts | 55 ++++++ apps/web/src/core/lib/map-user-privileges.ts | 25 +++ apps/web/src/types/events.d.ts | 1 - apps/web/vite.config.ts | 6 +- .../base-remote.data-services.test.ts | 12 +- .../base-remote.data-services.ts | 42 ++++- .../common-remote.data-services.ts | 9 +- packages/core-api/src/errors/api-error.ts | 34 +--- packages/core-api/src/errors/error-codes.ts | 30 ++- .../src/http-client/create-http-client.ts | 7 +- packages/core-api/src/http-client/types.ts | 20 +- .../src/observability/otel.adapter.test.ts | 5 +- .../src/observability/otel.adapter.ts | 17 +- packages/core-api/src/observability/setup.ts | 9 +- packages/core-events/src/event-bus.test.ts | 3 +- packages/core-events/src/event-bus.ts | 10 +- packages/core-events/src/hooks.ts | 5 +- packages/core-i18n/src/react-i18next.d.ts | 2 +- .../src/indexed-db/indexed-db.service.ts | 2 +- .../core-storage/src/tests/pouch-base.test.ts | 28 +-- .../__tests__/async-select.field.test.tsx | 12 +- .../Form/__tests__/checkbox.field.test.tsx | 17 +- .../__tests__/local-select.field.test.tsx | 42 +++-- .../Form/__tests__/text-input.field.test.tsx | 22 +-- .../Form/__tests__/withRHF.test.tsx | 32 +--- .../Form/custom/selects/AsyncSelect.tsx | 4 +- .../Form/custom/selects/LocalSelect.tsx | 23 +-- .../components/Form/custom/selects/types.ts | 6 +- .../Form/fields/async-select.field.tsx | 40 ++-- .../Form/fields/local-select.field.tsx | 33 ++-- .../Form/fields/rich-text.field.tsx | 19 +- .../Form/fields/segmented-control.field.tsx | 8 +- packages/ui/src/components/Form/types.ts | 15 +- packages/ui/src/components/Form/withRHF.tsx | 18 +- .../ui/src/components/actions-tools/index.ts | 2 +- .../components/system-pages/status-page.tsx | 6 +- .../components/table-filter-drawer.tsx | 11 +- packages/ui/src/hooks/useConditionalField.ts | 33 +--- packages/ui/src/theme/tokens/radius.ts | 6 +- .../__tests__/registry.validator.test.ts | 32 ++-- .../ui/src/validators/registry.validator.ts | 35 ++-- 94 files changed, 1742 insertions(+), 849 deletions(-) create mode 100644 apps/web/src/apps/auth/data/auth.remote.service.test.ts create mode 100644 apps/web/src/apps/auth/data/auth.remote.service.ts create mode 100644 apps/web/src/apps/auth/domain/entities/auth.entity.ts create mode 100644 apps/web/src/apps/auth/domain/factories/index.ts create mode 100644 apps/web/src/apps/auth/domain/validators/login.validator.test.ts create mode 100644 apps/web/src/apps/auth/domain/validators/login.validator.ts create mode 100644 apps/web/src/apps/auth/login/get-login-error-key.test.ts create mode 100644 apps/web/src/apps/auth/login/get-login-error-key.ts delete mode 100644 apps/web/src/apps/auth/login/validators/login.validator.ts create mode 100644 apps/web/src/core/lib/auth.helper.test.ts create mode 100644 apps/web/src/core/lib/auth.types.ts create mode 100644 apps/web/src/core/lib/enterprise-storage-adapter.test.ts create mode 100644 apps/web/src/core/lib/get-http-error-status.ts create mode 100644 apps/web/src/core/lib/handle-unauthorized.test.ts create mode 100644 apps/web/src/core/lib/handle-unauthorized.ts create mode 100644 apps/web/src/core/lib/map-user-privileges.test.ts create mode 100644 apps/web/src/core/lib/map-user-privileges.ts diff --git a/apps/desktop/scripts/copy-web-dist.ts b/apps/desktop/scripts/copy-web-dist.ts index be3aa81..d552c79 100644 --- a/apps/desktop/scripts/copy-web-dist.ts +++ b/apps/desktop/scripts/copy-web-dist.ts @@ -52,8 +52,7 @@ console.log(` Destination : ${DEST_DIR}`); // ── Validate ─────────────────────────────────────────────────── if (!existsSync(SOURCE_DIR)) { console.error( - `\n❌ Build output not found at: ${SOURCE_DIR}\n` + - ` Run "pnpm build --filter=${targetApp}" first.\n`, + `\n❌ Build output not found at: ${SOURCE_DIR}\n` + ` Run "pnpm build --filter=${targetApp}" first.\n`, ); process.exit(1); } diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index c083294..44a859d 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -1,11 +1,4 @@ -import { - app, - shell, - BrowserWindow, - ipcMain, - protocol, - session, -} from 'electron'; +import { app, shell, BrowserWindow, ipcMain, protocol, session } from 'electron'; import { autoUpdater } from 'electron-updater'; import { join, extname, normalize } from 'path'; import { readFileSync, existsSync, statSync } from 'fs'; @@ -227,8 +220,7 @@ function setupCorsBypass(): void { // a "normal" request or a null origin if ( requestHeaders['Origin'] && - (requestHeaders['Origin'].startsWith('app://') || - requestHeaders['Origin'].startsWith('file://')) + (requestHeaders['Origin'].startsWith('app://') || requestHeaders['Origin'].startsWith('file://')) ) { delete requestHeaders['Origin']; } @@ -427,10 +419,7 @@ if (!gotTheLock) { app.on('web-contents-created', (_event, contents) => { contents.on('will-navigate', (event, url) => { // Allow navigation within the app protocol and dev server - if ( - url.startsWith('app://') || - (IS_DEV && url.startsWith(DEV_SERVER_URL)) - ) { + if (url.startsWith('app://') || (IS_DEV && url.startsWith(DEV_SERVER_URL))) { return; } event.preventDefault(); diff --git a/apps/docs-dev/src/.vitepress/theme/index.ts b/apps/docs-dev/src/.vitepress/theme/index.ts index 15492a8..f789d9f 100644 --- a/apps/docs-dev/src/.vitepress/theme/index.ts +++ b/apps/docs-dev/src/.vitepress/theme/index.ts @@ -1,6 +1,6 @@ -import DefaultTheme from 'vitepress/theme' -import './custom.css' +import DefaultTheme from 'vitepress/theme'; +import './custom.css'; export default { extends: DefaultTheme, -} +}; diff --git a/apps/landing/src/features/public-content/presentation/LandingSample.tsx b/apps/landing/src/features/public-content/presentation/LandingSample.tsx index 42fd681..63f73b4 100644 --- a/apps/landing/src/features/public-content/presentation/LandingSample.tsx +++ b/apps/landing/src/features/public-content/presentation/LandingSample.tsx @@ -29,7 +29,7 @@ export default function LandingSample() { telemetryContext: { customSpanName: 'public.content.fetch', tags: { - 'feature': 'landing', + feature: 'landing', 'ui.component': 'LandingSample', 'ui.action': 'content_fetch', }, @@ -74,14 +74,19 @@ export default function LandingSample() { {loading ? 'Fetching…' : 'Test Public Fetch'} - {error && ( -
-          ❌ {error}
-        
- )} + {error &&
❌ {error}
} {result && ( -
+        
           {JSON.stringify(result, null, 2)}
         
)} diff --git a/apps/showcase/src/App.tsx b/apps/showcase/src/App.tsx index c2e1869..64bc47a 100644 --- a/apps/showcase/src/App.tsx +++ b/apps/showcase/src/App.tsx @@ -21,25 +21,29 @@ export default function App() { const colorScheme = useThemeStore((s) => s.colorScheme); const [density, setDensity] = useState('compact'); - const router = useMemo(() => createBrowserRouter([ - { - path: "/", - element: , - children: [ - { index: true, element: }, - { path: "ui-components", element: }, - { path: "forms", element: }, - { path: "storage", element: }, - { path: "events", element: }, - { path: "hardware", element: }, - { path: "rbac", element: }, - { path: "auth", element: }, - { path: "action-tools", element: }, - { path: "ag-grid", element: }, - ] - }, - { path: "/shell-demo", element: } - ]), [density]); + const router = useMemo( + () => + createBrowserRouter([ + { + path: '/', + element: , + children: [ + { index: true, element: }, + { path: 'ui-components', element: }, + { path: 'forms', element: }, + { path: 'storage', element: }, + { path: 'events', element: }, + { path: 'hardware', element: }, + { path: 'rbac', element: }, + { path: 'auth', element: }, + { path: 'action-tools', element: }, + { path: 'ag-grid', element: }, + ], + }, + { path: '/shell-demo', element: }, + ]), + [density], + ); return ( diff --git a/apps/showcase/src/core/hooks/use-electron-printer.ts b/apps/showcase/src/core/hooks/use-electron-printer.ts index bf732b4..66da48c 100644 --- a/apps/showcase/src/core/hooks/use-electron-printer.ts +++ b/apps/showcase/src/core/hooks/use-electron-printer.ts @@ -74,30 +74,27 @@ export function useElectronPrinter(): UseElectronPrinterReturn { } }, []); - const print = useCallback( - async (options?: ElectronPrintOptions): Promise => { - if (!window.electronAPI) { - return { success: false, failureReason: 'Not running in Electron' }; - } + const print = useCallback(async (options?: ElectronPrintOptions): Promise => { + if (!window.electronAPI) { + return { success: false, failureReason: 'Not running in Electron' }; + } - setLoading(true); - setError(null); - try { - const result = await window.electronAPI.print(options); - if (!result.success && result.failureReason) { - setError(result.failureReason); - } - return result; - } catch (err) { - const message = err instanceof Error ? err.message : 'Print failed'; - setError(message); - return { success: false, failureReason: message }; - } finally { - setLoading(false); + setLoading(true); + setError(null); + try { + const result = await window.electronAPI.print(options); + if (!result.success && result.failureReason) { + setError(result.failureReason); } - }, - [], - ); + return result; + } catch (err) { + const message = err instanceof Error ? err.message : 'Print failed'; + setError(message); + return { success: false, failureReason: message }; + } finally { + setLoading(false); + } + }, []); return { printers, diff --git a/apps/showcase/src/core/hooks/use-electron-updater.ts b/apps/showcase/src/core/hooks/use-electron-updater.ts index 1fa8cf5..51574f1 100644 --- a/apps/showcase/src/core/hooks/use-electron-updater.ts +++ b/apps/showcase/src/core/hooks/use-electron-updater.ts @@ -3,14 +3,7 @@ import { useIsElectron } from './use-is-electron'; // ─── Types ────────────────────────────────────────────────────── -export type UpdateStatus = - | 'idle' - | 'checking' - | 'available' - | 'not-available' - | 'downloading' - | 'ready' - | 'error'; +export type UpdateStatus = 'idle' | 'checking' | 'available' | 'not-available' | 'downloading' | 'ready' | 'error'; export interface UseElectronUpdaterReturn { /** Current status of the auto-updater lifecycle */ diff --git a/apps/showcase/src/layouts/ShowcaseLayout.tsx b/apps/showcase/src/layouts/ShowcaseLayout.tsx index 6a96b27..ce78002 100644 --- a/apps/showcase/src/layouts/ShowcaseLayout.tsx +++ b/apps/showcase/src/layouts/ShowcaseLayout.tsx @@ -34,17 +34,28 @@ export default function ShowcaseLayout({ density, setDensity }: ShowcaseLayoutPr 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'; + 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'; } }; @@ -58,8 +69,12 @@ export default function ShowcaseLayout({ density, setDensity }: ShowcaseLayoutPr > - Eigen ERP - Architecture Showcase + + Eigen ERP + + + Architecture Showcase + {navItems.map((item) => ( @@ -74,7 +89,7 @@ export default function ShowcaseLayout({ density, setDensity }: ShowcaseLayoutPr /> ))} - + - 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. + 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. - Auth & Security + + Auth & Security + Auth Demo Component Coming Soon... diff --git a/apps/showcase/src/pages/events/components/events-demo/index.tsx b/apps/showcase/src/pages/events/components/events-demo/index.tsx index 0f4947b..c8961fe 100644 --- a/apps/showcase/src/pages/events/components/events-demo/index.tsx +++ b/apps/showcase/src/pages/events/components/events-demo/index.tsx @@ -1,12 +1,5 @@ import { useState, useRef, useEffect } from 'react'; -import { - Card, - Title, - Text, - Stack, - Badge, - Divider, -} from '@repo/ui/components'; +import { Card, Title, Text, Stack, Badge, Divider } from '@repo/ui/components'; // ── Showcase Components ────────────────────────────────────────── import { CashierUI } from './printer/cashier.ui'; @@ -65,8 +58,8 @@ export default function EventsDemoPage() { 🖨️ Showcase 1: Cross-Platform Printer Abstraction - The CashierUI publishes a {DEVICE_EVENTS.PRINT_RECEIPT} event. - The PrinterListener listens for it and simulates interacting with a physical printer. + The CashierUI publishes a {DEVICE_EVENTS.PRINT_RECEIPT} event. The PrinterListener listens for it + and simulates interacting with a physical printer. {/* Headless listener — renders nothing visible */} @@ -110,9 +103,8 @@ export default function EventsDemoPage() { 📈 Showcase 2: High-Frequency Real-Time Data (50 updates/sec) - A mock WebSocket fires 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. + A mock WebSocket fires 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. @@ -126,8 +118,8 @@ export default function EventsDemoPage() { 💾 Showcase 3: Auth/Profile Sync with @repo/core-storage - ProfileSettingsUI publishes {AUTH_EVENTS.PROFILE_UPDATED}. - StorageSyncListener silently catches it in the background and saves to IndexedDB via secureIndexedDB. + ProfileSettingsUI publishes {AUTH_EVENTS.PROFILE_UPDATED}. StorageSyncListener silently catches + it in the background and saves to IndexedDB via secureIndexedDB. {/* Headless listener — renders nothing visible */} diff --git a/apps/showcase/src/pages/events/components/events-demo/printer/cashier.ui.tsx b/apps/showcase/src/pages/events/components/events-demo/printer/cashier.ui.tsx index 9b66188..3e44c0e 100644 --- a/apps/showcase/src/pages/events/components/events-demo/printer/cashier.ui.tsx +++ b/apps/showcase/src/pages/events/components/events-demo/printer/cashier.ui.tsx @@ -1,15 +1,7 @@ import { useState } from 'react'; import { usePublishEvent } from '@repo/core-events'; import type { ReceiptItem } from '@repo/core-events'; -import { - Button, - Group, - Stack, - Text, - TextInput, - Table, - Badge, -} from '@repo/ui/components'; +import { Button, Group, Stack, Text, TextInput, Table, Badge } from '@repo/ui/components'; import { DEVICE_EVENTS } from '../../../../../core/constants/events'; // ─── Mock Receipt Data ────────────────────────────────────────── diff --git a/apps/showcase/src/pages/events/components/events-demo/printer/printer.listener.tsx b/apps/showcase/src/pages/events/components/events-demo/printer/printer.listener.tsx index eb52f3e..727f368 100644 --- a/apps/showcase/src/pages/events/components/events-demo/printer/printer.listener.tsx +++ b/apps/showcase/src/pages/events/components/events-demo/printer/printer.listener.tsx @@ -38,8 +38,8 @@ export function PrinterListener({ onLog }: PrinterListenerProps) { // Uses the preload-exposed API. The Electron main process // handles the actual OS-level print job via `webContents.print()`. onLog(`[Electron] Sending receipt ${payload.receiptId} to OS printer via IPC bridge...`); - window.electronAPI! - .print({ silent: true, printBackground: true }) + window + .electronAPI!.print({ silent: true, printBackground: true }) .then((result) => { if (result.success) { onLog(`[Electron] ✅ Receipt ${payload.receiptId} printed successfully.`); @@ -55,7 +55,9 @@ export function PrinterListener({ onLog }: PrinterListenerProps) { // Opens the native browser print dialog. In production, you'd // likely render a hidden print-optimized iframe first. onLog(`[Browser] 🖨️ Receipt ${payload.receiptId} — opening browser print dialog...`); - onLog(` → Cashier: ${payload.cashierName} | Items: ${payload.items.length} | Total: $${payload.total.toFixed(2)}`); + onLog( + ` → Cashier: ${payload.cashierName} | Items: ${payload.items.length} | Total: $${payload.total.toFixed(2)}`, + ); window.print(); } }); diff --git a/apps/showcase/src/pages/events/components/events-demo/stock-grid/mock-websocket.service.ts b/apps/showcase/src/pages/events/components/events-demo/stock-grid/mock-websocket.service.ts index d841e80..d19cb41 100644 --- a/apps/showcase/src/pages/events/components/events-demo/stock-grid/mock-websocket.service.ts +++ b/apps/showcase/src/pages/events/components/events-demo/stock-grid/mock-websocket.service.ts @@ -8,9 +8,26 @@ import { WS_EVENTS } from '../../../../../core/constants/events'; * We generate 1000 unique IDs from these base tickers + numeric suffix. */ const BASE_TICKERS = [ - 'AAPL', 'GOOG', 'MSFT', 'AMZN', 'META', 'NVDA', 'TSLA', 'AMD', - 'NFLX', 'ORCL', 'CRM', 'INTC', 'PYPL', 'ADBE', 'CSCO', 'QCOM', - 'AVGO', 'TXN', 'MU', 'SHOP', + 'AAPL', + 'GOOG', + 'MSFT', + 'AMZN', + 'META', + 'NVDA', + 'TSLA', + 'AMD', + 'NFLX', + 'ORCL', + 'CRM', + 'INTC', + 'PYPL', + 'ADBE', + 'CSCO', + 'QCOM', + 'AVGO', + 'TXN', + 'MU', + 'SHOP', ]; /** diff --git a/apps/showcase/src/pages/events/components/events-demo/stock-grid/stock-row.ui.tsx b/apps/showcase/src/pages/events/components/events-demo/stock-grid/stock-row.ui.tsx index 8d38047..e6df34e 100644 --- a/apps/showcase/src/pages/events/components/events-demo/stock-grid/stock-row.ui.tsx +++ b/apps/showcase/src/pages/events/components/events-demo/stock-grid/stock-row.ui.tsx @@ -58,18 +58,12 @@ export const StockRow = memo(function StockRow({ stockId }: StockRowProps) { : '#fa5252' // red : undefined; - const changeArrow = data - ? data.change >= 0 - ? '▲' - : '▼' - : ''; + const changeArrow = data ? (data.change >= 0 ? '▲' : '▼') : ''; return ( {stockId} - - {data ? `$${data.price.toFixed(2)}` : '—'} - + {data ? `$${data.price.toFixed(2)}` : '—'} {data ? `${changeArrow} ${data.change >= 0 ? '+' : ''}${data.change.toFixed(2)}` : '—'} - - {data ? data.volume.toLocaleString() : '—'} - - - {renderCountRef.current} - + {data ? data.volume.toLocaleString() : '—'} + {renderCountRef.current} ); }); diff --git a/apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.transformer.ts b/apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.transformer.ts index 657d803..ea7992f 100644 --- a/apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.transformer.ts +++ b/apps/showcase/src/pages/events/components/example/features/booking/data/advanced-booking.transformer.ts @@ -151,9 +151,7 @@ export class AdvancedBookingTransformer extends BookingTransformer { return super.transformGetManyResponse(dtos).map((entity) => ({ ...entity, // Normalize 'cancelled' vs 'canceled' from different API versions - status: entity.status === ('canceled' as BookingEntity['status']) - ? 'cancelled' - : entity.status, + status: entity.status === ('canceled' as BookingEntity['status']) ? 'cancelled' : entity.status, })); } } diff --git a/apps/showcase/src/pages/events/components/example/features/booking/data/booking.data-services.ts b/apps/showcase/src/pages/events/components/example/features/booking/data/booking.data-services.ts index 2cd74ce..6d9d5bc 100644 --- a/apps/showcase/src/pages/events/components/example/features/booking/data/booking.data-services.ts +++ b/apps/showcase/src/pages/events/components/example/features/booking/data/booking.data-services.ts @@ -55,4 +55,3 @@ export const bookingServices = new CommonRemoteDataServices - {error && ( -
-          ❌ {error}
-        
- )} + {error &&
❌ {error}
} {result && ( -
+        
           {JSON.stringify(result, null, 2)}
         
)} diff --git a/apps/showcase/src/pages/events/index.tsx b/apps/showcase/src/pages/events/index.tsx index e5a4893..728020b 100644 --- a/apps/showcase/src/pages/events/index.tsx +++ b/apps/showcase/src/pages/events/index.tsx @@ -7,7 +7,9 @@ export default function EventsPage() { - Nested Showcase Example + + Nested Showcase Example + This is an example of a nested showcase component. diff --git a/apps/showcase/src/pages/forms/components/form-demo/components/all-fields-demo.tsx b/apps/showcase/src/pages/forms/components/form-demo/components/all-fields-demo.tsx index 73f84c4..13a7ef7 100644 --- a/apps/showcase/src/pages/forms/components/form-demo/components/all-fields-demo.tsx +++ b/apps/showcase/src/pages/forms/components/form-demo/components/all-fields-demo.tsx @@ -1,13 +1,31 @@ import { useForm } from 'react-hook-form'; import { Button, Paper, Title, Group, Stack, Code, Divider, Text, Chip, Radio } from '@repo/ui/components'; -import { - FieldTextInput, FieldPasswordInput, FieldTextarea, FieldNumberInput, - FieldJsonInput, FieldPinInput, FieldAutocomplete, FieldSelect, - FieldMultiSelect, FieldNativeSelect, FieldTagsInput, FieldCheckbox, - FieldRadioGroup, FieldSwitch, FieldChipGroup, FieldSegmentedControl, - FieldSlider, FieldRangeSlider, FieldRating, FieldColorInput, - FieldColorPicker, FieldFileInput, FieldLocalSelect, FieldAsyncSelect, - FieldRichTextEditor +import { + FieldTextInput, + FieldPasswordInput, + FieldTextarea, + FieldNumberInput, + FieldJsonInput, + FieldPinInput, + FieldAutocomplete, + FieldSelect, + FieldMultiSelect, + FieldNativeSelect, + FieldTagsInput, + FieldCheckbox, + FieldRadioGroup, + FieldSwitch, + FieldChipGroup, + FieldSegmentedControl, + FieldSlider, + FieldRangeSlider, + FieldRating, + FieldColorInput, + FieldColorPicker, + FieldFileInput, + FieldLocalSelect, + FieldAsyncSelect, + FieldRichTextEditor, } from '@repo/ui/form'; import type { LoadOptionsFn } from '@repo/ui/form'; import { useFormDemoTranslation } from '../i18n/useFormDemoTranslation'; @@ -48,13 +66,15 @@ const MOCK_VENDORS = [ const loadMockVendorsOptions: LoadOptionsFn = async (search, _page) => { await new Promise((resolve) => setTimeout(resolve, 500)); - const filtered = MOCK_VENDORS.filter(v => v.name.toLowerCase().includes(search.toLowerCase()) || v.code.toLowerCase().includes(search.toLowerCase())); + const filtered = MOCK_VENDORS.filter( + (v) => v.name.toLowerCase().includes(search.toLowerCase()) || v.code.toLowerCase().includes(search.toLowerCase()), + ); return { options: filtered, hasMore: false }; }; export default function AllFieldsDemo() { const t = useFormDemoTranslation(); - + const { control, handleSubmit, watch } = useForm({ defaultValues: { customerName: '', @@ -86,17 +106,18 @@ export default function AllFieldsDemo() { asyncSelectPrefilled: { id: 999, code: 'ASYNC-99', name: 'Pre-loaded Async Vendor' }, localMultiPrefilled: [ { id: 'V1', code: 'VN-01', name: 'Vendor One' }, - { id: 'V2', code: 'VN-02', name: 'Vendor Two' } + { id: 'V2', code: 'VN-02', name: 'Vendor Two' }, ], asyncMultiPrefilled: [ { id: 888, code: 'ASYNC-88', name: 'Ghost Async Vendor 1' }, - { id: 999, code: 'ASYNC-99', name: 'Ghost Async Vendor 2' } + { id: 999, code: 'ASYNC-99', name: 'Ghost Async Vendor 2' }, ], - richTextEmpty: "", - richTextPrefilled: "

ERP Release Notes

This is a highly important update. Please observe the following:

  • System maintenance at midnight.
  • All users must log out.

Thank you for your cooperation.

", + richTextEmpty: '', + richTextPrefilled: + '

ERP Release Notes

This is a highly important update. Please observe the following:

  • System maintenance at midnight.
  • All users must log out.

Thank you for your cooperation.

', realPokeSelect: null, - multiRealPokeSelect: [] - } + multiRealPokeSelect: [], + }, }); const onSubmit = (data: any) => console.log('All Fields Submitted:', data); @@ -109,7 +130,9 @@ export default function AllFieldsDemo() { {/* --- Text & Numbers --- */}
- {t.sections.textAndNumbers} + + {t.sections.textAndNumbers} + @@ -124,41 +147,40 @@ export default function AllFieldsDemo() {
- {t.fields.pin} + + {t.fields.pin} +
{/* --- Selections --- */}
- {t.sections.selections} + + {t.sections.selections} + - - + - - @@ -170,7 +192,7 @@ export default function AllFieldsDemo() { options={[ { id: 1, name: 'Apple', type: 'Fruit' }, { id: 2, name: 'Carrot', type: 'Vegetable' }, - { id: 3, name: 'Banana', type: 'Fruit' } + { id: 3, name: 'Banana', type: 'Fruit' }, ]} valueKey="id" labelKey="name" @@ -185,7 +207,7 @@ export default function AllFieldsDemo() { options={[ { id: 1, name: 'Red', hex: '#f00' }, { id: 2, name: 'Green', hex: '#0f0' }, - { id: 3, name: 'Blue', hex: '#00f' } + { id: 3, name: 'Blue', hex: '#00f' }, ]} valueKey="id" renderLabel={(item) => `${item.name} (${item.hex})`} @@ -240,7 +262,9 @@ export default function AllFieldsDemo() { - {t.sections.advancedObjectSelects} + + {t.sections.advancedObjectSelects} + - {t.sections.multiSelectEditMode} + + {t.sections.multiSelectEditMode} + - {t.sections.richTextEditor} + + {t.sections.richTextEditor} + - {t.sections.togglesAndChoices} + + {t.sections.togglesAndChoices} + - + -
- Chip Selection - + + Chip Selection + + Option 1 Option 2 @@ -372,7 +396,9 @@ export default function AllFieldsDemo() { {/* --- Ranges & Specialized --- */}
- {t.sections.rangesAndSpecialized} + + {t.sections.rangesAndSpecialized} + @@ -384,23 +410,31 @@ export default function AllFieldsDemo() {
- {t.fields.themeColor} Picker + + {t.fields.themeColor} Picker +
- {t.fields.rating} + + {t.fields.rating} +
- + - + - {t.common.submittedData} + + {t.common.submittedData} + {JSON.stringify(data, null, 2)} diff --git a/apps/showcase/src/pages/forms/components/form-demo/components/reactive-watch-demo.tsx b/apps/showcase/src/pages/forms/components/form-demo/components/reactive-watch-demo.tsx index e1c3119..cae10fe 100644 --- a/apps/showcase/src/pages/forms/components/form-demo/components/reactive-watch-demo.tsx +++ b/apps/showcase/src/pages/forms/components/form-demo/components/reactive-watch-demo.tsx @@ -2,7 +2,14 @@ import { useForm, useWatch } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Button, Paper, Title, Divider, Stack, Code, Alert, TypographyStylesProvider } from '@repo/ui/components'; -import { FieldTextInput, FieldSelect, FieldSwitch, FieldLocalSelect, FieldAsyncSelect, FieldRichTextEditor } from '@repo/ui/form'; +import { + FieldTextInput, + FieldSelect, + FieldSwitch, + FieldLocalSelect, + FieldAsyncSelect, + FieldRichTextEditor, +} from '@repo/ui/form'; import { useConditionalField } from '@repo/ui/hooks'; import { compose, required, emailValidator } from '@repo/ui/validators'; import { useFormDemoTranslation } from '../i18n/useFormDemoTranslation'; @@ -23,7 +30,7 @@ interface Warehouse { const REGIONS: Region[] = [ { id: 'R1', code: 'APAC', taxRate: 0.1 }, - { id: 'R2', code: 'EMEA', taxRate: 0.2 } + { id: 'R2', code: 'EMEA', taxRate: 0.2 }, ]; const mockFetchWarehouses = async (regionIds: string[], search: string, page: number) => { @@ -34,15 +41,17 @@ const mockFetchWarehouses = async (regionIds: string[], search: string, page: nu { id: 'W3', regionId: 'R2', name: 'London Central' }, { id: 'W4', regionId: 'R2', name: 'Berlin Storage' }, ]; - - const filtered = allWarehouses.filter(w => regionIds.includes(w.regionId) && w.name.toLowerCase().includes(search.toLowerCase())); + + const filtered = allWarehouses.filter( + (w) => regionIds.includes(w.regionId) && w.name.toLowerCase().includes(search.toLowerCase()), + ); const pageSize = 10; const start = (page - 1) * pageSize; const paginated = filtered.slice(start, start + pageSize); - + return { options: paginated, - hasMore: start + pageSize < filtered.length + hasMore: start + pageSize < filtered.length, }; }; @@ -55,44 +64,48 @@ export default function ReactiveWatchDemo() { const newsletterEmailValidator = compose(z.string(), required(t.fields.email), emailValidator()); const roleValidator = compose(z.string(), required(t.fields.role)); - const reactiveSchema = useMemo(() => z - .object({ - userType: z.enum(['PERSONAL', 'CORPORATE']), - corporateTaxId: z.string().optional(), - hasSpouse: z.boolean(), - spouseName: z.string().optional(), - newsletter: z.boolean(), - newsletterEmail: z.string().optional(), - department: z.string().optional(), - role: z.string().optional(), - regions: z.array(z.object({ id: z.string(), code: z.string(), taxRate: z.number() })).optional(), - warehouses: z.array(z.object({ id: z.string(), regionId: z.string(), name: z.string() })).optional(), - richTextLive: z.string().optional(), - }) - .and( - z.discriminatedUnion('userType', [ - z.object({ userType: z.literal('PERSONAL') }), - z.object({ userType: z.literal('CORPORATE'), corporateTaxId: taxIdValidator }), - ]), - ) - .and( - z.union([ - z.object({ hasSpouse: z.literal(false) }), - z.object({ hasSpouse: z.literal(true), spouseName: spouseNameValidator }), - ]), - ) - .and( - z.union([ - z.object({ newsletter: z.literal(false) }), - z.object({ newsletter: z.literal(true), newsletterEmail: newsletterEmailValidator }), - ]), - ) - .and( - z.union([ - z.object({ department: z.string().min(1), role: roleValidator }), - z.object({ department: z.union([z.literal(''), z.undefined(), z.null()]).optional() }), - ]), - ), [t, taxIdValidator, spouseNameValidator, newsletterEmailValidator, roleValidator]); + const reactiveSchema = useMemo( + () => + z + .object({ + userType: z.enum(['PERSONAL', 'CORPORATE']), + corporateTaxId: z.string().optional(), + hasSpouse: z.boolean(), + spouseName: z.string().optional(), + newsletter: z.boolean(), + newsletterEmail: z.string().optional(), + department: z.string().optional(), + role: z.string().optional(), + regions: z.array(z.object({ id: z.string(), code: z.string(), taxRate: z.number() })).optional(), + warehouses: z.array(z.object({ id: z.string(), regionId: z.string(), name: z.string() })).optional(), + richTextLive: z.string().optional(), + }) + .and( + z.discriminatedUnion('userType', [ + z.object({ userType: z.literal('PERSONAL') }), + z.object({ userType: z.literal('CORPORATE'), corporateTaxId: taxIdValidator }), + ]), + ) + .and( + z.union([ + z.object({ hasSpouse: z.literal(false) }), + z.object({ hasSpouse: z.literal(true), spouseName: spouseNameValidator }), + ]), + ) + .and( + z.union([ + z.object({ newsletter: z.literal(false) }), + z.object({ newsletter: z.literal(true), newsletterEmail: newsletterEmailValidator }), + ]), + ) + .and( + z.union([ + z.object({ department: z.string().min(1), role: roleValidator }), + z.object({ department: z.union([z.literal(''), z.undefined(), z.null()]).optional() }), + ]), + ), + [t, taxIdValidator, spouseNameValidator, newsletterEmailValidator, roleValidator], + ); const { control, handleSubmit, setValue, unregister, clearErrors } = useForm({ resolver: zodResolver(reactiveSchema as any), @@ -108,9 +121,9 @@ export default function ReactiveWatchDemo() { regions: [{ id: 'R1', code: 'APAC', taxRate: 0.1 }], warehouses: [ { id: 'W-99', regionId: 'R1', name: 'APAC Central Hub' }, - { id: 'W-98', regionId: 'R1', name: 'APAC Backup Hub' } + { id: 'W-98', regionId: 'R1', name: 'APAC Backup Hub' }, ], - richTextLive: "

Start typing to see the live preview...

", + richTextLive: '

Start typing to see the live preview...

', }, }); @@ -184,18 +197,18 @@ export default function ReactiveWatchDemo() { const isMounted = useRef(false); const prevRegionIds = useRef(regions?.map((r: Region) => r.id) || []); - + useEffect(() => { if (!isMounted.current) { isMounted.current = true; return; } - + const currentIds = regions?.map((r: Region) => r.id) || []; const prevIds = prevRegionIds.current; - + const hasChanged = currentIds.length !== prevIds.length || currentIds.some((id: string) => !prevIds.includes(id)); - + if (hasChanged) { setValue('warehouses', []); clearErrors('warehouses'); @@ -297,7 +310,7 @@ export default function ReactiveWatchDemo() { {t.sections.reactiveWatchCascading} - + multiple name="regions" @@ -308,7 +321,7 @@ export default function ReactiveWatchDemo() { labelKey="code" clearable /> - + multiple key={`warehouse-select-${regions?.map((r: any) => r.id).join(',')}`} @@ -316,18 +329,26 @@ export default function ReactiveWatchDemo() { control={control as any} label={t.fields.warehouses} disabled={!regions || regions.length === 0} - loadOptions={useCallback(async (search, page) => { - if (!regions || regions.length === 0) return { options: [], hasMore: false }; - return mockFetchWarehouses(regions.map((r: any) => r.id), search, page); - }, [regions])} + loadOptions={useCallback( + async (search, page) => { + if (!regions || regions.length === 0) return { options: [], hasMore: false }; + return mockFetchWarehouses( + regions.map((r: any) => r.id), + search, + page, + ); + }, + [regions], + )} valueKey="id" renderLabel={(item) => `[${item.id}] ${item.name}`} clearable /> - + {regions && regions.length > 0 && ( - {t.descriptions.selectedRegionsTax} {regions.map((r: any) => `${r.code} (${(r.taxRate * 100).toFixed(0)}%)`).join(', ')} + {t.descriptions.selectedRegionsTax}{' '} + {regions.map((r: any) => `${r.code} (${(r.taxRate * 100).toFixed(0)}%)`).join(', ')} )} @@ -335,16 +356,18 @@ export default function ReactiveWatchDemo() { {t.sections.reactiveRichTextPreview} - + - + - {t.sections.liveHtmlPreview} + + {t.sections.liveHtmlPreview} +
diff --git a/apps/showcase/src/pages/rbac/index.tsx b/apps/showcase/src/pages/rbac/index.tsx index bcd45dd..9ed72aa 100644 --- a/apps/showcase/src/pages/rbac/index.tsx +++ b/apps/showcase/src/pages/rbac/index.tsx @@ -5,7 +5,9 @@ export default function RbacPage() { - RBAC Engine + + RBAC Engine + RBAC Demo Component Coming Soon... diff --git a/apps/showcase/src/pages/ui-components/index.tsx b/apps/showcase/src/pages/ui-components/index.tsx index 80aa45c..c28a3d2 100644 --- a/apps/showcase/src/pages/ui-components/index.tsx +++ b/apps/showcase/src/pages/ui-components/index.tsx @@ -1,4 +1,24 @@ -import { Stack, Card, Title, Group, Select, Text, Badge, Divider, Button, TextInput, NumberInput, PasswordInput, Textarea, Checkbox, Switch, Table, StatusBadge, STATUS_DATA, Container } from '@repo/ui/components'; +import { + Stack, + Card, + Title, + Group, + Select, + Text, + Badge, + Divider, + Button, + TextInput, + NumberInput, + PasswordInput, + Textarea, + Checkbox, + Switch, + Table, + StatusBadge, + STATUS_DATA, + Container, +} from '@repo/ui/components'; import { useThemeStore } from '../../core/stores/theme.store'; import { ColorSchemeType } from '@repo/ui/provider'; @@ -16,7 +36,9 @@ export default function UiComponentsPage() { {/* Control Panel */} - Theme Controls + + Theme Controls +