@flusys/ng-shared 5.0.1 → 5.0.4

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.
@@ -1,12 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, PLATFORM_ID, Injectable, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, Pipe, Injector, isDevMode, NgModule, resource, model, runInInjectionContext, untracked, forwardRef, InjectionToken, Component, DestroyRef, ApplicationRef, viewChild, afterNextRender, ViewEncapsulation } from '@angular/core';
3
- import { isPlatformServer, CommonModule, NgOptimizedImage, NgComponentOutlet, DatePipe, DOCUMENT as DOCUMENT$1 } from '@angular/common';
4
- import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
5
- import { APP_CONFIG, getServiceUrl, TRANSLATE_ADAPTER, FALLBACK_MESSAGES_REGISTRY } from '@flusys/ng-core';
6
- import { of, firstValueFrom, map as map$1, from } from 'rxjs';
3
+ import { isPlatformServer, isPlatformBrowser, CommonModule, NgOptimizedImage, NgComponentOutlet, DatePipe, DOCUMENT as DOCUMENT$1 } from '@angular/common';
4
+ import { HttpClient, HttpContextToken, HttpHeaders, HttpParams } from '@angular/common/http';
5
+ import { APP_CONFIG, getServiceUrl, LOCALIZATION_STATE_SERVICE, LOCALIZATION_API_SERVICE, FALLBACK_MESSAGES_REGISTRY } from '@flusys/ng-core';
6
+ import { of, catchError as catchError$1, throwError, firstValueFrom, map as map$1, from } from 'rxjs';
7
7
  import { map, tap, catchError } from 'rxjs/operators';
8
8
  import * as i1$1 from '@angular/forms';
