@flusys/ng-core 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/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  ## Package Information
10
10
 
11
11
  - **Package:** `@flusys/ng-core`
12
+ - **Version:** 1.1.0
12
13
  - **Dependencies:** None (base package)
13
14
  - **Dependents:** ng-shared, ng-layout, ng-auth, ng-iam, ng-storage, flusysng
14
15
  - **Build Command:** `npm run build:ng-core`
@@ -18,37 +19,37 @@
18
19
  ```
19
20
  ng-core/
20
21
  ├── components/
21
- │ └── lib-app-config/
22
- │ ├── lib-app-config.component.ts
23
- │ ├── lib-app-config.component.html
24
- │ └── lib-app-config.component.scss
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
25
27
  ├── interceptors/
26
28
  │ ├── api-loader.interceptor.ts
27
- │ └── error-catching.interceptor.ts
29
+ │ ├── error-catching.interceptor.ts
30
+ │ └── public-api.ts
28
31
  ├── interfaces/
29
- │ └── app-config.interface.ts
32
+ │ ├── app-config.interface.ts
33
+ │ └── public-api.ts
30
34
  ├── services/
31
35
  │ ├── api-loader.service.ts
32
- │ └── base-api.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
33
42
  └── public-api.ts
34
43
  ```
35
44
 
36
45
  ## Public API Exports
37
46
 
38
47
  ```typescript
39
- // Components
40
- export * from './components/lib-app-config/lib-app-config.component';
41
-
42
- // Interceptors
43
- export * from './interceptors/api-loader.interceptor';
44
- export * from './interceptors/error-catching.interceptor';
45
-
46
- // Interfaces
47
- export * from './interfaces/app-config.interface';
48
-
49
- // Services
50
- export * from './services/api-loader.service';
51
- export * from './services/base-api.service';
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';
52
53
  ```
53
54
 
54
55
  ---
