@open-rlb/ng-app 3.1.110 → 3.1.112

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-rlb/ng-app",
3
- "version": "3.1.110",
3
+ "version": "3.1.112",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=21.0.0 <22.0.0",
6
6
  "@angular/core": ">=21.0.0 <22.0.0",
@@ -21,6 +21,10 @@
21
21
  "dependencies": {
22
22
  "tslib": "^2.8.1"
23
23
  },
24
+ "schematics": "./schematics/collection.json",
25
+ "ng-add": {
26
+ "save": "dependencies"
27
+ },
24
28
  "sideEffects": false,
25
29
  "module": "fesm2022/open-rlb-ng-app.mjs",
26
30
  "typings": "types/open-rlb-ng-app.d.ts",
@@ -33,5 +37,9 @@
33
37
  "default": "./fesm2022/open-rlb-ng-app.mjs"
34
38
  }
35
39
  },
36
- "type": "module"
40
+ "type": "module",
41
+ "files": [
42
+ "**/*",
43
+ "schematics/package.json"
44
+ ]
37
45
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "../../../../node_modules/@angular-devkit/schematics/collection-schema.json",
3
+ "schematics": {
4
+ "ng-add": {
5
+ "description": "Add @open-rlb/ng-app to an Angular project: install peer dependencies, register Bootstrap styles, and scaffold a runnable application shell (environment, providers, app shell, routes, i18n).",
6
+ "factory": "./ng-add/index#ngAdd",
7
+ "schema": "./ng-add/schema.json"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: rlb-app-apps
3
+ description: The @open-rlb/ng-app multi-app orchestration — the AppInfo/AppDescriber model, AppsService (the runtime engine), domain + ACL filtering of apps, current-app selection from the route, and root/deep-link redirect rules. Use when registering apps, building an app hub/selector, controlling which app is "current", or debugging why an app isn't shown or selected.
4
+ ---
5
+
6
+ # @open-rlb/ng-app — Apps & Orchestration
7
+
8
+ `@open-rlb/ng-app` is a **multi-app shell**: a single host hosts one or more logical "apps", each
9
+ described by an `AppInfo`. **`AppsService`** is the runtime engine that filters those apps by
10
+ domain and ACL, selects the current app from the active route, and applies redirect rules. It is
11
+ exported from the public API and **eagerly bootstrapped** by `provideRlbConfig`
12
+ (`provideAppInitializer(() => inject(AppsService))`), so it runs as soon as the app starts.
13
+
14
+ ## The app model — `AppInfo`
15
+
16
+ An app is registered via `provideApp(appDescriber)` (`AppDescriber.info` is an `AppInfo`). Fields
17
+ that drive runtime behavior:
18
+
19
+ | Field | Drives |
20
+ |---|---|
21
+ | `type` | App kind/category (e.g. `'app'`); used in `finalizeApp`/route matching. |
22
+ | `enabled` | Whether the app participates. |
23
+ | `core` / `settings` | `AppDetails` — `{ title, description, url, icon, auth }`. `core.url` is the app's landing route and the redirect target. |
24
+ | `viewMode` | `'app'` \| `'settings'` — how the shell frames the app. |
25
+ | `routes` | Route path strings owned by the app; matched against the active URL to select it. |
26
+ | `domains` | If set, the app only appears/activates on those `window.location.hostname`s (domain gating). |
27
+ | `actions` | ACL action names; the app is hidden unless the user holds a matching resource action (ACL gating). |
28
+ | `autoRedirectOnRootEnabled` | At root with multiple apps, redirect to this app's `core.url`. |
29
+ | `data` | Arbitrary payload; carries the ACL `businessIdKey` / `resourceIdKey` values used for permission checks. |
30
+
31
+ ```typescript
32
+ export const appDescriber: AppDescriber = {
33
+ info: {
34
+ type: 'app',
35
+ enabled: true,
36
+ actions: ['sysadmin'],
37
+ core: { title: 'My App', description: '', url: '/home', icon: 'bi bi-house', auth: true },
38
+ },
39
+ routes, // Angular Routes; flattened paths are stored on RLB_APPS
40
+ };
41
+ ```
42
+
43
+ ## Registration → runtime lifecycle
44
+
45
+ ```
46
+ provideApp(appDescriber) register the AppInfo into RLB_APPS (multi)
47
+ → RLB_INIT_PROVIDER.finalizeApps ACL hook dispatches AppContextActions.finalizeApp → apps gain an `id`
48
+ → AppsService.apps (computed) filters finalized apps by domain + ACL resources/actions
49
+ → router listener (NavigationEnd) matches the route → selects currentApp + applies redirects
50
+ ```
51
+
52
+ This is the "AppsService orchestration" step of the auth pipeline
53
+ (`checkAuthMultiple → resourcesByUser → finalizeApp → AppsService orchestration`). See
54
+ [[rlb-app-auth-acl]] for the ACL hook and [[rlb-app-config]] for `provideApp`/`RLB_APPS`.
55
+
56
+ ## Reading apps from a component
57
+
58
+ Inject `AppsService` and read its signals (zoneless — use signals, never zone.js):
59
+
60
+ ```typescript
61
+ private apps = inject(AppsService);
62
+
63
+ readonly visibleApps = this.apps.apps; // signal: domain- + ACL-filtered AppInfo[]
64
+ readonly current = this.apps.currentApp; // signal: AppInfo | null
65
+ readonly currentId = this.apps.currentAppId; // signal: string | undefined
66
+
67
+ canEdit = this.apps.checkPermissionInCurrentApp('catalog-editor'); // ACL in current app
68
+ this.apps.selectApp(app, 'app', '/home'); // manually select (the app hub/selector uses this)
69
+ ```
70
+
71
+ The built-in app/settings selector pages (`pages/apps/*`, `pages/settings/*`) render
72
+ `apps()` and call `selectApp(...)`.
73
+
74
+ ## Current-app selection & redirect rules
75
+
76
+ On each `NavigationEnd`, `AppsService` resolves the active route and applies (in order):
77
+
78
+ 1. **Single app at root** → auto-redirect to that app's `core.url`.
79
+ 2. **Multiple apps at root** → show the app hub (`currentApp` is `null`), unless an app has
80
+ `autoRedirectOnRootEnabled` (then redirect to it).
81
+ 3. **Deep link** (non-root) → select the app whose `routes`/`core.url` matches the path.
82
+
83
+ Selection waits until apps are **finalized** (every app has an `id`); until then `currentApp`
84
+ stays unresolved.
85
+
86
+ ## ACL on initial deep-link — `findAppForPath`
87
+
88
+ Because `currentApp` isn't selected until `NavigationEnd` fires, `permissionGuard` handles the
89
+ first navigation specially: if no `currentApp` yet, it calls
90
+ `appsService.findAppForPath(path)` to locate the owning app from the URL, then
91
+ `checkPermissionForApp(app, action)`. After that, it uses `checkPermissionInCurrentApp(action)`.
92
+ The `*roles` directive also reads `checkPermissionInCurrentApp` (re-runs reactively). See
93
+ [[rlb-app-auth-acl]].
94
+
95
+ ## Multi-provider auth by domain
96
+
97
+ `AppsService.initAuthProviders()` picks the OIDC provider at startup: if one provider, use it; if
98
+ several, match by `provider.domains` against the current hostname. Multiple matches (or none) log
99
+ a warning and leave the provider unset — give each provider a distinct `domains` entry.
100
+
101
+ ## Gotchas
102
+
103
+ - **Apps must be finalized** (have an `id`) before selection resolves — driven by the ACL init
104
+ hook; an empty `apps()` usually means ACL/finalize hasn't run yet.
105
+ - **`currentApp()` is `null` at root** in multi-app (hub) mode — that's expected, not an error.
106
+ - **Domain gating uses `window.location.hostname`** — `localhost` won't match production
107
+ `domains`; include `localhost` (or omit `domains`) for local dev.
108
+ - Don't re-run `selectApp` redundantly — it no-ops when the id + viewMode are unchanged.
109
+
110
+ Related: [[rlb-app-config]], [[rlb-app-auth-acl]], [[rlb-app-shell]], [[rlb-app-store]].
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: rlb-app-auth-acl
3
+ description: Authentication (OIDC) and authorization (ACL) in @open-rlb/ng-app — OAuth provider config, the oauthGuard and permissionGuard route guards, the *roles structural directive, and the RLB_INIT_PROVIDER ACL startup hook. Use when securing routes/UI or configuring login.
4
+ ---
5
+
6
+ # @open-rlb/ng-app — Auth & ACL
7
+
8
+ Authentication is OIDC via `angular-auth-oidc-client`; authorization is action-based ACL resolved from the backend on startup.
9
+
10
+ ## Configuring auth
11
+
12
+ In `environment.ts` under `auth`:
13
+
14
+ ```typescript
15
+ auth: {
16
+ protocol: 'oauth',
17
+ storage: 'localStorage', // 'cookies' | 'localStorage' | 'sessionStorage'
18
+ interceptor: 'oauth-code-ep', // attach tokens to endpoint calls
19
+ allowedUrls: ['https://api.example.com'],
20
+ enableCompanyInterceptor: true,
21
+ providers: [{
22
+ configId: 'default',
23
+ authority: 'https://login.example.com/realms/your-realm',
24
+ clientId: 'your-client-id',
25
+ redirectUrl: 'http://localhost:4200',
26
+ postLogoutRedirectUri: 'http://localhost:4200',
27
+ scope: 'openid profile offline_access',
28
+ acl: { endpointKey: 'http-gateway', path: 'admin/acl/resources' }, // where to fetch ACL
29
+ }],
30
+ }
31
+ ```
32
+
33
+ The startup pipeline is `checkAuthMultiple → resourcesByUser → finalizeApp → AppsService orchestration` (wired by `provideRlbConfig`). The final step — filtering apps by domain/ACL and selecting the current app from the route — is handled by `AppsService`; see [[rlb-app-apps]].
34
+
35
+ ## Guarding routes
36
+
37
+ ```typescript
38
+ import { oauthGuard, permissionGuard } from '@open-rlb/ng-app';
39
+
40
+ { path: 'account', component: AccountComponent, canActivate: [oauthGuard] },
41
+ { path: 'admin', component: AdminComponent, canActivate: [permissionGuard], data: { action: 'sysadmin' } },
42
+ ```
43
+
44
+ - `oauthGuard` — requires an authenticated session.
45
+ - `permissionGuard` — requires the ACL action named in `route.data.action`.
46
+
47
+ ## Guarding UI
48
+
49
+ The `*roles` structural directive shows content only if the user holds the action:
50
+
51
+ ```html
52
+ <button *roles="'sysadmin'">Admin only</button>
53
+ ```
54
+
55
+ (`RlbAppModule` provides the directive.)
56
+
57
+ ## ACL startup hook — RLB_INIT_PROVIDER
58
+
59
+ Implement `RlbInitProvider` to map the user's `UserResource[]` into app instances:
60
+
61
+ ```typescript
62
+ @Injectable()
63
+ export class AppInitAclProvider implements RlbInitProvider {
64
+ async finalizeApps(resources: UserResource[], store: Store<BaseState>, acl: AclConfiguration) {
65
+ resources.forEach(company => company.resources.forEach(res => {
66
+ store.dispatch(AppContextActions.finalizeApp({
67
+ appType: 'app',
68
+ appId: `app-${res.resourceId}`,
69
+ data: {
70
+ title: res.friendlyName,
71
+ [acl.businessIdKey]: company.companyId,
72
+ [acl.resourceIdKey]: res.resourceId,
73
+ },
74
+ }));
75
+ }));
76
+ }
77
+ }
78
+ ```
79
+
80
+ Register it: `{ provide: RLB_INIT_PROVIDER, useClass: AppInitAclProvider }`. The action names in `appDescriber.info.actions` must match the ACL actions your backend returns.
81
+
82
+ Related: [[rlb-app-apps]], [[rlb-app-config]], [[rlb-app-store]], [[rlb-app-shell]].
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: rlb-app-config
3
+ description: Configuration and bootstrap of an app built on @open-rlb/ng-app — the ProjectConfiguration/environment shape, provideRlbConfig(), provideApp(), the AppDescriber, and the RLB_INIT_PROVIDER startup hook. Use when wiring up app.config.ts, editing environment.ts, or changing app metadata/routes.
4
+ ---
5
+
6
+ # @open-rlb/ng-app — Configuration & Bootstrap
7
+
8
+ You are an expert in bootstrapping an Angular application on the **@open-rlb/ng-app** library. A host app is intentionally thin: it supplies a `ProjectConfiguration` (the environment), an `AppDescriber`, and an optional ACL init provider, then the library wires the store, router, auth, HTTP, i18n and service worker.
9
+
10
+ ## app.config.ts — the three building blocks
11
+
12
+ ```typescript
13
+ export const appConfig: ApplicationConfig = {
14
+ providers: [
15
+ provideRlbConfig(environment), // store + router + auth + i18n + http + SW + registries
16
+ provideApp(appDescriber), // app identity, ACL actions, routes, custom providers
17
+ { provide: RLB_INIT_PROVIDER, useClass: AppInitAclProvider }, // optional startup ACL mapping
18
+ provideZonelessChangeDetection(),
19
+ ],
20
+ };
21
+ ```
22
+
23
+ - `provideRlbConfig(environment)` is the only place the library config is read. It internally calls `provideRlbBootstrap()` (so do NOT call it again), registers the four NgRx feature slices, the default routes derived from `environment.pages`, OIDC auth (`provideRlbCodeBrowserOAuth(env.auth)`), i18n (`provideRlbI18n(env.i18n)`), `provideHttpClient(withInterceptorsFromDi())`, the service worker, and the built-in modal/toast registries.
24
+ - `provideApp(appDescriber)` registers `RLB_APPS` (multi), adds `appDescriber.routes` via `provideRouter`, and spreads any `appDescriber.providers`. At runtime those registered apps are filtered and selected by `AppsService` — see [[rlb-app-apps]].
25
+
26
+ ## environment.ts — ProjectConfiguration
27
+
28
+ `ProjectConfiguration = IConfiguration & { production: boolean }`. Sections:
29
+
30
+ | Key | Purpose |
31
+ |---|---|
32
+ | `environment` | app metadata: `appTitle`, `appLogo`, `baseUrl`, `errorDialogName`, `logLevel`, `pwaUpdateEnabled`, `navbarDisabled` |
33
+ | `auth` | OIDC: `protocol: 'oauth'`, `storage`, `interceptor`, `allowedUrls`, `providers[]` (per-provider `authority`/`clientId`/`redirectUrl`/`acl`) |
34
+ | `i18n` | `availableLangs`, `defaultLanguage`, `useLanguageBrowser`, `storeSelectedLanguage`, `cookieStoreName` |
35
+ | `pages` | named route paths for standard pages (notFound, forbidden, support, …) |
36
+ | `endpoints` | named HTTP/WS backends (`{ baseUrl, healthPath, auth, wss }`) referenced by key (e.g. `http-gateway`) |
37
+ | `acl` | `businessIdKey`, `resourceIdKey`, `interceptorMapping` |
38
+
39
+ Inject any slice with the tokens: `RLB_CFG`, `RLB_CFG_ENV`, `RLB_CFG_I18N`, `RLB_CFG_PAGES`, `RLB_CFG_ACL`, `RLB_CFG_CMS`.
40
+
41
+ ## AppDescriber
42
+
43
+ ```typescript
44
+ export const appDescriber: AppDescriber = {
45
+ info: {
46
+ type: 'app',
47
+ enabled: true,
48
+ actions: ['sysadmin', ...], // ACL action names this app understands
49
+ core: { title, description, url, icon, auth: true },
50
+ settings: { title, description, url, icon, auth: true },
51
+ },
52
+ routes, // Angular Routes; flattened paths are registered with RLB_APPS
53
+ providers: [ /* optional: e.g. RLB_APP_NAVCOMP overrides */ ],
54
+ };
55
+ ```
56
+
57
+ ## RLB_INIT_PROVIDER
58
+
59
+ A class implementing `RlbInitProvider.finalizeApps(resources, store, acl)`. It runs after authentication, turning the user's ACL `UserResource[]` into app instances via `AppContextActions.finalizeApp(...)`. Optional — omit the provider if you don't drive apps from ACL. See [[rlb-app-auth-acl]].
60
+
61
+ ## Common gotchas
62
+
63
+ - The app is **zoneless** — never rely on zone.js; use signals.
64
+ - `appLogo`/i18n files live under `src/assets`; ensure `assets` includes `src/assets` in `angular.json`.
65
+ - The service worker is `enabled: !isDevMode()`, so it's off under `ng serve`. For production builds, configure `ngsw-config.json` (e.g. `ng add @angular/pwa`).
66
+
67
+ Related: [[rlb-app-apps]], [[rlb-app-shell]], [[rlb-app-store]], [[rlb-app-auth-acl]].
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: rlb-app-shell
3
+ description: The @open-rlb/ng-app application shell — <rlb-app-container>, navbar/sidebar configuration via NgRx actions, language selection, and custom navbar item components (RLB_APP_NAVCOMP). Use when building or customizing the app layout/chrome.
4
+ ---
5
+
6
+ # @open-rlb/ng-app — Application Shell
7
+
8
+ The shell renders the full app chrome (navbar, sidebar, modal/toast containers, routed content). The host `AppComponent` is thin: it mounts `<rlb-app-container>` and dispatches store actions to configure visibility and items.
9
+
10
+ ## Mounting the shell
11
+
12
+ ```typescript
13
+ @Component({
14
+ selector: 'app-root',
15
+ imports: [RlbAppModule],
16
+ template: `
17
+ <rlb-app-container
18
+ modal-container-id="modal-c-1"
19
+ toast-container-ids="toast-c-1"
20
+ />`,
21
+ })
22
+ export class AppComponent { /* dispatch config in constructor */ }
23
+ ```
24
+
25
+ `RlbAppModule` aggregates the library's standalone components/directives/pipes; import it wherever you use shell elements (`rlb-app-container`, `rlb-navbar-item`, the `*roles` directive, etc.).
26
+
27
+ ## Configuring navbar & sidebar (NgRx actions)
28
+
29
+ Dispatch in `AppComponent`'s constructor:
30
+
31
+ ```typescript
32
+ this.store.dispatch(NavbarActions.setLoginVisible({ visible: true }));
33
+ this.store.dispatch(NavbarActions.setSettingsVisible({ visible: true }));
34
+ this.store.dispatch(NavbarActions.setAppsVisible({ visible: true }));
35
+
36
+ this.store.dispatch(SidebarActions.setAppsVisible({ visible: true }));
37
+ this.store.dispatch(SidebarActions.setItems({
38
+ items: [
39
+ { title: 'Menu' }, // section header
40
+ { label: 'Home', url: '/', icon: 'bi bi-house' },
41
+ { label: 'Profile', url: '/profile', icon: 'bi bi-person', badgeCounter: 3 },
42
+ { label: 'Links', icon: 'bi bi-link', items: [ // nested
43
+ { label: 'GitHub', icon: 'bi bi-github', externalUrl: 'https://github.com' },
44
+ ]},
45
+ ],
46
+ }));
47
+
48
+ this.store.dispatch(AppContextActions.setSupportedLanguages({ supportedLanguages: ['en', 'it'] }));
49
+ ```
50
+
51
+ Sidebar item shape: `{ title }` (header) or `{ label, url? | externalUrl?, icon?, badgeCounter?, items? }`.
52
+
53
+ ## Custom navbar item components
54
+
55
+ Override navbar slots by providing `RLB_APP_NAVCOMP` (via `appDescriber.providers`):
56
+
57
+ ```typescript
58
+ {
59
+ provide: RLB_APP_NAVCOMP,
60
+ useValue: {
61
+ left: [{ component: MyNavItemComponent, name: 'my-item' }],
62
+ right: [{ component: MyNavItemComponent, name: 'my-item' }],
63
+ },
64
+ }
65
+ ```
66
+
67
+ Icons are Bootstrap Icons (`bi bi-*`), available because the shell registers `bootstrap-icons.css`.
68
+
69
+ Related: [[rlb-app-config]], [[rlb-app-apps]], [[rlb-app-store]], [[rlb-app-auth-acl]].
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: rlb-app-store
3
+ description: The @open-rlb/ng-app NgRx store — the auth, appContext, navbars, and sidebars feature slices, their action groups, and reading state with signals. Use when dispatching actions or selecting app/auth/navbar/sidebar state.
4
+ ---
5
+
6
+ # @open-rlb/ng-app — Store (NgRx)
7
+
8
+ The library owns the store. `provideRlbConfig()` registers four feature slices; the host app dispatches actions and selects state. Prefer **signals** (`store.selectSignal`, `toSignal`) over the `async` pipe — the app is zoneless.
9
+
10
+ ## Feature slices
11
+
12
+ | Feature key | Purpose | Actions |
13
+ |---|---|---|
14
+ | `auth` | login/logout, user info, token | `AuthActions` |
15
+ | `app` (`appContextFeatureKey`) | current app, apps list, theme, language | `AppContextActions` |
16
+ | `navbars` | navbar items, layout mode, visibility | `NavbarActions` |
17
+ | `sidebars` | sidebar items (nested), visibility | `SidebarActions` |
18
+
19
+ `BaseState` is the combined root state (`AclState & AuthState & SidebarState & NavbarState & AppState`). Type your store as `Store<BaseState>`.
20
+
21
+ ## Reading state
22
+
23
+ ```typescript
24
+ private store = inject(Store<BaseState>);
25
+
26
+ // signal (preferred)
27
+ currentApp = this.store.selectSignal(s => s[appContextFeatureKey].currentApp);
28
+
29
+ // or observable
30
+ this.store.select(s => s[appContextFeatureKey].apps).subscribe(/* … */);
31
+ ```
32
+
33
+ ## Common actions
34
+
35
+ ```typescript
36
+ AppContextActions.setSupportedLanguages({ supportedLanguages: ['en', 'it'] });
37
+ AppContextActions.finalizeApp({ appType, appId, data }); // register an app instance (see ACL)
38
+
39
+ NavbarActions.setLoginVisible({ visible });
40
+ NavbarActions.setSettingsVisible({ visible });
41
+ NavbarActions.setAppsVisible({ visible });
42
+
43
+ SidebarActions.setItems({ items });
44
+ SidebarActions.setAppsVisible({ visible });
45
+ ```
46
+
47
+ `*Internal` action groups (e.g. `AppContextActionsInternal`) are driven by the library's effects — don't dispatch them from app code.
48
+
49
+ Related: [[rlb-app-shell]], [[rlb-app-apps]], [[rlb-app-auth-acl]], [[rlb-app-config]].
@@ -0,0 +1,54 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Store } from '@ngrx/store';
3
+ import {
4
+ AclConfiguration,
5
+ AppContextActions,
6
+ appContextFeatureKey,
7
+ BaseState,
8
+ RlbInitProvider,
9
+ UserResource,
10
+ } from '@open-rlb/ng-app';
11
+
12
+ /**
13
+ * Runs once on startup after authentication: turns the user's ACL resources into
14
+ * app instances via `finalizeApp`. This is an example — adapt `appType`/`appId`
15
+ * and the data mapping to your domain.
16
+ */
17
+ @Injectable()
18
+ export class AppInitAclProvider implements RlbInitProvider {
19
+ async finalizeApps(
20
+ resources: UserResource[],
21
+ store: Store<BaseState>,
22
+ acl: AclConfiguration,
23
+ ): Promise<void> {
24
+ if (!resources || resources.length === 0) {
25
+ return;
26
+ }
27
+
28
+ const currentApps = store.selectSignal(state => state[appContextFeatureKey].apps)();
29
+
30
+ resources.forEach(company => {
31
+ company.resources.forEach(res => {
32
+ const appId = `app-${res.resourceId}`;
33
+ if (currentApps.findIndex(app => app.id === appId) === -1) {
34
+ const appData = {
35
+ title: res.friendlyName,
36
+ appName: res.friendlyName,
37
+ [acl.businessIdKey]: company.companyId,
38
+ [acl.resourceIdKey]: res.resourceId,
39
+ };
40
+
41
+ store.dispatch(
42
+ AppContextActions.finalizeApp({
43
+ appType: 'app',
44
+ appId,
45
+ data: appData,
46
+ }),
47
+ );
48
+ } else {
49
+ console.warn(`[AppInitAclProvider]: appId ${appId} exists already, skip finalize`);
50
+ }
51
+ });
52
+ });
53
+ }
54
+ }
@@ -0,0 +1,44 @@
1
+ import { Component } from '@angular/core';
2
+ import { Store } from '@ngrx/store';
3
+ import {
4
+ AppContextActions,
5
+ BaseState,
6
+ NavbarActions,
7
+ RlbAppModule,
8
+ SidebarActions,
9
+ } from '@open-rlb/ng-app';
10
+
11
+ @Component({
12
+ selector: 'app-root',
13
+ imports: [RlbAppModule],
14
+ template: `
15
+ <rlb-app-container
16
+ modal-container-id="modal-c-1"
17
+ toast-container-ids="toast-c-1"
18
+ />
19
+ `,
20
+ })
21
+ export class AppComponent {
22
+ constructor(public store: Store<BaseState>) {
23
+ this.store.dispatch(
24
+ AppContextActions.setSupportedLanguages({
25
+ supportedLanguages: ['en', 'it'],
26
+ }),
27
+ );
28
+
29
+ this.store.dispatch(NavbarActions.setLoginVisible({ visible: true }));
30
+ this.store.dispatch(NavbarActions.setSettingsVisible({ visible: true }));
31
+ this.store.dispatch(NavbarActions.setAppsVisible({ visible: true }));
32
+ this.store.dispatch(SidebarActions.setLoginVisible({ visible: false }));
33
+ this.store.dispatch(SidebarActions.setSettingsVisible({ visible: false }));
34
+ this.store.dispatch(SidebarActions.setAppsVisible({ visible: true }));
35
+ this.store.dispatch(
36
+ SidebarActions.setItems({
37
+ items: [
38
+ { title: 'Menu' },
39
+ { label: 'Home', url: '/', icon: 'bi bi-house' },
40
+ ],
41
+ }),
42
+ );
43
+ }
44
+ }
@@ -0,0 +1,17 @@
1
+ import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
2
+ import { provideApp, provideRlbConfig, RLB_INIT_PROVIDER } from '@open-rlb/ng-app';
3
+ import { environment } from '../environments/environment';
4
+ import { appDescriber } from './app.describer';
5
+ import { AppInitAclProvider } from './app-init-acl.provider';
6
+
7
+ export const appConfig: ApplicationConfig = {
8
+ providers: [
9
+ provideRlbConfig(environment),
10
+ provideApp(appDescriber),
11
+ {
12
+ provide: RLB_INIT_PROVIDER,
13
+ useClass: AppInitAclProvider,
14
+ },
15
+ provideZonelessChangeDetection(),
16
+ ],
17
+ };
@@ -0,0 +1,30 @@
1
+ import { AppDescriber } from '@open-rlb/ng-app';
2
+ import { routes } from './app.routes';
3
+
4
+ /**
5
+ * Describes this app's identity and capabilities to @open-rlb/ng-app.
6
+ * `actions` are the ACL action names this app understands (used by `permissionGuard`
7
+ * and the `*roles` directive). Adjust `info` and `actions` to match your app.
8
+ */
9
+ export const appDescriber: AppDescriber = {
10
+ info: {
11
+ type: 'app',
12
+ enabled: true,
13
+ actions: ['sysadmin'],
14
+ core: {
15
+ title: 'Home',
16
+ description: 'Application home',
17
+ url: 'home',
18
+ icon: 'bi bi-house',
19
+ auth: true,
20
+ },
21
+ settings: {
22
+ title: 'Settings',
23
+ description: 'Application settings',
24
+ url: 'settings',
25
+ icon: 'bi bi-gear',
26
+ auth: true,
27
+ },
28
+ },
29
+ routes,
30
+ };
@@ -0,0 +1,21 @@
1
+ import { Routes } from '@angular/router';
2
+ import { HomeComponent } from './home/home.component';
3
+
4
+ export const routes: Routes = [
5
+ {
6
+ path: '',
7
+ data: { template: 'app' },
8
+ component: HomeComponent,
9
+ },
10
+ {
11
+ path: 'home',
12
+ component: HomeComponent,
13
+ },
14
+ // Example of a route guarded by an ACL action:
15
+ // {
16
+ // path: 'protected',
17
+ // component: ProtectedComponent,
18
+ // canActivate: [permissionGuard],
19
+ // data: { action: 'sysadmin' },
20
+ // },
21
+ ];
@@ -0,0 +1,4 @@
1
+ <div class="container py-4">
2
+ <h1>{{ title }}</h1>
3
+ <p>Your @open-rlb/ng-app shell is running. Edit <code>src/app/home/home.component.html</code> to get started.</p>
4
+ </div>
@@ -0,0 +1,13 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { RlbAppModule } from '@open-rlb/ng-app';
4
+
5
+ @Component({
6
+ selector: 'app-home',
7
+ changeDetection: ChangeDetectionStrategy.OnPush,
8
+ imports: [CommonModule, RlbAppModule],
9
+ templateUrl: './home.component.html',
10
+ })
11
+ export class HomeComponent {
12
+ title = 'Home';
13
+ }