9
9
  import { NgControl, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
10
+ import * as i3 from 'primeng/api';
11
+ import { MessageService, ConfirmationService } from 'primeng/api';
10
12
  import { RouterOutlet, RouterLink, RouterLinkActive, Router, ActivatedRoute } from '@angular/router';
11
13
  import { AutoCompleteModule } from 'primeng/autocomplete';
12
14
  import { AvatarModule } from 'primeng/avatar';
@@ -56,8 +58,6 @@ import { TooltipModule } from 'primeng/tooltip';
56
58
  import { TreeTableModule } from 'primeng/treetable';
57
59
  import { ProgressSpinnerModule } from 'primeng/progressspinner';
58
60
  import { ColorPickerModule } from 'primeng/colorpicker';
59
- import * as i3 from 'primeng/api';
60
- import { MessageService, ConfirmationService } from 'primeng/api';
61
61
  import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
62
62
 
63
63
  const SHARED_MESSAGES = {
@@ -209,6 +209,7 @@ const SHARED_MESSAGES = {
209
209
  'shared.upload': 'Upload',
210
210
  'shared.validation.required': '{{field}} is required',
211
211
  'shared.verified': 'Verified',
212
+ 'shared.unverified': 'Un-Verified',
212
213
  'shared.view': 'View',
213
214
  'shared.view.details': 'View Details',
214
215
  'shared.yes': 'Yes',
@@ -308,6 +309,7 @@ const SHARED_MESSAGES = {
308
309
  'primeng.country.placeholder': 'Choose a country',
309
310
  };
310
311
 
312
+ // ==================== AUTH MODULE ====================
311
313
  const USER_PERMISSIONS = {
312
314
  CREATE: 'user.create',
313
315
  READ: 'user.read',
@@ -326,6 +328,7 @@ const BRANCH_PERMISSIONS = {
326
328
  UPDATE: 'branch.update',
327
329
  DELETE: 'branch.delete',
328
330
  };
331
+ // ==================== IAM MODULE ====================
329
332
  const ACTION_PERMISSIONS = {
330
333
  CREATE: 'action.create',
331
334
  READ: 'action.read',
@@ -354,6 +357,7 @@ const COMPANY_ACTION_PERMISSIONS = {
354
357
  READ: 'company-action.read',
355
358
  ASSIGN: 'company-action.assign',
356
359
  };
360
+ // ==================== STORAGE MODULE ====================
357
361
  const FILE_PERMISSIONS = {
358
362
  CREATE: 'file.create',
359
363
  READ: 'file.read',
@@ -372,6 +376,10 @@ const STORAGE_CONFIG_PERMISSIONS = {
372
376
  UPDATE: 'storage-config.update',
373
377
  DELETE: 'storage-config.delete',
374
378
  };
379
+ const UPLOAD_CONFIG_PERMISSIONS = {
380
+ DELETE: 'upload-config.delete',
381
+ };
382
+ // ==================== EMAIL MODULE ====================
375
383
  const EMAIL_CONFIG_PERMISSIONS = {
376
384
  CREATE: 'email-config.create',
377
385
  READ: 'email-config.read',
@@ -384,30 +392,34 @@ const EMAIL_TEMPLATE_PERMISSIONS = {
384
392
  UPDATE: 'email-template.update',
385
393
  DELETE: 'email-template.delete',
386
394
  };
395
+ // ==================== FORM BUILDER MODULE ====================
387
396
  const FORM_PERMISSIONS = {
388
397
  CREATE: 'form.create',
389
398
  READ: 'form.read',
390
399
  UPDATE: 'form.update',
391
400
  DELETE: 'form.delete',
392
401
  };
402
+ const FORM_RESULT_PERMISSIONS = {
403
+ CREATE: 'form-result.create',
404
+ READ: 'form-result.read',
405
+ UPDATE: 'form-result.update',
406
+ DELETE: 'form-result.delete',
407
+ };
408
+ // ==================== EVENT MANAGER MODULE ====================
393
409
  const EVENT_PERMISSIONS = {
394
410
  CREATE: 'event.create',
395
411
  READ: 'event.read',
396
412
  UPDATE: 'event.update',
397
413
  DELETE: 'event.delete',
398
414
  };
399
- const EVENT_PARTICIPANT_PERMISSIONS = {
400
- CREATE: 'event-participant.create',
401
- READ: 'event-participant.read',
402
- UPDATE: 'event-participant.update',
403
- DELETE: 'event-participant.delete',
404
- };
415
+ // ==================== NOTIFICATION MODULE ====================
405
416
  const NOTIFICATION_PERMISSIONS = {
406
417
  CREATE: 'notification.create',
407
418
  READ: 'notification.read',
408
419
  UPDATE: 'notification.update',
409
420
  DELETE: 'notification.delete',
410
421
  };
422
+ // ==================== LOCALIZATION MODULE ====================
411
423
  const LANGUAGE_PERMISSIONS = {
412
424
  CREATE: 'language.create',
413
425
  READ: 'language.read',
@@ -426,10 +438,48 @@ const TRANSLATION_PERMISSIONS = {
426
438
  UPDATE: 'translation.update',
427
439
  DELETE: 'translation.delete',
428
440
  };
441
+ // ==================== AGGREGATED EXPORTS ====================
442
+ const PERMISSIONS = {
443
+ // Auth
444
+ USER: USER_PERMISSIONS,
445
+ COMPANY: COMPANY_PERMISSIONS,
446
+ BRANCH: BRANCH_PERMISSIONS,
447
+ // IAM
448
+ ACTION: ACTION_PERMISSIONS,
449
+ ROLE: ROLE_PERMISSIONS,
450
+ ROLE_ACTION: ROLE_ACTION_PERMISSIONS,
451
+ USER_ROLE: USER_ROLE_PERMISSIONS,
452
+ USER_ACTION: USER_ACTION_PERMISSIONS,
453
+ COMPANY_ACTION: COMPANY_ACTION_PERMISSIONS,
454
+ // Storage
455
+ FILE: FILE_PERMISSIONS,
456
+ FOLDER: FOLDER_PERMISSIONS,
457
+ STORAGE_CONFIG: STORAGE_CONFIG_PERMISSIONS,
458
+ // Email
459
+ EMAIL_CONFIG: EMAIL_CONFIG_PERMISSIONS,
460
+ EMAIL_TEMPLATE: EMAIL_TEMPLATE_PERMISSIONS,
461
+ // Form Builder
462
+ FORM: FORM_PERMISSIONS,
463
+ FORM_RESULT: FORM_RESULT_PERMISSIONS,
464
+ // Event Manager
465
+ EVENT: EVENT_PERMISSIONS,
466
+ // Notification
467
+ NOTIFICATION: NOTIFICATION_PERMISSIONS,
468
+ // Localization
469
+ LANGUAGE: LANGUAGE_PERMISSIONS,
470
+ TRANSLATION_KEY: TRANSLATION_KEY_PERMISSIONS,
471
+ TRANSLATION: TRANSLATION_PERMISSIONS,
472
+ };
429
473
 
430
474
  // ─── Constants ────────────────────────────────────────────────────────────────
431
475
  const FILE_TYPE_FILTERS = {
432
- IMAGES: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'],
476
+ IMAGES: [
477
+ 'image/jpeg',
478
+ 'image/png',
479
+ 'image/gif',
480
+ 'image/webp',
481
+ 'image/svg+xml',
482
+ ],
433
483
  DOCUMENTS: [
434
484
  'application/pdf',
435
485
  'application/msword',
@@ -448,7 +498,9 @@ function getAcceptString(contentTypes) {
448
498
  function isFileTypeAllowed(file, allowedTypes) {
449
499
  if (!allowedTypes.length)
450
500
  return true;
451
- return allowedTypes.some((type) => type.endsWith('/*') ? file.type.startsWith(type.replace('/*', '/')) : file.type === type);
501
+ return allowedTypes.some((type) => type.endsWith('/*')
502
+ ? file.type.startsWith(type.replace('/*', '/'))
503
+ : file.type === type);
452
504
  }
453
505
  function getFileIconClass(contentType) {
454
506
  if (contentType.startsWith('image/'))
@@ -521,16 +573,6 @@ class FileUrlService {
521
573
  appConfig = inject(APP_CONFIG);
522
574
  _cache = signal(new Map(), ...(ngDevMode ? [{ debugName: "_cache" }] : []));
523
575
  cache = this._cache.asReadonly();
524
- /** Get file URL by ID from cache (synchronous) */
525
- getFileUrl(fileId) {
526
- if (!fileId)
527
- return null;
528
- return this._cache().get(fileId)?.url ?? null;
529
- }
530
- /** Get file URL as computed signal */
531
- fileUrlSignal(fileId) {
532
- return computed(() => this.getFileUrl(fileId));
533
- }
534
576
  /** Fetch file URLs from backend and update cache */
535
577
  fetchFileUrls(fileIds, forceRefresh = false) {
536
578
  if (!fileIds.length)
@@ -551,18 +593,6 @@ class FileUrlService {
551
593
  fetchSingleFileUrl(fileId, forceRefresh = false) {
552
594
  return this.fetchFileUrls([fileId], forceRefresh).pipe(map((files) => files[0] ?? null));
553
595
  }
554
- /** Clear entire cache */
555
- clearCache() {
556
- this._cache.set(new Map());
557
- }
558
- /** Remove specific file from cache */
559
- removeFromCache(fileId) {
560
- this._cache.update((cache) => {
561
- const next = new Map(cache);
562
- next.delete(fileId);
563
- return next;
564
- });
565
- }
566
596
  addToCache(files) {
567
597
  this._cache.update((cache) => {
568
598
  const next = new Map(cache);
@@ -669,6 +699,10 @@ class PermissionValidatorService {
669
699
  hasPermission(code) {
670
700
  return hasPermission(code, this._permissions());
671
701
  }
702
+ /** Check if user has ANY of the specified permissions (OR logic) */
703
+ hasAnyPermission(code) {
704
+ return hasAnyPermission(code, this._permissions());
705
+ }
672
706
  isPermissionsLoaded() {
673
707
  return this._isLoaded();
674
708
  }
@@ -680,6 +714,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
680
714
  args: [{ providedIn: 'root' }]
681
715
  }] });
682
716
 
717
+ class TranslateService {
718
+ localizationState = inject(LOCALIZATION_STATE_SERVICE);
719
+ translate(key, params) {
720
+ const translations = this.localizationState.translations();
721
+ const value = translations[key];
722
+ if (!value) {
723
+ return this.getFromFallback(key, params);
724
+ }
725
+ return this.interpolate(value, params);
726
+ }
727
+ translateSignal(key, params) {
728
+ return computed(() => {
729
+ this.localizationState.currentLanguage();
730
+ return this.translate(key, params);
731
+ });
732
+ }
733
+ getFromFallback(key, params) {
734
+ const fallbacks = this.localizationState.fallbackMessages();
735
+ const value = fallbacks[key] ?? key;
736
+ return this.interpolate(value, params);
737
+ }
738
+ interpolate(template, params) {
739
+ if (!params || !template)
740
+ return template;
741
+ return template.replace(/\{\{(\w+)\}\}/g, (match, paramKey) => {
742
+ const value = params[paramKey];
743
+ return value !== undefined ? String(value) : match;
744
+ });
745
+ }
746
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
747
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, providedIn: 'root' });
748
+ }
749
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, decorators: [{
750
+ type: Injectable,
751
+ args: [{ providedIn: 'root' }]
752
+ }] });
753
+
683
754
  class EditModeElementChangerDirective {
684
755
  el = inject(ElementRef);
685
756
  ngControl = inject(NgControl, { optional: true });
@@ -939,38 +1010,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
939
1010
  }], propDecorators: { eventType: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventType", required: false }] }], preventKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventKey", required: false }] }], action: [{ type: i0.Output, args: ["action"] }] } });
940
1011
 
941
1012
  class TranslatePipe {
942
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
943
- fallbackMessages = inject(FALLBACK_MESSAGES_REGISTRY, { optional: true });
1013
+ translateAdapter = inject(TranslateService);
944
1014
  transform(key, params, fallback) {
945
1015
  if (!key)
946
1016
  return fallback ?? '';
947
- if (!this.translateAdapter) {
948
- const result = this.getFromFallback(key, params ?? undefined);
949
- return fallback !== undefined && result === key ? fallback : result;
950
- }
951
- this.translateAdapter.languageCode();
952
1017
  const result = this.translateAdapter.translate(key, params ?? undefined);
953
1018
  return fallback !== undefined && result === key ? fallback : result;
954
1019
  }
955
- getFromFallback(template, params) {
956
- // Try to get from fallback messages registry first
957
- if (this.fallbackMessages) {
958
- const value = this.fallbackMessages[template];
959
- if (value) {
960
- return this.interpolate(value, params);
961
- }
962
- }
963
- // Fall back to interpolating the key itself
964
- return this.interpolate(template, params);
965
- }
966
- interpolate(template, params) {
967
- if (!params)
968
- return template;
969
- return template.replace(/\{\{(\w+)\}\}/g, (match, paramKey) => {
970
- const value = params[paramKey];
971
- return value !== undefined ? String(value) : match;
972
- });
973
- }
974
1020
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
975
1021
  static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate", pure: false });
976
1022
  }
@@ -982,21 +1028,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
982
1028
  }]
983
1029
  }] });