@@ -74,11 +75,18 @@ export interface IAuthorConfig {
74
75
  url: string;
75
76
  }
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
+ }
82
+
77
83
  export interface IAppConfig {
78
84
  appName: string;
79
85
  author?: IAuthorConfig; // Author/Brand info for footer attribution
80
86
  apiBaseUrl: string; // Default fallback URL
81
87
  enableCompanyFeature: boolean; // Company/branch selection
88
+ enableSignUp?: boolean; // Enable registration (default: true)
89
+ registrationDefaults?: IRegistrationDefaults; // Pre-fill registration fields
82
90
 
83
91
  services: {
84
92
  auth: IServiceConfig;
@@ -87,7 +95,6 @@ export interface IAppConfig {
87
95
  storage: IStorageServiceConfig; // Extends IServiceConfig with defaultStorageConfigId
88
96
  formBuilder: IServiceConfig;
89
97
  email: IServiceConfig;
90
- [key: string]: IServiceConfig; // Extensible for custom services
91
98
  };
92
99
 
93
100
  multiTenant: {
@@ -162,6 +169,11 @@ import {
162
169
  isFormBuilderFeatureEnabled,
163
170
  isEmailFeatureEnabled,
164
171
  getPermissionMode,
172
+ getDefaultStorageConfigId,
173
+ isSignUpEnabled,
174
+ getDefaultCompanySlug,
175
+ getDefaultBranchSlug,
176
+ getRegistrationDefaults,
165
177
  } from '@flusys/ng-core';
166
178
  ```
167
179
 
@@ -176,6 +188,11 @@ import {
176
188
  | `isFormBuilderFeatureEnabled` | `(config) → boolean` | Check `services.formBuilder.enabled` |
177
189
  | `isEmailFeatureEnabled` | `(config) → boolean` | Check `services.email.enabled` |
178
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 |
179
196
 
180
197
  **ServiceName Type:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
181
198
 
@@ -273,10 +290,27 @@ Displays user-friendly error toast messages for failed HTTP requests via PrimeNG
273
290
 
274
291
  **Behavior:**
275
292
  - Skips 401 errors (handled by `tokenRefreshInterceptor` in ng-auth)
293
+ - Skips 403 errors (handled by permission denied UI in components)
276
294
  - Skips `/check-login` endpoint errors (expected to fail when not logged in)
277
- - Extracts message from `error.error.message` (string or array), `error.message`, or fallback
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
278
299
  - SSR-safe: only displays messages in browser (`isPlatformBrowser` check)
279
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:
304
+
305
+ ```typescript
306
+ import { HttpContext } from '@angular/common/http';
307
+ import { SKIP_ERROR_TOAST } from '@flusys/ng-core';
308
+
309
+ this.http.post(url, data, {
310
+ context: new HttpContext().set(SKIP_ERROR_TOAST, true)
311
+ });
312
+ ```
313
+
280
314
  #### Interceptor Chain
281
315
 
282
316
  ```typescript
@@ -530,7 +564,8 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
530
564
  | Function | Description |
531
565
  |----------|-------------|
532
566
  | `apiLoaderInterceptor` | Loading state management with tagged loaders |
533
- | `errorCatchingInterceptor` | Error toast display (skips 401 and check-login) |
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 |
534
569
 
535
570
  ### Services
536
571
 
@@ -545,7 +580,9 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
545
580
  |--------|-------------|
546
581
  | `IAppConfig` | App configuration interface |
547
582
  | `IServiceConfig` | Service configuration (`baseUrl`, `enabled`) |
583
+ | `IStorageServiceConfig` | Storage service config (extends IServiceConfig with `defaultStorageConfigId`) |
548
584
  | `IAuthorConfig` | Author/brand configuration (`name`, `url`) |
585
+ | `IRegistrationDefaults` | Registration defaults (`companySlug`, `branchSlug`) |
549
586
  | `PermissionMode` | `'rbac' \| 'direct' \| 'full'` |
550
587
 
551
588
  ### Helper Functions
@@ -561,6 +598,10 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
561
598
  | `isFormBuilderFeatureEnabled(config)` | Check formBuilder service enabled |
562
599
  | `isEmailFeatureEnabled(config)` | Check email service enabled |
563
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 |
564
605
 
565
606
  **ServiceName:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
566
607
 
@@ -586,5 +627,6 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
586
627
 
587
628
  ---
588
629
 
589
- **Last Updated:** 2026-02-22
630
+ **Last Updated:** 2026-02-23
631
+ **Version:** 1.1.0
590
632
  **Angular Version:** 21
@@ -275,6 +275,31 @@ function getPermissionMode(config) {
275
275
  function getDefaultStorageConfigId(config) {
276
276
  return config.services.storage?.defaultStorageConfigId;
277
277
  }
278
+ /**
279
+ * Check if sign-up/registration is enabled
280
+ * @default true
281
+ */
282
+ function isSignUpEnabled(config) {
283
+ return config.enableSignUp ?? true;
284
+ }
285
+ /**
286
+ * Get default company slug for registration
287
+ */
288
+ function getDefaultCompanySlug(config) {
289
+ return config.registrationDefaults?.companySlug;
290
+ }
291
+ /**
292
+ * Get default branch slug for registration
293
+ */
294
+ function getDefaultBranchSlug(config) {
295
+ return config.registrationDefaults?.branchSlug;
296
+ }
297
+ /**
298
+ * Get registration defaults
299
+ */
300
+ function getRegistrationDefaults(config) {
301
+ return config.registrationDefaults;
302
+ }
278
303
  // Default branding constants
279
304
  const DEFAULT_APP_NAME = 'FLUSYS';
280
305
  const DEFAULT_AUTHOR = {
@@ -330,5 +355,5 @@ class BaseApiService {
330
355
  * Generated bundle index. Do not edit.
331
356
  */
332
357
 
333
- export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
358
+ export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultBranchSlug, getDefaultCompanySlug, getDefaultStorageConfigId, getPermissionMode, getRegistrationDefaults, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isServiceEnabled, isSignUpEnabled, isStorageFeatureEnabled };
334
359
  //# sourceMappingURL=flusys-ng-core.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"flusys-ng-core.mjs","sources":["../../../projects/ng-core/services/api-loader.service.ts","../../../projects/ng-core/components/lib-app-config/lib-app-config.component.ts","../../../projects/ng-core/components/lib-app-config/lib-app-config.component.html","../../../projects/ng-core/interceptors/api-loader.interceptor.ts","../../../projects/ng-core/interceptors/error-catching.interceptor.ts","../../../projects/ng-core/interfaces/app-config.interface.ts","../../../projects/ng-core/services/base-api.service.ts","../../../projects/ng-core/public-api.ts","../../../projects/ng-core/flusys-ng-core.ts"],"sourcesContent":["import { computed, Injectable, Signal, signal } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class ApiLoaderService {\n private readonly loaders = signal<Record<string, number>>({});\n readonly DEFAULT_TAG = 'default';\n\n // Global loading state\n readonly show: Signal<boolean> = computed(() => {\n const loaders = this.loaders();\n return Object.values(loaders).some((count) => count > 0);\n });\n\n // Get specific loader as a signal\n getLoader(name: string): Signal<boolean> {\n return computed(() => (this.loaders()[name] ?? 0) > 0);\n }\n\n increase(name: string = this.DEFAULT_TAG): void {\n this.loaders.update((loaders) => ({\n ...loaders,\n [name]: (loaders[name] ?? 0) + 1,\n }));\n }\n\n decrease(name: string = this.DEFAULT_TAG): void {\n this.loaders.update((loaders) => {\n const current = loaders[name] ?? 0;\n return { ...loaders, [name]: Math.max(0, current - 1) };\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { ToastModule } from 'primeng/toast';\nimport { ConfirmDialog } from 'primeng/confirmdialog';\nimport { ApiLoaderService } from '../../services/api-loader.service';\nimport { ProgressBarModule } from 'primeng/progressbar';\n\n/**\n * Root configuration component providing global UI elements.\n * Include once at app root for global toast, confirm dialog, and loader.\n */\n@Component({\n selector: 'lib-app-config',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ConfirmDialog, ProgressBarModule, ToastModule],\n templateUrl: './lib-app-config.component.html',\n styleUrl: './lib-app-config.component.scss',\n})\nexport class LibAppConfigComponent {\n readonly apiLoaderService = inject(ApiLoaderService);\n}\n","@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { ApiLoaderService } from '../services/api-loader.service';\nimport { Observable } from 'rxjs';\nimport { finalize } from 'rxjs/operators';\n\n/**\n * Interceptor that tracks loading state for HTTP requests.\n * Uses 'x-loader-tag' header to group related requests.\n *\n * @example\n * ```typescript\n * http.get('/api/users', {\n * headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })\n * });\n * // In template: apiLoaderService.getLoader('users-list')\n * ```\n */\nexport function apiLoaderInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> {\n const loaderService = inject(ApiLoaderService);\n const loaderTag = req.headers.get('x-loader-tag') ?? loaderService.DEFAULT_TAG;\n\n loaderService.increase(loaderTag);\n\n const cleanReq = req.clone({\n headers: req.headers.delete('x-loader-tag'),\n });\n\n return next(cleanReq).pipe(\n finalize(() => loaderService.decrease(loaderTag))\n );\n}\n","import {\n HttpErrorResponse,\n HttpEvent,\n HttpHandlerFn,\n HttpRequest,\n HttpContextToken,\n} from '@angular/common/http';\nimport { inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MessageService } from 'primeng/api';\nimport { catchError, Observable, throwError } from 'rxjs';\n\n/**\n * HTTP context token to skip global error toast.\n * Use when component handles error display locally.\n *\n * @example\n * ```typescript\n * this.http.post(url, data, {\n * context: new HttpContext().set(SKIP_ERROR_TOAST, true)\n * });\n * ```\n */\nexport const SKIP_ERROR_TOAST = new HttpContextToken<boolean>(() => false);\n\n/**\n * Error Catching Interceptor\n * Displays error messages for failed HTTP requests using backend response.\n *\n * Features:\n * - Uses backend's error title (error.error.error) as summary\n * - Uses backend's error message (error.error.message) as detail\n * - Falls back to sensible defaults based on status code\n * - Skip with SKIP_ERROR_TOAST context token for local handling\n *\n * NOTE: This interceptor only handles error display.\n * For 401 handling and token refresh, use tokenRefreshInterceptor from @flusys/ng-auth.\n *\n * @example\n * ```typescript\n * // In app.config.ts\n * provideHttpClient(\n * withFetch(),\n * withInterceptors([\n * authInterceptor, // from @flusys/ng-auth\n * tokenRefreshInterceptor, // from @flusys/ng-auth\n * errorCatchingInterceptor, // from @flusys/ng-core\n * apiLoaderInterceptor, // from @flusys/ng-core\n * ])\n * )\n * ```\n */\nexport function errorCatchingInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> {\n const messageService = inject(MessageService);\n const platformId = inject(PLATFORM_ID);\n const isBrowser = isPlatformBrowser(platformId);\n\n // Skip error display for check-login (expected to fail when not logged in)\n const isCheckLogin = req.url.includes('/check-login');\n // Skip if component handles error locally\n const skipErrorToast = req.context.get(SKIP_ERROR_TOAST);\n\n return next(req).pipe(\n catchError((error: HttpErrorResponse) => {\n // Skip 401 errors - handled by tokenRefreshInterceptor in ng-auth\n // Skip 403 errors - handled by permission denied UI in components\n // Skip check-login errors - expected behavior\n // Skip if component handles error locally\n if (error.status === 401 || error.status === 403 || isCheckLogin || skipErrorToast) {\n return throwError(() => error);\n }\n\n // Display error message for other errors\n if (isBrowser) {\n const { summary, detail } = extractErrorInfo(error);\n messageService.add({\n severity: 'error',\n summary,\n detail,\n });\n }\n\n return throwError(() => error);\n })\n );\n}\n\n/**\n * Extract error summary and detail from HTTP error response.\n * Uses backend response format: { message, error, statusCode }\n */\nfunction extractErrorInfo(error: HttpErrorResponse): { summary: string; detail: string } {\n // Extract message from backend response\n let detail: string;\n if (typeof error.error?.message === 'string') {\n detail = error.error.message;\n } else if (Array.isArray(error.error?.message)) {\n // NestJS validation errors come as array\n detail = error.error.message[0];\n } else if (error.message) {\n detail = error.message;\n } else {\n detail = 'An unexpected error occurred.';\n }\n\n // Extract summary/title from backend response or use default based on status\n let summary: string;\n if (typeof error.error?.error === 'string') {\n // Backend sends error type (e.g., \"Not Found\", \"Bad Request\", \"Validation Error\")\n summary = error.error.error;\n } else {\n // Fallback to status-based title\n summary = getDefaultSummary(error.status);\n }\n\n return { summary, detail };\n}\n\n/**\n * Get default error summary based on HTTP status code\n */\nfunction getDefaultSummary(status: number): string {\n switch (status) {\n case 400:\n return 'Bad Request';\n case 404:\n return 'Not Found';\n case 409:\n return 'Conflict';\n case 422:\n return 'Validation Error';\n case 500:\n return 'Server Error';\n case 502:\n case 503:\n case 504:\n return 'Service Unavailable';\n default:\n return 'Error';\n }\n}\n","import { InjectionToken } from '@angular/core';\n\n/**\n * IAM Permission Mode - controls permission system behavior\n */\nexport type PermissionMode = 'rbac' | 'direct' | 'full';\n\n/**\n * Service Configuration Interface\n */\nexport interface IServiceConfig {\n baseUrl: string;\n enabled: boolean;\n}\n\n/**\n * Storage Service Configuration Interface\n * Extends base service config with storage-specific options\n */\nexport interface IStorageServiceConfig extends IServiceConfig {\n /**\n * Default storage config ID from backend storage_config table\n * Used when no storageConfigId is provided to upload operations\n * If not set, backend will use company/branch default config\n */\n defaultStorageConfigId?: string;\n}\n\n/**\n * Author/Brand Configuration\n */\nexport interface IAuthorConfig {\n name: string;\n url: string;\n}\n\n/**\n * App Configuration Interface\n * Centralized configuration for feature toggles and behavior control\n */\nexport interface IAppConfig {\n /** Application name */\n appName: string;\n\n /** Author/Brand info for footer attribution */\n author?: IAuthorConfig;\n\n /** Default API base URL (fallback) */\n apiBaseUrl: string; \n \n /**\n * Enable company feature across the app\n * When true, enables company selection/switching in layout and feature modules\n */\n enableCompanyFeature: boolean;\n\n /**\n * Service-specific base URLs\n * If a service exists in this object, its feature is enabled\n * Database mode is determined by multiTenant.enabled\n */\n services: {\n auth: IServiceConfig;\n administration: IServiceConfig;\n iam: IServiceConfig;\n storage: IStorageServiceConfig;\n formBuilder: IServiceConfig;\n email: IServiceConfig;\n };\n\n /**\n * Multi-tenant configuration\n * enabled: true → Database mode = multi-tenant\n * enabled: false → Database mode = single\n */\n multiTenant: {\n enabled: boolean;\n tenantHeader: string;\n tenantId?: string;\n };\n\n /**\n * IAM Permission Mode - controls permission system behavior\n * - 'rbac': Role-Based Access Control only (users get permissions via roles)\n * - 'direct': Direct action assignments only (actions assigned directly to users)\n * - 'full': Both RBAC and direct permissions (most flexible, default)\n */\n permissionMode?: PermissionMode;\n}\n\n/**\n * App Configuration Token\n * Use this to inject app config in services/components\n */\nexport const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');\n\n/**\n * Get database mode from config\n * Derived from multiTenant.enabled\n */\nexport function getDatabaseMode(config: IAppConfig): 'single' | 'multi-tenant' {\n return config.multiTenant.enabled ? 'multi-tenant' : 'single';\n}\n\n/**\n * Check if a service is enabled\n */\nexport function isServiceEnabled(\n config: IAppConfig,\n serviceName: 'auth' | 'iam' | 'storage' | (string & {}),\n): boolean {\n const service = config.services[serviceName as keyof typeof config.services];\n return service?.enabled ?? false;\n}\n\n/**\n * Check if a feature is enabled (alias for isServiceEnabled with strict types)\n */\nexport function isFeatureEnabled(\n config: IAppConfig,\n feature: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email',\n): boolean {\n return isServiceEnabled(config, feature);\n}\n\n/**\n * Get service base URL or fallback to default apiBaseUrl\n */\nexport function getServiceUrl(\n config: IAppConfig,\n serviceName: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | (string & {}),\n): string {\n if (isServiceEnabled(config, serviceName)) {\n const service = config.services[serviceName as keyof typeof config.services];\n return service.baseUrl;\n }\n return config.apiBaseUrl;\n}\n\n/**\n * Check if company feature is enabled\n */\nexport function isCompanyFeatureEnabled(config: IAppConfig): boolean {\n return config.enableCompanyFeature ?? false;\n}\n\n/**\n * Check if storage feature is enabled\n */\nexport function isStorageFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'storage');\n}\n\n/**\n * Check if form-builder feature is enabled\n */\nexport function isFormBuilderFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'formBuilder');\n}\n\n/**\n * Check if email feature is enabled\n */\nexport function isEmailFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'email');\n}\n\n/**\n * Get permission mode from config\n * Defaults to 'full' if not specified\n */\nexport function getPermissionMode(config: IAppConfig): PermissionMode {\n return config.permissionMode ?? 'full';\n}\n\n/**\n * Get default storage config ID from config\n * Used for file upload operations when no storageConfigId is provided\n */\nexport function getDefaultStorageConfigId(config: IAppConfig): string | undefined {\n return config.services.storage?.defaultStorageConfigId;\n}\n\n// Default branding constants\nexport const DEFAULT_APP_NAME = 'FLUSYS';\nexport const DEFAULT_AUTHOR: IAuthorConfig = {\n name: 'Flusys',\n url: 'https://flusys.vercel.app',\n};\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { APP_CONFIG, getServiceUrl } from '../interfaces/app-config.interface';\n\n/**\n * Base API Service\n * Provides service-specific base URL resolution\n *\n * Note: This project uses POST-only RPC - all endpoints use POST, not REST verbs.\n *\n * Usage:\n * ```typescript\n * @Injectable({ providedIn: 'root' })\n * export class MyService extends BaseApiService {\n * constructor() {\n * super('my-service'); // Service name from config\n * }\n *\n * getData() {\n * return this.http.post(`${this.baseUrl}/data/get`, {});\n * }\n * }\n * ```\n */\nexport abstract class BaseApiService {\n protected readonly http = inject(HttpClient);\n protected readonly appConfig = inject(APP_CONFIG);\n protected readonly baseUrl: string;\n\n /**\n * @param serviceName - Service name from config (e.g., 'auth', 'iam', 'storage')\n */\n constructor(serviceName: string) {\n this.baseUrl = getServiceUrl(this.appConfig, serviceName);\n }\n\n /**\n * Get full URL for an endpoint\n * @param endpoint - API endpoint (e.g., '/users/get-all')\n * @returns Full URL (e.g., 'http://localhost:2002/users/get-all')\n */\n protected getUrl(endpoint: string): string {\n // Remove leading slash if present to avoid double slashes\n const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;\n return `${this.baseUrl}/${cleanEndpoint}`;\n }\n}\n","// Components\nexport * from './components/public-api';\n\n// Interceptors\nexport * from './interceptors/public-api';\n\n// Interfaces\nexport * from './interfaces/public-api';\n\n// Services\nexport * from './services/public-api';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flusys-ng-core.mjs","sources":["../../../projects/ng-core/services/api-loader.service.ts","../../../projects/ng-core/components/lib-app-config/lib-app-config.component.ts","../../../projects/ng-core/components/lib-app-config/lib-app-config.component.html","../../../projects/ng-core/interceptors/api-loader.interceptor.ts","../../../projects/ng-core/interceptors/error-catching.interceptor.ts","../../../projects/ng-core/interfaces/app-config.interface.ts","../../../projects/ng-core/services/base-api.service.ts","../../../projects/ng-core/public-api.ts","../../../projects/ng-core/flusys-ng-core.ts"],"sourcesContent":["import { computed, Injectable, Signal, signal } from '@angular/core';\n\n@Injectable({ providedIn: 'root' })\nexport class ApiLoaderService {\n private readonly loaders = signal<Record<string, number>>({});\n readonly DEFAULT_TAG = 'default';\n\n // Global loading state\n readonly show: Signal<boolean> = computed(() => {\n const loaders = this.loaders();\n return Object.values(loaders).some((count) => count > 0);\n });\n\n // Get specific loader as a signal\n getLoader(name: string): Signal<boolean> {\n return computed(() => (this.loaders()[name] ?? 0) > 0);\n }\n\n increase(name: string = this.DEFAULT_TAG): void {\n this.loaders.update((loaders) => ({\n ...loaders,\n [name]: (loaders[name] ?? 0) + 1,\n }));\n }\n\n decrease(name: string = this.DEFAULT_TAG): void {\n this.loaders.update((loaders) => {\n const current = loaders[name] ?? 0;\n return { ...loaders, [name]: Math.max(0, current - 1) };\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { ToastModule } from 'primeng/toast';\nimport { ConfirmDialog } from 'primeng/confirmdialog';\nimport { ApiLoaderService } from '../../services/api-loader.service';\nimport { ProgressBarModule } from 'primeng/progressbar';\n\n/**\n * Root configuration component providing global UI elements.\n * Include once at app root for global toast, confirm dialog, and loader.\n */\n@Component({\n selector: 'lib-app-config',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ConfirmDialog, ProgressBarModule, ToastModule],\n templateUrl: './lib-app-config.component.html',\n styleUrl: './lib-app-config.component.scss',\n})\nexport class LibAppConfigComponent {\n readonly apiLoaderService = inject(ApiLoaderService);\n}\n","@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { ApiLoaderService } from '../services/api-loader.service';\nimport { Observable } from 'rxjs';\nimport { finalize } from 'rxjs/operators';\n\n/**\n * Interceptor that tracks loading state for HTTP requests.\n * Uses 'x-loader-tag' header to group related requests.\n *\n * @example\n * ```typescript\n * http.get('/api/users', {\n * headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })\n * });\n * // In template: apiLoaderService.getLoader('users-list')\n * ```\n */\nexport function apiLoaderInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> {\n const loaderService = inject(ApiLoaderService);\n const loaderTag = req.headers.get('x-loader-tag') ?? loaderService.DEFAULT_TAG;\n\n loaderService.increase(loaderTag);\n\n const cleanReq = req.clone({\n headers: req.headers.delete('x-loader-tag'),\n });\n\n return next(cleanReq).pipe(\n finalize(() => loaderService.decrease(loaderTag))\n );\n}\n","import {\n HttpErrorResponse,\n HttpEvent,\n HttpHandlerFn,\n HttpRequest,\n HttpContextToken,\n} from '@angular/common/http';\nimport { inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MessageService } from 'primeng/api';\nimport { catchError, Observable, throwError } from 'rxjs';\n\n/**\n * HTTP context token to skip global error toast.\n * Use when component handles error display locally.\n *\n * @example\n * ```typescript\n * this.http.post(url, data, {\n * context: new HttpContext().set(SKIP_ERROR_TOAST, true)\n * });\n * ```\n */\nexport const SKIP_ERROR_TOAST = new HttpContextToken<boolean>(() => false);\n\n/**\n * Error Catching Interceptor\n * Displays error messages for failed HTTP requests using backend response.\n *\n * Features:\n * - Uses backend's error title (error.error.error) as summary\n * - Uses backend's error message (error.error.message) as detail\n * - Falls back to sensible defaults based on status code\n * - Skip with SKIP_ERROR_TOAST context token for local handling\n *\n * NOTE: This interceptor only handles error display.\n * For 401 handling and token refresh, use tokenRefreshInterceptor from @flusys/ng-auth.\n *\n * @example\n * ```typescript\n * // In app.config.ts\n * provideHttpClient(\n * withFetch(),\n * withInterceptors([\n * authInterceptor, // from @flusys/ng-auth\n * tokenRefreshInterceptor, // from @flusys/ng-auth\n * errorCatchingInterceptor, // from @flusys/ng-core\n * apiLoaderInterceptor, // from @flusys/ng-core\n * ])\n * )\n * ```\n */\nexport function errorCatchingInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn\n): Observable<HttpEvent<unknown>> {\n const messageService = inject(MessageService);\n const platformId = inject(PLATFORM_ID);\n const isBrowser = isPlatformBrowser(platformId);\n\n // Skip error display for check-login (expected to fail when not logged in)\n const isCheckLogin = req.url.includes('/check-login');\n // Skip if component handles error locally\n const skipErrorToast = req.context.get(SKIP_ERROR_TOAST);\n\n return next(req).pipe(\n catchError((error: HttpErrorResponse) => {\n // Skip 401 errors - handled by tokenRefreshInterceptor in ng-auth\n // Skip 403 errors - handled by permission denied UI in components\n // Skip check-login errors - expected behavior\n // Skip if component handles error locally\n if (error.status === 401 || error.status === 403 || isCheckLogin || skipErrorToast) {\n return throwError(() => error);\n }\n\n // Display error message for other errors\n if (isBrowser) {\n const { summary, detail } = extractErrorInfo(error);\n messageService.add({\n severity: 'error',\n summary,\n detail,\n });\n }\n\n return throwError(() => error);\n })\n );\n}\n\n/**\n * Extract error summary and detail from HTTP error response.\n * Uses backend response format: { message, error, statusCode }\n */\nfunction extractErrorInfo(error: HttpErrorResponse): { summary: string; detail: string } {\n // Extract message from backend response\n let detail: string;\n if (typeof error.error?.message === 'string') {\n detail = error.error.message;\n } else if (Array.isArray(error.error?.message)) {\n // NestJS validation errors come as array\n detail = error.error.message[0];\n } else if (error.message) {\n detail = error.message;\n } else {\n detail = 'An unexpected error occurred.';\n }\n\n // Extract summary/title from backend response or use default based on status\n let summary: string;\n if (typeof error.error?.error === 'string') {\n // Backend sends error type (e.g., \"Not Found\", \"Bad Request\", \"Validation Error\")\n summary = error.error.error;\n } else {\n // Fallback to status-based title\n summary = getDefaultSummary(error.status);\n }\n\n return { summary, detail };\n}\n\n/**\n * Get default error summary based on HTTP status code\n */\nfunction getDefaultSummary(status: number): string {\n switch (status) {\n case 400:\n return 'Bad Request';\n case 404:\n return 'Not Found';\n case 409:\n return 'Conflict';\n case 422:\n return 'Validation Error';\n case 500:\n return 'Server Error';\n case 502:\n case 503:\n case 504:\n return 'Service Unavailable';\n default:\n return 'Error';\n }\n}\n","import { InjectionToken } from '@angular/core';\n\n/**\n * IAM Permission Mode - controls permission system behavior\n */\nexport type PermissionMode = 'rbac' | 'direct' | 'full';\n\n/**\n * Service Configuration Interface\n */\nexport interface IServiceConfig {\n baseUrl: string;\n enabled: boolean;\n}\n\n/**\n * Storage Service Configuration Interface\n * Extends base service config with storage-specific options\n */\nexport interface IStorageServiceConfig extends IServiceConfig {\n /**\n * Default storage config ID from backend storage_config table\n * Used when no storageConfigId is provided to upload operations\n * If not set, backend will use company/branch default config\n */\n defaultStorageConfigId?: string;\n}\n\n/**\n * Author/Brand Configuration\n */\nexport interface IAuthorConfig {\n name: string;\n url: string;\n}\n\n/**\n * Registration Defaults Configuration\n * Pre-fills and disables fields on registration page\n */\nexport interface IRegistrationDefaults {\n /** Pre-fill company code field and make it readonly */\n companySlug?: string;\n /** Pre-fill branch code field and make it readonly */\n branchSlug?: string;\n}\n\n/**\n * App Configuration Interface\n * Centralized configuration for feature toggles and behavior control\n */\nexport interface IAppConfig {\n /** Application name */\n appName: string;\n\n /** Author/Brand info for footer attribution */\n author?: IAuthorConfig;\n\n /** Default API base URL (fallback) */\n apiBaseUrl: string;\n\n /**\n * Enable company feature across the app\n * When true, enables company selection/switching in layout and feature modules\n */\n enableCompanyFeature: boolean;\n\n /**\n * Enable user sign-up/registration\n * When false: hides \"Sign Up\" link on login page and protects registration route\n * @default true\n */\n enableSignUp?: boolean;\n\n /**\n * Registration defaults for pre-filling company/branch fields\n * When set: pre-fills fields and makes them readonly\n */\n registrationDefaults?: IRegistrationDefaults;\n\n /**\n * Service-specific base URLs\n * If a service exists in this object, its feature is enabled\n * Database mode is determined by multiTenant.enabled\n */\n services: {\n auth: IServiceConfig;\n administration: IServiceConfig;\n iam: IServiceConfig;\n storage: IStorageServiceConfig;\n formBuilder: IServiceConfig;\n email: IServiceConfig;\n };\n\n /**\n * Multi-tenant configuration\n * enabled: true → Database mode = multi-tenant\n * enabled: false → Database mode = single\n */\n multiTenant: {\n enabled: boolean;\n tenantHeader: string;\n tenantId?: string;\n };\n\n /**\n * IAM Permission Mode - controls permission system behavior\n * - 'rbac': Role-Based Access Control only (users get permissions via roles)\n * - 'direct': Direct action assignments only (actions assigned directly to users)\n * - 'full': Both RBAC and direct permissions (most flexible, default)\n */\n permissionMode?: PermissionMode;\n}\n\n/**\n * App Configuration Token\n * Use this to inject app config in services/components\n */\nexport const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');\n\n/**\n * Get database mode from config\n * Derived from multiTenant.enabled\n */\nexport function getDatabaseMode(config: IAppConfig): 'single' | 'multi-tenant' {\n return config.multiTenant.enabled ? 'multi-tenant' : 'single';\n}\n\n/**\n * Check if a service is enabled\n */\nexport function isServiceEnabled(\n config: IAppConfig,\n serviceName: 'auth' | 'iam' | 'storage' | (string & {}),\n): boolean {\n const service = config.services[serviceName as keyof typeof config.services];\n return service?.enabled ?? false;\n}\n\n/**\n * Check if a feature is enabled (alias for isServiceEnabled with strict types)\n */\nexport function isFeatureEnabled(\n config: IAppConfig,\n feature: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email',\n): boolean {\n return isServiceEnabled(config, feature);\n}\n\n/**\n * Get service base URL or fallback to default apiBaseUrl\n */\nexport function getServiceUrl(\n config: IAppConfig,\n serviceName: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | (string & {}),\n): string {\n if (isServiceEnabled(config, serviceName)) {\n const service = config.services[serviceName as keyof typeof config.services];\n return service.baseUrl;\n }\n return config.apiBaseUrl;\n}\n\n/**\n * Check if company feature is enabled\n */\nexport function isCompanyFeatureEnabled(config: IAppConfig): boolean {\n return config.enableCompanyFeature ?? false;\n}\n\n/**\n * Check if storage feature is enabled\n */\nexport function isStorageFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'storage');\n}\n\n/**\n * Check if form-builder feature is enabled\n */\nexport function isFormBuilderFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'formBuilder');\n}\n\n/**\n * Check if email feature is enabled\n */\nexport function isEmailFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'email');\n}\n\n/**\n * Get permission mode from config\n * Defaults to 'full' if not specified\n */\nexport function getPermissionMode(config: IAppConfig): PermissionMode {\n return config.permissionMode ?? 'full';\n}\n\n/**\n * Get default storage config ID from config\n * Used for file upload operations when no storageConfigId is provided\n */\nexport function getDefaultStorageConfigId(config: IAppConfig): string | undefined {\n return config.services.storage?.defaultStorageConfigId;\n}\n\n/**\n * Check if sign-up/registration is enabled\n * @default true\n */\nexport function isSignUpEnabled(config: IAppConfig): boolean {\n return config.enableSignUp ?? true;\n}\n\n/**\n * Get default company slug for registration\n */\nexport function getDefaultCompanySlug(config: IAppConfig): string | undefined {\n return config.registrationDefaults?.companySlug;\n}\n\n/**\n * Get default branch slug for registration\n */\nexport function getDefaultBranchSlug(config: IAppConfig): string | undefined {\n return config.registrationDefaults?.branchSlug;\n}\n\n/**\n * Get registration defaults\n */\nexport function getRegistrationDefaults(config: IAppConfig): IRegistrationDefaults | undefined {\n return config.registrationDefaults;\n}\n\n// Default branding constants\nexport const DEFAULT_APP_NAME = 'FLUSYS';\nexport const DEFAULT_AUTHOR: IAuthorConfig = {\n name: 'Flusys',\n url: 'https://flusys.vercel.app',\n};\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { APP_CONFIG, getServiceUrl } from '../interfaces/app-config.interface';\n\n/**\n * Base API Service\n * Provides service-specific base URL resolution\n *\n * Note: This project uses POST-only RPC - all endpoints use POST, not REST verbs.\n *\n * Usage:\n * ```typescript\n * @Injectable({ providedIn: 'root' })\n * export class MyService extends BaseApiService {\n * constructor() {\n * super('my-service'); // Service name from config\n * }\n *\n * getData() {\n * return this.http.post(`${this.baseUrl}/data/get`, {});\n * }\n * }\n * ```\n */\nexport abstract class BaseApiService {\n protected readonly http = inject(HttpClient);\n protected readonly appConfig = inject(APP_CONFIG);\n protected readonly baseUrl: string;\n\n /**\n * @param serviceName - Service name from config (e.g., 'auth', 'iam', 'storage')\n */\n constructor(serviceName: string) {\n this.baseUrl = getServiceUrl(this.appConfig, serviceName);\n }\n\n /**\n * Get full URL for an endpoint\n * @param endpoint - API endpoint (e.g., '/users/get-all')\n * @returns Full URL (e.g., 'http://localhost:2002/users/get-all')\n */\n protected getUrl(endpoint: string): string {\n // Remove leading slash if present to avoid double slashes\n const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;\n return `${this.baseUrl}/${cleanEndpoint}`;\n }\n}\n","// Components\nexport * from './components/public-api';\n\n// Interceptors\nexport * from './interceptors/public-api';\n\n// Interfaces\nexport * from './interfaces/public-api';\n\n// Services\nexport * from './services/public-api';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/ng-core",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Core utilities and services for FLUSYS Angular packages",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {
@@ -110,6 +110,16 @@ interface IAuthorConfig {
110
110
  name: string;
111
111
  url: string;
112
112
  }
113
+ /**
114
+ * Registration Defaults Configuration
115
+ * Pre-fills and disables fields on registration page
116
+ */
117
+ interface IRegistrationDefaults {
118
+ /** Pre-fill company code field and make it readonly */
119
+ companySlug?: string;
120
+ /** Pre-fill branch code field and make it readonly */
121
+ branchSlug?: string;
122
+ }
113
123
  /**
114
124
  * App Configuration Interface
115
125
  * Centralized configuration for feature toggles and behavior control
@@ -126,6 +136,17 @@ interface IAppConfig {
126
136
  * When true, enables company selection/switching in layout and feature modules
127
137
  */
128
138
  enableCompanyFeature: boolean;
139
+ /**
140
+ * Enable user sign-up/registration
141
+ * When false: hides "Sign Up" link on login page and protects registration route
142
+ * @default true
143
+ */
144
+ enableSignUp?: boolean;
145
+ /**
146
+ * Registration defaults for pre-filling company/branch fields
147
+ * When set: pre-fills fields and makes them readonly
148
+ */
149
+ registrationDefaults?: IRegistrationDefaults;
129
150
  /**
130
151
  * Service-specific base URLs
131
152
  * If a service exists in this object, its feature is enabled
@@ -205,6 +226,23 @@ declare function getPermissionMode(config: IAppConfig): PermissionMode;
205
226
  * Used for file upload operations when no storageConfigId is provided
206
227
  */
207
228
  declare function getDefaultStorageConfigId(config: IAppConfig): string | undefined;
229
+ /**
230
+ * Check if sign-up/registration is enabled
231
+ * @default true
232
+ */
233
+ declare function isSignUpEnabled(config: IAppConfig): boolean;
234
+ /**
235
+ * Get default company slug for registration
236
+ */
237
+ declare function getDefaultCompanySlug(config: IAppConfig): string | undefined;
238
+ /**
239
+ * Get default branch slug for registration
240
+ */
241
+ declare function getDefaultBranchSlug(config: IAppConfig): string | undefined;
242
+ /**
243
+ * Get registration defaults
244
+ */
245
+ declare function getRegistrationDefaults(config: IAppConfig): IRegistrationDefaults | undefined;
208
246
  declare const DEFAULT_APP_NAME = "FLUSYS";
209
247
  declare const DEFAULT_AUTHOR: IAuthorConfig;
210
248
 
@@ -244,5 +282,5 @@ declare abstract class BaseApiService {
244
282
  protected getUrl(endpoint: string): string;
245
283
  }
246
284
 
247
- export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
248
- export type { IAppConfig, IAuthorConfig, IServiceConfig, IStorageServiceConfig, PermissionMode };
285
+ export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultBranchSlug, getDefaultCompanySlug, getDefaultStorageConfigId, getPermissionMode, getRegistrationDefaults, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isServiceEnabled, isSignUpEnabled, isStorageFeatureEnabled };
286
+ export type { IAppConfig, IAuthorConfig, IRegistrationDefaults, IServiceConfig, IStorageServiceConfig, PermissionMode };