@openmfp/portal-ui-lib 0.0.0 → 0.182.3

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 (73) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +712 -0
  3. package/assets/249.js +1 -0
  4. package/assets/560.js +1 -0
  5. package/assets/955.js +1 -0
  6. package/assets/images/favicon.ico +0 -0
  7. package/assets/images/logo.png +0 -0
  8. package/assets/moments/sapIllus-Dialog-BeforeSearch-Alt.svg +22 -0
  9. package/assets/moments/sapIllus-Dialog-BeforeSearch.svg +13 -0
  10. package/assets/moments/sapIllus-Dialog-NoActivities.svg +18 -0
  11. package/assets/moments/sapIllus-Dialog-NoData.svg +10 -0
  12. package/assets/moments/sapIllus-Dialog-NoEntries.svg +16 -0
  13. package/assets/moments/sapIllus-Dialog-NoMail.svg +11 -0
  14. package/assets/moments/sapIllus-Dialog-NoNotifications.svg +12 -0
  15. package/assets/moments/sapIllus-Dialog-NoSavedItems.svg +13 -0
  16. package/assets/moments/sapIllus-Dialog-NoSearchResults-Alt.svg +14 -0
  17. package/assets/moments/sapIllus-Dialog-NoSearchResults.svg +15 -0
  18. package/assets/moments/sapIllus-Dialog-NoTasks.svg +18 -0
  19. package/assets/moments/sapIllus-Dialog-SuccessBalloon.svg +16 -0
  20. package/assets/moments/sapIllus-Dialog-SuccessCheckMark-Alt.svg +14 -0
  21. package/assets/moments/sapIllus-Dialog-SuccessCheckMark.svg +7 -0
  22. package/assets/moments/sapIllus-Dialog-SuccessHighFive.svg +31 -0
  23. package/assets/moments/sapIllus-Dialog-UnableToLoad-Alt.svg +12 -0
  24. package/assets/moments/sapIllus-Dialog-UnableToLoad-Alt2.svg +10 -0
  25. package/assets/moments/sapIllus-Dialog-UnableToLoad.svg +11 -0
  26. package/assets/moments/sapIllus-Dialog-UnableToUpload.svg +13 -0
  27. package/assets/moments/sapIllus-Scene-BeforeSearch.svg +31 -0
  28. package/assets/moments/sapIllus-Scene-NoActivities-Alt.svg +24 -0
  29. package/assets/moments/sapIllus-Scene-NoActivities.svg +28 -0
  30. package/assets/moments/sapIllus-Scene-NoData.svg +21 -0
  31. package/assets/moments/sapIllus-Scene-NoEntries.svg +30 -0
  32. package/assets/moments/sapIllus-Scene-NoMail.svg +23 -0
  33. package/assets/moments/sapIllus-Scene-NoNotifications.svg +36 -0
  34. package/assets/moments/sapIllus-Scene-NoSavedItems.svg +36 -0
  35. package/assets/moments/sapIllus-Scene-NoSearchResults-Alt.svg +44 -0
  36. package/assets/moments/sapIllus-Scene-NoSearchResults.svg +15 -0
  37. package/assets/moments/sapIllus-Scene-NoTasks.svg +22 -0
  38. package/assets/moments/sapIllus-Scene-SuccessBalloon.svg +33 -0
  39. package/assets/moments/sapIllus-Scene-SuccessCheckMark.svg +43 -0
  40. package/assets/moments/sapIllus-Scene-UnableToLoad.svg +23 -0
  41. package/assets/moments/sapIllus-Scene-UnableToUpload.svg +13 -0
  42. package/assets/moments/sapIllus-Spot-BeforeSearch-Alt.svg +10 -0
  43. package/assets/moments/sapIllus-Spot-BeforeSearch.svg +10 -0
  44. package/assets/moments/sapIllus-Spot-NoActivities.svg +23 -0
  45. package/assets/moments/sapIllus-Spot-NoData.svg +9 -0
  46. package/assets/moments/sapIllus-Spot-NoEntries.svg +12 -0
  47. package/assets/moments/sapIllus-Spot-NoMail.svg +10 -0
  48. package/assets/moments/sapIllus-Spot-NoNotifications.svg +10 -0
  49. package/assets/moments/sapIllus-Spot-NoSavedItems.svg +9 -0
  50. package/assets/moments/sapIllus-Spot-NoSearchResults-Alt.svg +14 -0
  51. package/assets/moments/sapIllus-Spot-NoSearchResults.svg +13 -0
  52. package/assets/moments/sapIllus-Spot-NoTasks.svg +20 -0
  53. package/assets/moments/sapIllus-Spot-SuccessBalloon.svg +19 -0
  54. package/assets/moments/sapIllus-Spot-SuccessCheckMark-Alt.svg +13 -0
  55. package/assets/moments/sapIllus-Spot-SuccessCheckMark.svg +7 -0
  56. package/assets/moments/sapIllus-Spot-UnableToLoad-Alt.svg +13 -0
  57. package/assets/moments/sapIllus-Spot-UnableToLoad-Alt2.svg +13 -0
  58. package/assets/moments/sapIllus-Spot-UnableToLoad.svg +10 -0
  59. package/assets/moments/sapIllus-Spot-UnableToUpload.svg +10 -0
  60. package/assets/moments/tnt-Dialog-Components.svg +9 -0
  61. package/assets/moments/tnt-Dialog-ExternalLink.svg +11 -0
  62. package/assets/moments/tnt-Dialog-UnsuccessfulAuth.svg +11 -0
  63. package/assets/moments/tnt-Scene-Components.svg +16 -0
  64. package/assets/moments/tnt-Scene-ExternalLink.svg +14 -0
  65. package/assets/moments/tnt-Scene-UnsuccessfulAuth.svg +22 -0
  66. package/assets/moments/tnt-Spot-UnsuccessfulAuth.svg +10 -0
  67. package/assets/openmfp-portal-ui-wc.js +179 -0
  68. package/assets/translation-files/de.json +1 -0
  69. package/assets/translation-files/en.json +1 -0
  70. package/fesm2022/openmfp-portal-ui-lib.mjs +2716 -0
  71. package/fesm2022/openmfp-portal-ui-lib.mjs.map +1 -0
  72. package/index.d.ts +1071 -0
  73. package/package.json +50 -2
