@bluealba/platform-cli 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/index.js +278 -15
  2. package/docs/404.mdx +5 -0
  3. package/docs/architecture/api-explorer.mdx +478 -0
  4. package/docs/architecture/architecture-diagrams.mdx +12 -0
  5. package/docs/architecture/authentication-system.mdx +903 -0
  6. package/docs/architecture/authorization-system.mdx +886 -0
  7. package/docs/architecture/bootstrap.mdx +1442 -0
  8. package/docs/architecture/gateway-architecture.mdx +845 -0
  9. package/docs/architecture/multi-tenancy.mdx +1150 -0
  10. package/docs/architecture/overview.mdx +776 -0
  11. package/docs/architecture/scheduler.mdx +818 -0
  12. package/docs/architecture/shell.mdx +885 -0
  13. package/docs/architecture/ui-extension-points.mdx +781 -0
  14. package/docs/architecture/user-states.mdx +794 -0
  15. package/docs/development/overview.mdx +21 -0
  16. package/docs/development/workflow.mdx +914 -0
  17. package/docs/getting-started/core-concepts.mdx +892 -0
  18. package/docs/getting-started/installation.mdx +780 -0
  19. package/docs/getting-started/overview.mdx +83 -0
  20. package/docs/getting-started/quick-start.mdx +940 -0
  21. package/docs/guides/adding-documentation-sites.mdx +1367 -0
  22. package/docs/guides/creating-services.mdx +1736 -0
  23. package/docs/guides/creating-ui-modules.mdx +1860 -0
  24. package/docs/guides/identity-providers.mdx +1007 -0
  25. package/docs/guides/mermaid-diagrams.mdx +212 -0
  26. package/docs/guides/using-feature-flags.mdx +1059 -0
  27. package/docs/guides/working-with-rooms.mdx +566 -0
  28. package/docs/index.mdx +57 -0
  29. package/docs/platform-cli/commands.mdx +604 -0
  30. package/docs/platform-cli/overview.mdx +195 -0
  31. package/package.json +5 -2
  32. package/skills/ba-platform/platform-cli.skill.md +26 -0
  33. package/skills/ba-platform/platform.skill.md +35 -0
  34. package/templates/application-monorepo-template/gitignore +95 -0
  35. package/templates/bootstrap-service-template/Dockerfile.development +1 -1
  36. package/templates/bootstrap-service-template/gitignore +57 -0
  37. package/templates/bootstrap-service-template/package.json +1 -1
  38. package/templates/bootstrap-service-template/src/main.ts +6 -16
  39. package/templates/customization-ui-module-template/Dockerfile.development +1 -1
  40. package/templates/customization-ui-module-template/gitignore +73 -0
  41. package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
  42. package/templates/nestjs-service-module-template/gitignore +56 -0
  43. package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
  44. package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
  45. package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
  46. package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
  47. package/templates/react-ui-module-template/Dockerfile +1 -1
  48. package/templates/react-ui-module-template/Dockerfile.development +1 -3
  49. package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
  50. package/templates/react-ui-module-template/gitignore +72 -0
  51. package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
  52. package/templates/react-ui-module-template/nginx/default.conf +0 -23
