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