984
1030
 
985
- async function tryLoadLocalizationServices(injector) {
986
- try {
987
- // @ts-ignore - ng-localization is optional and loaded dynamically at runtime
988
- const locModule = await import('@flusys/ng-localization');
989
- const config = injector.get(locModule.LOCALIZATION_CONFIG, null);
990
- if (!config)
991
- return null;
992
- return {
993
- stateService: injector.get(locModule.LocalizationStateService),
994
- apiService: injector.get(locModule.LocalizationApiService),
995
- };
1031
+ /**
1032
+ * HTTP context token to skip global error toast.
1033
+ * Use when component handles error display locally.
1034
+ *
1035
+ * @example
1036
+ * ```typescript
1037
+ * this.http.post(url, data, {
1038
+ * context: new HttpContext().set(SKIP_ERROR_TOAST, true)
1039
+ * });
1040
+ * ```
1041
+ */
1042
+ const SKIP_ERROR_TOAST = new HttpContextToken(() => false);
1043
+ /**
1044
+ * Error Catching Interceptor
1045
+ * Displays error messages for failed HTTP requests using backend response.
1046
+ *
1047
+ * Features:
1048
+ * - Uses backend's error title (error.error.error) as summary
1049
+ * - Uses backend's error message (error.error.message) as detail
1050
+ * - Falls back to sensible defaults based on status code
1051
+ * - Skip with SKIP_ERROR_TOAST context token for local handling
1052
+ *
1053
+ * NOTE: This interceptor only handles error display.
1054
+ * For 401 handling and token refresh, use tokenRefreshInterceptor from @flusys/ng-auth.
1055
+ *
1056
+ * @example
1057
+ * ```typescript
1058
+ * // In app.config.ts
1059
+ * provideHttpClient(
1060
+ * withFetch(),
1061
+ * withInterceptors([
1062
+ * authInterceptor, // from @flusys/ng-auth
1063
+ * tokenRefreshInterceptor, // from @flusys/ng-auth
1064
+ * errorCatchingInterceptor, // from @flusys/ng-core
1065
+ * apiLoaderInterceptor, // from @flusys/ng-core
1066
+ * ])
1067
+ * )
1068
+ * ```
1069
+ */
1070
+ function errorCatchingInterceptor(req, next) {
1071
+ const messageService = inject(MessageService);
1072
+ const platformId = inject(PLATFORM_ID);
1073
+ const translateAdapter = inject(TranslateService, { optional: true });
1074
+ const isBrowser = isPlatformBrowser(platformId);
1075
+ // Skip error display for check-login (expected to fail when not logged in)
1076
+ const isCheckLogin = req.url.includes('/check-login');
1077
+ // Skip if component handles error locally
1078
+ const skipErrorToast = req.context.get(SKIP_ERROR_TOAST);
1079
+ return next(req).pipe(catchError$1((error) => {
1080
+ // Skip 401 errors - handled by tokenRefreshInterceptor in ng-auth
1081
+ // Skip 403 errors - handled by permission denied UI in components
1082
+ // Skip check-login errors - expected behavior
1083
+ // Skip if component handles error locally
1084
+ if (error.status === 401 ||
1085
+ error.status === 403 ||
1086
+ isCheckLogin ||
1087
+ skipErrorToast) {
1088
+ return throwError(() => error);
1089
+ }
1090
+ // Display error message for other errors
1091
+ if (isBrowser) {
1092
+ const { summary, detail } = extractErrorInfo(error, translateAdapter);
1093
+ messageService.add({
1094
+ severity: 'error',
1095
+ summary,
1096
+ detail,
1097
+ });
1098
+ }
1099
+ return throwError(() => error);
1100
+ }));
1101
+ }
1102
+ /**
1103
+ * Extract error summary and detail from HTTP error response.
1104
+ * Uses backend response format: { message, messageKey, messageVariables, error, statusCode }
1105
+ * If messageKey is present and translate adapter is available, translates the message.
1106
+ */
1107
+ function extractErrorInfo(error, translateAdapter) {
1108
+ // Check if we have a messageKey for translation
1109
+ const messageKey = error.error?.messageKey;
1110
+ const messageVariables = error.error?.messageVariables;
1111
+ let detail;
1112
+ if (messageKey && translateAdapter) {
1113
+ // Use translated message if available
1114
+ detail = translateAdapter.translate(messageKey, messageVariables);
1115
+ }
1116
+ else if (typeof error.error?.message === 'string') {
1117
+ detail = error.error.message;
1118
+ }
1119
+ else if (Array.isArray(error.error?.message)) {
1120
+ // NestJS validation errors come as array
1121
+ detail = error.error.message[0];
1122
+ }
1123
+ else if (error.message) {
1124
+ detail = error.message;
1125
+ }
1126
+ else {
1127
+ detail = translateAdapter
1128
+ ? translateAdapter.translate('shared.unexpected.error')
1129
+ : 'shared.unexpected.error';
1130
+ }
1131
+ // Extract summary/title from backend response or use default based on status
1132
+ let summary;
1133
+ if (typeof error.error?.error === 'string') {
1134
+ // Backend sends error type (e.g., "Not Found", "Bad Request", "Validation Error")
1135
+ summary = error.error.error;
1136
+ }
1137
+ else {
1138
+ // Fallback to status-based title
1139
+ summary = getDefaultSummary(error.status, translateAdapter);
1140
+ }
1141
+ return { summary, detail };
1142
+ }
1143
+ /**
1144
+ * Get default error summary based on HTTP status code.
1145
+ */
1146
+ function getDefaultSummary(status, translateAdapter) {
1147
+ const keyMap = {
1148
+ 400: 'shared.error.bad.request',
1149
+ 404: 'shared.error.not.found',
1150
+ 409: 'shared.error.conflict',
1151
+ 422: 'shared.error.validation.error',
1152
+ 500: 'shared.error.server.error',
1153
+ 502: 'shared.error.service.unavailable',
1154
+ 503: 'shared.error.service.unavailable',
1155
+ 504: 'shared.error.service.unavailable',
1156
+ };
1157
+ const key = keyMap[status] || 'shared.error';
1158
+ if (translateAdapter) {
1159
+ return translateAdapter.translate(key);
996
1160
  }
997
- catch {
1161
+ // Fallback to key when no translator available
1162
+ return key;
1163
+ }
1164
+
1165
+ /**
1166
+ * Resolver to load translations for specific modules.
1167
+ * Works with or without @flusys/ng-localization provider.
1168
+ *
1169
+ * When localization provider is enabled:
1170
+ * - Registers fallback messages in state
1171
+ * - Fetches translations from API
1172
+ * - Merges them into state
1173
+ *
1174
+ * When localization provider is not enabled:
1175
+ * - Registers fallback messages in FALLBACK_MESSAGES_REGISTRY
1176
+ * - TranslatePipe uses registry for translations
1177
+ * - No API calls
1178
+ *
1179
+ * @param config - Module config with modules array and optional fallback messages
1180
+ * @returns ResolveFn that loads translations before route activation
1181
+ *
1182
+ * @example
1183
+ * // With fallback messages (recommended)
1184
+ * { resolve: { t: resolveTranslationModule({ modules: ['auth'], fallbackMessages: AUTH_MESSAGES }) } }
1185
+ *
1186
+ * @example
1187
+ * // Without fallback (API translations only, requires localization provider)
1188
+ * { resolve: { t: resolveTranslationModule({ modules: ['auth', 'iam'] }) } }
1189
+ */
1190
+ async function tryLoadLocalizationServices(injector) {
1191
+ const stateService = injector.get(LOCALIZATION_STATE_SERVICE, null);
1192
+ const apiService = injector.get(LOCALIZATION_API_SERVICE, null);
1193
+ if (!stateService || !apiService)
998
1194
  return null;
999
- }
1195
+ return {
1196
+ stateService,
1197
+ apiService,
1198
+ };
1000
1199
  }
1001
1200
  function resolveTranslationModule(config) {
1002
1201
  return async () => {
@@ -1018,6 +1217,10 @@ function resolveTranslationModule(config) {
1018
1217
  if (registry) {
1019
1218
  Object.assign(registry, fallbackMessages);
1020
1219
  }
1220
+ else if (isDevMode()) {
1221
+ console.warn('resolveTranslationModule: FALLBACK_MESSAGES_REGISTRY not provided. ' +
1222
+ 'Add provideFallbackMessagesRegistry() to your app providers.');
1223
+ }
1021
1224
  }
1022
1225
  }
1023
1226
  if (!locServices)
@@ -1235,19 +1438,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1235
1438
  }]
1236
1439
  }] });