package/README.md ADDED
@@ -0,0 +1,712 @@
1
+ # Portal UI Library
2
+
3
+ <!-- CI trigger -->
4
+
5
+ ![Build Status](https://github.com/openmfp/portal-ui-lib/actions/workflows/pipeline.yaml/badge.svg)
6
+ [![REUSE status](
7
+ https://api.reuse.software/badge/github.com/openmfp/portal-ui-lib)](https://api.reuse.software/info/github.com/openmfp/portal-ui-lib)
8
+
9
+ This library helps you to set up your angular project consuming [Luigi](https://luigi-project.io/) configuration.
10
+
11
+ Main features of this library are:
12
+
13
+ * Enable dynamic Luigi configuration consumption in a microfrontend.
14
+ * Provide authentication capabilities with Auth Server
15
+ * Dynamic development capabilities by embedding your locally running microfrontend into a Luigi frame.
16
+
17
+ ## Table of Contents
18
+ - [Portal UI Library](#portal-ui-library)
19
+ - [Table of Contents](#table-of-contents)
20
+ - [Getting Started](#getting-started)
21
+ - [Dependencies](#dependencies)
22
+ - [Angular Configuration](#angular-configuration)
23
+ - [Import the Portal providers and Bootstrap the app with PortalComponent](#import-the-portal-providers-and-bootstrap-the-app-with-portalcomponent)
24
+ - [Update index html file of the project](#update-index-html-file-of-the-project)
25
+ - [Implement the Custom Service](#implement-the-custom-service)
26
+ - [Configuration services](#configuration-services)
27
+ - [The staticSettingsConfigService Option](#the-staticsettingsconfigservice-option)
28
+ - [The luigiExtendedGlobalContextConfigService Option](#the-luigiextendedglobalcontextconfigservice-option)
29
+ - [The userSettingsConfigService Option](#the-usersettingsconfigservice-option)
30
+ - [The globalSearchConfigService option](#the-globalsearchconfigservice-option)
31
+ - [The luigiBreadcrumbConfigService Option](#the-luigibreadcrumbconfigservice-option)
32
+ - [The userProfileConfigService Option](#the-userprofileconfigservice-option)
33
+ - [Functional Services](#functional-services)
34
+ - [The luigiAuthEventsCallbacksService Option](#the-luigiautheventscallbacksservice-option)
35
+ - [The customMessageListeners Option](#the-custommessagelisteners-option)
36
+ - [The customGlobalNodesService Option](#the-customglobalnodesservice-option)
37
+ - [Listen and react to Authentication Events](#listen-and-react-to-authentication-events)
38
+ - [Configure Proxy for Backend REST Calls](#configure-proxy-for-backend-rest-calls)
39
+ - [Start your Project](#start-your-project)
40
+ - [Local Extension Development](#local-extension-development)
41
+ - [Requirements](#requirements)
42
+ - [Contributing](#contributing)
43
+ - [Code of Conduct](#code-of-conduct)
44
+ - [Licensing](#licensing)
45
+
46
+
47
+ ## Getting Started
48
+
49
+ ### Dependencies
50
+
51
+ Besides putting the `@openmfp/portal-ui-lib` dependency into the `package.json` be sure as well to include the `@luigi-project/core`
52
+ and `@luigi-project/plugin-auth-oauth2` in proper versions (along with any other dependency required).
53
+
54
+ ### Angular Configuration
55
+
56
+ Configure the angular build process (in the `angular.json` file) to include the content of the Luigi core project and assets
57
+ from `@openmfp/portal-ui-lib` library into the project assets, as shown below:
58
+
59
+ ```
60
+ {
61
+ // ... the rest of the angular.json configuration
62
+
63
+ "assets": [
64
+ {
65
+ "glob": "**",
66
+ "input": "node_modules/@luigi-project/core",
67
+ "output": "/luigi-core"
68
+ },
69
+ {
70
+ "glob": "**",
71
+ "input": "node_modules/@openmfp/portal-ui-lib/assets/",
72
+ "output": "/assets/"
73
+ },
74
+ ]
75
+ // ... other configured assets
76
+ ]
77
+
78
+ }
79
+ ```
80
+
81
+ ## Import the Portal providers and Bootstrap the app with PortalComponent
82
+
83
+ First you have to import the `providePortal` and bootstrap the `PortalComponent` in your `main.ts` file.
84
+ To do this call `providePortal(portalOptions)` method, which takes the `PortalOptions` object as an argument,
85
+ inside the providers section of the application configuration. It is important to note that the `providePortal(portalOptions)` should be imported after any app specific
86
+ routing providers (e.g. `provideRouter(appRoutes)`).
87
+
88
+ The result may look like this:
89
+
90
+ ```ts
91
+ import { importProvidersFrom } from '@angular/core';
92
+ import { bootstrapApplication } from '@angular/platform-browser';
93
+ import { provideRouter } from '@angular/router';
94
+ import {
95
+ providePortal,
96
+ PortalComponent,
97
+ PortalOptions,
98
+ } from '@openmfp/portal-ui-lib';
99
+ import { appRoutes } from './app/app-routes';
100
+
101
+ const portalOptions: PortalOptions = {
102
+ // ... portal options
103
+ }
104
+
105
+ bootstrapApplication(PortalComponent, {
106
+ providers: [
107
+ provideRouter(appRoutes),
108
+ importProvidersFrom(AnyRequiredModule),
109
+ providePortal(portalOptions),
110
+
111
+ // ... any other providers imports
112
+ ]
113
+ }
114
+ );
115
+ ```
116
+
117
+ ## Update index html file of the project
118
+
119
+ The next step in order to have the portal working is to update the `index.html` file with the inclusion of:
120
+ - the `/luigi-core/luigi.js` script,
121
+ - the `/luigi-core/luigi_horizon.css` styles,
122
+ - and placing the `<app-portal></app-portal>` html tag inside the html body.
123
+
124
+ Below is an example:
125
+
126
+ ```html
127
+ <!DOCTYPE html>
128
+ <html lang="en">
129
+ <head>
130
+ <meta charset="utf-8">
131
+ <title>App</title>
132
+ <base href="/">
133
+ <meta name="viewport" content="width=device-width, initial-scale=1">
134
+ <link rel="stylesheet" href="/luigi-core/luigi_horizon.css" />
135
+ </head>
136
+ <body>
137
+ <noscript>You need to enable JavaScript to run this app.</noscript>
138
+ <script src="/luigi-core/luigi.js"></script>
139
+ <app-portal></app-portal>
140
+ </body>
141
+ </html>
142
+ ```
143
+
144
+
145
+
146
+ ## Implement the Custom Service
147
+
148
+
149
+ There are two types of services that are considered: Services that provide Luigi configuration (aka. Configuration Services) and services that provide or extend functionality (aka. Functional Services).
150
+ For each service there is a corresponding interface that you have to implement.
151
+ Afterward you provide your specific implementation in the `providePortal(portalOptions)` by placing it in the `portalOptions` object and thus make it available to the `Portal`.
152
+
153
+ ### Configuration services
154
+
155
+ #### The staticSettingsConfigService Option
156
+
157
+ With this you can customize [Luigis general settings](https://docs.luigi-project.io/docs/general-settings) and override any defaults.
158
+ Make sure to return a valid Luigi configuration object.
159
+
160
+ ```ts
161
+ import { StaticSettingsConfigService } from '@openmfp/portal-ui-lib';
162
+
163
+ export class StaticSettingsConfigServiceImpl
164
+ implements StaticSettingsConfigService
165
+ {
166
+ constructor() {}
167
+
168
+ getInitialStaticSettingsConfig() {
169
+ const logo = 'assets/my-logo.svg';
170
+
171
+ return {
172
+ header: {
173
+ title: 'My App',
174
+ logo: logo,
175
+ favicon: logo,
176
+ },
177
+ appLoadingIndicator: {
178
+ hideAutomatically: false,
179
+ },
180
+ links: [{ title: 'OpemMFP', link: 'https://openmfp.org/' }],
181
+ // ... the rest of the configuration
182
+ };
183
+ }
184
+
185
+ getStaticSettingsConfig() {
186
+ return {
187
+ ...this.getInitialStaticSettingsConfig(),
188
+ appLoadingIndicator: {
189
+ hideAutomatically: true,
190
+ },
191
+ // ... the rest of the configuration
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ The `getInitialStaticSettingsConfig()` method is called while constructing the Luigi initial config object.
198
+ The `getStaticSettingsConfig()` is called while [Luigi lifecycle hook luigiAfterInit](https://docs.luigi-project.io/docs/lifecycle-hooks?section=luigiafterinit).
199
+ The latter adds additional setup to the root of the Luigi configuration object.
200
+
201
+ In your `main.ts` you can provide your custom implementation like so:
202
+
203
+ ```ts
204
+ const portalOptions: PortalOptions = {
205
+ staticSettingsConfigService: StaticSettingsConfigServiceImpl,
206
+ // ... other portal options
207
+ }
208
+ ```
209
+
210
+ #### The routingConfigService Option
211
+
212
+ With this you can customize [Luigis routing settings](https://docs.luigi-project.io/docs/navigation-parameters-reference?section=routing-parameters) and override any defaults.
213
+ Make sure to return a valid Luigi configuration object.
214
+
215
+ ```ts
216
+ import { Injectable, inject } from '@angular/core';
217
+ import { RoutingConfigService } from '@openmfp/portal-ui-lib';
218
+
219
+ @Injectable()
220
+ export class CustomRoutingConfigServiceImpl implements RoutingConfigService {
221
+ getInitialRoutingConfig(): any {
222
+ return {
223
+ useHashRouting: false,
224
+ showModalPathInUrl: false,
225
+ modalPathParam: 'modalPathParamDisabled',
226
+ skipRoutingForUrlPatterns: [/.*/],
227
+ pageNotFoundHandler: () => {},
228
+ };
229
+ }
230
+
231
+ getRoutingConfig(): any {
232
+ return {
233
+ useHashRouting: false,
234
+ showModalPathInUrl: true,
235
+ modalPathParam: 'modal',
236
+ pageNotFoundHandler: (
237
+ notFoundPath: string,
238
+ isAnyPathMatched: boolean,
239
+ ) => {
240
+ return {
241
+ redirectTo: 'error/404',
242
+ keepURL: true,
243
+ };
244
+ },
245
+ };
246
+ }
247
+ }
248
+ ```
249
+
250
+ The `getInitialRoutingConfig()` method is called while constructing the Luigi initial config object.
251
+ The `getRoutingConfig()` is called while [Luigi lifecycle hook luigiAfterInit](https://docs.luigi-project.io/docs/lifecycle-hooks?section=luigiafterinit).
252
+ The latter adds additional setup to the root of the Luigi configuration object.
253
+
254
+ In your `main.ts` you can provide your custom implementation like so:
255
+
256
+ ```ts
257
+ const portalOptions: PortalOptions = {
258
+ routingConfigService: CustomRoutingConfigService,
259
+ // ... other portal options
260
+ }
261
+ ```
262
+
263
+ #### The luigiExtendedGlobalContextConfigService Option
264
+
265
+ By default, in the [Luigi's global context](https://docs.luigi-project.io/docs/navigation-parameters-reference?section=globalcontext) following data is set by the library and available:
266
+
267
+ ```json
268
+ {
269
+ "portalContext": {...} ,
270
+ "userId": "logged in user id",
271
+ "userEmail": "logged in user email",
272
+ "token": "id token of the logged in user"
273
+ }
274
+ ```
275
+
276
+ With the `luigiExtendedGlobalContextConfigService` option you can define global data you want to be available alongside with the default values present.
277
+ The Luigi's global context is available afterwards in all the micro-frontends.
278
+
279
+ ```ts
280
+ import { LuigiExtendedGlobalContextConfigService, LuigiNode } from '@openmfp/portal-ui-lib';
281
+
282
+ export class LuigiExtendedGlobalContextConfigServiceImpl implements LuigiExtendedGlobalContextConfigService {
283
+
284
+ async createLuigiExtendedGlobalContext(): Promise<ExtendedGlobalContext> {
285
+
286
+ return {
287
+ isLocal: true,
288
+ analyticsConfig: 'global',
289
+ };
290
+ }
291
+ }
292
+ ```
293
+
294
+ In your `main.ts` you can provide your custom implementation like so:
295
+
296
+ ```ts
297
+ const portalOptions: PortalOptions = {
298
+ luigiExtendedGlobalContextConfigService: LuigiExtendedGlobalContextConfigServiceImpl,
299
+ // ... other portal options
300
+ }
301
+ ```
302
+
303
+
304
+ #### The userSettingsConfigService Option
305
+
306
+ With this you can define the [Luigi user settings and a corresponding userSettingGroups configuration](https://docs.luigi-project.io/docs/user-settings?section=user-settings)
307
+ Make sure to return a valid Luigi configuration object.
308
+
309
+ ```ts
310
+ import { UserSettingsConfigService, LuigiNode } from '@openmfp/portal-ui-lib';
311
+
312
+ export class UserSettingsConfigServiceImpl implements UserSettingsConfigService {
313
+
314
+ async getUserSettings(childrenByEntity: Record<string, LuigiNode[]>) {
315
+ return {
316
+ userSettingsDialog: {
317
+ dialogHeader: 'User Settings',
318
+ },
319
+ userSettingGroups: {
320
+ // ...the rest of the configuration
321
+ }
322
+ // ...the rest of the configuration
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ In your `main.ts` you can provide your custom implementation like so:
329
+
330
+ ```ts
331
+ const portalOptions: PortalOptions = {
332
+ userSettingsConfigService: UserSettingsConfigServiceImpl,
333
+ // ... other portal options
334
+ }
335
+ ```
336
+
337
+ #### The globalSearchConfigService option
338
+
339
+ With this you have the possibility configure [Luigis global search element](https://docs.luigi-project.io/docs/global-search) with provided configuration and events handlers.
340
+ Make sure to return a valid Luigi configuration object.
341
+
342
+ ```ts
343
+ import { GlobalSearchConfigService} from '@openmfp/portal-ui-lib';
344
+
345
+ export class GlobalSearchConfigServiceImpl implements GlobalSearchConfigService {
346
+
347
+ getGlobalSearchConfig() {
348
+ return {
349
+ searchFieldCentered: true,
350
+ searchProvider: {
351
+ onEnter: () => {
352
+ // ... handler implementation
353
+ },
354
+ onSearchBtnClick: () => {
355
+ // ... handler implementation
356
+ },
357
+ onEscape: () => {
358
+ // ... handler implementation
359
+ },
360
+
361
+ // ...the rest of the configuration
362
+ },
363
+ // ...the rest of the configuration
364
+ };
365
+ }
366
+ }
367
+ ```
368
+
369
+ In your `main.ts` you can provide your custom implementation like so:
370
+
371
+ ```ts
372
+ const portalOptions: PortalOptions = {
373
+ globalSearchConfigService: GlobalSearchConfigServiceImpl,
374
+ // ... other portal options
375
+ }
376
+ ```
377
+
378
+ #### The headerBarConfigService Option
379
+
380
+ This enables you to define configiration for your header bar section. Under the hood It extends [Luigi breadcrumbs](https://docs.luigi-project.io/docs/navigation-advanced?section=breadcrumbs) by adding ability to provide multiple renderer functions
381
+
382
+ Make sure to return a valid HeaderBar configuration object.
383
+
384
+
385
+ ```ts
386
+ import {
387
+ HeaderBarConfigService,
388
+ HeaderBarConfig,
389
+ } from '@openmfp/portal-ui-lib';
390
+
391
+
392
+ export class HeaderBarConfigServiceImpl implements HeaderBarConfigService
393
+ {
394
+ getBreadcrumbsConfig(): Promise<HeaderBarConfig> {
395
+ return {
396
+ autoHide: true,
397
+ omitRoot: false,
398
+ pendingItemLabel: '...',
399
+ rightRenderers: [(
400
+ containerElement: HTMLElement,
401
+ nodeItems: NodeItem[],
402
+ clickHandler,
403
+ ) => {
404
+ // ... renderer implementation
405
+ }];
406
+ leftRenderers: [(
407
+ containerElement: HTMLElement,
408
+ nodeItems: NodeItem[],
409
+ clickHandler,
410
+ ) => {
411
+ // ... renderer implementation
412
+ }];
413
+ };
414
+ }
415
+ }
416
+ ```
417
+
418
+ In your `main.ts` you can provide your custom implementation like so:
419
+
420
+ ```ts
421
+ const portalOptions: PortalOptions = {
422
+ luigiBreadcrumbConfigService: LuigiBreadcrumbConfigServiceImpl,
423
+ // ... other portal options
424
+ }
425
+ ```
426
+
427
+ #### The userProfileConfigService Option
428
+
429
+ This option allows you to define the [Luigi user profile](https://docs.luigi-project.io/docs/navigation-advanced?section=profile).
430
+ Make sure to return a valid Luigi configuration object.
431
+
432
+ ```ts
433
+ import { UserProfileConfigService, UserProfile } from '@openmfp/portal-ui-lib';
434
+
435
+
436
+ export class UserProfileConfigServiceImpl implements UserProfileConfigService
437
+ {
438
+ async getProfile(): Promise<UserProfile> {
439
+ return {
440
+ logout: {
441
+ label: 'Sign out',
442
+ icon: 'log',
443
+ },
444
+ items: [
445
+ {
446
+ label: 'Overview',
447
+ icon: 'overview',
448
+ link: `/users/overview`,
449
+ },
450
+ ],
451
+ };
452
+ }
453
+ }
454
+ ```
455
+
456
+ In your `main.ts` you can provide your custom implementation like so:
457
+
458
+ ```ts
459
+ const portalOptions: PortalOptions = {
460
+ userProfileConfigService: UserProfileConfigServiceImpl,
461
+ // ... other portal options
462
+ }
463
+ ```
464
+
465
+ ### Functional Services
466
+
467
+ #### The luigiAuthEventsCallbacksService Option
468
+
469
+ This option allows you to provide a service that listens to [Luigi authorization events](https://docs.luigi-project.io/docs/authorization-events)
470
+ Make sure to return a valid Luigi configuration object.
471
+
472
+ ```ts
473
+ import { LuigiAuthEventsCallbacksService } from '@openmfp/portal-ui-lib';
474
+
475
+ export class LuigiAuthEventsCallbacksServiceImpl
476
+ implements LuigiAuthEventsCallbacksService {
477
+
478
+ onAuthSuccessful(settings: any, authData: any) {
479
+ concole.log('User succesfully authenticated.');
480
+ }
481
+
482
+ // ...
483
+
484
+ onLogout(settings: any) {
485
+ concole.log('User succesfully logged out.');
486
+ }
487
+ }
488
+ ```
489
+
490
+ In your `main.ts` you can provide your custom implementation like so:
491
+
492
+ ```ts
493
+
494
+ const portalOptions: PortalOptions = {
495
+ luigiAuthEventsCallbacksService: LuigiAuthEventsCallbacksServiceImpl,
496
+ // ... other portal options
497
+ }
498
+ ```
499
+
500
+ #### The customMessageListeners Option
501
+
502
+ With this option it is possible to define listeners for [Luigi custom messages](https://docs.luigi-project.io/docs/communication?section=custom-messages).
503
+
504
+ Custom messages are sent from any part of your application to Luigi and then routed to any other micro frontend application in the same application.
505
+ A custom message is sent by using Luigis `sendCustomMessage({ id: 'unique.message.id'});` method (see also the following example).
506
+
507
+ ```ts
508
+ import { inject, Injectable } from '@angular/core';
509
+ import { LuigiCoreService } from '@openmfp/portal-ui-lib';
510
+
511
+ @Injectable({ providedIn: 'root' })
512
+ export class MessageService {
513
+ private luigiCoreService: LuigiCoreService = inject(LuigiCoreService);
514
+
515
+ public sendMessage() {
516
+ this.luigiCoreService.sendCustomMessage({ id: 'unique.message.id' });
517
+ }
518
+ }
519
+ ```
520
+
521
+ In order to react on such a message in your micro frontend, you have to provide a custom message listener.
522
+ You have to specify the corresponding message id you want to listen to.
523
+ If there is a match, the callback function `onCustomMessageReceived()` will be called.
524
+ Make sure to return a valid Luigi configuration object.
525
+
526
+ ```ts
527
+ import { CustomMessageListener } from '@openmfp/portal-ui-lib';
528
+
529
+ export class CustomMessageListenerImpl
530
+ implements CustomMessageListener
531
+ {
532
+ messageId(): string {
533
+ return `unique.message.id`;
534
+ }
535
+
536
+ onCustomMessageReceived(
537
+ customMessage: string,
538
+ mfObject: any,
539
+ mfNodesObject: any,
540
+ ): void {
541
+ // ... logic to be executed
542
+ }
543
+ }
544
+ ```
545
+
546
+ In your `main.ts` you can provide your custom implementation like so:
547
+
548
+ ```ts
549
+ const portalOptions: PortalOptions = {
550
+ customMessageListeners: [CustomMessageListenerImpl, CustomMessageListenerImpl2, ...],
551
+ // ... other portal options
552
+ }
553
+ ```
554
+
555
+ #### The customGlobalNodesService Option
556
+
557
+ This option adds the possibility to define and add the global level Luigi nodes to your application.
558
+
559
+ ```ts
560
+ import { LuigiNode, CustomGlobalNodesService } from '@openmfp/portal-ui-lib';
561
+
562
+ export class CustomGlobalNodesServiceImpl implements CustomGlobalNodesService {
563
+
564
+ async getCustomGlobalNodes(): Promise<LuigiNode[]> {
565
+ return [
566
+ this.createGlobalNode1(),
567
+ this.createGlobalNode2(),
568
+ // ...other globaL nodes
569
+ ];
570
+ }
571
+
572
+ private async createGlobalNode1(): LuigiNode {
573
+ return {
574
+ label: 'Global 1',
575
+ entityType: 'global.topnav',
576
+ link: '/global_1',
577
+ // ... other luigi node properties
578
+ };
579
+ }
580
+
581
+ private createGlobalNode2(): LuigiNode {
582
+ return {
583
+ label: 'Global 2',
584
+ entityType: 'global.topnav',
585
+ viewUrl: '/global_2',
586
+ pathSegment: 'global_2',
587
+ // ... other luigi node properties
588
+ };
589
+ }
590
+ }
591
+ ```
592
+
593
+ In your `main.ts` you can provide your custom implementation like so:
594
+
595
+ ```ts
596
+ const portalOptions: PortalOptions = {
597
+ customGlobalNodesService: CustomGlobalNodesServiceImpl,
598
+ // ... other portal options
599
+ }
600
+ ```
601
+
602
+ ### Listen and react to Authentication Events
603
+
604
+ There are following Authentication Events, to which the library consuming application can subscribe and react upon, in case required.
605
+
606
+ ```ts
607
+ export enum AuthEvent {
608
+ AUTH_SUCCESSFUL = 'AuthSuccessful',
609
+ AUTH_ERROR = 'AuthError',
610
+ AUTH_EXPIRED = 'AuthExpired',
611
+ AUTH_REFRESHED = 'AuthRefreshed',
612
+ AUTH_EXPIRE_SOON = 'AuthExpireSoon',
613
+ AUTH_CONFIG_ERROR = 'AuthConfigError',
614
+ LOGOUT = 'Logout',
615
+ }
616
+ ```
617
+ Below is an example of reacting to `AuthEvent.AUTH_SUCCESSFUL`
618
+
619
+ ```ts
620
+ import { AuthEvent, AuthService } from '@openmfp/portal-ui-lib';
621
+ import { filter } from 'rxjs';
622
+ import { MyService } from '../services';
623
+
624
+ export function actWhenUserAuthSuccedsful(
625
+ myService: MyService,
626
+ authService: AuthService,
627
+ ): () => void {
628
+ return () => {
629
+ authService.authEvents
630
+ .pipe(
631
+ filter((event: AuthEvent) => event === AuthEvent.AUTH_SUCCESSFUL),
632
+ )
633
+ .subscribe({
634
+ next: (event: AuthEvent) => {
635
+ myService.act();
636
+ },
637
+ });
638
+ };
639
+ }
640
+ ```
641
+
642
+ ### Configure Proxy for Backend REST Calls
643
+
644
+ The library executes rest calls `"/rest/**"` against backend running with the library [portal-server-lib](https://github.com/openmfp/portal-server-lib?tab=readme-ov-file#portal-server-library).
645
+ In order for the calls to reach your backend the `proxy.config.json` needs to be provided,
646
+ with the target reaching the place where and on what port the backend is running `"target": "http://localhost:3000"`.
647
+
648
+ ```json
649
+ {
650
+ "/rest/**": {
651
+ "target": "http://localhost:3000",
652
+ "secure": false,
653
+ "logLevel": "debug",
654
+ "changeOrigin": true
655
+ }
656
+ }
657
+ ```
658
+
659
+ The proxy file needs to be indicated in the file `angular.json` section `serve`:
660
+
661
+ ```json
662
+ {
663
+ "serve": {
664
+ "builder": "@angular-devkit/build-angular:dev-server",
665
+ "options": {
666
+ "proxyConfig": "proxy.config.json"
667
+ },
668
+ "configurations": {
669
+ "production": {
670
+ "browserTarget": "build:production"
671
+ },
672
+ "development": {
673
+ "browserTarget": "build:development"
674
+ }
675
+ },
676
+ "defaultConfiguration": "development"
677
+ }
678
+
679
+ }
680
+ ```
681
+
682
+
683
+ ### Start your Project
684
+
685
+ After finishing all the required steps you might want to check your integration with the library and run your local application.
686
+ In order to do that, firstly you need to run the local server part of the portal,
687
+ please follow the instruction provided [here](https://github.com/openmfp/portal-server-lib?tab=readme-ov-file#portal-server-library)
688
+ Once the server is running execute your ui starting script (e.g. `ng serve --port 4300` ) remembering that the default localhost port
689
+ should be `4300` otherwise you need to set the environment variable to expected `FRONTEND_PORT=ZZZZ` and restart the server.
690
+
691
+ ## Local Extension Development
692
+
693
+ You can set up a local instance of your application.
694
+ This allows you to thoroughly test your application before you release it to production.
695
+ Please follow our [local setup guide](./docs/readme-local-setup.md) for this task.
696
+
697
+ ## Requirements
698
+
699
+ The portal requires a installation of node.js and npm.
700
+ Checkout the [package.json](package.json) for the required node version and dependencies.
701
+
702
+ ## Contributing
703
+
704
+ Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file in this repository for instructions on how to contribute to openMFP.
705
+
706
+ ## Code of Conduct
707
+
708
+ Please refer to the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) file in this repository for information on the expected Code of Conduct for contributing to openMFP.
709
+
710
+ ## Licensing
711
+
712
+ Copyright 2025 SAP SE or an SAP affiliate company and openMFP contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/openmfp/portal-ui-lib).