@@ -0,0 +1,1860 @@
1
+ ---
2
+ title: Creating UI Modules
3
+ description: Complete guide to creating React-based micro-frontend UI modules for the Blue Alba Platform
4
+ ---
5
+
6
+ import { Card, CardGrid, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
7
+ import MermaidDiagram from '~/components/MermaidDiagram.astro';
8
+
9
+ The Blue Alba Platform uses a micro-frontend architecture built on React and single-spa, enabling you to develop, deploy, and integrate independent UI modules that compose into a cohesive application experience. This guide walks you through everything you need to create production-ready UI modules.
10
+
11
+ ## Overview
12
+
13
+ UI modules are self-contained React applications that integrate seamlessly into the platform's shell. Each module runs independently but can communicate with other modules through the platform's shared state, extension points, and event system.
14
+
15
+ ---
16
+
17
+ ## Architecture
18
+
19
+ The platform's micro-frontend architecture uses single-spa as the orchestration layer, with custom integrations for authorization, routing, and inter-module communication.
20
+
21
+ <MermaidDiagram
22
+ title="Micro-Frontend Architecture"
23
+ code={`graph TB
24
+ A[Platform Shell<br/>pae-shell-ui]:::shell --> B[Application Catalog]:::catalog
25
+ B --> C[UI Module 1<br/>pae-admin-ui]:::module
26
+ B --> D[UI Module 2<br/>Custom App UI]:::module
27
+ B --> E[UI Module N<br/>...]:::module
28
+
29
+ F[pae-ui-react-core<br/>Shared Library]:::lib --> C
30
+ F --> D
31
+ F --> E
32
+
33
+ G[Gateway API]:::gateway --> H[Backend Services]:::services
34
+ C -.HTTP.-> G
35
+ D -.HTTP.-> G
36
+ E -.HTTP.-> G
37
+
38
+ I[single-spa<br/>Orchestrator]:::orchestrator --> A
39
+ I --> C
40
+ I --> D
41
+ I --> E
42
+
43
+ classDef shell fill:#90EE90,color:#333
44
+ classDef catalog fill:#FFD700,color:#333
45
+ classDef module fill:#87CEEB,color:#333
46
+ classDef lib fill:#FFB6C1,color:#333
47
+ classDef gateway fill:#DDA0DD,color:#333
48
+ classDef services fill:#F0E68C,color:#333
49
+ classDef orchestrator fill:#FFA07A,color:#333`}
50
+ />
51
+
52
+ ### Architecture Components
53
+
54
+ - **Platform Shell**: The main container that hosts all UI modules and provides navigation
55
+ - **Application Catalog**: Registry of all available modules and their routes
56
+ - **UI Modules**: Independent React applications loaded dynamically
57
+ - **pae-ui-react-core**: Shared library providing hooks, components, and utilities
58
+ - **single-spa**: Runtime orchestration layer managing module lifecycles
59
+ - **Gateway API**: Unified backend interface for all services
60
+
61
+ ---
62
+
63
+ ## Quick Start
64
+
65
+ Create your first UI module in five steps:
66
+
67
+ ### 1. Create Project Structure
68
+
69
+ ```bash
70
+ mkdir my-app-ui
71
+ cd my-app-ui
72
+ npm init -y
73
+ ```
74
+
75
+ ### 2. Install Dependencies
76
+
77
+ ```bash
78
+ # Install SDK and core library as dev dependencies
79
+ npm install --save-dev @bluealba/pae-ui-react-sdk
80
+ npm install --save-dev @bluealba/pae-ui-react-core
81
+
82
+ # Install runtime dependencies
83
+ npm install react@18 react-dom@18
84
+ npm install react-router@6 react-router-dom@6
85
+ ```
86
+
87
+ ### 3. Configure package.json
88
+
89
+ ```json
90
+ {
91
+ "name": "@myorg/my-app-ui",
92
+ "version": "1.0.0",
93
+ "displayName": "My Application",
94
+ "scripts": {
95
+ "start": "pae-ui-sdk start",
96
+ "start:dev": "pae-ui-sdk start --dev",
97
+ "build": "pae-ui-sdk build",
98
+ "lint": "pae-ui-sdk lint",
99
+ "lint:fix": "pae-ui-sdk lint --fix"
100
+ },
101
+ "devDependencies": {
102
+ "@bluealba/pae-ui-react-sdk": "*",
103
+ "@bluealba/pae-ui-react-core": "*"
104
+ },
105
+ "dependencies": {
106
+ "@bluealba/pae-core": "*",
107
+ "react": "^18.3.1",
108
+ "react-dom": "^18.3.1",
109
+ "react-router": "^6.26.1",
110
+ "react-router-dom": "^6.26.1"
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### 4. Create Source Files
116
+
117
+ Create the following file structure:
118
+
119
+ ```
120
+ my-app-ui/
121
+ ├── package.json
122
+ └── src/
123
+ ├── main.tsx # Module entry point
124
+ ├── root.component.tsx # Root React component
125
+ ├── menu.ts # Navigation menu
126
+ └── Icon.tsx # Application icon
127
+ ```
128
+
129
+ ### 5. Implement Core Files
130
+
131
+ <Tabs>
132
+ <TabItem label="main.tsx">
133
+
134
+ ```typescript
135
+ import React from "react";
136
+ import ReactDOMClient from "react-dom/client";
137
+ import { ErrorState, initializeMicroFrontend } from "@bluealba/pae-ui-react-core";
138
+ import Root from "./root.component";
139
+
140
+ export const { bootstrap, mount, unmount } = initializeMicroFrontend({
141
+ React,
142
+ ReactDOMClient,
143
+ rootComponent: Root,
144
+ errorBoundary(err) {
145
+ console.error(err);
146
+ return <ErrorState />
147
+ }
148
+ });
149
+
150
+ // Export menu for shell integration
151
+ export { default as menu } from "./menu";
152
+
153
+ // Export application icon
154
+ export { default as icon } from './Icon';
155
+ ```
156
+
157
+ </TabItem>
158
+
159
+ <TabItem label="root.component.tsx">
160
+
161
+ ```typescript
162
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
163
+
164
+ export default function Root() {
165
+ return (
166
+ <BrowserRouter basename="/my-app">
167
+ <Routes>
168
+ <Route path="/" element={<HomePage />} />
169
+ <Route path="/about" element={<AboutPage />} />
170
+ </Routes>
171
+ </BrowserRouter>
172
+ );
173
+ }
174
+
175
+ function HomePage() {
176
+ return <h1>Welcome to My App</h1>;
177
+ }
178
+
179
+ function AboutPage() {
180
+ return <h1>About My App</h1>;
181
+ }
182
+ ```
183
+
184
+ </TabItem>
185
+
186
+ <TabItem label="menu.ts">
187
+
188
+ ```typescript
189
+ export default [
190
+ {
191
+ label: 'Home',
192
+ path: '/',
193
+ },
194
+ {
195
+ label: 'Features',
196
+ items: [
197
+ {
198
+ label: 'Dashboard',
199
+ path: '/dashboard',
200
+ operations: ['my-app::dashboard::read']
201
+ },
202
+ {
203
+ label: 'Settings',
204
+ path: '/settings',
205
+ operations: ['my-app::settings::read']
206
+ }
207
+ ]
208
+ },
209
+ {
210
+ label: 'About',
211
+ path: '/about',
212
+ }
213
+ ];
214
+ ```
215
+
216
+ </TabItem>
217
+
218
+ <TabItem label="Icon.tsx">
219
+
220
+ ```typescript
221
+ const Icon = () => (
222
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
223
+ <path d="M12 2L2 7v10c0 5.5 4.5 10 10 10s10-4.5 10-10V7L12 2z"
224
+ fill="currentColor"/>
225
+ </svg>
226
+ );
227
+
228
+ export default Icon;
229
+ ```
230
+
231
+ </TabItem>
232
+ </Tabs>
233
+
234
+ Now run `npm run start:dev` to start the development server!
235
+
236
+ ---
237
+
238
+ ## Project Structure
239
+
240
+ A typical UI module follows this structure:
241
+
242
+ ```
243
+ my-app-ui/
244
+ ├── package.json # Project configuration
245
+ ├── tsconfig.json # TypeScript configuration (optional)
246
+ ├── webpack.config.js # Custom webpack config (optional)
247
+ ├── assets/ # Static assets (images, fonts)
248
+ │ └── logo.svg
249
+ └── src/
250
+ ├── main.tsx # Module entry point (required)
251
+ ├── root.component.tsx # Root component (required)
252
+ ├── menu.ts # Navigation menu
253
+ ├── Icon.tsx # Application icon
254
+ ├── views/ # Page components
255
+ │ ├── HomePage.tsx
256
+ │ ├── DashboardPage.tsx
257
+ │ └── SettingsPage.tsx
258
+ ├── components/ # Reusable components
259
+ │ ├── Header.tsx
260
+ │ └── Card.tsx
261
+ ├── hooks/ # Custom hooks
262
+ │ └── useMyData.ts
263
+ ├── services/ # API service layer
264
+ │ └── api.ts
265
+ ├── utils/ # Utility functions
266
+ │ └── helpers.ts
267
+ └── styles/ # Stylesheets
268
+ └── global.css
269
+ ```
270
+
271
+ ### Key Files Explained
272
+
273
+ <CardGrid>
274
+ <Card title="main.tsx" icon="seti:javascript">
275
+ Entry point that initializes the micro-frontend using `initializeMicroFrontend()`. Exports lifecycle methods (bootstrap, mount, unmount) and optional menu/icon.
276
+ </Card>
277
+
278
+ <Card title="root.component.tsx" icon="react">
279
+ Root React component containing your application's main structure, typically including routing configuration.
280
+ </Card>
281
+
282
+ <Card title="menu.ts" icon="list">
283
+ Menu configuration exported for shell integration. Defines navigation structure with optional operation-based access control.
284
+ </Card>
285
+
286
+ <Card title="Icon.tsx" icon="seti:image">
287
+ SVG component displayed in the application selector. Should be 24x24px and use `currentColor` for theme support.
288
+ </Card>
289
+ </CardGrid>
290
+
291
+ ---
292
+
293
+ ## Development Workflow
294
+
295
+ ### Running Locally with Docker
296
+
297
+ The recommended way to develop UI modules is using Docker Compose, which provides a consistent development environment and automatic integration with the platform.
298
+
299
+ ### Project Structure Setup
300
+
301
+ Your repositories should be organized at the same level:
302
+
303
+ ```
304
+ my-projects/
305
+ ├── my-app-local/ # Local environment with docker-compose.yml
306
+ │ └── docker-compose.yml
307
+ └── my-app-ui/ # Your UI module repository
308
+ ├── Dockerfile.development
309
+ ├── package.json
310
+ └── src/
311
+ ```
312
+
313
+ <Aside type="note">
314
+ The docker-compose.yml uses `${PWD}/../my-app-ui` to reference the UI repository. This requires both repositories to be siblings in the same parent directory.
315
+ </Aside>
316
+
317
+ ### Step 1: Create Development Dockerfile
318
+
319
+ Create a `Dockerfile.development` in your UI module repository:
320
+
321
+ ```dockerfile
322
+ FROM node:20-alpine as development
323
+
324
+ ENV NODE_ENV=development
325
+ ENV PORT=80
326
+
327
+ ARG BA_NPM_AUTH_TOKEN
328
+
329
+ WORKDIR /app
330
+
331
+ EXPOSE 80
332
+
333
+ CMD ["npm", "run", "start:dev"]
334
+ ```
335
+
336
+ **Key points:**
337
+ - Uses Node 20 Alpine for a lightweight image
338
+ - Sets `NODE_ENV=development` for development mode
339
+ - Exposes port 80 (configurable)
340
+ - Runs `start:dev` command for hot reload
341
+
342
+ ### Step 2: Configure Docker Compose
343
+
344
+ Add your UI module to the `docker-compose.yml` in your local environment repository:
345
+
346
+ ```yaml
347
+ services:
348
+ my-app-ui:
349
+ build:
350
+ context: ../my-app-ui
351
+ dockerfile: Dockerfile.development
352
+ ports:
353
+ - 9001:80
354
+ volumes:
355
+ - ${PWD}/../my-app-ui:/app
356
+ environment:
357
+ - NODE_ENV=development
358
+ ```
359
+
360
+ **Configuration explained:**
361
+
362
+ | Property | Description |
363
+ |----------|-------------|
364
+ | `context: ../my-app-ui` | Path to your UI repository (relative to compose file) |
365
+ | `dockerfile: Dockerfile.development` | Development-specific Dockerfile |
366
+ | `ports: 9001:80` | Maps host port 9001 to container port 80 |
367
+ | `volumes` | Mounts your code for hot reload (changes reflect immediately) |
368
+ | `environment` | Sets environment variables |
369
+
370
+ ### Step 3: Start Development Environment
371
+
372
+ From your local environment repository:
373
+
374
+ ```bash
375
+ # Start your UI module
376
+ docker-compose up my-app-ui
377
+
378
+ # Or start in detached mode
379
+ docker-compose up -d my-app-ui
380
+
381
+ # View logs
382
+ docker-compose logs -f my-app-ui
383
+ ```
384
+
385
+ **What happens:**
386
+ 1. Docker builds the development image
387
+ 2. Installs npm dependencies inside the container
388
+ 3. Starts the dev server with hot reload (`npm run start:dev`)
389
+ 4. Your code is mounted as a volume - changes trigger automatic rebuilds
390
+ 5. Module is accessible at `http://localhost:9001`
391
+
392
+ ---
393
+
394
+ ## Core Concepts
395
+
396
+ ### Initializing a Micro-Frontend
397
+
398
+ The `initializeMicroFrontend()` function is the foundation of every UI module. It creates a single-spa application with platform integration.
399
+
400
+ ```typescript
401
+ import { initializeMicroFrontend } from "@bluealba/pae-ui-react-core";
402
+
403
+ export const { bootstrap, mount, unmount } = initializeMicroFrontend({
404
+ React,
405
+ ReactDOMClient,
406
+ rootComponent: Root,
407
+ errorBoundary(err, info) {
408
+ console.error(err);
409
+ // Return custom error UI
410
+ return <ErrorState error={err} />;
411
+ }
412
+ });
413
+ ```
414
+
415
+ **Configuration Options:**
416
+
417
+ | Option | Type | Description | Required |
418
+ |--------|------|-------------|----------|
419
+ | `React` | React | React library instance | Yes |
420
+ | `ReactDOMClient` | ReactDOMClient | React DOM client for rendering | Yes |
421
+ | `rootComponent` | React.Component | Your application's root component | Yes |
422
+ | `errorBoundary` | Function | Error boundary handler returning JSX | Yes |
423
+
424
+ **Lifecycle Methods:**
425
+
426
+ The function returns three methods that single-spa calls:
427
+
428
+ - `bootstrap()`: Called once when module first loads (initialize resources)
429
+ - `mount()`: Called when module becomes active (render to DOM)
430
+ - `unmount()`: Called when module becomes inactive (cleanup)
431
+
432
+ ### Navigation and Routing
433
+
434
+ Use React Router for internal navigation and the platform's `navigateTo()` for cross-module navigation.
435
+
436
+ <Tabs>
437
+ <TabItem label="Internal Routing">
438
+
439
+ ```typescript
440
+ import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
441
+
442
+ export default function Root() {
443
+ return (
444
+ <BrowserRouter basename="/my-app">
445
+ <nav>
446
+ <Link to="/">Home</Link>
447
+ <Link to="/dashboard">Dashboard</Link>
448
+ </nav>
449
+
450
+ <Routes>
451
+ <Route path="/" element={<HomePage />} />
452
+ <Route path="/dashboard" element={<DashboardPage />} />
453
+ <Route path="/settings" element={<SettingsPage />} />
454
+ <Route path="*" element={<NotFoundPage />} />
455
+ </Routes>
456
+ </BrowserRouter>
457
+ );
458
+ }
459
+ ```
460
+
461
+ <Aside type="note">
462
+ Always set `basename` to match your module's route in the catalog (e.g., `/my-app`).
463
+ </Aside>
464
+
465
+ </TabItem>
466
+
467
+ <TabItem label="Cross-Module Navigation">
468
+
469
+ ```typescript
470
+ import { navigateTo } from '@bluealba/pae-ui-react-core';
471
+
472
+ function NavigationExample() {
473
+ const goToAdmin = () => {
474
+ // Navigate to another module
475
+ navigateTo('/admin/users');
476
+ };
477
+
478
+ const goToExternal = () => {
479
+ // Open external link
480
+ window.open('https://example.com', '_blank');
481
+ };
482
+
483
+ return (
484
+ <div>
485
+ <button onClick={goToAdmin}>
486
+ Go to Admin
487
+ </button>
488
+ <button onClick={goToExternal}>
489
+ External Link
490
+ </button>
491
+ </div>
492
+ );
493
+ }
494
+ ```
495
+
496
+ </TabItem>
497
+
498
+ <TabItem label="Programmatic Navigation">
499
+
500
+ ```typescript
501
+ import { useNavigate } from 'react-router-dom';
502
+ import { navigateTo } from '@bluealba/pae-ui-react-core';
503
+
504
+ function NavigationComponent() {
505
+ // Internal navigation (within module)
506
+ const navigate = useNavigate();
507
+
508
+ const handleInternalNav = () => {
509
+ navigate('/dashboard');
510
+ };
511
+
512
+ // External navigation (to other modules)
513
+ const handleExternalNav = () => {
514
+ navigateTo('/admin');
515
+ };
516
+
517
+ return (
518
+ <div>
519
+ <button onClick={handleInternalNav}>Dashboard</button>
520
+ <button onClick={handleExternalNav}>Admin Panel</button>
521
+ </div>
522
+ );
523
+ }
524
+ ```
525
+
526
+ </TabItem>
527
+
528
+ <TabItem label="Route Parameters">
529
+
530
+ ```typescript
531
+ import { useParams, useSearchParams } from 'react-router-dom';
532
+
533
+ function UserDetailPage() {
534
+ // Path parameters: /users/:userId
535
+ const { userId } = useParams();
536
+
537
+ // Query parameters: /users?tab=profile
538
+ const [searchParams] = useSearchParams();
539
+ const tab = searchParams.get('tab');
540
+
541
+ return (
542
+ <div>
543
+ <h1>User {userId}</h1>
544
+ <p>Current tab: {tab}</p>
545
+ </div>
546
+ );
547
+ }
548
+ ```
549
+
550
+ </TabItem>
551
+ </Tabs>
552
+
553
+ ### Authentication and Authorization
554
+
555
+ The platform provides built-in authentication and operation-based authorization.
556
+
557
+ <Tabs>
558
+ <TabItem label="useAuth Hook">
559
+
560
+ ```typescript
561
+ import { useAuth } from '@bluealba/pae-ui-react-core';
562
+
563
+ function ProfileComponent() {
564
+ const { authUser, hasAccess } = useAuth();
565
+
566
+ return (
567
+ <div>
568
+ <h2>Welcome, {authUser.displayName}</h2>
569
+ <p>Username: {authUser.username}</p>
570
+ <p>Email: {authUser.email}</p>
571
+ <p>Initials: {authUser.initials}</p>
572
+
573
+ {hasAccess('admin::users::write') && (
574
+ <button>Edit User</button>
575
+ )}
576
+ </div>
577
+ );
578
+ }
579
+ ```
580
+
581
+ **AuthUser Properties:**
582
+ - `displayName`: Full name (e.g., "John Doe")
583
+ - `username`: Username (e.g., "johndoe")
584
+ - `email`: Email address
585
+ - `givenName`: First name
586
+ - `familyName`: Last name
587
+ - `initials`: Initials (e.g., "JD")
588
+ - `operations`: Array of granted operation strings
589
+
590
+ </TabItem>
591
+
592
+ <TabItem label="Authorized Component">
593
+
594
+ ```typescript
595
+ import { Authorized } from '@bluealba/pae-ui-react-core';
596
+
597
+ function DashboardPage() {
598
+ return (
599
+ <div>
600
+ <h1>Dashboard</h1>
601
+
602
+ {/* Render only if user has operations */}
603
+ <Authorized operations={['dashboard::read']}>
604
+ <DashboardContent />
605
+ </Authorized>
606
+
607
+ {/* Show alternative content if unauthorized */}
608
+ <Authorized
609
+ operations={['admin::access']}
610
+ forbiddenContent={<div>Admin access required</div>}
611
+ >
612
+ <AdminSection />
613
+ </Authorized>
614
+
615
+ {/* Multiple operations (user needs ALL) */}
616
+ <Authorized operations={['users::read', 'users::write']}>
617
+ <UserManagement />
618
+ </Authorized>
619
+ </div>
620
+ );
621
+ }
622
+ ```
623
+
624
+ **forbiddenContent Options:**
625
+ ```typescript
626
+ // Render function
627
+ forbiddenContent={({ operations }) => (
628
+ <NoAccessMessage operations={operations} />
629
+ )}
630
+
631
+ // Component reference
632
+ forbiddenContent={NoAccessComponent}
633
+
634
+ // Direct JSX
635
+ forbiddenContent={<div>Access Denied</div>}
636
+ ```
637
+
638
+ </TabItem>
639
+
640
+ <TabItem label="Route Protection">
641
+
642
+ ```typescript
643
+ import { useAuth } from '@bluealba/pae-ui-react-core';
644
+ import { Navigate } from 'react-router-dom';
645
+
646
+ // Protected route wrapper
647
+ function ProtectedRoute({
648
+ children,
649
+ operations
650
+ }: {
651
+ children: React.ReactNode;
652
+ operations: string[];
653
+ }) {
654
+ const { hasAccess } = useAuth();
655
+
656
+ if (!hasAccess(...operations)) {
657
+ return <Navigate to="/unauthorized" replace />;
658
+ }
659
+
660
+ return <>{children}</>;
661
+ }
662
+
663
+ // Usage in routes
664
+ function Root() {
665
+ return (
666
+ <BrowserRouter basename="/my-app">
667
+ <Routes>
668
+ <Route path="/" element={<HomePage />} />
669
+
670
+ <Route
671
+ path="/admin"
672
+ element={
673
+ <ProtectedRoute operations={['admin::access']}>
674
+ <AdminPage />
675
+ </ProtectedRoute>
676
+ }
677
+ />
678
+
679
+ <Route path="/unauthorized" element={<UnauthorizedPage />} />
680
+ </Routes>
681
+ </BrowserRouter>
682
+ );
683
+ }
684
+ ```
685
+
686
+ </TabItem>
687
+
688
+ <TabItem label="Menu Authorization">
689
+
690
+ ```typescript
691
+ // menu.ts
692
+ export default [
693
+ {
694
+ label: 'Home',
695
+ path: '/',
696
+ // No operations = visible to all
697
+ },
698
+ {
699
+ label: 'Dashboard',
700
+ path: '/dashboard',
701
+ operations: ['my-app::dashboard::read']
702
+ },
703
+ {
704
+ label: 'Admin',
705
+ operations: ['my-app::admin::access'],
706
+ items: [
707
+ {
708
+ label: 'Users',
709
+ path: '/admin/users',
710
+ operations: ['my-app::users::read']
711
+ },
712
+ {
713
+ label: 'Settings',
714
+ path: '/admin/settings',
715
+ operations: ['my-app::settings::write']
716
+ }
717
+ ]
718
+ }
719
+ ];
720
+ ```
721
+
722
+ The shell automatically filters menu items based on user operations.
723
+
724
+ </TabItem>
725
+ </Tabs>
726
+
727
+ ### Service Communication
728
+
729
+ Communicate with backend services using the `useServiceInvoker` hook.
730
+
731
+ <Tabs>
732
+ <TabItem label="Basic Usage">
733
+
734
+ ```typescript
735
+ import { useServiceInvoker } from '@bluealba/pae-ui-react-core';
736
+
737
+ function UsersComponent() {
738
+ const invoke = useServiceInvoker('@myorg/users-service');
739
+ const [users, setUsers] = useState([]);
740
+
741
+ useEffect(() => {
742
+ // GET request
743
+ invoke('/users', { method: 'GET' })
744
+ .then(res => res.json())
745
+ .then(setUsers)
746
+ .catch(console.error);
747
+ }, [invoke]);
748
+
749
+ return (
750
+ <ul>
751
+ {users.map(user => (
752
+ <li key={user.id}>{user.name}</li>
753
+ ))}
754
+ </ul>
755
+ );
756
+ }
757
+ ```
758
+
759
+ </TabItem>
760
+
761
+ <TabItem label="POST/PUT/DELETE">
762
+
763
+ ```typescript
764
+ import { useServiceInvoker } from '@bluealba/pae-ui-react-core';
765
+
766
+ function UserForm() {
767
+ const invoke = useServiceInvoker('@myorg/users-service');
768
+
769
+ const createUser = async (userData: any) => {
770
+ const response = await invoke('/users', {
771
+ method: 'POST',
772
+ headers: {
773
+ 'Content-Type': 'application/json',
774
+ },
775
+ body: JSON.stringify(userData)
776
+ });
777
+
778
+ if (!response.ok) {
779
+ throw new Error('Failed to create user');
780
+ }
781
+
782
+ return response.json();
783
+ };
784
+
785
+ const updateUser = async (userId: string, userData: any) => {
786
+ const response = await invoke(`/users/${userId}`, {
787
+ method: 'PUT',
788
+ headers: {
789
+ 'Content-Type': 'application/json',
790
+ },
791
+ body: JSON.stringify(userData)
792
+ });
793
+
794
+ return response.json();
795
+ };
796
+
797
+ const deleteUser = async (userId: string) => {
798
+ await invoke(`/users/${userId}`, {
799
+ method: 'DELETE'
800
+ });
801
+ };
802
+
803
+ // ... form implementation
804
+ }
805
+ ```
806
+
807
+ </TabItem>
808
+
809
+ <TabItem label="With React Query">
810
+
811
+ ```typescript
812
+ import { useServiceInvoker } from '@bluealba/pae-ui-react-core';
813
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
814
+
815
+ function UsersManagement() {
816
+ const invoke = useServiceInvoker('@myorg/users-service');
817
+ const queryClient = useQueryClient();
818
+
819
+ // Fetch users
820
+ const { data: users, isLoading } = useQuery({
821
+ queryKey: ['users'],
822
+ queryFn: () => invoke('/users').then(res => res.json())
823
+ });
824
+
825
+ // Create user mutation
826
+ const createMutation = useMutation({
827
+ mutationFn: (userData: any) =>
828
+ invoke('/users', {
829
+ method: 'POST',
830
+ headers: { 'Content-Type': 'application/json' },
831
+ body: JSON.stringify(userData)
832
+ }).then(res => res.json()),
833
+ onSuccess: () => {
834
+ queryClient.invalidateQueries({ queryKey: ['users'] });
835
+ }
836
+ });
837
+
838
+ // Delete user mutation
839
+ const deleteMutation = useMutation({
840
+ mutationFn: (userId: string) =>
841
+ invoke(`/users/${userId}`, { method: 'DELETE' }),
842
+ onSuccess: () => {
843
+ queryClient.invalidateQueries({ queryKey: ['users'] });
844
+ }
845
+ });
846
+
847
+ if (isLoading) return <div>Loading...</div>;
848
+
849
+ return (
850
+ <div>
851
+ {users?.map(user => (
852
+ <div key={user.id}>
853
+ {user.name}
854
+ <button onClick={() => deleteMutation.mutate(user.id)}>
855
+ Delete
856
+ </button>
857
+ </div>
858
+ ))}
859
+ </div>
860
+ );
861
+ }
862
+ ```
863
+
864
+ </TabItem>
865
+
866
+ <TabItem label="Error Handling">
867
+
868
+ ```typescript
869
+ import { useServiceInvoker } from '@bluealba/pae-ui-react-core';
870
+
871
+ function RobustComponent() {
872
+ const invoke = useServiceInvoker('@myorg/my-service');
873
+
874
+ const fetchData = async () => {
875
+ try {
876
+ const response = await invoke('/data', { method: 'GET' });
877
+
878
+ if (!response.ok) {
879
+ if (response.status === 404) {
880
+ throw new Error('Data not found');
881
+ } else if (response.status === 403) {
882
+ throw new Error('Access denied');
883
+ } else {
884
+ throw new Error(`Request failed: ${response.status}`);
885
+ }
886
+ }
887
+
888
+ const data = await response.json();
889
+ return data;
890
+
891
+ } catch (error) {
892
+ if (error instanceof TypeError) {
893
+ // Network error
894
+ console.error('Network error:', error);
895
+ throw new Error('Cannot connect to server');
896
+ }
897
+ throw error;
898
+ }
899
+ };
900
+
901
+ // ... component implementation
902
+ }
903
+ ```
904
+
905
+ </TabItem>
906
+ </Tabs>
907
+
908
+ ### Extension Points
909
+
910
+ Extension points allow modules to declare customizable parts that other modules can extend.
911
+
912
+ <Tabs>
913
+ <TabItem label="Declaring Extension Points">
914
+
915
+ **Inline with Component:**
916
+
917
+ ```typescript
918
+ import { ExtensionPoint } from '@bluealba/pae-ui-react-core';
919
+
920
+ function DashboardPage() {
921
+ return (
922
+ <div>
923
+ <h1>Dashboard</h1>
924
+
925
+ {/* Declare an extension point */}
926
+ <ExtensionPoint name="DashboardWidgets">
927
+ {/* Default content if not extended */}
928
+ <DefaultWidget />
929
+ </ExtensionPoint>
930
+
931
+ <ExtensionPoint name="DashboardActions" />
932
+ </div>
933
+ );
934
+ }
935
+ ```
936
+
937
+ **Whole Component as Extension Point:**
938
+
939
+ ```typescript
940
+ import { extensionPoint } from '@bluealba/pae-ui-react-core';
941
+
942
+ const CustomHeader: React.FC = () => {
943
+ return (
944
+ <header>
945
+ <h1>Default Header</h1>
946
+ </header>
947
+ );
948
+ };
949
+
950
+ // Set display name for extension point identification
951
+ CustomHeader.displayName = 'ApplicationHeader';
952
+
953
+ // Make entire component replaceable
954
+ export default extensionPoint(CustomHeader);
955
+ ```
956
+
957
+ </TabItem>
958
+
959
+ <TabItem label="Extending Extension Points">
960
+
961
+ **Using Component:**
962
+
963
+ ```typescript
964
+ import { ExtendExtensionPoint } from '@bluealba/pae-ui-react-core';
965
+
966
+ function Root() {
967
+ return (
968
+ <div>
969
+ {/* Your app content */}
970
+ <MyAppContent />
971
+
972
+ {/* Extend another module's extension point */}
973
+ <ExtendExtensionPoint
974
+ module="@bluealba/pae-admin-ui"
975
+ point="DashboardWidgets"
976
+ >
977
+ <CustomWidget title="My Custom Widget">
978
+ <p>This content extends the admin dashboard!</p>
979
+ </CustomWidget>
980
+ </ExtendExtensionPoint>
981
+ </div>
982
+ );
983
+ }
984
+ ```
985
+
986
+ **Using Hook:**
987
+
988
+ ```typescript
989
+ import { useExtendExtensionPoint } from '@bluealba/pae-ui-react-core';
990
+
991
+ const CustomApplicationSelector = () => {
992
+ return <div>Custom App Selector</div>;
993
+ };
994
+
995
+ function Root() {
996
+ // Register extension programmatically
997
+ useExtendExtensionPoint(
998
+ '@bluealba/pae-shell-ui',
999
+ 'ApplicationSelector',
1000
+ CustomApplicationSelector
1001
+ );
1002
+
1003
+ return <div>My App Content</div>;
1004
+ }
1005
+ ```
1006
+
1007
+ </TabItem>
1008
+
1009
+ <TabItem label="Real-World Example">
1010
+
1011
+ ```typescript
1012
+ // In pae-admin-ui: Declare extension point
1013
+ import { ExtensionPoint } from '@bluealba/pae-ui-react-core';
1014
+
1015
+ function SettingsPage() {
1016
+ return (
1017
+ <div>
1018
+ <h1>Settings</h1>
1019
+
1020
+ <section>
1021
+ <h2>Core Settings</h2>
1022
+ {/* Core settings content */}
1023
+ </section>
1024
+
1025
+ {/* Allow other modules to add settings sections */}
1026
+ <ExtensionPoint name="AdditionalSettings" />
1027
+ </div>
1028
+ );
1029
+ }
1030
+
1031
+ // In custom-app-ui: Extend the settings page
1032
+ import { ExtendExtensionPoint } from '@bluealba/pae-ui-react-core';
1033
+
1034
+ function Root() {
1035
+ return (
1036
+ <>
1037
+ <MyAppRoutes />
1038
+
1039
+ <ExtendExtensionPoint
1040
+ module="@bluealba/pae-admin-ui"
1041
+ point="AdditionalSettings"
1042
+ >
1043
+ <section>
1044
+ <h2>My App Settings</h2>
1045
+ <CustomSettings />
1046
+ </section>
1047
+ </ExtendExtensionPoint>
1048
+ </>
1049
+ );
1050
+ }
1051
+ ```
1052
+
1053
+ </TabItem>
1054
+ </Tabs>
1055
+
1056
+ ---
1057
+
1058
+ ## Platform Integration
1059
+
1060
+ ### Registering in the Application Catalog
1061
+
1062
+ UI modules must be registered in the catalog to be discovered and loaded by the shell.
1063
+
1064
+ <Tabs>
1065
+ <TabItem label="Via API">
1066
+
1067
+ ```bash
1068
+ POST /api/catalog
1069
+ Content-Type: application/json
1070
+ Authorization: Bearer <admin-jwt>
1071
+
1072
+ {
1073
+ "name": "@myorg/my-app-ui",
1074
+ "type": "app",
1075
+ "displayName": "My Application",
1076
+ "baseUrl": "/my-app",
1077
+ "host": "my-app-ui",
1078
+ "port": 80,
1079
+ "activationRoute": "/my-app",
1080
+ "dependsOn": ["@bluealba/pae-shell-ui"]
1081
+ "bundleFile": "my-app-ui.js",
1082
+ "mountAtSelector": "#pae-shell-ui-content",
1083
+ "customProps": {},
1084
+ "applicationName": "my-app-id",
1085
+ "authorization": {
1086
+ "operations": []
1087
+ }
1088
+ }
1089
+ ```
1090
+
1091
+ <Aside type="tip">
1092
+ Use `"type": "tool"` instead of `"type": "app"` if your module should appear in the navbar tools section (bottom area) rather than the application selector. This is appropriate for platform utilities and administration modules such as Admin UI or Documentation UI.
1093
+ </Aside>
1094
+
1095
+ </TabItem>
1096
+
1097
+ <TabItem label="Via Bootstrap">
1098
+
1099
+ For automated deployments, include in bootstrap configuration:
1100
+
1101
+ ```json
1102
+ {
1103
+ {
1104
+ "name": "@myorg/my-app-ui",
1105
+ "displayName": "My Application",
1106
+ "type": "app",
1107
+ "baseUrl": "/my-app",
1108
+ "service": {
1109
+ "host": "my-app-ui",
1110
+ "port": 80
1111
+ },
1112
+ "ui": {
1113
+ "route": "/my-app",
1114
+ "mountAtSelector": "#pae-shell-ui-content",
1115
+ "bundleFile": "my-app-ui.js",
1116
+ "customProps": {}
1117
+ },
1118
+ "dependsOn": ["@bluealba/pae-shell-ui"]
1119
+ },
1120
+ }
1121
+ ```
1122
+
1123
+ </TabItem>
1124
+
1125
+ </Tabs>
1126
+
1127
+ ### Menu Integration
1128
+
1129
+ The shell automatically renders your menu in the navigation sidebar.
1130
+
1131
+ <Tabs>
1132
+ <TabItem label="Simple Menu">
1133
+
1134
+ ```typescript
1135
+ // menu.ts
1136
+ export default [
1137
+ {
1138
+ label: 'Home',
1139
+ path: '/'
1140
+ },
1141
+ {
1142
+ label: 'Dashboard',
1143
+ path: '/dashboard'
1144
+ },
1145
+ {
1146
+ label: 'Settings',
1147
+ path: '/settings'
1148
+ }
1149
+ ];
1150
+ ```
1151
+
1152
+ </TabItem>
1153
+
1154
+ <TabItem label="Nested Menu">
1155
+
1156
+ ```typescript
1157
+ // menu.ts
1158
+ export default [
1159
+ {
1160
+ label: 'Home',
1161
+ path: '/'
1162
+ },
1163
+ {
1164
+ label: 'Management',
1165
+ items: [
1166
+ {
1167
+ label: 'Users',
1168
+ path: '/users'
1169
+ },
1170
+ {
1171
+ label: 'Roles',
1172
+ path: '/roles'
1173
+ },
1174
+ {
1175
+ label: 'Permissions',
1176
+ path: '/permissions'
1177
+ }
1178
+ ]
1179
+ },
1180
+ {
1181
+ label: 'Reports',
1182
+ items: [
1183
+ {
1184
+ label: 'Analytics',
1185
+ path: '/reports/analytics'
1186
+ },
1187
+ {
1188
+ label: 'Audit Log',
1189
+ path: '/reports/audit'
1190
+ }
1191
+ ]
1192
+ }
1193
+ ];
1194
+ ```
1195
+
1196
+ </TabItem>
1197
+
1198
+ <TabItem label="With Operations">
1199
+
1200
+ ```typescript
1201
+ // menu.ts
1202
+ export default [
1203
+ {
1204
+ label: 'Home',
1205
+ path: '/',
1206
+ // No operations = visible to all
1207
+ },
1208
+ {
1209
+ label: 'Dashboard',
1210
+ path: '/dashboard',
1211
+ operations: ['my-app::dashboard::read']
1212
+ },
1213
+ {
1214
+ label: 'Admin',
1215
+ // Parent requires operation
1216
+ operations: ['my-app::admin::access'],
1217
+ items: [
1218
+ {
1219
+ label: 'Users',
1220
+ path: '/admin/users',
1221
+ // Child inherits parent operations + own operations
1222
+ operations: ['my-app::users::manage']
1223
+ },
1224
+ {
1225
+ label: 'Settings',
1226
+ path: '/admin/settings',
1227
+ operations: ['my-app::settings::write']
1228
+ }
1229
+ ]
1230
+ }
1231
+ ];
1232
+ ```
1233
+
1234
+ Shell filters menu items based on user operations automatically.
1235
+
1236
+ </TabItem>
1237
+
1238
+ <TabItem label="Dynamic Menu">
1239
+
1240
+ ```typescript
1241
+ // menu.ts - can export a function for dynamic menus
1242
+ export default function getMenu(context: any) {
1243
+ const items = [
1244
+ {
1245
+ label: 'Home',
1246
+ path: '/'
1247
+ }
1248
+ ];
1249
+
1250
+ // Add items conditionally
1251
+ if (context.user.hasFeature('advanced')) {
1252
+ items.push({
1253
+ label: 'Advanced',
1254
+ path: '/advanced'
1255
+ });
1256
+ }
1257
+
1258
+ return items;
1259
+ }
1260
+ ```
1261
+
1262
+ </TabItem>
1263
+ </Tabs>
1264
+
1265
+ ### Application Icon
1266
+
1267
+ Provide an icon for the application selector in the shell.
1268
+
1269
+ ```typescript
1270
+ // Icon.tsx
1271
+ const Icon = () => (
1272
+ <svg
1273
+ width="24"
1274
+ height="24"
1275
+ viewBox="0 0 24 24"
1276
+ fill="none"
1277
+ >
1278
+ <path
1279
+ d="M12 2L2 7v10c0 5.5 4.5 10 10 10s10-4.5 10-10V7L12 2z"
1280
+ fill="currentColor"
1281
+ />
1282
+ </svg>
1283
+ );
1284
+
1285
+ export default Icon;
1286
+ ```
1287
+
1288
+ **Icon Guidelines:**
1289
+ - Size: 24x24px viewBox
1290
+ - Color: Use `currentColor` for theme support
1291
+ - Format: Inline SVG component
1292
+ - Style: Simple, recognizable at small sizes
1293
+ - Export: Default export from `Icon.tsx`
1294
+
1295
+ ---
1296
+
1297
+ ## State Management
1298
+
1299
+ Choose a state management solution based on your needs.
1300
+
1301
+ <Tabs>
1302
+ <TabItem label="React Context">
1303
+
1304
+ ```typescript
1305
+ // AppContext.tsx
1306
+ import { createContext, useContext, useState } from 'react';
1307
+
1308
+ interface AppState {
1309
+ user: any;
1310
+ settings: any;
1311
+ }
1312
+
1313
+ const AppContext = createContext<{
1314
+ state: AppState;
1315
+ setState: (state: Partial<AppState>) => void;
1316
+ } | null>(null);
1317
+
1318
+ export function AppProvider({ children }: { children: React.ReactNode }) {
1319
+ const [state, setState] = useState<AppState>({
1320
+ user: null,
1321
+ settings: {}
1322
+ });
1323
+
1324
+ const updateState = (updates: Partial<AppState>) => {
1325
+ setState(prev => ({ ...prev, ...updates }));
1326
+ };
1327
+
1328
+ return (
1329
+ <AppContext.Provider value={{ state, setState: updateState }}>
1330
+ {children}
1331
+ </AppContext.Provider>
1332
+ );
1333
+ }
1334
+
1335
+ export function useAppState() {
1336
+ const context = useContext(AppContext);
1337
+ if (!context) throw new Error('useAppState must be used within AppProvider');
1338
+ return context;
1339
+ }
1340
+ ```
1341
+
1342
+ </TabItem>
1343
+
1344
+ <TabItem label="Zustand">
1345
+
1346
+ ```typescript
1347
+ // store.ts
1348
+ import { create } from 'zustand';
1349
+
1350
+ interface AppStore {
1351
+ user: any;
1352
+ settings: any;
1353
+ setUser: (user: any) => void;
1354
+ setSettings: (settings: any) => void;
1355
+ }
1356
+
1357
+ export const useStore = create<AppStore>((set) => ({
1358
+ user: null,
1359
+ settings: {},
1360
+ setUser: (user) => set({ user }),
1361
+ setSettings: (settings) => set({ settings }),
1362
+ }));
1363
+
1364
+ // Usage
1365
+ function Component() {
1366
+ const user = useStore(state => state.user);
1367
+ const setUser = useStore(state => state.setUser);
1368
+
1369
+ return <div>{user?.name}</div>;
1370
+ }
1371
+ ```
1372
+
1373
+ </TabItem>
1374
+
1375
+ <TabItem label="React Query">
1376
+
1377
+ ```typescript
1378
+ // Ideal for server state management
1379
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1380
+
1381
+ const queryClient = new QueryClient({
1382
+ defaultOptions: {
1383
+ queries: {
1384
+ refetchOnWindowFocus: false,
1385
+ retry: 1
1386
+ }
1387
+ }
1388
+ });
1389
+
1390
+ function Root() {
1391
+ return (
1392
+ <QueryClientProvider client={queryClient}>
1393
+ <App />
1394
+ </QueryClientProvider>
1395
+ );
1396
+ }
1397
+ ```
1398
+
1399
+ </TabItem>
1400
+
1401
+ <TabItem label="Platform State">
1402
+
1403
+ ```typescript
1404
+ // Access platform-wide state
1405
+ import { usePAEState } from '@bluealba/pae-ui-react-core';
1406
+
1407
+ function Component() {
1408
+ const paeState = usePAEState();
1409
+
1410
+ // Access catalog
1411
+ console.log(paeState.catalog);
1412
+
1413
+ // Access current user
1414
+ console.log(paeState.user);
1415
+
1416
+ // Access current application
1417
+ console.log(paeState.currentApplication);
1418
+
1419
+ return <div>Component</div>;
1420
+ }
1421
+ ```
1422
+
1423
+ </TabItem>
1424
+ </Tabs>
1425
+
1426
+ ---
1427
+
1428
+ ## Best Practices
1429
+
1430
+ ### Component Organization
1431
+
1432
+ <CardGrid>
1433
+ <Card title="Feature-Based Structure" icon="folder">
1434
+ Organize by feature/domain rather than technical type (components, hooks, etc.).
1435
+ </Card>
1436
+
1437
+ <Card title="Colocation" icon="document">
1438
+ Keep related files together: component, styles, tests, and types in the same directory.
1439
+ </Card>
1440
+
1441
+ <Card title="Barrel Exports" icon="seti:javascript">
1442
+ Use index files to create clean import paths and encapsulate internal structure.
1443
+ </Card>
1444
+
1445
+ <Card title="Separation of Concerns" icon="setting">
1446
+ Separate presentational components from container components and business logic.
1447
+ </Card>
1448
+ </CardGrid>
1449
+
1450
+ **Recommended Structure:**
1451
+
1452
+ ```
1453
+ src/
1454
+ ├── features/
1455
+ │ ├── users/
1456
+ │ │ ├── components/
1457
+ │ │ │ ├── UserList.tsx
1458
+ │ │ │ ├── UserList.test.tsx
1459
+ │ │ │ ├── UserList.module.css
1460
+ │ │ │ └── index.ts
1461
+ │ │ ├── hooks/
1462
+ │ │ │ └── useUsers.ts
1463
+ │ │ ├── services/
1464
+ │ │ │ └── usersApi.ts
1465
+ │ │ ├── types/
1466
+ │ │ │ └── User.ts
1467
+ │ │ └── index.ts
1468
+ │ └── dashboard/
1469
+ ├── shared/
1470
+ │ ├── components/
1471
+ │ ├── hooks/
1472
+ │ └── utils/
1473
+ ├── main.tsx
1474
+ └── root.component.tsx
1475
+ ```
1476
+
1477
+ ### Error Handling
1478
+
1479
+ ```typescript
1480
+ // Global error boundary
1481
+ import { ErrorBoundary } from 'react-error-boundary';
1482
+
1483
+ function ErrorFallback({ error, resetErrorBoundary }: any) {
1484
+ return (
1485
+ <div role="alert">
1486
+ <h2>Something went wrong</h2>
1487
+ <pre>{error.message}</pre>
1488
+ <button onClick={resetErrorBoundary}>Try again</button>
1489
+ </div>
1490
+ );
1491
+ }
1492
+
1493
+ function Root() {
1494
+ return (
1495
+ <ErrorBoundary FallbackComponent={ErrorFallback}>
1496
+ <App />
1497
+ </ErrorBoundary>
1498
+ );
1499
+ }
1500
+
1501
+ // Error handling in async operations
1502
+ async function fetchData() {
1503
+ try {
1504
+ const response = await invoke('/data');
1505
+ if (!response.ok) {
1506
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1507
+ }
1508
+ return await response.json();
1509
+ } catch (error) {
1510
+ console.error('Failed to fetch data:', error);
1511
+ // Show user-friendly error message
1512
+ showNotification('Failed to load data. Please try again.', 'error');
1513
+ throw error;
1514
+ }
1515
+ }
1516
+ ```
1517
+
1518
+ ### Accessibility
1519
+
1520
+ ```typescript
1521
+ // Semantic HTML
1522
+ function AccessibleForm() {
1523
+ return (
1524
+ <form onSubmit={handleSubmit}>
1525
+ <label htmlFor="username">
1526
+ Username
1527
+ <input
1528
+ id="username"
1529
+ type="text"
1530
+ aria-required="true"
1531
+ aria-describedby="username-help"
1532
+ />
1533
+ </label>
1534
+ <span id="username-help">Enter your username</span>
1535
+
1536
+ <button type="submit">Submit</button>
1537
+ </form>
1538
+ );
1539
+ }
1540
+
1541
+ // Keyboard navigation
1542
+ function AccessibleButton() {
1543
+ return (
1544
+ <button
1545
+ onClick={handleClick}
1546
+ onKeyDown={(e) => {
1547
+ if (e.key === 'Enter' || e.key === ' ') {
1548
+ handleClick();
1549
+ }
1550
+ }}
1551
+ aria-label="Close dialog"
1552
+ >
1553
+ ×
1554
+ </button>
1555
+ );
1556
+ }
1557
+
1558
+ // Focus management
1559
+ function Modal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
1560
+ const modalRef = useRef<HTMLDivElement>(null);
1561
+
1562
+ useEffect(() => {
1563
+ if (isOpen) {
1564
+ modalRef.current?.focus();
1565
+ }
1566
+ }, [isOpen]);
1567
+
1568
+ return (
1569
+ <div
1570
+ ref={modalRef}
1571
+ role="dialog"
1572
+ aria-modal="true"
1573
+ tabIndex={-1}
1574
+ >
1575
+ {/* Modal content */}
1576
+ </div>
1577
+ );
1578
+ }
1579
+ ```
1580
+
1581
+ ### Testing
1582
+
1583
+ ```typescript
1584
+ // Component testing
1585
+ import { render, screen, fireEvent } from '@testing-library/react';
1586
+ import { UserList } from './UserList';
1587
+
1588
+ describe('UserList', () => {
1589
+ it('renders users', () => {
1590
+ const users = [{ id: '1', name: 'John' }];
1591
+ render(<UserList users={users} />);
1592
+
1593
+ expect(screen.getByText('John')).toBeInTheDocument();
1594
+ });
1595
+
1596
+ it('handles user click', () => {
1597
+ const onUserClick = jest.fn();
1598
+ const users = [{ id: '1', name: 'John' }];
1599
+
1600
+ render(<UserList users={users} onUserClick={onUserClick} />);
1601
+
1602
+ fireEvent.click(screen.getByText('John'));
1603
+ expect(onUserClick).toHaveBeenCalledWith(users[0]);
1604
+ });
1605
+ });
1606
+
1607
+ // Hook testing
1608
+ import { renderHook } from '@testing-library/react';
1609
+ import { useAuth } from '@bluealba/pae-ui-react-core';
1610
+
1611
+ describe('useAuth', () => {
1612
+ it('returns auth user', () => {
1613
+ const { result } = renderHook(() => useAuth());
1614
+
1615
+ expect(result.current.authUser).toBeDefined();
1616
+ });
1617
+ });
1618
+ ```
1619
+
1620
+ ---
1621
+
1622
+ ## Troubleshooting
1623
+
1624
+ <Tabs>
1625
+ <TabItem label="Build Failures">
1626
+
1627
+ **Problem: Module not found errors**
1628
+
1629
+ ```
1630
+ ERROR in ./src/main.tsx
1631
+ Module not found: Error: Can't resolve '@bluealba/pae-ui-react-core'
1632
+ ```
1633
+
1634
+ **Solution:**
1635
+ ```bash
1636
+ # Install missing dependencies
1637
+ npm install
1638
+
1639
+ # Clear node_modules and reinstall
1640
+ rm -rf node_modules package-lock.json
1641
+ npm install
1642
+ ```
1643
+
1644
+ ---
1645
+
1646
+ **Problem: TypeScript errors**
1647
+
1648
+ ```
1649
+ TS2307: Cannot find module '@bluealba/pae-ui-react-core'
1650
+ ```
1651
+
1652
+ **Solution:**
1653
+ ```bash
1654
+ # Ensure types are available
1655
+ npm install --save-dev @types/react @types/react-dom
1656
+
1657
+ # Rebuild
1658
+ npm run build
1659
+ ```
1660
+
1661
+ </TabItem>
1662
+
1663
+ <TabItem label="Runtime Errors">
1664
+
1665
+ **Problem: Module doesn't load in shell**
1666
+
1667
+ **Checklist:**
1668
+ 1. Verify bundle URL is accessible:
1669
+ ```bash
1670
+ curl https://cdn.example.com/my-app/main.js
1671
+ ```
1672
+
1673
+ 2. Check browser console for errors
1674
+
1675
+ 3. Verify catalog registration:
1676
+ ```bash
1677
+ curl -H "Authorization: Bearer $TOKEN" \
1678
+ https://platform.example.com/api/catalog
1679
+ ```
1680
+
1681
+ 4. Check module exports bootstrap, mount, unmount:
1682
+ ```typescript
1683
+ export const { bootstrap, mount, unmount } = initializeMicroFrontend(/*...*/);
1684
+ ```
1685
+
1686
+ ---
1687
+
1688
+ **Problem: Routing doesn't work**
1689
+
1690
+ **Solution:**
1691
+ Ensure `basename` matches catalog route:
1692
+
1693
+ ```typescript
1694
+ // Catalog route: /my-app
1695
+ <BrowserRouter basename="/my-app">
1696
+ ```
1697
+
1698
+ </TabItem>
1699
+
1700
+ <TabItem label="Authorization Issues">
1701
+
1702
+ **Problem: User can't access module**
1703
+
1704
+ **Causes:**
1705
+
1706
+ 1. **Missing operation:**
1707
+ - Check user operations in Admin UI
1708
+ - Grant required operation to user/role
1709
+
1710
+ 2. **Wrong route:**
1711
+ - Verify route in catalog matches navigation
1712
+
1713
+ **Debug:**
1714
+ ```typescript
1715
+ import { useAuth } from '@bluealba/pae-ui-react-core';
1716
+
1717
+ function Debug() {
1718
+ const { authUser } = useAuth();
1719
+ console.log('User operations:', authUser.operations);
1720
+ return null;
1721
+ }
1722
+ ```
1723
+
1724
+ </TabItem>
1725
+
1726
+ <TabItem label="Performance Issues">
1727
+
1728
+ **Problem: Slow initial load**
1729
+
1730
+ **Solutions:**
1731
+
1732
+ 1. **Code splitting:**
1733
+ ```typescript
1734
+ const HeavyComponent = lazy(() => import('./HeavyComponent'));
1735
+ ```
1736
+
1737
+ 2. **Bundle analysis:**
1738
+ ```bash
1739
+ ANALYZE=true npm run build
1740
+ ```
1741
+
1742
+ 3. **Optimize dependencies:**
1743
+ ```json
1744
+ // Use lighter alternatives
1745
+ // dayjs instead of moment
1746
+ // zustand instead of redux
1747
+ ```
1748
+
1749
+ ---
1750
+
1751
+ **Problem: Slow navigation**
1752
+
1753
+ **Solution:**
1754
+ Preload routes:
1755
+ ```typescript
1756
+ import { Link } from 'react-router-dom';
1757
+
1758
+ <Link
1759
+ to="/dashboard"
1760
+ onMouseEnter={() => {
1761
+ // Preload route component
1762
+ import('./views/DashboardPage');
1763
+ }}
1764
+ >
1765
+ Dashboard
1766
+ </Link>
1767
+ ```
1768
+
1769
+ </TabItem>
1770
+ </Tabs>
1771
+
1772
+ ---
1773
+
1774
+ ## Deployment
1775
+
1776
+ ### Building for Production
1777
+
1778
+ ```bash
1779
+ # Build optimized bundle
1780
+ npm run build
1781
+
1782
+ # Output: dist/main.js and dist/assets/
1783
+ ```
1784
+
1785
+ ### Hosting Options
1786
+
1787
+ <Tabs>
1788
+
1789
+ <TabItem label="Docker Container">
1790
+
1791
+ ```dockerfile
1792
+ FROM nginxinc/nginx-unprivileged:mainline-alpine-slim
1793
+
1794
+ WORKDIR /usr/share/nginx/html
1795
+
1796
+ COPY ./version.json ./version.json
1797
+ COPY ./dist /usr/share/nginx/html
1798
+
1799
+ COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
1800
+
1801
+ EXPOSE 8080
1802
+ ```
1803
+
1804
+ ```nginx
1805
+ # nginx.conf
1806
+ server {
1807
+ listen 8080;
1808
+ listen [::]:8080;
1809
+ server_name localhost;
1810
+
1811
+ location / {
1812
+ root /usr/share/nginx/html;
1813
+ index index.html index.htm;
1814
+ }
1815
+
1816
+ location /version {
1817
+ default_type application/json;
1818
+ alias /usr/share/nginx/html/version.json;
1819
+ }
1820
+
1821
+ error_page 500 502 503 504 /50x.html;
1822
+ location = /50x.html {
1823
+ root /usr/share/nginx/html;
1824
+ }
1825
+ }
1826
+ ```
1827
+
1828
+ Deploy:
1829
+ ```bash
1830
+ docker build -t my-app-ui:1.0.0 .
1831
+ docker push registry.example.com/my-app-ui:1.0.0
1832
+ ```
1833
+
1834
+ </TabItem>
1835
+ </Tabs>
1836
+
1837
+ ### Versioning Strategy
1838
+
1839
+ ```bash
1840
+ # Semantic versioning
1841
+ v1.0.0 - Initial release
1842
+ v1.1.0 - New features (backward compatible)
1843
+ v1.1.1 - Bug fixes
1844
+ v2.0.0 - Breaking changes
1845
+
1846
+ ```
1847
+
1848
+ ---
1849
+
1850
+ ## Related Documentation
1851
+
1852
+ - **[Architecture Overview](../architecture/overview)** - Understand platform architecture
1853
+ - **[Gateway Architecture](../architecture/gateway-architecture)** - Learn about the API gateway
1854
+ - **[Shell Architecture](../architecture/shell)** - Deep dive into the platform shell
1855
+ - **[UI Extension Points](../architecture/ui-extension-points)** - Master extension point patterns
1856
+ - **[Authorization System](../architecture/authorization-system)** - Understand operations and RBAC
1857
+ - **[Adding Documentation Sites](./adding-documentation-sites)** - Document your UI module
1858
+ - **[Using Feature Flags](./using-feature-flags)** - Implement feature toggles
1859
+
1860
+ ---