@flusys/ng-core 1.1.0 → 1.2.0-rc.1

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/README.md CHANGED
@@ -1,632 +1,153 @@
1
- # @flusys/ng-core Package Guide
1
+ # @flusys/ng-core
2
2
 
3
- ## Overview
3
+ Foundation library for the FLUSYS Angular platform — app configuration token, base HTTP service, loader service, and HTTP interceptors.
4
4
 
5
- `@flusys/ng-core` is the foundation package that provides core functionality for all FLUSYS Angular libraries and applications. It contains configuration management, HTTP interceptors, base services, and type definitions.
6
-
7
- **Key Principle:** ng-core has NO internal dependencies on other @flusys packages. All other packages depend on ng-core.
8
-
9
- ## Package Information
10
-
11
- - **Package:** `@flusys/ng-core`
12
- - **Version:** 1.1.0
13
- - **Dependencies:** None (base package)
14
- - **Dependents:** ng-shared, ng-layout, ng-auth, ng-iam, ng-storage, flusysng
15
- - **Build Command:** `npm run build:ng-core`
16
-
17
- ## Package Structure
18
-
19
- ```
20
- ng-core/
21
- ├── components/
22
- │ ├── lib-app-config/
23
- │ │ ├── lib-app-config.component.ts
24
- │ │ ├── lib-app-config.component.html
25
- │ │ └── lib-app-config.component.scss
26
- │ └── public-api.ts
27
- ├── interceptors/
28
- │ ├── api-loader.interceptor.ts
29
- │ ├── error-catching.interceptor.ts
30
- │ └── public-api.ts
31
- ├── interfaces/
32
- │ ├── app-config.interface.ts
33
- │ └── public-api.ts
34
- ├── services/
35
- │ ├── api-loader.service.ts
36
- │ ├── base-api.service.ts
37
- │ └── public-api.ts
38
- ├── ng-package.json
39
- ├── package.json
40
- ├── tsconfig.lib.json
41
- ├── tsconfig.lib.prod.json
42
- └── public-api.ts
43
- ```
44
-
45
- ## Public API Exports
46
-
47
- ```typescript
48
- // public-api.ts - re-exports from each module's public-api
49
- export * from './components/public-api';
50
- export * from './interceptors/public-api';
51
- export * from './interfaces/public-api';
52
- export * from './services/public-api';
53
- ```
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/ng-core.svg)](https://www.npmjs.com/package/@flusys/ng-core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
54
7
 
55
8
  ---
56
9
 
57
- ## Features
58
-
59
- ### 1. APP_CONFIG (Configuration Management)
60
-
61
- Centralized configuration system with dynamic service URLs and feature flags.
62
-
63
- #### IAppConfig Interface
64
-
65
- ```typescript
66
- export type PermissionMode = 'rbac' | 'direct' | 'full';
67
-
68
- export interface IServiceConfig {
69
- baseUrl: string;
70
- enabled: boolean;
71
- }
72
-
73
- export interface IAuthorConfig {
74
- name: string;
75
- url: string;
76
- }
77
-
78
- export interface IRegistrationDefaults {
79
- companySlug?: string; // Pre-fill company code on registration
80
- branchSlug?: string; // Pre-fill branch code on registration
81
- }
10
+ ## Installation
82
11
 
83
- export interface IAppConfig {
84
- appName: string;
85
- author?: IAuthorConfig; // Author/Brand info for footer attribution
86
- apiBaseUrl: string; // Default fallback URL
87
- enableCompanyFeature: boolean; // Company/branch selection
88
- enableSignUp?: boolean; // Enable registration (default: true)
89
- registrationDefaults?: IRegistrationDefaults; // Pre-fill registration fields
90
-
91
- services: {
92
- auth: IServiceConfig;
93
- administration: IServiceConfig;
94
- iam: IServiceConfig;
95
- storage: IStorageServiceConfig; // Extends IServiceConfig with defaultStorageConfigId
96
- formBuilder: IServiceConfig;
97
- email: IServiceConfig;
98
- };
99
-
100
- multiTenant: {
101
- enabled: boolean; // true = multi-tenant DB, false = single DB
102
- tenantHeader: string; // Header name (e.g., 'x-tenant-id')
103
- tenantId?: string; // Optional tenant ID value
104
- };
105
-
106
- permissionMode?: PermissionMode; // Defaults to 'full'
107
- }
12
+ ```bash
13
+ npm install @flusys/ng-core
108
14
  ```
109
15
 
110
- #### APP_CONFIG Token
16
+ ---
111
17
 
112
- ```typescript
113
- export const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');
114
- ```
18
+ ## 1. Register APP_CONFIG and Interceptors
115
19
 
116
- **Provider setup in app.config.ts:**
20
+ Provide the config token and HTTP interceptors once in `app.config.ts`. Every FLUSYS package reads from `APP_CONFIG`.
117
21
 
118
22
  ```typescript
119
- import { APP_CONFIG } from '@flusys/ng-core';
120
- import { DEFAULT_APP_CONFIG } from './config/app.config';
23
+ import { ApplicationConfig } from "@angular/core";
24
+ import { provideHttpClient, withFetch, withInterceptors } from "@angular/common/http";
25
+ import { APP_CONFIG, errorCatchingInterceptor, apiLoaderInterceptor } from "@flusys/ng-core";
121
26
 
122
27
  export const appConfig: ApplicationConfig = {
123
28
  providers: [
124
- { provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
125
- ]
29
+ {
30
+ provide: APP_CONFIG,
31
+ useValue: {
32
+ appName: "My App",
33
+ appLogo: "/assets/logo.png",
34
+ apiBaseUrl: "http://localhost:2002",
35
+ production: false,
36
+ permissionMode: "FULL", // 'RBAC' | 'DIRECT' | 'FULL'
37
+ multiTenant: false,
38
+ services: {
39
+ auth: {
40
+ enabled: true,
41
+ features: {
42
+ signUp: true,
43
+ emailVerification: true,
44
+ companySelection: true,
45
+ },
46
+ },
47
+ iam: { enabled: true },
48
+ storage: { enabled: true },
49
+ formBuilder: { enabled: false },
50
+ email: { enabled: false },
51
+ eventManager: { enabled: false },
52
+ notification: { enabled: false, socketUrl: "ws://localhost:2002" }, // optional
53
+ localization: { enabled: false },
54
+ },
55
+ },
56
+ },
57
+ provideHttpClient(
58
+ withFetch(),
59
+ withInterceptors([
60
+ // authInterceptor, // from @flusys/ng-auth — add first
61
+ // tokenRefreshInterceptor, // from @flusys/ng-auth — add second
62
+ errorCatchingInterceptor, // shows toast on non-401/403 errors
63
+ apiLoaderInterceptor, // drives global progress bar
64
+ ]),
65
+ ),
66
+ ],
126
67
  };
127
68
  ```
128
69
 
129
- #### Environment Structure (Shared Base Pattern)
70
+ Add `<lib-app-config />` once in your app root template. It renders the global toast, confirm dialog, and progress bar.
130
71
 
131
- All common configuration is extracted to a shared base:
132
-
133
- ```typescript
134
- // projects/flusysng/src/environments/environment.base.ts
135
- export const BASE_ENVIRONMENT = {
136
- appName: 'FLUSYS',
137
- multiTenant: { enabled: false, tenantHeader: 'x-tenant-id' },
138
- storage: { apiPath: '/upload/file', maxFileSize: 10485760, allowedMimeTypes: [...] },
139
- };
140
-
141
- // projects/flusysng/src/environments/environment.ts (development)
142
- import { BASE_ENVIRONMENT } from './environment.base';
143
-
144
- export const environment = {
145
- ...BASE_ENVIRONMENT,
146
- production: false,
147
- apiBaseUrl: 'http://localhost:2002',
148
- services: {
149
- auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
150
- administration: { baseUrl: 'http://localhost:2002/administration', enabled: true },
151
- iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
152
- storage: { baseUrl: 'http://localhost:2002/storage', enabled: true, defaultStorageConfigId: '...' },
153
- formBuilder: { baseUrl: 'http://localhost:2002/form-builder', enabled: true },
154
- email: { baseUrl: 'http://localhost:2002/email', enabled: true },
155
- },
156
- };
157
- ```
158
-
159
- #### Helper Functions
72
+ ---
160
73
 
161
- ```typescript
162
- import {
163
- getDatabaseMode,
164
- isFeatureEnabled,
165
- getServiceUrl,
166
- isServiceEnabled,
167
- isCompanyFeatureEnabled,
168
- isStorageFeatureEnabled,
169
- isFormBuilderFeatureEnabled,
170
- isEmailFeatureEnabled,
171
- getPermissionMode,
172
- getDefaultStorageConfigId,
173
- isSignUpEnabled,
174
- getDefaultCompanySlug,
175
- getDefaultBranchSlug,
176
- getRegistrationDefaults,
177
- } from '@flusys/ng-core';
178
- ```
74
+ ## 2. BaseApiService
179
75
 
180
- | Function | Signature | Description |
181
- |----------|-----------|-------------|
182
- | `getDatabaseMode` | `(config) → 'single' \| 'multi-tenant'` | Get database mode from `multiTenant.enabled` |
183
- | `isFeatureEnabled` | `(config, feature: ServiceName) → boolean` | Check if a service feature is enabled |
184
- | `getServiceUrl` | `(config, serviceName: ServiceName) → string` | Get service URL or fallback to `apiBaseUrl` |
185
- | `isServiceEnabled` | `(config, serviceName: ServiceName) → boolean` | Check if a service is enabled |
186
- | `isCompanyFeatureEnabled` | `(config) → boolean` | Check `enableCompanyFeature` flag |
187
- | `isStorageFeatureEnabled` | `(config) → boolean` | Check `services.storage.enabled` |
188
- | `isFormBuilderFeatureEnabled` | `(config) → boolean` | Check `services.formBuilder.enabled` |
189
- | `isEmailFeatureEnabled` | `(config) → boolean` | Check `services.email.enabled` |
190
- | `getPermissionMode` | `(config) → PermissionMode` | Get permission mode (defaults to `'full'`) |
191
- | `getDefaultStorageConfigId` | `(config) → string \| undefined` | Get `services.storage.defaultStorageConfigId` |
192
- | `isSignUpEnabled` | `(config) → boolean` | Check `enableSignUp` flag (defaults to `true`) |
193
- | `getDefaultCompanySlug` | `(config) → string \| undefined` | Get `registrationDefaults.companySlug` |
194
- | `getDefaultBranchSlug` | `(config) → string \| undefined` | Get `registrationDefaults.branchSlug` |
195
- | `getRegistrationDefaults` | `(config) → IRegistrationDefaults \| undefined` | Get full registration defaults object |
196
-
197
- **ServiceName Type:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
198
-
199
- #### Constants
76
+ Extend to create a typed service for any POST-only RPC endpoint. The base URL is derived from `APP_CONFIG.apiBaseUrl`.
200
77
 
201
78
  ```typescript
202
- export const DEFAULT_APP_NAME = 'FLUSYS';
203
- export const DEFAULT_AUTHOR: IAuthorConfig = {
204
- name: 'Flusys',
205
- url: 'https://flusys.vercel.app',
206
- };
207
- ```
79
+ import { Injectable } from "@angular/core";
80
+ import { BaseApiService } from "@flusys/ng-core";
208
81
 
209
- #### Configuration Modes
210
-
211
- **Mode 1: Simple (No Company, No IAM)**
212
- ```typescript
213
- {
214
- enableCompanyFeature: false,
215
- multiTenant: { enabled: false, tenantHeader: 'x-tenant-id' },
216
- services: {
217
- auth: { baseUrl: '...', enabled: false },
218
- administration: { baseUrl: '...', enabled: false },
219
- iam: { baseUrl: '...', enabled: false },
220
- storage: { baseUrl: '...', enabled: false },
221
- formBuilder: { baseUrl: '...', enabled: false },
222
- email: { baseUrl: '...', enabled: false },
82
+ @Injectable({ providedIn: "root" })
83
+ export class ProductService extends BaseApiService {
84
+ getAll() {
85
+ return this.http.post(`${this.baseUrl}/products/get-all`, {});
223
86
  }
224
- }
225
- ```
226
87
 
227
- **Mode 2: Single DB + Multi-Company**
228
- ```typescript
229
- {
230
- enableCompanyFeature: true,
231
- multiTenant: { enabled: false, tenantHeader: 'x-tenant-id' },
232
- services: {
233
- auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
234
- administration: { baseUrl: 'http://localhost:2002/administration', enabled: true },
235
- iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
236
- storage: { baseUrl: 'http://localhost:2002/storage', enabled: true, defaultStorageConfigId: '...' },
237
- formBuilder: { baseUrl: 'http://localhost:2002/form-builder', enabled: true },
238
- email: { baseUrl: 'http://localhost:2002/email', enabled: true },
239
- }
240
- }
241
- ```
242
-
243
- **Mode 3: Multi-Tenant**
244
- ```typescript
245
- {
246
- enableCompanyFeature: true,
247
- multiTenant: { enabled: true, tenantHeader: 'x-tenant-id', tenantId: 'tenant-123' },
248
- services: {
249
- auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
250
- administration: { baseUrl: 'http://localhost:2002/administration', enabled: true },
251
- iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
252
- storage: { baseUrl: 'http://localhost:2002/storage', enabled: true, defaultStorageConfigId: '...' },
253
- formBuilder: { baseUrl: 'http://localhost:2002/form-builder', enabled: true },
254
- email: { baseUrl: 'http://localhost:2002/email', enabled: true },
88
+ findById(id: string) {
89
+ return this.http.post(this.getUrl(`products/get/${id}`), {});
255
90
  }
256
91
  }
257
92
  ```
258
93
 
259
94
  ---
260
95
 
261
- ### 2. HTTP Interceptors
262
-
263
- ng-core provides two functional interceptors. Both use the modern Angular functional interceptor pattern.
96
+ ## 3. ApiLoaderService — Global Progress Bar
264
97
 
265
- #### apiLoaderInterceptor
266
-
267
- Tracks HTTP request loading state with tagged loaders. Uses a custom `x-loader-tag` header to group related requests.
268
-
269
- **Behavior:**
270
- 1. Reads `x-loader-tag` header from request (defaults to `'default'`)
271
- 2. Increments loader count for that tag
272
- 3. Removes `x-loader-tag` header before sending to backend
273
- 4. Decrements loader count on request completion (success or error)
98
+ `apiLoaderInterceptor` automatically increments/decrements a reference count per request. `show` is `true` while any request is in flight.
274
99
 
275
100
  ```typescript
276
- import { apiLoaderInterceptor } from '@flusys/ng-core';
277
-
278
- // Tag a specific request
279
- this.http.post('/api/users/get-all', filter, {
280
- headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })
281
- });
101
+ import { ApiLoaderService } from '@flusys/ng-core';
282
102
 
283
- // Check loading state for that tag
284
- apiLoaderService.getLoader('users-list')(); // true while loading
103
+ readonly loader = inject(ApiLoaderService);
104
+ // loader.show — Signal<boolean>
285
105
  ```
286
106
 
287
- #### errorCatchingInterceptor
288
-
289
- Displays user-friendly error toast messages for failed HTTP requests via PrimeNG `MessageService`.
290
-
291
- **Behavior:**
292
- - Skips 401 errors (handled by `tokenRefreshInterceptor` in ng-auth)
293
- - Skips 403 errors (handled by permission denied UI in components)
294
- - Skips `/check-login` endpoint errors (expected to fail when not logged in)
295
- - Skips requests with `SKIP_ERROR_TOAST` context token set to `true`
296
- - Uses backend's error title (`error.error.error`) as toast summary
297
- - Uses backend's error message (`error.error.message`) as toast detail
298
- - Falls back to status-based defaults for unknown error formats
299
- - SSR-safe: only displays messages in browser (`isPlatformBrowser` check)
300
-
301
- **SKIP_ERROR_TOAST Context Token:**
302
-
303
- Use this to skip global error toast for specific requests where the component handles errors locally:
107
+ Tag requests to get per-section loading signals:
304
108
 
305
109
  ```typescript
306
- import { HttpContext } from '@angular/common/http';
307
- import { SKIP_ERROR_TOAST } from '@flusys/ng-core';
110
+ import { HttpHeaders } from "@angular/common/http";
308
111
 
309
- this.http.post(url, data, {
310
- context: new HttpContext().set(SKIP_ERROR_TOAST, true)
112
+ this.http.post(url, body, {
113
+ headers: new HttpHeaders({ "x-loader-tag": "user-list" }),
311
114
  });
312
- ```
313
115
 
314
- #### Interceptor Chain
315
-
316
- ```typescript
317
- import { errorCatchingInterceptor, apiLoaderInterceptor } from '@flusys/ng-core';
318
- import { authInterceptor, tokenRefreshInterceptor } from '@flusys/ng-auth';
319
-
320
- provideHttpClient(
321
- withFetch(),
322
- withInterceptors([
323
- authInterceptor, // from @flusys/ng-auth (adds auth + tenant headers)
324
- tokenRefreshInterceptor, // from @flusys/ng-auth (handles 401 + token refresh)
325
- errorCatchingInterceptor, // from @flusys/ng-core (displays error toasts)
326
- apiLoaderInterceptor, // from @flusys/ng-core (tracks loading state)
327
- ])
328
- )
116
+ const isUserListLoading = this.loader.getLoader("user-list"); // Signal<boolean>
329
117
  ```
330
118
 
331
- **Order matters:** Auth interceptors first, then error handling, then loader tracking.
332
-
333
119
  ---
334
120
 
335
- ### 3. Services
336
-
337
- #### ApiLoaderService
338
-
339
- Signal-based loading state management with tagged loaders. Provided at root level.
340
-
341
- **Signals:**
342
-
343
- | Signal | Type | Description |
344
- |--------|------|-------------|
345
- | `show` | `Signal<boolean>` | `true` if ANY loader tag has active requests |
346
-
347
- **Methods:**
348
-
349
- | Method | Parameters | Description |
350
- |--------|-----------|-------------|
351
- | `increase(name?)` | `name: string = 'default'` | Increment loader count for a tag |
352
- | `decrease(name?)` | `name: string = 'default'` | Decrement loader count for a tag (min 0) |
353
- | `getLoader(name)` | `name: string` | Get computed `Signal<boolean>` for a specific tag |
354
-
355
- **Properties:**
121
+ ## 4. Skip Error Toast Per Request
356
122
 
357
- | Property | Type | Description |
358
- |----------|------|-------------|
359
- | `DEFAULT_TAG` | `string` | Default tag name (`'default'`) |
360
-
361
- **Usage:**
123
+ Opt out of the global error toast for requests you handle locally:
362
124
 
363
125
  ```typescript
364
- // Global loading state
365
- @if (apiLoaderService.show()) {
366
- <p-progressBar mode="indeterminate" />
367
- }
368
-
369
- // Tag-specific loading state
370
- @if (apiLoaderService.getLoader('users-list')()) {
371
- <p>Loading users...</p>
372
- }
373
- ```
374
-
375
- #### BaseApiService
376
-
377
- Abstract base class for API services. Provides automatic URL resolution from APP_CONFIG.
378
-
379
- **Protected members:**
380
-
381
- | Member | Type | Description |
382
- |--------|------|-------------|
383
- | `http` | `HttpClient` | Injected HTTP client |
384
- | `appConfig` | `IAppConfig` | Injected app configuration |
385
- | `baseUrl` | `string` | Resolved base URL for the service |
386
-
387
- **Protected methods:**
126
+ import { SKIP_ERROR_TOAST } from "@flusys/ng-core";
127
+ import { HttpContext } from "@angular/common/http";
388
128
 
389
- | Method | Parameters | Returns | Description |
390
- |--------|-----------|---------|-------------|
391
- | `getUrl(endpoint)` | `endpoint: string` | `string` | Build full URL, removes leading slash to avoid double slashes |
392
-
393
- **Usage:**
394
-
395
- ```typescript
396
- @Injectable({ providedIn: 'root' })
397
- export class UserApiService extends BaseApiService {
398
- constructor() {
399
- super('auth'); // Resolves to services.auth.baseUrl
400
- }
401
-
402
- getAll(filter: any) {
403
- return this.http.post(this.getUrl('user/get-all'), filter);
404
- }
405
-
406
- getById(id: string) {
407
- return this.http.post(`${this.getUrl('user/get')}/${id}`, {});
408
- }
409
- }
410
- ```
411
-
412
- ---
413
-
414
- ### 4. Components
415
-
416
- #### LibAppConfigComponent
417
-
418
- Root configuration component providing global UI elements. Include once at app root.
419
-
420
- **Selector:** `lib-app-config`
421
-
422
- **Change Detection:** `OnPush` (optimal performance)
423
-
424
- **Provides:**
425
- - Global loading progress bar (fixed top, 4px height, z-index 998)
426
- - Toast notifications (PrimeNG Toast)
427
- - Confirm dialogs (PrimeNG ConfirmDialog)
428
-
429
- **Template:**
430
- ```html
431
- @if (apiLoaderService.show()) {
432
- <p-progressBar mode="indeterminate" />
433
- }
434
- <p-toast />
435
- <p-confirmdialog />
436
- ```
437
-
438
- **Usage:**
439
- ```typescript
440
- import { LibAppConfigComponent } from '@flusys/ng-core';
441
-
442
- @Component({
443
- selector: 'app-root',
444
- standalone: true,
445
- imports: [LibAppConfigComponent, RouterOutlet],
446
- template: `
447
- <lib-app-config />
448
- <router-outlet />
449
- `
450
- })
451
- export class AppComponent {}
452
- ```
453
-
454
- ---
455
-
456
- ## Usage Examples
457
-
458
- ### Basic App Setup
459
-
460
- ```typescript
461
- // app.config.ts
462
- import { ApplicationConfig } from '@angular/core';
463
- import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
464
- import { APP_CONFIG, errorCatchingInterceptor, apiLoaderInterceptor } from '@flusys/ng-core';
465
- import { authInterceptor, tokenRefreshInterceptor } from '@flusys/ng-auth';
466
- import { DEFAULT_APP_CONFIG } from './config/app.config';
467
-
468
- export const appConfig: ApplicationConfig = {
469
- providers: [
470
- provideHttpClient(
471
- withFetch(),
472
- withInterceptors([
473
- authInterceptor,
474
- tokenRefreshInterceptor,
475
- errorCatchingInterceptor,
476
- apiLoaderInterceptor,
477
- ])
478
- ),
479
- { provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
480
- ]
481
- };
482
- ```
483
-
484
- ### Creating an API Service
485
-
486
- ```typescript
487
- import { Injectable } from '@angular/core';
488
- import { BaseApiService } from '@flusys/ng-core';
489
-
490
- @Injectable({ providedIn: 'root' })
491
- export class AuthApiService extends BaseApiService {
492
- constructor() {
493
- super('auth'); // Resolves to services.auth.baseUrl
494
- }
495
-
496
- login(credentials: any) {
497
- return this.http.post(this.getUrl('auth/login'), credentials);
498
- }
499
- }
500
- ```
501
-
502
- ### Using Tagged Loaders
503
-
504
- ```typescript
505
- import { HttpHeaders } from '@angular/common/http';
506
- import { ApiLoaderService } from '@flusys/ng-core';
507
-
508
- // In a service - tag requests
509
- this.http.post('/api/users/get-all', filter, {
510
- headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })
129
+ this.http.post(url, body, {
130
+ context: new HttpContext().set(SKIP_ERROR_TOAST, true),
511
131
  });
512
-
513
- // In a component - check specific loader
514
- readonly usersLoading = this.apiLoaderService.getLoader('users-list');
515
-
516
- // In template
517
- @if (usersLoading()) {
518
- <p-skeleton />
519
- }
520
132
  ```
521
133
 
522
134
  ---
523
135
 
524
- ## Best Practices
525
-
526
- 1. **Always provide APP_CONFIG** at application root level
527
- 2. **Match backend configuration mode** (`multiTenant.enabled` should align with FLUSYS_NEST setup)
528
- 3. **Enable features based on backend availability** (`services.iam.enabled` only if IAM module is deployed)
529
- 4. **Never import from other @flusys packages** in ng-core
530
- 5. **Keep ng-core minimal** - only foundation functionality
531
- 6. **Use `BaseApiService`** for all API services to avoid hardcoded URLs
532
- 7. **Don't create custom error handling** - use `errorCatchingInterceptor`
533
- 8. **Don't manually track loading** - use `apiLoaderInterceptor` with `x-loader-tag` headers
136
+ ## 6. Feature Helper Functions
534
137
 
535
- ## Common Issues
138
+ Read flags without injecting `APP_CONFIG` directly:
536
139
 
537
- ### APP_CONFIG Not Found
538
-
539
- **Problem:** `NullInjectorError: No provider for InjectionToken APP_CONFIG`
540
-
541
- **Solution:** Provide APP_CONFIG in app.config.ts:
542
140
  ```typescript
543
- { provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG }
544
- ```
545
-
546
- ### Circular Dependency
547
-
548
- **Problem:** Build fails with circular dependency error involving ng-core.
141
+ import { isSignUpEnabled, getPermissionMode, getNotificationSocketUrl } from "@flusys/ng-core";
549
142
 
550
- **Solution:** ng-core should NEVER import from other @flusys packages. Check imports and remove any references to ng-shared, ng-layout, or ng-auth.
143
+ const config = inject(APP_CONFIG);
144
+ isSignUpEnabled(config); // boolean — reads services.auth.features.signUp
145
+ getPermissionMode(config); // 'RBAC' | 'DIRECT' | 'FULL'
146
+ getNotificationSocketUrl(config); // string | undefined
147
+ ```
551
148
 
552
149
  ---
553
150
 
554
- ## API Reference
555
-
556
- ### Tokens
557
-
558
- | Token | Type | Description |
559
- |-------|------|-------------|
560
- | `APP_CONFIG` | `InjectionToken<IAppConfig>` | Application configuration |
561
-
562
- ### Interceptors
563
-
564
- | Function | Description |
565
- |----------|-------------|
566
- | `apiLoaderInterceptor` | Loading state management with tagged loaders |
567
- | `errorCatchingInterceptor` | Error toast display (skips 401, 403, check-login, and SKIP_ERROR_TOAST) |
568
- | `SKIP_ERROR_TOAST` | HttpContextToken to skip global error toast for specific requests |
569
-
570
- ### Services
571
-
572
- | Service | Description |
573
- |---------|-------------|
574
- | `ApiLoaderService` | Signal-based loading state with tagged loaders |
575
- | `BaseApiService` | Abstract base class for API services with URL resolution |
576
-
577
- ### Interfaces & Types
578
-
579
- | Export | Description |
580
- |--------|-------------|
581
- | `IAppConfig` | App configuration interface |
582
- | `IServiceConfig` | Service configuration (`baseUrl`, `enabled`) |
583
- | `IStorageServiceConfig` | Storage service config (extends IServiceConfig with `defaultStorageConfigId`) |
584
- | `IAuthorConfig` | Author/brand configuration (`name`, `url`) |
585
- | `IRegistrationDefaults` | Registration defaults (`companySlug`, `branchSlug`) |
586
- | `PermissionMode` | `'rbac' \| 'direct' \| 'full'` |
587
-
588
- ### Helper Functions
589
-
590
- | Function | Description |
591
- |----------|-------------|
592
- | `getDatabaseMode(config)` | Returns `'single'` or `'multi-tenant'` |
593
- | `isFeatureEnabled(config, feature)` | Check service feature enabled (ServiceName) |
594
- | `getServiceUrl(config, serviceName)` | Get service URL with fallback |
595
- | `isServiceEnabled(config, serviceName)` | Check if service is enabled |
596
- | `isCompanyFeatureEnabled(config)` | Check company feature flag |
597
- | `isStorageFeatureEnabled(config)` | Check storage service enabled |
598
- | `isFormBuilderFeatureEnabled(config)` | Check formBuilder service enabled |
599
- | `isEmailFeatureEnabled(config)` | Check email service enabled |
600
- | `getPermissionMode(config)` | Get permission mode (default `'full'`) |
601
- | `getDefaultStorageConfigId(config)` | Get default storage config ID for uploads |
602
- | `isSignUpEnabled(config)` | Check sign-up enabled (default `true`) |
603
- | `getDefaultCompanySlug(config)` | Get default company slug for registration |
604
- | `getDefaultBranchSlug(config)` | Get default branch slug for registration |
605
-
606
- **ServiceName:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
607
-
608
- ### Constants
609
-
610
- | Constant | Value | Description |
611
- |----------|-------|-------------|
612
- | `DEFAULT_APP_NAME` | `'FLUSYS'` | Default application name |
613
- | `DEFAULT_AUTHOR` | `{ name: 'Flusys', url: '...' }` | Default author/brand info |
614
-
615
- ### Components
616
-
617
- | Component | Selector | Description |
618
- |-----------|----------|-------------|
619
- | `LibAppConfigComponent` | `lib-app-config` | Global toast, confirm dialog, and loading bar |
620
-
621
- ## See Also
622
-
623
- - **[SHARED-GUIDE.md](./SHARED-GUIDE.md)** - API services, components, directives
624
- - **[LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md)** - Layout system
625
- - **[Angular Patterns](../../.claude/docs/angular/)** - Framework patterns
626
- - **[FLUSYS_NEST/docs/CORE-GUIDE.md](../../FLUSYS_NEST/docs/CORE-GUIDE.md)** - Backend core module
627
-
628
- ---
151
+ ## License
629
152
 
630
- **Last Updated:** 2026-02-23
631
- **Version:** 1.1.0
632
- **Angular Version:** 21
153
+ MIT © FLUSYS