1237
1440
 
1441
+ // ── Separate default filter into a named constant so it's sharable/testable ──
1442
+ const DEFAULT_FILTER = {
1443
+ pagination: { currentPage: 0, pageSize: 10 },
1444
+ filter: {},
1445
+ select: [],
1446
+ sort: {},
1447
+ };
1238
1448
  class ApiResourceService {
1449
+ // ── Injected once via `inject()` ──────────────────────────────────────────
1450
+ http = inject(HttpClient);
1451
+ _config = inject(APP_CONFIG);
1239
1452
  baseUrl;
1240
- http;
1241
1453
  moduleApiName;
1454
+ // ── Public writable signals ───────────────────────────────────────────────
1242
1455
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1243
- filterData = signal({
1244
- pagination: { currentPage: 0, pageSize: 10 },
1245
- filter: {},
1246
- select: [],
1247
- sort: {},
1248
- }, ...(ngDevMode ? [{ debugName: "filterData" }] : []));
1456
+ filterData = signal({ ...DEFAULT_FILTER }, ...(ngDevMode ? [{ debugName: "filterData" }] : []));
1457
+ // ── Private activation gate: avoids an initial fetch before fetchList() ──
1249
1458
  _active = signal(false, ...(ngDevMode ? [{ debugName: "_active" }] : []));
1459
+ // ── Resource (reactive list fetching with abort support) ──────────────────
1250
1460
  listResource;
1461
+ // ── Derived/computed signals (pure reads, no side-effects) ────────────────
1251
1462
  isLoading = computed(() => this.listResource.isLoading(), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1252
1463
  data = computed(() => this.listResource.value()?.data ?? [], ...(ngDevMode ? [{ debugName: "data" }] : []));
1253
1464
  total = computed(() => this.listResource.value()?.meta?.total ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : []));
@@ -1260,52 +1471,64 @@ class ApiResourceService {
1260
1471
  }, ...(ngDevMode ? [{ debugName: "hasMore" }] : []));
