@flusys/ng-core 3.0.0-rc → 3.0.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 +169 -392
- package/fesm2022/flusys-ng-core.mjs +38 -40
- package/fesm2022/flusys-ng-core.mjs.map +1 -1
- package/package.json +7 -7
- package/types/flusys-ng-core.d.ts +31 -80
package/README.md
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
`@flusys/ng-core` is the foundation package
|
|
5
|
+
`@flusys/ng-core` is the foundation package providing core functionality for all FLUSYS Angular libraries. Contains configuration management, HTTP interceptors, base services, and type definitions.
|
|
6
6
|
|
|
7
|
-
**Key Principle:** ng-core has NO
|
|
7
|
+
**Key Principle:** ng-core has NO dependencies on other @flusys packages. All other packages depend on ng-core.
|
|
8
8
|
|
|
9
9
|
## Package Information
|
|
10
10
|
|
|
11
11
|
- **Package:** `@flusys/ng-core`
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
12
|
+
- **Version:** 3.0.1
|
|
13
|
+
- **Peer Dependencies:** `@angular/*: >=21.0.0`, `primeng: >=21.0.0`
|
|
14
14
|
- **Build Command:** `npm run build:ng-core`
|
|
15
15
|
|
|
16
16
|
## Package Structure
|
|
@@ -18,48 +18,55 @@
|
|
|
18
18
|
```
|
|
19
19
|
ng-core/
|
|
20
20
|
├── components/
|
|
21
|
-
│ └── lib-app-config/
|
|
22
|
-
│ ├── lib-app-config.component.ts
|
|
23
|
-
│ ├── lib-app-config.component.html
|
|
24
|
-
│ └── lib-app-config.component.scss
|
|
21
|
+
│ └── lib-app-config/ # Global toast, confirm, loader
|
|
25
22
|
├── interceptors/
|
|
26
23
|
│ ├── api-loader.interceptor.ts
|
|
27
24
|
│ └── error-catching.interceptor.ts
|
|
28
25
|
├── interfaces/
|
|
29
|
-
│ └── app-config.interface.ts
|
|
26
|
+
│ └── app-config.interface.ts # IAppConfig, helpers, constants
|
|
30
27
|
├── services/
|
|
31
28
|
│ ├── api-loader.service.ts
|
|
32
29
|
│ └── base-api.service.ts
|
|
33
30
|
└── public-api.ts
|
|
34
31
|
```
|
|
35
32
|
|
|
36
|
-
## Public API
|
|
33
|
+
## Public API
|
|
37
34
|
|
|
38
35
|
```typescript
|
|
39
36
|
// Components
|
|
40
|
-
export
|
|
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';
|
|
37
|
+
export { LibAppConfigComponent } from './components/public-api';
|
|
48
38
|
|
|
49
39
|
// Services
|
|
50
|
-
export
|
|
51
|
-
|
|
40
|
+
export { ApiLoaderService, BaseApiService } from './services/public-api';
|
|
41
|
+
|
|
42
|
+
// Interceptors
|
|
43
|
+
export { apiLoaderInterceptor, errorCatchingInterceptor, SKIP_ERROR_TOAST } from './interceptors/public-api';
|
|
44
|
+
|
|
45
|
+
// Interfaces & Types
|
|
46
|
+
export {
|
|
47
|
+
APP_CONFIG, IAppConfig, IServiceConfig, IStorageServiceConfig,
|
|
48
|
+
IAuthorConfig, IRegistrationDefaults, PermissionMode,
|
|
49
|
+
} from './interfaces/public-api';
|
|
50
|
+
|
|
51
|
+
// Helper Functions
|
|
52
|
+
export {
|
|
53
|
+
getDatabaseMode, isServiceEnabled, isFeatureEnabled, getServiceUrl,
|
|
54
|
+
isCompanyFeatureEnabled, isStorageFeatureEnabled, isFormBuilderFeatureEnabled,
|
|
55
|
+
isEmailFeatureEnabled, getPermissionMode, getDefaultStorageConfigId,
|
|
56
|
+
isSignUpEnabled, getDefaultCompanySlug, getDefaultBranchSlug, getRegistrationDefaults,
|
|
57
|
+
} from './interfaces/public-api';
|
|
58
|
+
|
|
59
|
+
// Constants
|
|
60
|
+
export { DEFAULT_APP_NAME, DEFAULT_AUTHOR } from './interfaces/public-api';
|
|
52
61
|
```
|
|
53
62
|
|
|
54
63
|
---
|
|
55
64
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
### 1. APP_CONFIG (Configuration Management)
|
|
65
|
+
## 1. APP_CONFIG (Configuration Management)
|
|
59
66
|
|
|
60
67
|
Centralized configuration system with dynamic service URLs and feature flags.
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
### IAppConfig Interface
|
|
63
70
|
|
|
64
71
|
```typescript
|
|
65
72
|
export type PermissionMode = 'rbac' | 'direct' | 'full';
|
|
@@ -69,356 +76,233 @@ export interface IServiceConfig {
|
|
|
69
76
|
enabled: boolean;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
export interface
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
export interface IStorageServiceConfig extends IServiceConfig {
|
|
80
|
+
defaultStorageConfigId?: string; // Default storage config for uploads
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface IAuthorConfig { name: string; url: string; }
|
|
84
|
+
|
|
85
|
+
export interface IRegistrationDefaults {
|
|
86
|
+
companySlug?: string; // Pre-fill company code (readonly)
|
|
87
|
+
branchSlug?: string; // Pre-fill branch code (readonly)
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
export interface IAppConfig {
|
|
78
91
|
appName: string;
|
|
79
|
-
author?: IAuthorConfig;
|
|
92
|
+
author?: IAuthorConfig;
|
|
80
93
|
apiBaseUrl: string; // Default fallback URL
|
|
81
|
-
enableCompanyFeature: boolean; // Company
|
|
94
|
+
enableCompanyFeature: boolean; // Company selection/switching
|
|
95
|
+
enableSignUp?: boolean; // Registration enabled (default: true)
|
|
96
|
+
registrationDefaults?: IRegistrationDefaults;
|
|
82
97
|
|
|
83
98
|
services: {
|
|
84
99
|
auth: IServiceConfig;
|
|
100
|
+
administration: IServiceConfig;
|
|
85
101
|
iam: IServiceConfig;
|
|
86
|
-
storage:
|
|
87
|
-
|
|
102
|
+
storage: IStorageServiceConfig;
|
|
103
|
+
formBuilder: IServiceConfig;
|
|
104
|
+
email: IServiceConfig;
|
|
88
105
|
};
|
|
89
106
|
|
|
90
107
|
multiTenant: {
|
|
91
|
-
enabled: boolean;
|
|
92
|
-
tenantHeader: string;
|
|
93
|
-
tenantId?: string;
|
|
108
|
+
enabled: boolean; // true = multi-tenant DB, false = single DB
|
|
109
|
+
tenantHeader: string; // Header name (e.g., 'x-tenant-id')
|
|
110
|
+
tenantId?: string;
|
|
94
111
|
};
|
|
95
112
|
|
|
96
|
-
permissionMode?: PermissionMode;
|
|
113
|
+
permissionMode?: PermissionMode; // Defaults to 'full'
|
|
97
114
|
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### APP_CONFIG Token
|
|
101
115
|
|
|
102
|
-
```typescript
|
|
103
116
|
export const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');
|
|
104
117
|
```
|
|
105
118
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
import { APP_CONFIG } from '@flusys/ng-core';
|
|
110
|
-
import { DEFAULT_APP_CONFIG } from './config/app.config';
|
|
111
|
-
|
|
112
|
-
export const appConfig: ApplicationConfig = {
|
|
113
|
-
providers: [
|
|
114
|
-
{ provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
|
|
115
|
-
]
|
|
116
|
-
};
|
|
117
|
-
```
|
|
119
|
+
### Helper Functions
|
|
118
120
|
|
|
119
|
-
|
|
121
|
+
| Function | Returns | Description |
|
|
122
|
+
|----------|---------|-------------|
|
|
123
|
+
| `getDatabaseMode(config)` | `'single' \| 'multi-tenant'` | Database mode from `multiTenant.enabled` |
|
|
124
|
+
| `isServiceEnabled(config, name)` | `boolean` | Check if service is enabled |
|
|
125
|
+
| `isFeatureEnabled(config, feature)` | `boolean` | Same as above with strict ServiceName type |
|
|
126
|
+
| `getServiceUrl(config, name)` | `string` | Service URL or fallback to `apiBaseUrl` |
|
|
127
|
+
| `isCompanyFeatureEnabled(config)` | `boolean` | Check `enableCompanyFeature` |
|
|
128
|
+
| `isStorageFeatureEnabled(config)` | `boolean` | Check `services.storage.enabled` |
|
|
129
|
+
| `isFormBuilderFeatureEnabled(config)` | `boolean` | Check `services.formBuilder.enabled` |
|
|
130
|
+
| `isEmailFeatureEnabled(config)` | `boolean` | Check `services.email.enabled` |
|
|
131
|
+
| `getPermissionMode(config)` | `PermissionMode` | Permission mode (default: `'full'`) |
|
|
132
|
+
| `getDefaultStorageConfigId(config)` | `string \| undefined` | Default storage config ID |
|
|
133
|
+
| `isSignUpEnabled(config)` | `boolean` | Sign-up enabled (default: `true`) |
|
|
134
|
+
| `getDefaultCompanySlug(config)` | `string \| undefined` | Registration default company |
|
|
135
|
+
| `getDefaultBranchSlug(config)` | `string \| undefined` | Registration default branch |
|
|
136
|
+
| `getRegistrationDefaults(config)` | `IRegistrationDefaults \| undefined` | Full registration defaults |
|
|
137
|
+
|
|
138
|
+
**ServiceName:** `'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email'`
|
|
120
139
|
|
|
121
|
-
|
|
140
|
+
### Constants
|
|
122
141
|
|
|
123
142
|
```typescript
|
|
124
|
-
|
|
125
|
-
export const
|
|
126
|
-
appName: 'FLUSYS',
|
|
127
|
-
multiTenant: { enabled: false, tenantHeader: 'x-tenant-id' },
|
|
128
|
-
storage: { apiPath: '/upload/file', maxFileSize: 10485760, allowedMimeTypes: [...] },
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// projects/flusysng/src/environments/environment.ts (development)
|
|
132
|
-
import { BASE_ENVIRONMENT } from './environment.base';
|
|
133
|
-
|
|
134
|
-
export const environment = {
|
|
135
|
-
...BASE_ENVIRONMENT,
|
|
136
|
-
production: false,
|
|
137
|
-
apiBaseUrl: 'http://localhost:2002',
|
|
138
|
-
services: {
|
|
139
|
-
auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
|
|
140
|
-
iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
|
|
141
|
-
storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
|
|
142
|
-
},
|
|
143
|
-
};
|
|
143
|
+
export const DEFAULT_APP_NAME = 'FLUSYS';
|
|
144
|
+
export const DEFAULT_AUTHOR: IAuthorConfig = { name: 'Flusys', url: 'https://flusys.vercel.app' };
|
|
144
145
|
```
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
### Configuration Modes
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
isServiceEnabled,
|
|
154
|
-
isCompanyFeatureEnabled,
|
|
155
|
-
isStorageFeatureEnabled,
|
|
156
|
-
getPermissionMode,
|
|
157
|
-
} from '@flusys/ng-core';
|
|
158
|
-
```
|
|
149
|
+
| Mode | `enableCompanyFeature` | `multiTenant.enabled` | Services |
|
|
150
|
+
|------|------------------------|----------------------|----------|
|
|
151
|
+
| Simple | `false` | `false` | Disabled |
|
|
152
|
+
| Single DB + Multi-Company | `true` | `false` | Enabled |
|
|
153
|
+
| Multi-Tenant | `true` | `true` | Enabled |
|
|
159
154
|
|
|
160
|
-
|
|
161
|
-
|----------|-----------|-------------|
|
|
162
|
-
| `getDatabaseMode` | `(config) → 'single' \| 'multi-tenant'` | Get database mode from `multiTenant.enabled` |
|
|
163
|
-
| `isFeatureEnabled` | `(config, feature: 'auth' \| 'iam' \| 'storage') → boolean` | Check if a service feature is enabled |
|
|
164
|
-
| `getServiceUrl` | `(config, serviceName) → string` | Get service URL or fallback to `apiBaseUrl` |
|
|
165
|
-
| `isServiceEnabled` | `(config, serviceName) → boolean` | Check if a service is enabled |
|
|
166
|
-
| `isCompanyFeatureEnabled` | `(config) → boolean` | Check `enableCompanyFeature` flag |
|
|
167
|
-
| `isStorageFeatureEnabled` | `(config) → boolean` | Check `services.storage.enabled` |
|
|
168
|
-
| `getPermissionMode` | `(config) → PermissionMode` | Get permission mode (defaults to `'full'`) |
|
|
155
|
+
---
|
|
169
156
|
|
|
170
|
-
|
|
157
|
+
## 2. HTTP Interceptors
|
|
171
158
|
|
|
172
|
-
|
|
173
|
-
export const DEFAULT_APP_NAME = 'FLUSYS';
|
|
174
|
-
export const DEFAULT_AUTHOR: IAuthorConfig = {
|
|
175
|
-
name: 'Flusys',
|
|
176
|
-
url: 'https://flusys.vercel.app',
|
|
177
|
-
};
|
|
178
|
-
```
|
|
159
|
+
Both use modern Angular functional interceptor pattern.
|
|
179
160
|
|
|
180
|
-
|
|
161
|
+
### apiLoaderInterceptor
|
|
181
162
|
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
{
|
|
185
|
-
enableCompanyFeature: false,
|
|
186
|
-
multiTenant: { enabled: false, tenantHeader: 'x-tenant-id' },
|
|
187
|
-
services: {
|
|
188
|
-
auth: { baseUrl: '...', enabled: false },
|
|
189
|
-
iam: { baseUrl: '...', enabled: false },
|
|
190
|
-
storage: { baseUrl: '...', enabled: false },
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
163
|
+
Tracks loading state with tagged loaders via `x-loader-tag` header.
|
|
194
164
|
|
|
195
|
-
**Mode 2: Single DB + Multi-Company**
|
|
196
165
|
```typescript
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
services: {
|
|
201
|
-
auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
|
|
202
|
-
iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
|
|
203
|
-
storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
166
|
+
export function apiLoaderInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
|
|
167
|
+
const loaderService = inject(ApiLoaderService);
|
|
168
|
+
const loaderTag = req.headers.get('x-loader-tag') ?? loaderService.DEFAULT_TAG;
|
|
207
169
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
multiTenant: { enabled: true, tenantHeader: 'x-tenant-id', tenantId: 'tenant-123' },
|
|
213
|
-
services: {
|
|
214
|
-
auth: { baseUrl: 'http://localhost:2002/auth', enabled: true },
|
|
215
|
-
iam: { baseUrl: 'http://localhost:2002/iam', enabled: true },
|
|
216
|
-
storage: { baseUrl: 'http://localhost:2002/storage', enabled: true },
|
|
217
|
-
}
|
|
170
|
+
loaderService.increase(loaderTag);
|
|
171
|
+
const cleanReq = req.clone({ headers: req.headers.delete('x-loader-tag') });
|
|
172
|
+
|
|
173
|
+
return next(cleanReq).pipe(finalize(() => loaderService.decrease(loaderTag)));
|
|
218
174
|
}
|
|
219
175
|
```
|
|
220
176
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
### 2. HTTP Interceptors
|
|
224
|
-
|
|
225
|
-
ng-core provides two functional interceptors. Both use the modern Angular functional interceptor pattern.
|
|
226
|
-
|
|
227
|
-
#### apiLoaderInterceptor
|
|
228
|
-
|
|
229
|
-
Tracks HTTP request loading state with tagged loaders. Uses a custom `x-loader-tag` header to group related requests.
|
|
230
|
-
|
|
231
|
-
**Behavior:**
|
|
232
|
-
1. Reads `x-loader-tag` header from request (defaults to `'default'`)
|
|
233
|
-
2. Increments loader count for that tag
|
|
234
|
-
3. Removes `x-loader-tag` header before sending to backend
|
|
235
|
-
4. Decrements loader count on request completion (success or error)
|
|
236
|
-
|
|
177
|
+
**Usage:**
|
|
237
178
|
```typescript
|
|
238
|
-
import { apiLoaderInterceptor } from '@flusys/ng-core';
|
|
239
|
-
|
|
240
|
-
// Tag a specific request
|
|
241
179
|
this.http.post('/api/users/get-all', filter, {
|
|
242
180
|
headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })
|
|
243
181
|
});
|
|
244
|
-
|
|
245
|
-
// Check loading state for that tag
|
|
246
|
-
apiLoaderService.getLoader('users-list')(); // true while loading
|
|
247
182
|
```
|
|
248
183
|
|
|
249
|
-
|
|
184
|
+
### errorCatchingInterceptor
|
|
250
185
|
|
|
251
|
-
Displays
|
|
186
|
+
Displays error toasts via PrimeNG `MessageService`.
|
|
252
187
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
188
|
+
| Skips | Reason |
|
|
189
|
+
|-------|--------|
|
|
190
|
+
| 401 | Handled by `tokenRefreshInterceptor` |
|
|
191
|
+
| 403 | Handled by permission UI |
|
|
192
|
+
| `/check-login` | Expected to fail when not logged in |
|
|
193
|
+
| `SKIP_ERROR_TOAST` context | Component handles locally |
|
|
258
194
|
|
|
259
|
-
|
|
195
|
+
**Error Extraction:** Uses `error.error.error` as summary, `error.error.message` as detail (handles NestJS array format). SSR-safe.
|
|
260
196
|
|
|
197
|
+
**Skip Toast for Local Handling:**
|
|
261
198
|
```typescript
|
|
262
|
-
|
|
263
|
-
import { authInterceptor, tokenRefreshInterceptor } from '@flusys/ng-auth';
|
|
264
|
-
|
|
265
|
-
provideHttpClient(
|
|
266
|
-
withFetch(),
|
|
267
|
-
withInterceptors([
|
|
268
|
-
authInterceptor, // from @flusys/ng-auth (adds auth + tenant headers)
|
|
269
|
-
tokenRefreshInterceptor, // from @flusys/ng-auth (handles 401 + token refresh)
|
|
270
|
-
errorCatchingInterceptor, // from @flusys/ng-core (displays error toasts)
|
|
271
|
-
apiLoaderInterceptor, // from @flusys/ng-core (tracks loading state)
|
|
272
|
-
])
|
|
273
|
-
)
|
|
199
|
+
this.http.post(url, data, { context: new HttpContext().set(SKIP_ERROR_TOAST, true) });
|
|
274
200
|
```
|
|
275
201
|
|
|
276
|
-
**Order matters:** Auth interceptors first, then error handling, then loader tracking.
|
|
277
|
-
|
|
278
202
|
---
|
|
279
203
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#### ApiLoaderService
|
|
283
|
-
|
|
284
|
-
Signal-based loading state management with tagged loaders. Provided at root level.
|
|
204
|
+
## 3. Services
|
|
285
205
|
|
|
286
|
-
|
|
206
|
+
### ApiLoaderService
|
|
287
207
|
|
|
288
|
-
|
|
289
|
-
|--------|------|-------------|
|
|
290
|
-
| `show` | `Signal<boolean>` | `true` if ANY loader tag has active requests |
|
|
208
|
+
Signal-based loading state with tagged loaders.
|
|
291
209
|
|
|
292
|
-
|
|
210
|
+
```typescript
|
|
211
|
+
@Injectable({ providedIn: 'root' })
|
|
212
|
+
export class ApiLoaderService {
|
|
213
|
+
private readonly loaders = signal<Record<string, number>>({});
|
|
214
|
+
readonly DEFAULT_TAG = 'default';
|
|
293
215
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
| `decrease(name?)` | `name: string = 'default'` | Decrement loader count for a tag (min 0) |
|
|
298
|
-
| `getLoader(name)` | `name: string` | Get computed `Signal<boolean>` for a specific tag |
|
|
216
|
+
readonly show: Signal<boolean> = computed(() =>
|
|
217
|
+
Object.values(this.loaders()).some((count) => count > 0)
|
|
218
|
+
);
|
|
299
219
|
|
|
300
|
-
|
|
220
|
+
getLoader(name: string): Signal<boolean> {
|
|
221
|
+
return computed(() => (this.loaders()[name] ?? 0) > 0);
|
|
222
|
+
}
|
|
301
223
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
224
|
+
increase(name: string = this.DEFAULT_TAG): void { /* increment */ }
|
|
225
|
+
decrease(name: string = this.DEFAULT_TAG): void { /* decrement, min 0 */ }
|
|
226
|
+
}
|
|
227
|
+
```
|
|
305
228
|
|
|
306
229
|
**Usage:**
|
|
307
|
-
|
|
308
230
|
```typescript
|
|
309
|
-
|
|
310
|
-
@if (apiLoaderService.
|
|
311
|
-
<p-progressBar mode="indeterminate" />
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Tag-specific loading state
|
|
315
|
-
@if (apiLoaderService.getLoader('users-list')()) {
|
|
316
|
-
<p>Loading users...</p>
|
|
317
|
-
}
|
|
231
|
+
@if (apiLoaderService.show()) { <p-progressBar mode="indeterminate" /> }
|
|
232
|
+
@if (apiLoaderService.getLoader('users-list')()) { <p-skeleton /> }
|
|
318
233
|
```
|
|
319
234
|
|
|
320
|
-
|
|
235
|
+
### BaseApiService
|
|
321
236
|
|
|
322
|
-
Abstract base
|
|
237
|
+
Abstract base for API services with automatic URL resolution.
|
|
323
238
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
| `appConfig` | `IAppConfig` | Injected app configuration |
|
|
330
|
-
| `baseUrl` | `string` | Resolved base URL for the service |
|
|
239
|
+
```typescript
|
|
240
|
+
export abstract class BaseApiService {
|
|
241
|
+
protected readonly http = inject(HttpClient);
|
|
242
|
+
protected readonly appConfig = inject(APP_CONFIG);
|
|
243
|
+
protected readonly baseUrl: string;
|
|
331
244
|
|
|
332
|
-
|
|
245
|
+
constructor(serviceName: string) {
|
|
246
|
+
this.baseUrl = getServiceUrl(this.appConfig, serviceName);
|
|
247
|
+
}
|
|
333
248
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
249
|
+
protected getUrl(endpoint: string): string {
|
|
250
|
+
const clean = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
|
|
251
|
+
return `${this.baseUrl}/${clean}`;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
337
255
|
|
|
338
256
|
**Usage:**
|
|
339
|
-
|
|
340
257
|
```typescript
|
|
341
258
|
@Injectable({ providedIn: 'root' })
|
|
342
259
|
export class UserApiService extends BaseApiService {
|
|
343
|
-
constructor() {
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
getAll(filter: any) {
|
|
348
|
-
return this.http.post(this.getUrl('user/get-all'), filter);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
getById(id: string) {
|
|
352
|
-
return this.http.post(`${this.getUrl('user/get')}/${id}`, {});
|
|
353
|
-
}
|
|
260
|
+
constructor() { super('auth'); }
|
|
261
|
+
getAll(filter: any) { return this.http.post(this.getUrl('user/get-all'), filter); }
|
|
354
262
|
}
|
|
355
263
|
```
|
|
356
264
|
|
|
357
265
|
---
|
|
358
266
|
|
|
359
|
-
|
|
267
|
+
## 4. Components
|
|
360
268
|
|
|
361
|
-
|
|
269
|
+
### LibAppConfigComponent
|
|
362
270
|
|
|
363
|
-
Root configuration component
|
|
271
|
+
Root configuration component. Include once at app root.
|
|
364
272
|
|
|
365
|
-
**Selector:** `lib-app-config`
|
|
273
|
+
- **Selector:** `lib-app-config`
|
|
274
|
+
- **Change Detection:** `OnPush`
|
|
366
275
|
|
|
367
|
-
**
|
|
368
|
-
|
|
369
|
-
**Provides:**
|
|
370
|
-
- Global loading progress bar (fixed top, 4px height, z-index 998)
|
|
371
|
-
- Toast notifications (PrimeNG Toast)
|
|
372
|
-
- Confirm dialogs (PrimeNG ConfirmDialog)
|
|
373
|
-
|
|
374
|
-
**Template:**
|
|
375
|
-
```html
|
|
376
|
-
@if (apiLoaderService.show()) {
|
|
377
|
-
<p-progressBar mode="indeterminate" />
|
|
378
|
-
}
|
|
379
|
-
<p-toast />
|
|
380
|
-
<p-confirmdialog />
|
|
381
|
-
```
|
|
276
|
+
**Provides:** Global loading progress bar (fixed top, 4px, z-index 998), toast notifications (success uses primary color), confirm dialogs.
|
|
382
277
|
|
|
383
278
|
**Usage:**
|
|
384
279
|
```typescript
|
|
385
|
-
import { LibAppConfigComponent } from '@flusys/ng-core';
|
|
386
|
-
|
|
387
280
|
@Component({
|
|
388
281
|
selector: 'app-root',
|
|
389
|
-
standalone: true,
|
|
390
282
|
imports: [LibAppConfigComponent, RouterOutlet],
|
|
391
|
-
template:
|
|
392
|
-
<lib-app-config />
|
|
393
|
-
<router-outlet />
|
|
394
|
-
`
|
|
283
|
+
template: `<lib-app-config /><router-outlet />`
|
|
395
284
|
})
|
|
396
285
|
export class AppComponent {}
|
|
397
286
|
```
|
|
398
287
|
|
|
399
288
|
---
|
|
400
289
|
|
|
401
|
-
##
|
|
402
|
-
|
|
403
|
-
### Basic App Setup
|
|
290
|
+
## Setup
|
|
404
291
|
|
|
405
292
|
```typescript
|
|
406
293
|
// app.config.ts
|
|
407
|
-
import { ApplicationConfig } from '@angular/core';
|
|
408
|
-
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
|
|
409
294
|
import { APP_CONFIG, errorCatchingInterceptor, apiLoaderInterceptor } from '@flusys/ng-core';
|
|
410
295
|
import { authInterceptor, tokenRefreshInterceptor } from '@flusys/ng-auth';
|
|
411
|
-
import { DEFAULT_APP_CONFIG } from './config/app.config';
|
|
412
296
|
|
|
413
297
|
export const appConfig: ApplicationConfig = {
|
|
414
298
|
providers: [
|
|
415
299
|
provideHttpClient(
|
|
416
300
|
withFetch(),
|
|
417
301
|
withInterceptors([
|
|
418
|
-
authInterceptor,
|
|
419
|
-
tokenRefreshInterceptor,
|
|
420
|
-
errorCatchingInterceptor,
|
|
421
|
-
apiLoaderInterceptor,
|
|
302
|
+
authInterceptor, // ng-auth: auth + tenant headers
|
|
303
|
+
tokenRefreshInterceptor, // ng-auth: 401 + refresh
|
|
304
|
+
errorCatchingInterceptor, // ng-core: error toasts
|
|
305
|
+
apiLoaderInterceptor, // ng-core: loading state
|
|
422
306
|
])
|
|
423
307
|
),
|
|
424
308
|
{ provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG },
|
|
@@ -426,140 +310,33 @@ export const appConfig: ApplicationConfig = {
|
|
|
426
310
|
};
|
|
427
311
|
```
|
|
428
312
|
|
|
429
|
-
### Creating an API Service
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
import { Injectable } from '@angular/core';
|
|
433
|
-
import { BaseApiService } from '@flusys/ng-core';
|
|
434
|
-
|
|
435
|
-
@Injectable({ providedIn: 'root' })
|
|
436
|
-
export class AuthApiService extends BaseApiService {
|
|
437
|
-
constructor() {
|
|
438
|
-
super('auth'); // Resolves to services.auth.baseUrl
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
login(credentials: any) {
|
|
442
|
-
return this.http.post(this.getUrl('auth/login'), credentials);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Using Tagged Loaders
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { HttpHeaders } from '@angular/common/http';
|
|
451
|
-
import { ApiLoaderService } from '@flusys/ng-core';
|
|
452
|
-
|
|
453
|
-
// In a service - tag requests
|
|
454
|
-
this.http.post('/api/users/get-all', filter, {
|
|
455
|
-
headers: new HttpHeaders({ 'x-loader-tag': 'users-list' })
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// In a component - check specific loader
|
|
459
|
-
readonly usersLoading = this.apiLoaderService.getLoader('users-list');
|
|
460
|
-
|
|
461
|
-
// In template
|
|
462
|
-
@if (usersLoading()) {
|
|
463
|
-
<p-skeleton />
|
|
464
|
-
}
|
|
465
|
-
```
|
|
466
|
-
|
|
467
313
|
---
|
|
468
314
|
|
|
469
315
|
## Best Practices
|
|
470
316
|
|
|
471
|
-
1. **Always provide APP_CONFIG** at application root
|
|
472
|
-
2. **Match backend
|
|
473
|
-
3. **
|
|
474
|
-
4. **
|
|
475
|
-
5. **
|
|
476
|
-
6. **Use `BaseApiService`** for all API services to avoid hardcoded URLs
|
|
477
|
-
7. **Don't create custom error handling** - use `errorCatchingInterceptor`
|
|
478
|
-
8. **Don't manually track loading** - use `apiLoaderInterceptor` with `x-loader-tag` headers
|
|
317
|
+
1. **Always provide APP_CONFIG** at application root
|
|
318
|
+
2. **Match backend mode** - `multiTenant.enabled` should align with FLUSYS_NEST
|
|
319
|
+
3. **Never import other @flusys packages** in ng-core
|
|
320
|
+
4. **Use `BaseApiService`** for all API services
|
|
321
|
+
5. **Use tagged loaders** for granular loading states
|
|
479
322
|
|
|
480
323
|
## Common Issues
|
|
481
324
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
{ provide: APP_CONFIG, useValue: DEFAULT_APP_CONFIG }
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### Circular Dependency
|
|
492
|
-
|
|
493
|
-
**Problem:** Build fails with circular dependency error involving ng-core.
|
|
494
|
-
|
|
495
|
-
**Solution:** ng-core should NEVER import from other @flusys packages. Check imports and remove any references to ng-shared, ng-layout, or ng-auth.
|
|
325
|
+
| Issue | Solution |
|
|
326
|
+
|-------|----------|
|
|
327
|
+
| `NullInjectorError: APP_CONFIG` | Add `{ provide: APP_CONFIG, useValue: config }` to providers |
|
|
328
|
+
| Circular dependency | ng-core must NOT import other @flusys packages |
|
|
329
|
+
| Loader flickers | Counter-based system handles concurrent requests automatically |
|
|
330
|
+
| No error toast | Check: MessageService configured, not 401/403, not SKIP_ERROR_TOAST |
|
|
496
331
|
|
|
497
332
|
---
|
|
498
333
|
|
|
499
|
-
## API Reference
|
|
500
|
-
|
|
501
|
-
### Tokens
|
|
502
|
-
|
|
503
|
-
| Token | Type | Description |
|
|
504
|
-
|-------|------|-------------|
|
|
505
|
-
| `APP_CONFIG` | `InjectionToken<IAppConfig>` | Application configuration |
|
|
506
|
-
|
|
507
|
-
### Interceptors
|
|
508
|
-
|
|
509
|
-
| Function | Description |
|
|
510
|
-
|----------|-------------|
|
|
511
|
-
| `apiLoaderInterceptor` | Loading state management with tagged loaders |
|
|
512
|
-
| `errorCatchingInterceptor` | Error toast display (skips 401 and check-login) |
|
|
513
|
-
|
|
514
|
-
### Services
|
|
515
|
-
|
|
516
|
-
| Service | Description |
|
|
517
|
-
|---------|-------------|
|
|
518
|
-
| `ApiLoaderService` | Signal-based loading state with tagged loaders |
|
|
519
|
-
| `BaseApiService` | Abstract base class for API services with URL resolution |
|
|
520
|
-
|
|
521
|
-
### Interfaces & Types
|
|
522
|
-
|
|
523
|
-
| Export | Description |
|
|
524
|
-
|--------|-------------|
|
|
525
|
-
| `IAppConfig` | App configuration interface |
|
|
526
|
-
| `IServiceConfig` | Service configuration (`baseUrl`, `enabled`) |
|
|
527
|
-
| `IAuthorConfig` | Author/brand configuration (`name`, `url`) |
|
|
528
|
-
| `PermissionMode` | `'rbac' \| 'direct' \| 'full'` |
|
|
529
|
-
|
|
530
|
-
### Helper Functions
|
|
531
|
-
|
|
532
|
-
| Function | Description |
|
|
533
|
-
|----------|-------------|
|
|
534
|
-
| `getDatabaseMode(config)` | Returns `'single'` or `'multi-tenant'` |
|
|
535
|
-
| `isFeatureEnabled(config, feature)` | Check service feature enabled (`'auth' \| 'iam' \| 'storage'`) |
|
|
536
|
-
| `getServiceUrl(config, serviceName)` | Get service URL with fallback |
|
|
537
|
-
| `isServiceEnabled(config, serviceName)` | Check if service is enabled |
|
|
538
|
-
| `isCompanyFeatureEnabled(config)` | Check company feature flag |
|
|
539
|
-
| `isStorageFeatureEnabled(config)` | Check storage service enabled |
|
|
540
|
-
| `getPermissionMode(config)` | Get permission mode (default `'full'`) |
|
|
541
|
-
|
|
542
|
-
### Constants
|
|
543
|
-
|
|
544
|
-
| Constant | Value | Description |
|
|
545
|
-
|----------|-------|-------------|
|
|
546
|
-
| `DEFAULT_APP_NAME` | `'FLUSYS'` | Default application name |
|
|
547
|
-
| `DEFAULT_AUTHOR` | `{ name: 'Flusys', url: '...' }` | Default author/brand info |
|
|
548
|
-
|
|
549
|
-
### Components
|
|
550
|
-
|
|
551
|
-
| Component | Selector | Description |
|
|
552
|
-
|-----------|----------|-------------|
|
|
553
|
-
| `LibAppConfigComponent` | `lib-app-config` | Global toast, confirm dialog, and loading bar |
|
|
554
|
-
|
|
555
334
|
## See Also
|
|
556
335
|
|
|
557
|
-
-
|
|
558
|
-
-
|
|
559
|
-
-
|
|
560
|
-
- **[FLUSYS_NEST/docs/CORE-GUIDE.md](../../FLUSYS_NEST/docs/CORE-GUIDE.md)** - Backend core module
|
|
336
|
+
- [SHARED-GUIDE.md](./SHARED-GUIDE.md) - Components, directives, pipes
|
|
337
|
+
- [LAYOUT-GUIDE.md](./LAYOUT-GUIDE.md) - Layout system
|
|
338
|
+
- [FLUSYS_NEST/docs/CORE-GUIDE.md](../../FLUSYS_NEST/docs/CORE-GUIDE.md) - Backend core
|
|
561
339
|
|
|
562
340
|
---
|
|
563
341
|
|
|
564
|
-
**Last Updated:** 2026-02-21
|
|
565
|
-
**Angular Version:** 21
|
|
342
|
+
**Last Updated:** 2026-02-25 | **Version:** 3.0.1 | **Angular:** 21
|
|
@@ -35,10 +35,10 @@ class ApiLoaderService {
|
|
|
35
35
|
return { ...loaders, [name]: Math.max(0, current - 1) };
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
39
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
38
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ApiLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
39
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ApiLoaderService, providedIn: 'root' });
|
|
40
40
|
}
|
|
41
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
41
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ApiLoaderService, decorators: [{
|
|
42
42
|
type: Injectable,
|
|
43
43
|
args: [{ providedIn: 'root' }]
|
|
44
44
|
}] });
|
|
@@ -49,10 +49,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
49
49
|
*/
|
|
50
50
|
class LibAppConfigComponent {
|
|
51
51
|
apiLoaderService = inject(ApiLoaderService);
|
|
52
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
53
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
52
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LibAppConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
53
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
54
54
|
}
|
|
55
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
55
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LibAppConfigComponent, decorators: [{
|
|
56
56
|
type: Component,
|
|
57
57
|
args: [{ selector: 'lib-app-config', changeDetection: ChangeDetectionStrategy.OnPush, 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
|
}] });
|
|
@@ -202,68 +202,66 @@ function getDefaultSummary(status) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
/**
|
|
206
|
-
* App Configuration Token
|
|
207
|
-
* Use this to inject app config in services/components
|
|
208
|
-
*/
|
|
209
205
|
const APP_CONFIG = new InjectionToken('APP_CONFIG');
|
|
210
|
-
/**
|
|
211
|
-
* Get database mode from config
|
|
212
|
-
* Derived from multiTenant.enabled
|
|
213
|
-
*/
|
|
214
206
|
function getDatabaseMode(config) {
|
|
215
207
|
return config.multiTenant.enabled ? 'multi-tenant' : 'single';
|
|
216
208
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Check if a service is enabled
|
|
219
|
-
*/
|
|
220
209
|
function isServiceEnabled(config, serviceName) {
|
|
221
210
|
const service = config.services[serviceName];
|
|
222
211
|
return service?.enabled ?? false;
|
|
223
212
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Check if a feature is enabled (alias for isServiceEnabled with strict types)
|
|
226
|
-
*/
|
|
227
213
|
function isFeatureEnabled(config, feature) {
|
|
228
214
|
return isServiceEnabled(config, feature);
|
|
229
215
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Get service base URL or fallback to default apiBaseUrl
|
|
232
|
-
*/
|
|
233
216
|
function getServiceUrl(config, serviceName) {
|
|
234
217
|
if (isServiceEnabled(config, serviceName)) {
|
|
235
218
|
const service = config.services[serviceName];
|
|
236
|
-
return service.
|
|
219
|
+
return service?.baseUrl ?? config.apiBaseUrl;
|
|
237
220
|
}
|
|
238
221
|
return config.apiBaseUrl;
|
|
239
222
|
}
|
|
240
|
-
/**
|
|
241
|
-
* Check if company feature is enabled
|
|
242
|
-
*/
|
|
243
223
|
function isCompanyFeatureEnabled(config) {
|
|
244
224
|
return config.enableCompanyFeature ?? false;
|
|
245
225
|
}
|
|
246
|
-
/**
|
|
247
|
-
* Check if storage feature is enabled
|
|
248
|
-
*/
|
|
249
226
|
function isStorageFeatureEnabled(config) {
|
|
250
227
|
return isServiceEnabled(config, 'storage');
|
|
251
228
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
229
|
+
function isFormBuilderFeatureEnabled(config) {
|
|
230
|
+
return isServiceEnabled(config, 'formBuilder');
|
|
231
|
+
}
|
|
232
|
+
function isEmailFeatureEnabled(config) {
|
|
233
|
+
return isServiceEnabled(config, 'email');
|
|
234
|
+
}
|
|
235
|
+
function isEventManagerFeatureEnabled(config) {
|
|
236
|
+
return isServiceEnabled(config, 'eventManager');
|
|
237
|
+
}
|
|
238
|
+
function isNotificationFeatureEnabled(config) {
|
|
239
|
+
return isServiceEnabled(config, 'notification');
|
|
240
|
+
}
|
|
241
|
+
function getNotificationSocketUrl(config) {
|
|
242
|
+
const service = config.services.notification;
|
|
243
|
+
if (!service?.enabled)
|
|
244
|
+
return null;
|
|
245
|
+
return service.socketUrl ?? service.baseUrl;
|
|
246
|
+
}
|
|
256
247
|
function getPermissionMode(config) {
|
|
257
248
|
return config.permissionMode ?? 'full';
|
|
258
249
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Get default storage config ID from config
|
|
261
|
-
* Used for file upload operations when no storageConfigId is provided
|
|
262
|
-
*/
|
|
263
250
|
function getDefaultStorageConfigId(config) {
|
|
264
251
|
return config.services.storage?.defaultStorageConfigId;
|
|
265
252
|
}
|
|
266
|
-
|
|
253
|
+
function isSignUpEnabled(config) {
|
|
254
|
+
return config.enableSignUp ?? true;
|
|
255
|
+
}
|
|
256
|
+
function getDefaultCompanySlug(config) {
|
|
257
|
+
return config.registrationDefaults?.companySlug;
|
|
258
|
+
}
|
|
259
|
+
function getDefaultBranchSlug(config) {
|
|
260
|
+
return config.registrationDefaults?.branchSlug;
|
|
261
|
+
}
|
|
262
|
+
function getRegistrationDefaults(config) {
|
|
263
|
+
return config.registrationDefaults;
|
|
264
|
+
}
|
|
267
265
|
const DEFAULT_APP_NAME = 'FLUSYS';
|
|
268
266
|
const DEFAULT_AUTHOR = {
|
|
269
267
|
name: 'Flusys',
|
|
@@ -318,5 +316,5 @@ class BaseApiService {
|
|
|
318
316
|
* Generated bundle index. Do not edit.
|
|
319
317
|
*/
|
|
320
318
|
|
|
321
|
-
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
319
|
+
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultBranchSlug, getDefaultCompanySlug, getDefaultStorageConfigId, getNotificationSocketUrl, getPermissionMode, getRegistrationDefaults, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isEventManagerFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isNotificationFeatureEnabled, isServiceEnabled, isSignUpEnabled, isStorageFeatureEnabled };
|
|
322
320
|
//# 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 iam: IServiceConfig;\n storage: 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 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' | 'iam' | 'storage',\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' | 'iam' | 'storage' | (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 * 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\nexport type PermissionMode = 'rbac' | 'direct' | 'full';\n\nexport interface IServiceConfig {\n baseUrl: string;\n enabled: boolean;\n}\n\nexport interface IStorageServiceConfig extends IServiceConfig {\n /** Default storage config ID (falls back to company/branch default if not set) */\n defaultStorageConfigId?: string;\n}\n\nexport interface INotificationServiceConfig extends IServiceConfig {\n /** WebSocket URL (defaults to baseUrl if not set) */\n socketUrl?: string;\n}\n\nexport interface IAuthorConfig {\n name: string;\n url: string;\n}\n\nexport interface IRegistrationDefaults {\n companySlug?: string;\n branchSlug?: string;\n}\n\nexport interface IAppConfig {\n appName: string;\n author?: IAuthorConfig;\n apiBaseUrl: string;\n enableCompanyFeature: boolean;\n /** @default true */\n enableSignUp?: boolean;\n registrationDefaults?: IRegistrationDefaults;\n services: {\n auth: IServiceConfig;\n administration: IServiceConfig;\n iam: IServiceConfig;\n storage: IStorageServiceConfig;\n formBuilder: IServiceConfig;\n email: IServiceConfig;\n eventManager: IServiceConfig;\n notification?: INotificationServiceConfig;\n };\n multiTenant: {\n enabled: boolean;\n tenantHeader: string;\n tenantId?: string;\n };\n /** 'rbac' | 'direct' | 'full' (default: 'full') */\n permissionMode?: PermissionMode;\n}\n\nexport const APP_CONFIG = new InjectionToken<IAppConfig>('APP_CONFIG');\n\nexport function getDatabaseMode(config: IAppConfig): 'single' | 'multi-tenant' {\n return config.multiTenant.enabled ? 'multi-tenant' : 'single';\n}\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\nexport function isFeatureEnabled(\n config: IAppConfig,\n feature: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | 'eventManager' | 'notification',\n): boolean {\n return isServiceEnabled(config, feature);\n}\n\nexport function getServiceUrl(\n config: IAppConfig,\n serviceName: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | 'eventManager' | 'notification' | (string & {}),\n): string {\n if (isServiceEnabled(config, serviceName)) {\n const service = config.services[serviceName as keyof typeof config.services];\n return service?.baseUrl ?? config.apiBaseUrl;\n }\n return config.apiBaseUrl;\n}\n\nexport function isCompanyFeatureEnabled(config: IAppConfig): boolean {\n return config.enableCompanyFeature ?? false;\n}\n\nexport function isStorageFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'storage');\n}\n\nexport function isFormBuilderFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'formBuilder');\n}\n\nexport function isEmailFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'email');\n}\n\nexport function isEventManagerFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'eventManager');\n}\n\nexport function isNotificationFeatureEnabled(config: IAppConfig): boolean {\n return isServiceEnabled(config, 'notification');\n}\n\nexport function getNotificationSocketUrl(config: IAppConfig): string | null {\n const service = config.services.notification;\n if (!service?.enabled) return null;\n return service.socketUrl ?? service.baseUrl;\n}\n\nexport function getPermissionMode(config: IAppConfig): PermissionMode {\n return config.permissionMode ?? 'full';\n}\n\nexport function getDefaultStorageConfigId(config: IAppConfig): string | undefined {\n return config.services.storage?.defaultStorageConfigId;\n}\n\nexport function isSignUpEnabled(config: IAppConfig): boolean {\n return config.enableSignUp ?? true;\n}\n\nexport function getDefaultCompanySlug(config: IAppConfig): string | undefined {\n return config.registrationDefaults?.companySlug;\n}\n\nexport function getDefaultBranchSlug(config: IAppConfig): string | undefined {\n return config.registrationDefaults?.branchSlug;\n}\n\nexport function getRegistrationDefaults(config: IAppConfig): IRegistrationDefaults | undefined {\n return config.registrationDefaults;\n}\n\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,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/ng-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Core utilities and services for FLUSYS Angular packages",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@angular/common": "
|
|
8
|
-
"@angular/core": "
|
|
9
|
-
"@primeuix/themes": "
|
|
10
|
-
"primeicons": "
|
|
11
|
-
"primeng": "
|
|
7
|
+
"@angular/common": ">=21.0.0",
|
|
8
|
+
"@angular/core": ">=21.0.0",
|
|
9
|
+
"@primeuix/themes": ">=1.0.0",
|
|
10
|
+
"primeicons": ">=7.0.0",
|
|
11
|
+
"primeng": ">=21.0.0"
|
|
12
12
|
},
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"module": "fesm2022/flusys-ng-core.mjs",
|
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"tslib": "^2.3.0"
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
}
|
|
@@ -80,120 +80,71 @@ declare const SKIP_ERROR_TOAST: HttpContextToken<boolean>;
|
|
|
80
80
|
*/
|
|
81
81
|
declare function errorCatchingInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>>;
|
|
82
82
|
|
|
83
|
-
/**
|
|
84
|
-
* IAM Permission Mode - controls permission system behavior
|
|
85
|
-
*/
|
|
86
83
|
type PermissionMode = 'rbac' | 'direct' | 'full';
|
|
87
|
-
/**
|
|
88
|
-
* Service Configuration Interface
|
|
89
|
-
*/
|
|
90
84
|
interface IServiceConfig {
|
|
91
85
|
baseUrl: string;
|
|
92
86
|
enabled: boolean;
|
|
93
87
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Storage Service Configuration Interface
|
|
96
|
-
* Extends base service config with storage-specific options
|
|
97
|
-
*/
|
|
98
88
|
interface IStorageServiceConfig extends IServiceConfig {
|
|
99
|
-
/**
|
|
100
|
-
* Default storage config ID from backend storage_config table
|
|
101
|
-
* Used when no storageConfigId is provided to upload operations
|
|
102
|
-
* If not set, backend will use company/branch default config
|
|
103
|
-
*/
|
|
89
|
+
/** Default storage config ID (falls back to company/branch default if not set) */
|
|
104
90
|
defaultStorageConfigId?: string;
|
|
105
91
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
interface INotificationServiceConfig extends IServiceConfig {
|
|
93
|
+
/** WebSocket URL (defaults to baseUrl if not set) */
|
|
94
|
+
socketUrl?: string;
|
|
95
|
+
}
|
|
109
96
|
interface IAuthorConfig {
|
|
110
97
|
name: string;
|
|
111
98
|
url: string;
|
|
112
99
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
100
|
+
interface IRegistrationDefaults {
|
|
101
|
+
companySlug?: string;
|
|
102
|
+
branchSlug?: string;
|
|
103
|
+
}
|
|
117
104
|
interface IAppConfig {
|
|
118
|
-
/** Application name */
|
|
119
105
|
appName: string;
|
|
120
|
-
/** Author/Brand info for footer attribution */
|
|
121
106
|
author?: IAuthorConfig;
|
|
122
|
-
/** Default API base URL (fallback) */
|
|
123
107
|
apiBaseUrl: string;
|
|
124
|
-
/**
|
|
125
|
-
* Enable company feature across the app
|
|
126
|
-
* When true, enables company selection/switching in layout and feature modules
|
|
127
|
-
*/
|
|
128
108
|
enableCompanyFeature: boolean;
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
* Database mode is determined by multiTenant.enabled
|
|
133
|
-
*/
|
|
109
|
+
/** @default true */
|
|
110
|
+
enableSignUp?: boolean;
|
|
111
|
+
registrationDefaults?: IRegistrationDefaults;
|
|
134
112
|
services: {
|
|
135
113
|
auth: IServiceConfig;
|
|
114
|
+
administration: IServiceConfig;
|
|
136
115
|
iam: IServiceConfig;
|
|
137
116
|
storage: IStorageServiceConfig;
|
|
117
|
+
formBuilder: IServiceConfig;
|
|
118
|
+
email: IServiceConfig;
|
|
119
|
+
eventManager: IServiceConfig;
|
|
120
|
+
notification?: INotificationServiceConfig;
|
|
138
121
|
};
|
|
139
|
-
/**
|
|
140
|
-
* Multi-tenant configuration
|
|
141
|
-
* enabled: true → Database mode = multi-tenant
|
|
142
|
-
* enabled: false → Database mode = single
|
|
143
|
-
*/
|
|
144
122
|
multiTenant: {
|
|
145
123
|
enabled: boolean;
|
|
146
124
|
tenantHeader: string;
|
|
147
125
|
tenantId?: string;
|
|
148
126
|
};
|
|
149
|
-
/**
|
|
150
|
-
* IAM Permission Mode - controls permission system behavior
|
|
151
|
-
* - 'rbac': Role-Based Access Control only (users get permissions via roles)
|
|
152
|
-
* - 'direct': Direct action assignments only (actions assigned directly to users)
|
|
153
|
-
* - 'full': Both RBAC and direct permissions (most flexible, default)
|
|
154
|
-
*/
|
|
127
|
+
/** 'rbac' | 'direct' | 'full' (default: 'full') */
|
|
155
128
|
permissionMode?: PermissionMode;
|
|
156
129
|
}
|
|
157
|
-
/**
|
|
158
|
-
* App Configuration Token
|
|
159
|
-
* Use this to inject app config in services/components
|
|
160
|
-
*/
|
|
161
130
|
declare const APP_CONFIG: InjectionToken<IAppConfig>;
|
|
162
|
-
/**
|
|
163
|
-
* Get database mode from config
|
|
164
|
-
* Derived from multiTenant.enabled
|
|
165
|
-
*/
|
|
166
131
|
declare function getDatabaseMode(config: IAppConfig): 'single' | 'multi-tenant';
|
|
167
|
-
/**
|
|
168
|
-
* Check if a service is enabled
|
|
169
|
-
*/
|
|
170
132
|
declare function isServiceEnabled(config: IAppConfig, serviceName: 'auth' | 'iam' | 'storage' | (string & {})): boolean;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
*/
|
|
174
|
-
declare function isFeatureEnabled(config: IAppConfig, feature: 'auth' | 'iam' | 'storage'): boolean;
|
|
175
|
-
/**
|
|
176
|
-
* Get service base URL or fallback to default apiBaseUrl
|
|
177
|
-
*/
|
|
178
|
-
declare function getServiceUrl(config: IAppConfig, serviceName: 'auth' | 'iam' | 'storage' | (string & {})): string;
|
|
179
|
-
/**
|
|
180
|
-
* Check if company feature is enabled
|
|
181
|
-
*/
|
|
133
|
+
declare function isFeatureEnabled(config: IAppConfig, feature: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | 'eventManager' | 'notification'): boolean;
|
|
134
|
+
declare function getServiceUrl(config: IAppConfig, serviceName: 'auth' | 'administration' | 'iam' | 'storage' | 'formBuilder' | 'email' | 'eventManager' | 'notification' | (string & {})): string;
|
|
182
135
|
declare function isCompanyFeatureEnabled(config: IAppConfig): boolean;
|
|
183
|
-
/**
|
|
184
|
-
* Check if storage feature is enabled
|
|
185
|
-
*/
|
|
186
136
|
declare function isStorageFeatureEnabled(config: IAppConfig): boolean;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
137
|
+
declare function isFormBuilderFeatureEnabled(config: IAppConfig): boolean;
|
|
138
|
+
declare function isEmailFeatureEnabled(config: IAppConfig): boolean;
|
|
139
|
+
declare function isEventManagerFeatureEnabled(config: IAppConfig): boolean;
|
|
140
|
+
declare function isNotificationFeatureEnabled(config: IAppConfig): boolean;
|
|
141
|
+
declare function getNotificationSocketUrl(config: IAppConfig): string | null;
|
|
191
142
|
declare function getPermissionMode(config: IAppConfig): PermissionMode;
|
|
192
|
-
/**
|
|
193
|
-
* Get default storage config ID from config
|
|
194
|
-
* Used for file upload operations when no storageConfigId is provided
|
|
195
|
-
*/
|
|
196
143
|
declare function getDefaultStorageConfigId(config: IAppConfig): string | undefined;
|
|
144
|
+
declare function isSignUpEnabled(config: IAppConfig): boolean;
|
|
145
|
+
declare function getDefaultCompanySlug(config: IAppConfig): string | undefined;
|
|
146
|
+
declare function getDefaultBranchSlug(config: IAppConfig): string | undefined;
|
|
147
|
+
declare function getRegistrationDefaults(config: IAppConfig): IRegistrationDefaults | undefined;
|
|
197
148
|
declare const DEFAULT_APP_NAME = "FLUSYS";
|
|
198
149
|
declare const DEFAULT_AUTHOR: IAuthorConfig;
|
|
199
150
|
|
|
@@ -233,5 +184,5 @@ declare abstract class BaseApiService {
|
|
|
233
184
|
protected getUrl(endpoint: string): string;
|
|
234
185
|
}
|
|
235
186
|
|
|
236
|
-
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultStorageConfigId, getPermissionMode, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, isServiceEnabled, isStorageFeatureEnabled };
|
|
237
|
-
export type { IAppConfig, IAuthorConfig, IServiceConfig, IStorageServiceConfig, PermissionMode };
|
|
187
|
+
export { APP_CONFIG, ApiLoaderService, BaseApiService, DEFAULT_APP_NAME, DEFAULT_AUTHOR, LibAppConfigComponent, SKIP_ERROR_TOAST, apiLoaderInterceptor, errorCatchingInterceptor, getDatabaseMode, getDefaultBranchSlug, getDefaultCompanySlug, getDefaultStorageConfigId, getNotificationSocketUrl, getPermissionMode, getRegistrationDefaults, getServiceUrl, isCompanyFeatureEnabled, isEmailFeatureEnabled, isEventManagerFeatureEnabled, isFeatureEnabled, isFormBuilderFeatureEnabled, isNotificationFeatureEnabled, isServiceEnabled, isSignUpEnabled, isStorageFeatureEnabled };
|
|
188
|
+
export type { IAppConfig, IAuthorConfig, INotificationServiceConfig, IRegistrationDefaults, IServiceConfig, IStorageServiceConfig, PermissionMode };
|