@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.
- package/dist/index.js +278 -15
- package/docs/404.mdx +5 -0
- package/docs/architecture/api-explorer.mdx +478 -0
- package/docs/architecture/architecture-diagrams.mdx +12 -0
- package/docs/architecture/authentication-system.mdx +903 -0
- package/docs/architecture/authorization-system.mdx +886 -0
- package/docs/architecture/bootstrap.mdx +1442 -0
- package/docs/architecture/gateway-architecture.mdx +845 -0
- package/docs/architecture/multi-tenancy.mdx +1150 -0
- package/docs/architecture/overview.mdx +776 -0
- package/docs/architecture/scheduler.mdx +818 -0
- package/docs/architecture/shell.mdx +885 -0
- package/docs/architecture/ui-extension-points.mdx +781 -0
- package/docs/architecture/user-states.mdx +794 -0
- package/docs/development/overview.mdx +21 -0
- package/docs/development/workflow.mdx +914 -0
- package/docs/getting-started/core-concepts.mdx +892 -0
- package/docs/getting-started/installation.mdx +780 -0
- package/docs/getting-started/overview.mdx +83 -0
- package/docs/getting-started/quick-start.mdx +940 -0
- package/docs/guides/adding-documentation-sites.mdx +1367 -0
- package/docs/guides/creating-services.mdx +1736 -0
- package/docs/guides/creating-ui-modules.mdx +1860 -0
- package/docs/guides/identity-providers.mdx +1007 -0
- package/docs/guides/mermaid-diagrams.mdx +212 -0
- package/docs/guides/using-feature-flags.mdx +1059 -0
- package/docs/guides/working-with-rooms.mdx +566 -0
- package/docs/index.mdx +57 -0
- package/docs/platform-cli/commands.mdx +604 -0
- package/docs/platform-cli/overview.mdx +195 -0
- package/package.json +5 -2
- package/skills/ba-platform/platform-cli.skill.md +26 -0
- package/skills/ba-platform/platform.skill.md +35 -0
- package/templates/application-monorepo-template/gitignore +95 -0
- package/templates/bootstrap-service-template/Dockerfile.development +1 -1
- package/templates/bootstrap-service-template/gitignore +57 -0
- package/templates/bootstrap-service-template/package.json +1 -1
- package/templates/bootstrap-service-template/src/main.ts +6 -16
- package/templates/customization-ui-module-template/Dockerfile.development +1 -1
- package/templates/customization-ui-module-template/gitignore +73 -0
- package/templates/nestjs-service-module-template/Dockerfile.development +1 -1
- package/templates/nestjs-service-module-template/gitignore +56 -0
- package/templates/platform-init-template/{{platformName}}-core/gitignore +97 -0
- package/templates/platform-init-template/{{platformName}}-core/local/.env.example +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/platform-docker-compose.yml +1 -1
- package/templates/platform-init-template/{{platformName}}-core/local/{{platformName}}-core-docker-compose.yml +0 -1
- package/templates/react-ui-module-template/Dockerfile +1 -1
- package/templates/react-ui-module-template/Dockerfile.development +1 -3
- package/templates/react-ui-module-template/caddy/Caddyfile +1 -1
- package/templates/react-ui-module-template/gitignore +72 -0
- package/templates/react-ui-module-template/Dockerfile_nginx +0 -11
- package/templates/react-ui-module-template/nginx/default.conf +0 -23
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Working with Rooms
|
|
3
|
+
description: Complete guide to implementing real-time presence tracking using the Blue Alba Platform's rooms system
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { Card, CardGrid, Aside, Tabs, TabItem, Code } from '@astrojs/starlight/components';
|
|
7
|
+
import MermaidDiagram from '~/components/MermaidDiagram.astro';
|
|
8
|
+
|
|
9
|
+
The Blue Alba Platform's rooms system provides real-time presence tracking for users within application contexts. This built-in feature enables you to see who is currently viewing or working in the same space, perfect for implementing collaborative features, live user indicators, and shared presence experiences.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The rooms system enables you to:
|
|
14
|
+
|
|
15
|
+
- Track which users are currently active in a specific context (room)
|
|
16
|
+
- Display real-time user avatars showing who else is present
|
|
17
|
+
- Build collaborative features without managing WebSocket infrastructure
|
|
18
|
+
- Implement live indicators for shared documents, dashboards, or views
|
|
19
|
+
- Show presence information with minimal code
|
|
20
|
+
|
|
21
|
+
### Use Cases
|
|
22
|
+
|
|
23
|
+
- **Collaborative Editing**: Show who else is viewing or editing a document
|
|
24
|
+
- **Live Dashboards**: Display users currently viewing a dashboard
|
|
25
|
+
- **Real-time Indicators**: Add "currently viewing" badges to any page
|
|
26
|
+
- **Team Awareness**: Help teams know when colleagues are in the same context
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Architecture Overview
|
|
31
|
+
|
|
32
|
+
The rooms system consists of two integrated components:
|
|
33
|
+
|
|
34
|
+
<MermaidDiagram
|
|
35
|
+
title="Rooms System Architecture"
|
|
36
|
+
code={`graph TB
|
|
37
|
+
A[React Application<br/>with RoomsProvider]:::ui --> B[WebSocket Connection]:::ws
|
|
38
|
+
B --> C[pae-rooms-service<br/>WebSocket Gateway]:::service
|
|
39
|
+
C --> D[User Registry<br/>Rooms & Connections]:::data
|
|
40
|
+
|
|
41
|
+
E[Gateway<br/>Authentication]:::gateway --> C
|
|
42
|
+
|
|
43
|
+
F[Catalog Service]:::catalog --> A
|
|
44
|
+
|
|
45
|
+
A -->|joinRoom| C
|
|
46
|
+
A -->|leaveRoom| C
|
|
47
|
+
C -->|user_list broadcast| A
|
|
48
|
+
|
|
49
|
+
classDef ui fill:#87CEEB,color:#333
|
|
50
|
+
classDef ws fill:#FFB6C1,color:#333
|
|
51
|
+
classDef service fill:#90EE90,color:#333
|
|
52
|
+
classDef data fill:#FFD700,color:#333
|
|
53
|
+
classDef gateway fill:#DDA0DD,color:#333
|
|
54
|
+
classDef catalog fill:#F0E68C,color:#333`}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
### Backend Service: pae-rooms-service
|
|
58
|
+
|
|
59
|
+
The backend is a NestJS WebSocket gateway that:
|
|
60
|
+
- Manages real-time connections from UI applications
|
|
61
|
+
- Tracks which users are in which rooms
|
|
62
|
+
- Broadcasts user list updates to all connected clients
|
|
63
|
+
- Runs behind the gateway (authentication handled upstream)
|
|
64
|
+
|
|
65
|
+
<Aside type="note" title="Built-in Module">
|
|
66
|
+
The rooms service is a **built-in platform module**, so no bootstrap configuration is needed. Simply deploy the service with the name 'pae-rooms-service'.
|
|
67
|
+
</Aside>
|
|
68
|
+
|
|
69
|
+
### Frontend Library
|
|
70
|
+
|
|
71
|
+
The frontend components are available in the `@bluealba/pae-ui-react-core` package, providing:
|
|
72
|
+
- `RoomsProvider`: React context provider managing WebSocket connection
|
|
73
|
+
- `useRoom`: Hook for joining rooms and accessing user lists
|
|
74
|
+
- `RoomAvatars`: Pre-built component for displaying user avatars
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Backend Deployment
|
|
79
|
+
|
|
80
|
+
The `pae-rooms-service` must be deployed in your infrastructure.
|
|
81
|
+
|
|
82
|
+
### Docker Compose Example
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
services:
|
|
86
|
+
pae-rooms-service:
|
|
87
|
+
image: your-registry/pae-rooms-service:latest
|
|
88
|
+
environment:
|
|
89
|
+
- PORT=3000
|
|
90
|
+
- NODE_ENV=production
|
|
91
|
+
ports:
|
|
92
|
+
- "3010:3000"
|
|
93
|
+
networks:
|
|
94
|
+
- platform-network
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
<Aside type="tip" title="Zero Configuration">
|
|
98
|
+
The rooms service requires no special configuration. Once deployed and accessible, it's ready to use. The gateway handles authentication and forwards user information via headers.
|
|
99
|
+
</Aside>
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Frontend Integration
|
|
104
|
+
|
|
105
|
+
This is where you'll spend most of your time - integrating rooms functionality into your React applications.
|
|
106
|
+
|
|
107
|
+
### Installation
|
|
108
|
+
|
|
109
|
+
The rooms functionality is available in the `@bluealba/pae-ui-react-core` package, which should already be installed in your UI module:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install @bluealba/pae-ui-react-core
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Setting Up the RoomsProvider
|
|
116
|
+
|
|
117
|
+
The `RoomsProvider` must wrap any components that need to use rooms functionality. Typically, you'll add it at a high level in your component tree.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { RoomsProvider } from '@bluealba/pae-ui-react-core';
|
|
121
|
+
|
|
122
|
+
function App() {
|
|
123
|
+
return (
|
|
124
|
+
<RoomsProvider>
|
|
125
|
+
{/* Your application components */}
|
|
126
|
+
<Dashboard />
|
|
127
|
+
<DocumentViewer />
|
|
128
|
+
<WorkspaceArea />
|
|
129
|
+
</RoomsProvider>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default App;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**What the RoomsProvider does:**
|
|
137
|
+
- Automatically establishes a WebSocket connection to the rooms service
|
|
138
|
+
- Retrieves the service URL from the platform's application catalog
|
|
139
|
+
- Manages connection state (loading, connected, closed, error)
|
|
140
|
+
- Handles all WebSocket communication internally
|
|
141
|
+
- Broadcasts user list updates to all consuming components
|
|
142
|
+
- Cleans up the connection when unmounted
|
|
143
|
+
|
|
144
|
+
<Aside type="caution">
|
|
145
|
+
Any component attempting to use `useRoom` outside of a `RoomsProvider` will throw an error. Make sure the provider wraps all components that need rooms functionality.
|
|
146
|
+
</Aside>
|
|
147
|
+
|
|
148
|
+
### Using the useRoom Hook
|
|
149
|
+
|
|
150
|
+
The `useRoom` hook is your primary interface for working with rooms. It automatically manages room membership and provides the current user list.
|
|
151
|
+
|
|
152
|
+
#### Basic Usage (Automatic Mode)
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { useRoom } from '@bluealba/pae-ui-react-core';
|
|
156
|
+
|
|
157
|
+
function DocumentViewer({ documentId }) {
|
|
158
|
+
const { users, roomId, hasJoined } = useRoom(`document-${documentId}`);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
<h1>Document Viewer</h1>
|
|
163
|
+
<p>Room: {roomId}</p>
|
|
164
|
+
<p>Status: {hasJoined ? 'Joined' : 'Not in room'}</p>
|
|
165
|
+
<p>Currently viewing: {users.length} user(s)</p>
|
|
166
|
+
|
|
167
|
+
<ul>
|
|
168
|
+
{users.map(user => (
|
|
169
|
+
<li key={user.username}>{user.displayName}</li>
|
|
170
|
+
))}
|
|
171
|
+
</ul>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Default Hook Behavior:**
|
|
178
|
+
- Automatically joins the specified room when the component mounts
|
|
179
|
+
- Automatically leaves the room when the component unmounts
|
|
180
|
+
- Re-joins if the `roomId` parameter changes
|
|
181
|
+
- Returns the current list of users in that room
|
|
182
|
+
- Updates automatically when users join or leave
|
|
183
|
+
|
|
184
|
+
#### Hook Options
|
|
185
|
+
|
|
186
|
+
The `useRoom` hook accepts an optional second parameter for controlling automatic behavior:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
interface UseRoomOptions {
|
|
190
|
+
/**
|
|
191
|
+
* Automatically join the room when the hook is used.
|
|
192
|
+
* Defaults to true.
|
|
193
|
+
*/
|
|
194
|
+
autoJoin?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Automatically leave the room when the component unmounts.
|
|
197
|
+
* Defaults to true.
|
|
198
|
+
*/
|
|
199
|
+
autoLeave?: boolean;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Default Options:** `{ autoJoin: true, autoLeave: true }` (backward compatible with previous versions)
|
|
204
|
+
|
|
205
|
+
**Return Value:**
|
|
206
|
+
```typescript
|
|
207
|
+
interface UseRoomReturn {
|
|
208
|
+
users: RoomUser[]; // Array of users currently in the room
|
|
209
|
+
roomId: string; // The current room ID
|
|
210
|
+
hasJoined: boolean; // Whether the authenticated user has joined the room
|
|
211
|
+
joinRoom: () => void; // Function to manually join the room
|
|
212
|
+
leaveRoom: () => void; // Function to manually leave the room
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Understanding RoomUser Type
|
|
217
|
+
|
|
218
|
+
Each user in the room is represented by a `RoomUser` object:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
interface RoomUser {
|
|
222
|
+
username: string; // Unique username from authentication
|
|
223
|
+
displayName: string; // User's display name (human-readable)
|
|
224
|
+
rooms: string[]; // All rooms this user is currently in
|
|
225
|
+
color?: string; // Auto-generated color for avatars
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Key Points:**
|
|
230
|
+
- `username`: Unique identifier, suitable for keys in React lists
|
|
231
|
+
- `displayName`: What you should show to end users
|
|
232
|
+
- `rooms`: Array of all room IDs the user has joined (useful for multi-room scenarios)
|
|
233
|
+
- `color`: Automatically generated consistent color based on username, perfect for avatars
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Manual Room Control
|
|
238
|
+
|
|
239
|
+
In some scenarios, you may want full control over when a user joins or leaves a room rather than relying on automatic behavior. The `useRoom` hook supports this through manual mode.
|
|
240
|
+
|
|
241
|
+
<Aside type="tip" title="When to Use Manual Mode">
|
|
242
|
+
Manual room control is useful when:
|
|
243
|
+
- Room joining should be triggered by user action (e.g., clicking "Join Session")
|
|
244
|
+
- You need conditional room participation based on user preferences or permissions
|
|
245
|
+
- You want to implement "preview" mode where users can see the room before joining
|
|
246
|
+
- You need to coordinate room joining with other asynchronous operations
|
|
247
|
+
- You're building chat rooms, live sessions, or collaborative spaces with explicit join/leave actions
|
|
248
|
+
</Aside>
|
|
249
|
+
|
|
250
|
+
### Enabling Manual Mode
|
|
251
|
+
|
|
252
|
+
Set both `autoJoin` and `autoLeave` to `false` to gain full control:
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { useRoom } from '@bluealba/pae-ui-react-core';
|
|
256
|
+
|
|
257
|
+
function ManualRoomExample({ roomId }) {
|
|
258
|
+
const { users, hasJoined, joinRoom, leaveRoom } = useRoom(roomId, {
|
|
259
|
+
autoJoin: false,
|
|
260
|
+
autoLeave: false,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<div>
|
|
265
|
+
<div>
|
|
266
|
+
<p>Status: {hasJoined ? 'Joined' : 'Not Joined'}</p>
|
|
267
|
+
<p>Participants: {users.length}</p>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<div>
|
|
271
|
+
<button onClick={joinRoom} disabled={hasJoined}>
|
|
272
|
+
Join Room
|
|
273
|
+
</button>
|
|
274
|
+
<button onClick={leaveRoom} disabled={!hasJoined}>
|
|
275
|
+
Leave Room
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
{hasJoined && (
|
|
280
|
+
<ul>
|
|
281
|
+
{users.map(user => (
|
|
282
|
+
<li key={user.username}>{user.displayName}</li>
|
|
283
|
+
))}
|
|
284
|
+
</ul>
|
|
285
|
+
)}
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Manual Control API
|
|
292
|
+
|
|
293
|
+
**`joinRoom()`**: Manually join the room
|
|
294
|
+
- Only works when `autoJoin: false`
|
|
295
|
+
- Calling this when `autoJoin: true` will log a warning and be ignored
|
|
296
|
+
- Safe to call multiple times (no-op if already joined)
|
|
297
|
+
|
|
298
|
+
**`leaveRoom()`**: Manually leave the room
|
|
299
|
+
- Only works when `autoLeave: false`
|
|
300
|
+
- Calling this when `autoLeave: true` will log a warning and be ignored
|
|
301
|
+
- Safe to call multiple times (no-op if not joined)
|
|
302
|
+
|
|
303
|
+
**`hasJoined`**: Boolean indicating if the current user is in the room
|
|
304
|
+
- Returns `true` if the authenticated user appears in the `users` array
|
|
305
|
+
- Updates automatically when join/leave operations complete
|
|
306
|
+
- Useful for conditional rendering and button states
|
|
307
|
+
|
|
308
|
+
### Advanced Examples
|
|
309
|
+
|
|
310
|
+
<Tabs>
|
|
311
|
+
<TabItem label="Conditional Joining">
|
|
312
|
+
```tsx
|
|
313
|
+
function ConditionalRoom({ roomId, userHasPermission }) {
|
|
314
|
+
const { users, hasJoined, joinRoom, leaveRoom } = useRoom(roomId, {
|
|
315
|
+
autoJoin: false,
|
|
316
|
+
autoLeave: false,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
// Only join if user has permission
|
|
321
|
+
if (userHasPermission && !hasJoined) {
|
|
322
|
+
joinRoom();
|
|
323
|
+
}
|
|
324
|
+
}, [userHasPermission, hasJoined, joinRoom]);
|
|
325
|
+
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
// Leave when component unmounts
|
|
328
|
+
return () => {
|
|
329
|
+
if (hasJoined) {
|
|
330
|
+
leaveRoom();
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}, [hasJoined, leaveRoom]);
|
|
334
|
+
|
|
335
|
+
if (!userHasPermission) {
|
|
336
|
+
return <p>You don't have permission to join this room.</p>;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<div>
|
|
341
|
+
<p>Participants: {users.length}</p>
|
|
342
|
+
{/* ... */}
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
Join the room only when specific conditions are met
|
|
348
|
+
</TabItem>
|
|
349
|
+
|
|
350
|
+
<TabItem label="Preview Mode">
|
|
351
|
+
```tsx
|
|
352
|
+
function RoomPreview({ roomId }) {
|
|
353
|
+
const [mode, setMode] = useState<'preview' | 'active'>('preview');
|
|
354
|
+
const { users, hasJoined, joinRoom, leaveRoom } = useRoom(roomId, {
|
|
355
|
+
autoJoin: false,
|
|
356
|
+
autoLeave: false,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
if (mode === 'active' && !hasJoined) {
|
|
361
|
+
joinRoom();
|
|
362
|
+
} else if (mode === 'preview' && hasJoined) {
|
|
363
|
+
leaveRoom();
|
|
364
|
+
}
|
|
365
|
+
}, [mode, hasJoined, joinRoom, leaveRoom]);
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<div>
|
|
369
|
+
<div>
|
|
370
|
+
<button onClick={() => setMode('preview')}>Preview</button>
|
|
371
|
+
<button onClick={() => setMode('active')}>Join Active</button>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<p>Mode: {mode}</p>
|
|
375
|
+
<p>Participants: {users.length}</p>
|
|
376
|
+
|
|
377
|
+
{mode === 'preview' && (
|
|
378
|
+
<p>You are viewing participant count without joining</p>
|
|
379
|
+
)}
|
|
380
|
+
|
|
381
|
+
{mode === 'active' && hasJoined && (
|
|
382
|
+
<ul>
|
|
383
|
+
{users.map(user => (
|
|
384
|
+
<li key={user.username}>{user.displayName}</li>
|
|
385
|
+
))}
|
|
386
|
+
</ul>
|
|
387
|
+
)}
|
|
388
|
+
</div>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
Allow users to preview participant count before joining
|
|
393
|
+
</TabItem>
|
|
394
|
+
|
|
395
|
+
<TabItem label="Session Management">
|
|
396
|
+
```tsx
|
|
397
|
+
function LiveSession({ sessionId }) {
|
|
398
|
+
const [sessionStarted, setSessionStarted] = useState(false);
|
|
399
|
+
const { users, hasJoined, joinRoom, leaveRoom } = useRoom(
|
|
400
|
+
`session-${sessionId}`,
|
|
401
|
+
{ autoJoin: false, autoLeave: false }
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const handleStartSession = async () => {
|
|
405
|
+
// Perform any async setup
|
|
406
|
+
await initializeSession(sessionId);
|
|
407
|
+
|
|
408
|
+
// Then join the room
|
|
409
|
+
joinRoom();
|
|
410
|
+
setSessionStarted(true);
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const handleEndSession = async () => {
|
|
414
|
+
// Leave the room first
|
|
415
|
+
leaveRoom();
|
|
416
|
+
|
|
417
|
+
// Then cleanup
|
|
418
|
+
await cleanupSession(sessionId);
|
|
419
|
+
setSessionStarted(false);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
useEffect(() => {
|
|
423
|
+
// Cleanup on unmount
|
|
424
|
+
return () => {
|
|
425
|
+
if (hasJoined) {
|
|
426
|
+
leaveRoom();
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}, [hasJoined, leaveRoom]);
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<div>
|
|
433
|
+
{!sessionStarted ? (
|
|
434
|
+
<button onClick={handleStartSession}>
|
|
435
|
+
Start Live Session
|
|
436
|
+
</button>
|
|
437
|
+
) : (
|
|
438
|
+
<>
|
|
439
|
+
<p>Session Active - {users.length} participants</p>
|
|
440
|
+
<button onClick={handleEndSession}>
|
|
441
|
+
End Session
|
|
442
|
+
</button>
|
|
443
|
+
</>
|
|
444
|
+
)}
|
|
445
|
+
</div>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
Coordinate room joining with other async operations
|
|
450
|
+
</TabItem>
|
|
451
|
+
</Tabs>
|
|
452
|
+
|
|
453
|
+
<Aside type="caution">
|
|
454
|
+
**Important Behavior Notes:**
|
|
455
|
+
- When `autoJoin: true` (default), calling `joinRoom()` manually will log a warning and be ignored
|
|
456
|
+
- When `autoLeave: true` (default), calling `leaveRoom()` manually will log a warning and be ignored
|
|
457
|
+
- Always check the console for warnings if manual join/leave isn't working as expected
|
|
458
|
+
- The `hasJoined` flag is based on the authenticated user's presence in the `users` array
|
|
459
|
+
</Aside>
|
|
460
|
+
|
|
461
|
+
### Displaying User Avatars with RoomAvatars
|
|
462
|
+
|
|
463
|
+
The `RoomAvatars` component provides a beautiful, ready-to-use interface for displaying room users:
|
|
464
|
+
|
|
465
|
+
```tsx
|
|
466
|
+
import { useRoom, RoomAvatars } from '@bluealba/pae-ui-react-core';
|
|
467
|
+
|
|
468
|
+
function DashboardHeader({ dashboardId }) {
|
|
469
|
+
const { users } = useRoom(`dashboard-${dashboardId}`);
|
|
470
|
+
|
|
471
|
+
return (
|
|
472
|
+
<header>
|
|
473
|
+
<h1>Analytics Dashboard</h1>
|
|
474
|
+
|
|
475
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
476
|
+
<span>Currently viewing:</span>
|
|
477
|
+
<RoomAvatars
|
|
478
|
+
users={users}
|
|
479
|
+
size="md"
|
|
480
|
+
maxVisible={5}
|
|
481
|
+
overlap={true}
|
|
482
|
+
/>
|
|
483
|
+
</div>
|
|
484
|
+
</header>
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Component Features:**
|
|
490
|
+
- Renders circular avatars with user initials
|
|
491
|
+
- Automatically uses the user's generated color
|
|
492
|
+
- Supports three sizes: `sm` (24px), `md` (32px), `lg` (40px)
|
|
493
|
+
- Can overlap avatars for a compact display
|
|
494
|
+
- Shows overflow count when too many users (e.g., "+3")
|
|
495
|
+
- Includes tooltips showing full display names
|
|
496
|
+
- Optional click handlers for custom interactions
|
|
497
|
+
|
|
498
|
+
**Props:**
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
interface RoomAvatarsProps {
|
|
502
|
+
users: RoomUser[]; // Array of users to display (required)
|
|
503
|
+
maxVisible?: number; // Max avatars before showing overflow (default: 5)
|
|
504
|
+
size?: 'sm' | 'md' | 'lg'; // Avatar size (default: 'md')
|
|
505
|
+
overlap?: boolean; // Whether avatars overlap (default: true)
|
|
506
|
+
className?: string; // Additional CSS classes
|
|
507
|
+
onUserClick?: (user: RoomUser) => void; // Optional click handler
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Examples:**
|
|
512
|
+
|
|
513
|
+
<Tabs>
|
|
514
|
+
<TabItem label="Compact Display">
|
|
515
|
+
```tsx
|
|
516
|
+
<RoomAvatars
|
|
517
|
+
users={users}
|
|
518
|
+
size="sm"
|
|
519
|
+
maxVisible={3}
|
|
520
|
+
overlap={true}
|
|
521
|
+
/>
|
|
522
|
+
```
|
|
523
|
+
Perfect for tight spaces, shows 3 overlapping small avatars
|
|
524
|
+
</TabItem>
|
|
525
|
+
|
|
526
|
+
<TabItem label="Spacious List">
|
|
527
|
+
```tsx
|
|
528
|
+
<RoomAvatars
|
|
529
|
+
users={users}
|
|
530
|
+
size="lg"
|
|
531
|
+
maxVisible={10}
|
|
532
|
+
overlap={false}
|
|
533
|
+
/>
|
|
534
|
+
```
|
|
535
|
+
Larger avatars with no overlap, suitable for prominent displays
|
|
536
|
+
</TabItem>
|
|
537
|
+
|
|
538
|
+
<TabItem label="Interactive">
|
|
539
|
+
```tsx
|
|
540
|
+
<RoomAvatars
|
|
541
|
+
users={users}
|
|
542
|
+
size="md"
|
|
543
|
+
maxVisible={5}
|
|
544
|
+
onUserClick={(user) => {
|
|
545
|
+
console.log('Clicked user:', user.displayName);
|
|
546
|
+
// Open user profile, start chat, etc.
|
|
547
|
+
}}
|
|
548
|
+
/>
|
|
549
|
+
```
|
|
550
|
+
Adds click interaction to each avatar
|
|
551
|
+
</TabItem>
|
|
552
|
+
</Tabs>
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Multi-Tenancy Awareness
|
|
557
|
+
|
|
558
|
+
**Important**: Room IDs are **NOT** automatically scoped by tenant. If you need tenant isolation, you must include the tenant ID as part of the room name:
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
// ✅ Good: Explicitly scoped by tenant
|
|
562
|
+
const { users } = useRoom(`tenant-${tenantId}-dashboard-main`);
|
|
563
|
+
|
|
564
|
+
// ⚠️ Caution: This is a global room across all tenants
|
|
565
|
+
const { users } = useRoom('dashboard-main');
|
|
566
|
+
```
|
package/docs/index.mdx
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Blue Alba Platform
|
|
3
|
+
description: a generic and extensible framework and tools for developing customer facing applications from scratch
|
|
4
|
+
template: splash
|
|
5
|
+
hero:
|
|
6
|
+
image:
|
|
7
|
+
alt: Blue Alba Platform Logo
|
|
8
|
+
dark: ~/assets/logo-dark.png
|
|
9
|
+
light: ~/assets/logo-light.png
|
|
10
|
+
tagline: A generic and extensible framework and tools for developing customer facing applications from scratch
|
|
11
|
+
actions:
|
|
12
|
+
- text: Get Started
|
|
13
|
+
link: /_/docs/getting-started/overview/
|
|
14
|
+
icon: right-arrow
|
|
15
|
+
variant: primary
|
|
16
|
+
- text: View Architecture
|
|
17
|
+
link: /_/docs/architecture/overview/
|
|
18
|
+
icon: open-book
|
|
19
|
+
variant: minimal
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
import { Card, CardGrid } from '@astrojs/starlight/components';
|
|
23
|
+
|
|
24
|
+
## Key Features
|
|
25
|
+
|
|
26
|
+
<CardGrid stagger>
|
|
27
|
+
<Card title="API Gateway" icon="star">
|
|
28
|
+
Powerful HTTP gateway with application routing, catalog management, HTTP/2 and WebSocket support, and Lambda integration for serverless functions.
|
|
29
|
+
</Card>
|
|
30
|
+
|
|
31
|
+
<Card title="Authentication & Security" icon="approve-check">
|
|
32
|
+
Multi-IDP authentication (Okta, EntraId, OneLogin, Github, Cognito), API keys support, user impersonation, and JWT-based security.
|
|
33
|
+
</Card>
|
|
34
|
+
|
|
35
|
+
<Card title="Authorization & Access Control" icon="approve-check-circle">
|
|
36
|
+
Comprehensive RBAC with permissions, operations, roles, groups management, and fine-grained access control throughout the platform.
|
|
37
|
+
</Card>
|
|
38
|
+
|
|
39
|
+
<Card title="Multi-Tenancy & Provisioning" icon="codeberg">
|
|
40
|
+
Built-in multi-tenant architecture with tenant context management and automated Okta tenant/roles/applications provisioning.
|
|
41
|
+
</Card>
|
|
42
|
+
|
|
43
|
+
<Card title="Micro-Frontend Architecture" icon="puzzle">
|
|
44
|
+
React-based micro-frontends using single-spa with platform Shell UI and Administration UI for independent development and deployment.
|
|
45
|
+
</Card>
|
|
46
|
+
|
|
47
|
+
<Card title="User & State Management" icon="seti:favicon">
|
|
48
|
+
Complete user management with groups, saved views, bookmarks, and user-related data persistence.
|
|
49
|
+
</Card>
|
|
50
|
+
|
|
51
|
+
<Card title="Developer Experience" icon="rocket">
|
|
52
|
+
Comprehensive development tools including templates, conventions, shared libraries, frameworks, and platform bootstrapping utilities.
|
|
53
|
+
</Card>
|
|
54
|
+
</CardGrid>
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|