@flusys/ng-core 1.0.0-beta → 1.1.0-beta
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 +13 -1
- package/fesm2022/flusys-ng-core.mjs +10 -3
- package/fesm2022/flusys-ng-core.mjs.map +1 -1
- package/package.json +1 -1
- package/types/flusys-ng-core.d.ts +21 -4
package/README.md
CHANGED
|
@@ -69,8 +69,14 @@ export interface IServiceConfig {
|
|
|
69
69
|
enabled: boolean;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
export interface IAuthorConfig {
|
|
73
|
+
name: string;
|
|
74
|
+
url: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
export interface IAppConfig {
|
|
73
78
|
appName: string;
|
|
79
|
+
author?: IAuthorConfig; // Author/Brand info for footer attribution
|
|
74
80
|
apiBaseUrl: string; // Default fallback URL
|
|
75
81
|
enableCompanyFeature: boolean; // Company/branch selection
|
|
76
82
|
|
|
@@ -165,6 +171,10 @@ import {
|
|
|
165
171
|
|
|
166
172
|
```typescript
|
|
167
173
|
export const DEFAULT_APP_NAME = 'FLUSYS';
|
|
174
|
+
export const DEFAULT_AUTHOR: IAuthorConfig = {
|
|
175
|
+
name: 'Flusys',
|
|
176
|
+
url: 'https://flusys.vercel.app',
|
|
177
|
+
};
|
|
168
178
|
```
|
|
169
179
|
|
|
170
180
|
#### Configuration Modes
|
|
@@ -512,6 +522,7 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
|
|
|
512
522
|
|--------|-------------|
|
|
513
523
|
| `IAppConfig` | App configuration interface |
|
|
514
524
|
| `IServiceConfig` | Service configuration (`baseUrl`, `enabled`) |
|
|
525
|
+
| `IAuthorConfig` | Author/brand configuration (`name`, `url`) |
|
|
515
526
|
| `PermissionMode` | `'rbac' \| 'direct' \| 'full'` |
|
|
516
527
|
|
|
517
528
|
### Helper Functions
|
|
@@ -531,6 +542,7 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
|
|
|
531
542
|
| Constant | Value | Description |
|
|
532
543
|
|----------|-------|-------------|
|
|
533
544
|
| `DEFAULT_APP_NAME` | `'FLUSYS'` | Default application name |
|
|
545
|
+
| `DEFAULT_AUTHOR` | `{ name: 'Flusys', url: '...' }` | Default author/brand info |
|
|
534
546
|
|
|
535
547
|
### Components
|
|
536
548
|
|
|
@@ -547,5 +559,5 @@ readonly usersLoading = this.apiLoaderService.getLoader('users-list');
|
|
|
547
559
|
|
|
548
560
|
---
|
|
549
561
|
|
|
550
|
-
**Last Updated:** 2026-02-
|
|
562
|
+
**Last Updated:** 2026-02-16
|
|
551
563
|
**Angular Version:** 21
|
|
@@ -50,11 +50,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
50
50
|
class LibAppConfigComponent {
|
|
51
51
|
apiLoaderService = inject(ApiLoaderService);
|
|
52
52
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LibAppConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
53
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LibAppConfigComponent, isStandalone: true, selector: "lib-app-config", ngImport: i0, template: "@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n", styles: [":host ::ng-deep .p-progressbar{position:fixed;height:4px;z-index:998;left:0;right:0}:host ::ng-deep .p-toast-message-success{background:color-mix(in srgb,var(--p-primary-color),transparent 84%)!important;border-color:var(--p-primary-color)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-icon-close{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-icon-close:hover{background:color-mix(in srgb,var(--p-primary-color),transparent
|
|
53
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LibAppConfigComponent, isStandalone: true, selector: "lib-app-config", ngImport: i0, template: "@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n", styles: [":host ::ng-deep .p-progressbar{position:fixed;height:4px;z-index:998;left:0;right:0}:host ::ng-deep .p-toast-message-success{background:color-mix(in srgb,var(--p-primary-color),transparent 84%)!important;border-color:var(--p-primary-color)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon{color:var(--p-primary-color)!important;fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon svg{color:var(--p-primary-color)!important;fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon path{fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button .p-toast-close-icon{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button:hover{background:color-mix(in srgb,var(--p-primary-color),transparent 70%)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button:focus{box-shadow:0 0 0 .2rem color-mix(in srgb,var(--p-primary-color),transparent 50%)!important}:host ::ng-deep .p-toast-message-success .p-toast-detail{color:var(--p-text-color)!important}\n"], dependencies: [{ kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i1.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i2.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }] });
|
|
54
54
|
}
|
|
55
55
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LibAppConfigComponent, decorators: [{
|
|
56
56
|
type: Component,
|
|
57
|
-
args: [{ selector: 'lib-app-config', standalone: true, imports: [ConfirmDialog, ProgressBarModule, ToastModule], template: "@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n", styles: [":host ::ng-deep .p-progressbar{position:fixed;height:4px;z-index:998;left:0;right:0}:host ::ng-deep .p-toast-message-success{background:color-mix(in srgb,var(--p-primary-color),transparent 84%)!important;border-color:var(--p-primary-color)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-icon-close{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-icon-close:hover{background:color-mix(in srgb,var(--p-primary-color),transparent
|
|
57
|
+
args: [{ selector: 'lib-app-config', standalone: true, imports: [ConfirmDialog, ProgressBarModule, ToastModule], template: "@if (apiLoaderService.show()) {\n<p-progressBar mode=\"indeterminate\" />\n}\n<p-toast />\n<p-confirmdialog />\n", styles: [":host ::ng-deep .p-progressbar{position:fixed;height:4px;z-index:998;left:0;right:0}:host ::ng-deep .p-toast-message-success{background:color-mix(in srgb,var(--p-primary-color),transparent 84%)!important;border-color:var(--p-primary-color)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon{color:var(--p-primary-color)!important;fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon svg{color:var(--p-primary-color)!important;fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-message-icon path{fill:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button .p-toast-close-icon{color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button:hover{background:color-mix(in srgb,var(--p-primary-color),transparent 70%)!important;color:var(--p-primary-color)!important}:host ::ng-deep .p-toast-message-success .p-toast-close-button:focus{box-shadow:0 0 0 .2rem color-mix(in srgb,var(--p-primary-color),transparent 50%)!important}:host ::ng-deep .p-toast-message-success .p-toast-detail{color:var(--p-text-color)!important}\n"] }]
|
|
58
58
|
}] });
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -201,6 +201,13 @@ function isStorageFeatureEnabled(config) {
|
|
|
201
201
|
function getPermissionMode(config) {
|
|
202
202
|
return config.permissionMode ?? 'full';
|
|
203
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Get default storage config ID from config
|
|
206
|
+
* Used for file upload operations when no storageConfigId is provided
|
|
207
|
+
*/
|
|
208
|
+
function getDefaultStorageConfigId(config) {
|
|
209
|
+
return config.services.storage?.defaultStorageConfigId;
|
|
210
|
+
}
|
|
204
211
|
// Default branding constants
|
|
205
212
|
const DEFAULT_APP_NAME = 'FLUSYS';
|
|
206
213
|
const DEFAULT_AUTHOR = {
|
|
@@ -254,5 +261,5 @@ class BaseApiService {
|
|
|
254
261
|
* Generated bundle index. Do not edit.
|
|
255
262
|
*/
|
|
256
263
|
|
|
257
|
-
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
264
|
+
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
258
265
|
//# 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 { 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 standalone: true,\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} 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 * Error Catching Interceptor\n * Displays error messages for failed HTTP requests.\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\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 if (error.status === 401 || error.status === 403 || isCheckLogin) {\n return throwError(() => error);\n }\n\n // Display error message for other errors\n if (isBrowser) {\n const message = extractErrorMessage(error);\n messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: message,\n });\n }\n\n return throwError(() => error);\n })\n );\n}\n\n/**\n * Extract error message from HTTP error response\n */\nfunction extractErrorMessage(error: HttpErrorResponse): string {\n if (typeof error.error?.message === 'string') {\n return error.error.message;\n }\n\n if (Array.isArray(error.error?.message)) {\n return error.error.message[0];\n }\n\n if (error.message) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\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 * 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 * enableCompanyFeature in across the app\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 iam: IServiceConfig;\n storage: IServiceConfig;\n [key: string]: 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 feature is enabled\n * Features are enabled if their corresponding service exists and is enabled\n */\nexport function isFeatureEnabled(\n config: IAppConfig,\n feature: 'auth' | 'iam' | 'storage',\n): boolean {\n const service = config.services[feature];\n return service?.enabled ?? false;\n}\n\n/**\n * Helper function to get service base URL\n * Returns service-specific URL or falls back to default apiBaseUrl\n */\nexport function getServiceUrl(\n config: IAppConfig,\n serviceName: keyof IAppConfig['services'],\n): string {\n const service = config.services[serviceName];\n if (service?.enabled) {\n return service.baseUrl;\n }\n // Fallback to default API base URL\n return config.apiBaseUrl;\n}\n\n/**\n * Helper function to check if a service is enabled\n */\nexport function isServiceEnabled(\n config: IAppConfig,\n serviceName: keyof IAppConfig['services'],\n): boolean {\n const service = config.services[serviceName];\n return service?.enabled ?? false;\n}\n\n/**\n * Check if company feature is enabled (backward compatible)\n * Company feature is enabled if auth service is enabled\n */\nexport function isCompanyFeatureEnabled(config: IAppConfig): boolean {\n return config.enableCompanyFeature ?? false;\n}\n\n/**\n * Check if storage feature is enabled (backward compatible)\n * Storage feature is enabled if storage service is enabled\n */\nexport function isStorageFeatureEnabled(config: IAppConfig): boolean {\n return config.services.storage?.enabled ?? false;\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// 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 * 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.get(`${this.baseUrl}/data`);\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/lib-app-config/lib-app-config.component';\n\n// Interceptors\nexport * from './interceptors/api-loader.interceptor';\nexport * from './interceptors/error-catching.interceptor';\n\n// Interfaces\nexport * from './interfaces/app-config.interface';\n\n// Services\nexport * from './services/api-loader.service';\nexport * from './services/base-api.service';\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 { 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 standalone: true,\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} 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 * Error Catching Interceptor\n * Displays error messages for failed HTTP requests.\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\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 if (error.status === 401 || error.status === 403 || isCheckLogin) {\n return throwError(() => error);\n }\n\n // Display error message for other errors\n if (isBrowser) {\n const message = extractErrorMessage(error);\n messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: message,\n });\n }\n\n return throwError(() => error);\n })\n );\n}\n\n/**\n * Extract error message from HTTP error response\n */\nfunction extractErrorMessage(error: HttpErrorResponse): string {\n if (typeof error.error?.message === 'string') {\n return error.error.message;\n }\n\n if (Array.isArray(error.error?.message)) {\n return error.error.message[0];\n }\n\n if (error.message) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\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 * enableCompanyFeature in across the app\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 iam: IServiceConfig;\n storage: IStorageServiceConfig;\n [key: string]: IServiceConfig | IStorageServiceConfig;\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 feature is enabled\n * Features are enabled if their corresponding service exists and is enabled\n */\nexport function isFeatureEnabled(\n config: IAppConfig,\n feature: 'auth' | 'iam' | 'storage',\n): boolean {\n const service = config.services[feature];\n return service?.enabled ?? false;\n}\n\n/**\n * Helper function to get service base URL\n * Returns service-specific URL or falls back to default apiBaseUrl\n */\nexport function getServiceUrl(\n config: IAppConfig,\n serviceName: keyof IAppConfig['services'],\n): string {\n const service = config.services[serviceName];\n if (service?.enabled) {\n return service.baseUrl;\n }\n // Fallback to default API base URL\n return config.apiBaseUrl;\n}\n\n/**\n * Helper function to check if a service is enabled\n */\nexport function isServiceEnabled(\n config: IAppConfig,\n serviceName: keyof IAppConfig['services'],\n): boolean {\n const service = config.services[serviceName];\n return service?.enabled ?? false;\n}\n\n/**\n * Check if company feature is enabled (backward compatible)\n * Company feature is enabled if auth service is enabled\n */\nexport function isCompanyFeatureEnabled(config: IAppConfig): boolean {\n return config.enableCompanyFeature ?? false;\n}\n\n/**\n * Check if storage feature is enabled (backward compatible)\n * Storage feature is enabled if storage service is enabled\n */\nexport function isStorageFeatureEnabled(config: IAppConfig): boolean {\n return config.services.storage?.enabled ?? false;\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 * 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.get(`${this.baseUrl}/data`);\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/lib-app-config/lib-app-config.component';\n\n// Interceptors\nexport * from './interceptors/api-loader.interceptor';\nexport * from './interceptors/error-catching.interceptor';\n\n// Interfaces\nexport * from './interfaces/app-config.interface';\n\n// Services\nexport * from './services/api-loader.service';\nexport * from './services/base-api.service';\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
|
@@ -73,6 +73,18 @@ interface IServiceConfig {
|
|
|
73
73
|
baseUrl: string;
|
|
74
74
|
enabled: boolean;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Storage Service Configuration Interface
|
|
78
|
+
* Extends base service config with storage-specific options
|
|
79
|
+
*/
|
|
80
|
+
interface IStorageServiceConfig extends IServiceConfig {
|
|
81
|
+
/**
|
|
82
|
+
* Default storage config ID from backend storage_config table
|
|
83
|
+
* Used when no storageConfigId is provided to upload operations
|
|
84
|
+
* If not set, backend will use company/branch default config
|
|
85
|
+
*/
|
|
86
|
+
defaultStorageConfigId?: string;
|
|
87
|
+
}
|
|
76
88
|
/**
|
|
77
89
|
* Author/Brand Configuration
|
|
78
90
|
*/
|
|
@@ -103,8 +115,8 @@ interface IAppConfig {
|
|
|
103
115
|
services: {
|
|
104
116
|
auth: IServiceConfig;
|
|
105
117
|
iam: IServiceConfig;
|
|
106
|
-
storage:
|
|
107
|
-
[key: string]: IServiceConfig;
|
|
118
|
+
storage: IStorageServiceConfig;
|
|
119
|
+
[key: string]: IServiceConfig | IStorageServiceConfig;
|
|
108
120
|
};
|
|
109
121
|
/**
|
|
110
122
|
* Multi-tenant configuration
|
|
@@ -163,6 +175,11 @@ declare function isStorageFeatureEnabled(config: IAppConfig): boolean;
|
|
|
163
175
|
* Defaults to 'full' if not specified
|
|
164
176
|
*/
|
|
165
177
|
declare function getPermissionMode(config: IAppConfig): PermissionMode;
|
|
178
|
+
/**
|
|
179
|
+
* Get default storage config ID from config
|
|
180
|
+
* Used for file upload operations when no storageConfigId is provided
|
|
181
|
+
*/
|
|
182
|
+
declare function getDefaultStorageConfigId(config: IAppConfig): string | undefined;
|
|
166
183
|
declare const DEFAULT_APP_NAME = "FLUSYS";
|
|
167
184
|
declare const DEFAULT_AUTHOR: IAuthorConfig;
|
|
168
185
|
|
|
@@ -200,5 +217,5 @@ declare abstract class BaseApiService {
|
|
|
200
217
|
protected getUrl(endpoint: string): string;
|
|
201
218
|
}
|
|
202
219
|
|
|
203
|
-
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
204
|
-
export type { IAppConfig, IAuthorConfig, IServiceConfig, PermissionMode };
|
|
220
|
+
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
221
|
+
export type { IAppConfig, IAuthorConfig, IServiceConfig, IStorageServiceConfig, PermissionMode };
|