1261
1472
  error = computed(() => this.listResource.error(), ...(ngDevMode ? [{ debugName: "error" }] : []));
1262
1473
  status = computed(() => this.listResource.status(), ...(ngDevMode ? [{ debugName: "status" }] : []));
1263
- constructor(moduleApiName, http, serviceName) {
1264
- this.moduleApiName = moduleApiName || serviceName || 'api';
1265
- const config = inject(APP_CONFIG);
1266
- const serviceBaseUrl = serviceName ? getServiceUrl(config, serviceName) : config.apiBaseUrl;
1267
- this.baseUrl = moduleApiName ? `${serviceBaseUrl}/${moduleApiName}` : serviceBaseUrl;
1268
- this.http = http;
1474
+ constructor(moduleApiName, serviceName) {
1475
+ this.moduleApiName = moduleApiName;
1476
+ // ── Resolve base URL once at construction, not per-request ───────────────
1477
+ const serviceBaseUrl = serviceName
1478
+ ? getServiceUrl(this._config, serviceName)
1479
+ : this._config.apiBaseUrl;
1480
+ this.baseUrl = moduleApiName
1481
+ ? `${serviceBaseUrl}/${moduleApiName}`
1482
+ : serviceBaseUrl;
1269
1483
  this.listResource = resource({ ...(ngDevMode ? { debugName: "listResource" } : {}), params: () => {
1484
+ // Short-circuit: skip the loader entirely when not yet activated
1270
1485
  if (!this._active())
1271
1486
  return undefined;
1272
1487
  return { search: this.searchTerm(), filter: this.filterData() };
1273
1488
  },
1274
- loader: ({ params, abortSignal }) => {
1275
- const obs = this._getAllObs(params.filter, params.search);
1276
- return new Promise((resolve, reject) => {
1277
- const sub = obs.subscribe({ next: resolve, error: reject });
1278
- abortSignal.addEventListener('abort', () => sub.unsubscribe());
1489
+ loader: ({ params, abortSignal }) =>
1490
+ // ── Inline promise keeps the closure tight; no temp variable needed ─
1491
+ new Promise((resolve, reject) => {
1492
+ const sub = this._getAllObs(params.filter, params.search).subscribe({
1493
+ next: resolve,
1494
+ error: reject,
1279
1495
  });
1280
- } });
1496
+ abortSignal.addEventListener('abort', () => sub.unsubscribe());
1497
+ }) });
1281
1498
  }
1499
+ // ── Single, memoised header builder – avoids object churn per call ────────
1282
1500
  getHttpOptions(endpoint, params) {
1283
1501
  return {
1284
- headers: new HttpHeaders({ 'x-loader-tag': `${this.moduleApiName}/${endpoint}` }),
1502
+ headers: new HttpHeaders({
1503
+ 'x-loader-tag': `${this.moduleApiName}/${endpoint}`,
1504
+ }),
1285
1505
  ...(params ? { params } : {}),
1286
1506
  };
1287
1507
  }
1508
+ // ── Private observable – shared by the resource loader and the public getAll() ──
1288
1509
  _getAllObs(filter, search = '') {
1289
- let params = new HttpParams();
1290
- if (search)
1291
- params = params.append('q', search);
1510
+ const params = search ? new HttpParams().append('q', search) : undefined;
1292
1511
  return this.http.post(`${this.baseUrl}/get-all`, filter, this.getHttpOptions('get-all', params));
1293
1512
  }
1513
+ // ── Activate guard: all list-management helpers call this first ───────────
1514
+ _activate() {
1515
+ this._active.set(true);
1516
+ }
1294
1517
  // ==========================================================================
1295
- // List Management
1518
+ // List management
1296
1519
  // ==========================================================================
1297
1520
  fetchList(search = '', filter) {
1298
1521
  this.searchTerm.set(search);
1299
1522
  if (filter)
1300
1523
  this.filterData.update((prev) => ({ ...prev, ...filter }));
1301
- this._active.set(true);
1524
+ this._activate();
1302
1525
  }
1303
1526
  setPagination(pagination) {
1304
- this._active.set(true);
1527
+ this._activate();
1305
1528
  this.filterData.update((prev) => ({ ...prev, pagination }));
1306
1529
  }
1307
1530
  nextPage() {
1308
- this._active.set(true);
1531
+ this._activate();
1309
1532
  this.filterData.update((prev) => ({
1310
1533
  ...prev,
1311
1534
  pagination: {
@@ -1315,7 +1538,7 @@ class ApiResourceService {
1315
1538
  }));
1316
1539
  }
1317
1540
  resetPagination() {
1318
- this._active.set(true);
1541
+ this._activate();
1319
1542
  this.filterData.update((prev) => ({
1320
1543
  ...prev,
1321
1544
  pagination: { currentPage: 0, pageSize: prev.pagination?.pageSize ?? 10 },
@@ -1325,131 +1548,107 @@ class ApiResourceService {
1325
1548
  this.listResource.reload();
1326
1549
  }
1327
1550
  // ==========================================================================
1328
- // Promise API (IApiService)
1551
+ // Promise API (IApiService implementation)
1329
1552
  // ==========================================================================
1553
+ // ── DRY helper: all promise methods are one-liners via this ───────────────
1554
+ _post(endpoint, body, params) {
1555
+ return firstValueFrom(this.http.post(`${this.baseUrl}/${endpoint}`, body, this.getHttpOptions(endpoint, params)));
1556
+ }
1330
1557
  insert(dto) {
1331
- return firstValueFrom(this.http.post(`${this.baseUrl}/insert`, dto, this.getHttpOptions('insert')));
1558
+ return this._post('insert', dto);
1332
1559
  }
1333
1560
  insertMany(dtos) {
1334
- return firstValueFrom(this.http.post(`${this.baseUrl}/insert-many`, dtos, this.getHttpOptions('insert-many')));
1561
+ return this._post('insert-many', dtos);
1335
1562
  }
1336
1563
  findById(id, select) {
1337
- return firstValueFrom(this.http.post(`${this.baseUrl}/get/${id}`, { select }, this.getHttpOptions(`get/${id}`)));
1564
+ return this._post(`get/${id}`, { select });
1338
1565
  }
1339
1566
  getAll(filter, search = '') {
1340
1567
  return firstValueFrom(this._getAllObs(filter, search));
1341
1568
  }
1342
1569
  update(dto) {
1343
- return firstValueFrom(this.http.post(`${this.baseUrl}/update`, dto, this.getHttpOptions('update')));
1570
+ return this._post('update', dto);
1344
1571
  }
1345
1572
  updateMany(dtos) {
1346
- return firstValueFrom(this.http.post(`${this.baseUrl}/update-many`, dtos, this.getHttpOptions('update-many')));
1573
+ return this._post('update-many', dtos);
1347
1574
  }
1348
- findByIds(ids, select) {
1349
- return firstValueFrom(this.http.post(`${this.baseUrl}/get-by-ids`, { ids, select }, this.getHttpOptions('get-by-ids')));
1575
+ delete(deleteDto) {
1576
+ return this._post('delete', deleteDto);
1350
1577
  }
1351
- bulkUpsert(toInsert, toUpdate) {
1352
- return firstValueFrom(this.http.post(`${this.baseUrl}/bulk-upsert`, [...toInsert, ...toUpdate], this.getHttpOptions('bulk-upsert'))).then((res) => res.data ?? []);
1578
+ findByIds(ids, select) {
1579
+ return this._post('get-by-ids', { ids, select });
1353
1580
  }
1354
1581
  getByFilter(filter) {
1355
- return firstValueFrom(this.http.post(`${this.baseUrl}/get-by-filter`, filter, this.getHttpOptions('get-by-filter')));
1582
+ return this._post('get-by-filter', filter);
1356
1583
  }
1357
- delete(deleteDto) {
1358
- return firstValueFrom(this.http.post(`${this.baseUrl}/delete`, deleteDto, this.getHttpOptions('delete')));
1584
+ async bulkUpsert(toInsert, toUpdate) {
1585
+ const res = await this._post('bulk-upsert', [
1586
+ ...toInsert,
1587
+ ...toUpdate,
1588
+ ]);
1589
+ return res.data ?? [];
1359
1590
  }
1360
1591
  }
1361
1592
 
1362
- /**
1363
- * Base class for form controls that support ALL Angular form patterns:
1364
- *
1365
- * 1. **Template-driven forms:** `[(value)]="mySignal"` or `[(ngModel)]="myValue"`
1366
- * 2. **Reactive forms:** `[formControl]="ctrl"` or `formControlName="field"`
1367
- * 3. **Signal forms (Angular 21+):** `[formField]="formTree.field"`
1368
- *
1369
- * Implements both `ControlValueAccessor` (reactive forms) and `FormValueControl` (signal forms).
1370
- *
1371
- * @example
1372
- * ```typescript
1373
- * @Component({
1374
- * providers: [provideValueAccessor(MySelectComponent)]
1375
- * })
1376
- * export class MySelectComponent extends BaseFormControl<string | null> {
1377
- * override readonly value = model<string | null>(null);
1378
- *
1379
- * constructor() {
1380
- * super();
1381
- * this.initializeFormControl();
1382
- * }
1383
- * }
1384
- *
1385
- * // Usage:
1386
- * // Template-driven: <my-select [(value)]="selectedId" />
1387
- * // Reactive forms: <my-select [formControl]="myControl" />
1388
- * // Signal forms: <my-select [formField]="formTree.myField" />
1389
- * ```
1390
- */
1391
1593
  class BaseFormControl {
1392
1594
  injector = inject(Injector);
1393
- /**
1394
- * Disabled state model - bound to form control disabled state.
1395
- * Using model() to satisfy FormValueControl interface requirements.
1396
- */
1397
1595
  disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1398
- /**
1399
- * Touched state model - for validation styling.
1400
- * Using model() to satisfy FormValueControl interface requirements.
1401
- */
1402
1596
  touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
1597
+ // ── CVA callbacks ─────────────────────────────────────────────────────────
1598
+ // Initialised to no-ops so subclasses never need null-guards
1403
1599
  onChange = () => { };
1404
1600
  onTouched = () => { };
1405
- isWritingValue = false;
1601
+ // ── Write-guard: prevents echo loop (writeValue → effect → onChange) ──────
1602
+ // Using a counter instead of a boolean handles the (rare) re-entrant case
1603
+ writeDepth = 0;
1406
1604
  /**
1407
- * Initialize the form control synchronization for ControlValueAccessor.
1408
- * Must be called in the subclass constructor after super().
1605
+ * Wire up the ControlValueAccessor ↔ signal bridge.
1606
+ * Call once at the end of the subclass constructor.
1409
1607
  *
1410
- * Note: For signal forms ([formField]), this is not needed as
1411
- * the FormField directive binds directly to the value model.
1608
+ * Signal-form consumers ([formField]) don't need this — Angular's
1609
+ * FormField directive binds directly to `value`.
1610
+ *
1611
+ * @example
1612
+ * constructor() { super(); this.initializeFormControl(); }
1412
1613
  */
1413
1614
  initializeFormControl() {
1414
1615
  runInInjectionContext(this.injector, () => {
1415
1616
  effect(() => {
1416
- const currentValue = this.value();
1417
- if (!this.isWritingValue) {
1418
- untracked(() => this.onChange(currentValue));
1617
+ const next = this.value();
1618
+ // Skip propagation while writeValue() is on the call stack
1619
+ if (this.writeDepth === 0) {
1620
+ untracked(() => this.onChange(next));
1419
1621
  }
1420
1622
  });
1421
1623
  });
1422
1624
  }
1423
- // ============================================
1424
- // ControlValueAccessor Implementation
1425
- // (For reactive forms: formControl, formControlName)
1426
- // ============================================
1427
- /** ControlValueAccessor: Write value from form control to signal */
1625
+ // ==========================================================================
1626
+ // ControlValueAccessor
1627
+ // ==========================================================================
1428
1628
  writeValue(value) {
1429
- this.isWritingValue = true;
1430
- this.value.set(value);
1431
- this.isWritingValue = false;
1629
+ this.writeDepth++;
1630
+ try {
1631
+ this.value.set(value);
1632
+ }
1633
+ finally {
1634
+ // Always decrement — even if set() throws — so the guard never sticks
1635
+ this.writeDepth--;
1636
+ }
1432
1637
  }
1433
- /** ControlValueAccessor: Register change callback */
1434
1638
  registerOnChange(fn) {
1435
1639
  this.onChange = fn;
1436
1640
  }
1437
- /** ControlValueAccessor: Register touched callback */
1438
1641
  registerOnTouched(fn) {
1439
1642
  this.onTouched = fn;
1440
1643
  }
1441
- /** ControlValueAccessor: Set disabled state from form control */
1442
1644
  setDisabledState(isDisabled) {
1443
1645
  this.disabled.set(isDisabled);
1444
1646
  }
1445
- // ============================================
1446
- // FormValueControl Implementation
1447
- // (For signal forms: [formField])
1448
- // ============================================
1449
- // The FormValueControl interface only requires:
1450
- // - value: ModelSignal<T> (already declared as abstract above)
1451
- // The FormField directive automatically binds to the value model.
1452
- /** Mark the control as touched (call on blur or panel close) */
1647
+ // ==========================================================================
1648
+ // FormValueControl (signal forms)
1649
+ // `value: ModelSignal<T>` satisfies the interface — nothing else needed.
1650
+ // ==========================================================================
1651
+ /** Call on blur / panel-close to propagate touched state. */
1453
1652
  markAsTouched() {
1454
1653
  if (!this.touched()) {
1455
1654
  this.touched.set(true);
@@ -1478,7 +1677,7 @@ function provideValueAccessor(component) {
1478
1677
  return {
1479
1678
  provide: NG_VALUE_ACCESSOR,
1480
1679
  useExisting: forwardRef(() => component),
1481
- multi: true
1680
+ multi: true,
1482
1681
  };
1483
1682
  }
1484
1683
 
@@ -1606,7 +1805,7 @@ function checkScrollPagination(event, config) {
1606
1805
 
1607
1806
  class FileUploaderComponent {
1608
1807
  messageService = inject(MessageService);
1609
- translateAdapter = inject(TRANSLATE_ADAPTER, {
1808
+ translateAdapter = inject(TranslateService, {
1610
1809
  optional: true,
1611
1810
  });
1612
1811
  fileProvider = inject(FILE_PROVIDER, { optional: true });
@@ -2068,7 +2267,7 @@ const DEFAULT_PAGE_SIZE$1 = 20;
2068
2267
  const DEFAULT_SELECTOR_PAGE_SIZE = 50;
2069
2268
  class FileSelectorDialogComponent {
2070
2269
  destroyRef = inject(DestroyRef);
2071
- translateAdapter = inject(TRANSLATE_ADAPTER, {
2270
+ translateAdapter = inject(TranslateService, {
2072
2271
  optional: true,
2073
2272
  });
2074
2273
  fileProvider = inject(FILE_PROVIDER, { optional: true });
@@ -2964,7 +3163,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2964
3163
  class LazyMultiSelectComponent extends BaseFormControl {
2965
3164
  document = inject(DOCUMENT$1);
2966
3165
  appRef = inject(ApplicationRef);
2967
- translateAdapter = inject(TRANSLATE_ADAPTER, {
3166
+ translateAdapter = inject(TranslateService, {
2968
3167
  optional: true,
2969
3168
  });
2970
3169
  onDocumentClickBound = this.handleDocumentClick.bind(this);
@@ -3170,7 +3369,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3170
3369
  */
3171
3370
  class LazySelectComponent extends BaseFormControl {
3172
3371
  destroyRef = inject(DestroyRef);
3173
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
3372
+ translateAdapter = inject(TranslateService, {
3373
+ optional: true,
3374
+ });
3174
3375
  onScrollBound = this.onScroll.bind(this);
3175
3376
  scrollTargetEl = null;
3176
3377
  isDestroyed = false;
@@ -3294,7 +3495,7 @@ class BaseUserSelectComponent {
3294
3495
  destroyRef = inject(DestroyRef);
3295
3496
  injector = inject(Injector);
3296
3497
  userProvider = inject(USER_PROVIDER);
3297
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
3498
+ translateAdapter = inject(TranslateService);
3298
3499
  abortController = null;
3299
3500
  // Inputs
3300
3501
  loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
@@ -3309,7 +3510,10 @@ class BaseUserSelectComponent {
3309
3510
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
3310
3511
  users = signal([], ...(ngDevMode ? [{ debugName: "users" }] : []));
3311
3512
  total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
3312
- pagination = signal({ pageSize: DEFAULT_PAGE_SIZE, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
3513
+ pagination = signal({
3514
+ pageSize: DEFAULT_PAGE_SIZE,
3515
+ currentPage: 0,
3516
+ }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
3313
3517
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
3314
3518
  // Computed dropdown data
3315
3519
  dropdownUsers = computed(() => this.users().map((user) => ({
@@ -3399,12 +3603,16 @@ class BaseUserSelectComponent {
3399
3603
  }
3400
3604
  /** Load users from USER_PROVIDER with active filter */
3401
3605
  loadUsersFromProvider(filter) {
3606
+ const extraFilters = {
3607
+ isActive: this.filterActive() ? true : undefined,
3608
+ ...this.additionalFilters(),
3609
+ };
3402
3610
  return this.userProvider
3403
3611
  .getUsers({
3404
3612
  page: filter.page,
3405
3613
  pageSize: filter.pageSize,
3406
3614
  search: filter.search,
3407
- isActive: this.filterActive() ? true : undefined,
3615
+ ...extraFilters,
3408
3616
  })
3409
3617
  .pipe(map$1((res) => ({
3410
3618
  ...res,
@@ -3765,7 +3973,7 @@ class BaseFormPage {
3765
3973
  route = inject(ActivatedRoute);
3766
3974
  messageService = inject(MessageService);
3767
3975
  destroyRef = inject(DestroyRef);
3768
- translateAdapter = inject(TRANSLATE_ADAPTER, {
3976
+ translateAdapter = inject(TranslateService, {
3769
3977
  optional: true,
3770
3978
  });
3771
3979
  routeParams = toSignal(this.route.paramMap);
@@ -3901,7 +4109,9 @@ class BaseListPage {
3901
4109
  appConfig = inject(APP_CONFIG);
3902
4110
  confirmationService = inject(ConfirmationService);
3903
4111
  destroyRef = inject(DestroyRef);
3904
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
4112
+ translateAdapter = inject(TranslateService, {
4113
+ optional: true,
4114
+ });
3905
4115
  /** Items list */
3906
4116
  items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
3907
4117
  /** Loading state */
@@ -3951,25 +4161,41 @@ class BaseListPage {
3951
4161
  * Show success toast message
3952
4162
  */
3953
4163
  showSuccess(detail, summary) {
3954
- this.messageService.add({ severity: 'success', summary: summary ?? this.t('shared.success'), detail });
4164
+ this.messageService.add({
4165
+ severity: 'success',
4166
+ summary: summary ?? this.t('shared.success'),
4167
+ detail,
4168
+ });
3955
4169
  }
3956
4170
  /**
3957
4171
  * Show error toast message
3958
4172
  */
3959
4173
  showError(detail, summary) {
3960
- this.messageService.add({ severity: 'error', summary: summary ?? this.t('shared.error'), detail });
4174
+ this.messageService.add({
4175
+ severity: 'error',
4176
+ summary: summary ?? this.t('shared.error'),
4177
+ detail,
4178
+ });
3961
4179
  }
3962
4180
  /**
3963
4181
  * Show info toast message
3964
4182
  */
3965
4183
  showInfo(detail, summary) {
3966
- this.messageService.add({ severity: 'info', summary: summary ?? this.t('shared.info'), detail });
4184
+ this.messageService.add({
4185
+ severity: 'info',
4186
+ summary: summary ?? this.t('shared.info'),
4187
+ detail,
4188
+ });
3967
4189
  }
3968
4190
  /**
3969
4191
  * Show warning toast message
3970
4192
  */
3971
4193
  showWarn(detail, summary) {
3972
- this.messageService.add({ severity: 'warn', summary: summary ?? this.t('shared.warning'), detail });
4194
+ this.messageService.add({
4195
+ severity: 'warn',
4196
+ summary: summary ?? this.t('shared.warning'),
4197
+ detail,
4198
+ });
3973
4199
  }
3974
4200
  /**
3975
4201
  * Translate a key using the adapter, fallback to key if not available.
@@ -3996,7 +4222,9 @@ class BaseListPage {
3996
4222
  .subscribe({
3997
4223
  next: (res) => {
3998
4224
  const r = res;
3999
- this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
4225
+ this.showSuccess(r?.messageKey
4226
+ ? this.t(r.messageKey, r.messageVariables)
4227
+ : (r?.message ?? ''));
4000
4228
  this.loadData();
4001
4229
  },
4002
4230
  });
@@ -4019,7 +4247,9 @@ class BaseListPage {
4019
4247
  accept: async () => {
4020
4248
  const res = await deleteApiCall(idGetter(item));
4021
4249
  const r = res;
4022
- this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
4250
+ this.showSuccess(r?.messageKey
4251
+ ? this.t(r.messageKey, r.messageVariables)
4252
+ : (r?.message ?? ''));
4023
4253
  await this.loadData();
4024
4254
  },
4025
4255
  });
@@ -4104,5 +4334,5 @@ function getUserTimezone() {
4104
4334
  * Generated bundle index. Do not edit.
4105
4335
  */
4106
4336
 
4107
- export { ACTION_PERMISSIONS, AUTH_STATE_PROVIDER, AngularModule, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseFormControl, BaseFormPage, BaseListPage, BaseUserSelectComponent, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, EVENT_PARTICIPANT_PERMISSIONS, EVENT_PERMISSIONS, EditModeElementChangerDirective, FILE_PERMISSIONS, FILE_PROVIDER, FILE_TYPE_FILTERS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LANGUAGE_PERMISSIONS, LazyMultiSelectComponent, LazySelectComponent, NOTIFICATION_PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, SHARED_MESSAGES, STORAGE_CONFIG_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TRANSLATION_PERMISSIONS, TranslatePipe, USER_ACTION_PERMISSIONS, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, checkScrollPagination, evaluateLogicNode, evaluatePermission, formatFileSize, formatInTimezone, getAcceptString, getFileIconClass, getUserTimezone, hasAllPermissions, hasAnyPermission, hasPermission, isFileTypeAllowed, localToUtc, permissionGuard, provideValueAccessor, resolveTranslationModule, utcToLocal };
4337
+ export { ACTION_PERMISSIONS, AUTH_STATE_PROVIDER, AngularModule, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseFormControl, BaseFormPage, BaseListPage, BaseUserSelectComponent, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, EVENT_PERMISSIONS, EditModeElementChangerDirective, FILE_PERMISSIONS, FILE_PROVIDER, FILE_TYPE_FILTERS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FORM_RESULT_PERMISSIONS, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LANGUAGE_PERMISSIONS, LazyMultiSelectComponent, LazySelectComponent, NOTIFICATION_PERMISSIONS, PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, SHARED_MESSAGES, SKIP_ERROR_TOAST, STORAGE_CONFIG_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TRANSLATION_PERMISSIONS, TranslatePipe, TranslateService, UPLOAD_CONFIG_PERMISSIONS, USER_ACTION_PERMISSIONS, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, checkScrollPagination, errorCatchingInterceptor, evaluateLogicNode, evaluatePermission, formatFileSize, formatInTimezone, getAcceptString, getFileIconClass, getUserTimezone, hasAllPermissions, hasAnyPermission, hasPermission, isFileTypeAllowed, localToUtc, permissionGuard, provideValueAccessor, resolveTranslationModule, utcToLocal };
4108
4338
  //# sourceMappingURL=flusys-ng-shared.mjs.map