@flusys/ng-shared 5.0.3 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, FALLBACK_MESSAGES_REGISTRY, LOCALIZATION_API_SERVICE } 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 = {
@@ -309,6 +309,7 @@ const SHARED_MESSAGES = {
309
309
  'primeng.country.placeholder': 'Choose a country',
310
310
  };
311
311
 
312
+ // ==================== AUTH MODULE ====================
312
313
  const USER_PERMISSIONS = {
313
314
  CREATE: 'user.create',
314
315
  READ: 'user.read',
@@ -327,6 +328,7 @@ const BRANCH_PERMISSIONS = {
327
328
  UPDATE: 'branch.update',
328
329
  DELETE: 'branch.delete',
329
330
  };
331
+ // ==================== IAM MODULE ====================
330
332
  const ACTION_PERMISSIONS = {
331
333
  CREATE: 'action.create',
332
334
  READ: 'action.read',
@@ -355,6 +357,7 @@ const COMPANY_ACTION_PERMISSIONS = {
355
357
  READ: 'company-action.read',
356
358
  ASSIGN: 'company-action.assign',
357
359
  };
360
+ // ==================== STORAGE MODULE ====================
358
361
  const FILE_PERMISSIONS = {
359
362
  CREATE: 'file.create',
360
363
  READ: 'file.read',
@@ -373,6 +376,10 @@ const STORAGE_CONFIG_PERMISSIONS = {
373
376
  UPDATE: 'storage-config.update',
374
377
  DELETE: 'storage-config.delete',
375
378
  };
379
+ const UPLOAD_CONFIG_PERMISSIONS = {
380
+ DELETE: 'upload-config.delete',
381
+ };
382
+ // ==================== EMAIL MODULE ====================
376
383
  const EMAIL_CONFIG_PERMISSIONS = {
377
384
  CREATE: 'email-config.create',
378
385
  READ: 'email-config.read',
@@ -385,30 +392,34 @@ const EMAIL_TEMPLATE_PERMISSIONS = {
385
392
  UPDATE: 'email-template.update',
386
393
  DELETE: 'email-template.delete',
387
394
  };
395
+ // ==================== FORM BUILDER MODULE ====================
388
396
  const FORM_PERMISSIONS = {
389
397
  CREATE: 'form.create',
390
398
  READ: 'form.read',
391
399
  UPDATE: 'form.update',
392
400
  DELETE: 'form.delete',
393
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 ====================
394
409
  const EVENT_PERMISSIONS = {
395
410
  CREATE: 'event.create',
396
411
  READ: 'event.read',
397
412
  UPDATE: 'event.update',
398
413
  DELETE: 'event.delete',
399
414
  };
400
- const EVENT_PARTICIPANT_PERMISSIONS = {
401
- CREATE: 'event-participant.create',
402
- READ: 'event-participant.read',
403
- UPDATE: 'event-participant.update',
404
- DELETE: 'event-participant.delete',
405
- };
415
+ // ==================== NOTIFICATION MODULE ====================
406
416
  const NOTIFICATION_PERMISSIONS = {
407
417
  CREATE: 'notification.create',
408
418
  READ: 'notification.read',
409
419
  UPDATE: 'notification.update',
410
420
  DELETE: 'notification.delete',
411
421
  };
422
+ // ==================== LOCALIZATION MODULE ====================
412
423
  const LANGUAGE_PERMISSIONS = {
413
424
  CREATE: 'language.create',
414
425
  READ: 'language.read',
@@ -427,10 +438,48 @@ const TRANSLATION_PERMISSIONS = {
427
438
  UPDATE: 'translation.update',
428
439
  DELETE: 'translation.delete',
429
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
+ };
430
473
 
431
474
  // ─── Constants ────────────────────────────────────────────────────────────────
432
475
  const FILE_TYPE_FILTERS = {
433
- 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
+ ],
434
483
  DOCUMENTS: [
435
484
  'application/pdf',
436
485
  'application/msword',
@@ -449,7 +498,9 @@ function getAcceptString(contentTypes) {
449
498
  function isFileTypeAllowed(file, allowedTypes) {
450
499
  if (!allowedTypes.length)
451
500
  return true;
452
- 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);
453
504
  }
454
505
  function getFileIconClass(contentType) {
455
506
  if (contentType.startsWith('image/'))
@@ -522,16 +573,6 @@ class FileUrlService {
522
573
  appConfig = inject(APP_CONFIG);
523
574
  _cache = signal(new Map(), ...(ngDevMode ? [{ debugName: "_cache" }] : []));
524
575
  cache = this._cache.asReadonly();
525
- /** Get file URL by ID from cache (synchronous) */
526
- getFileUrl(fileId) {
527
- if (!fileId)
528
- return null;
529
- return this._cache().get(fileId)?.url ?? null;
530
- }
531
- /** Get file URL as computed signal */
532
- fileUrlSignal(fileId) {
533
- return computed(() => this.getFileUrl(fileId));
534
- }
535
576
  /** Fetch file URLs from backend and update cache */
536
577
  fetchFileUrls(fileIds, forceRefresh = false) {
537
578
  if (!fileIds.length)
@@ -552,18 +593,6 @@ class FileUrlService {
552
593
  fetchSingleFileUrl(fileId, forceRefresh = false) {
553
594
  return this.fetchFileUrls([fileId], forceRefresh).pipe(map((files) => files[0] ?? null));
554
595
  }
555
- /** Clear entire cache */
556
- clearCache() {
557
- this._cache.set(new Map());
558
- }
559
- /** Remove specific file from cache */
560
- removeFromCache(fileId) {
561
- this._cache.update((cache) => {
562
- const next = new Map(cache);
563
- next.delete(fileId);
564
- return next;
565
- });
566
- }
567
596
  addToCache(files) {
568
597
  this._cache.update((cache) => {
569
598
  const next = new Map(cache);
@@ -670,6 +699,10 @@ class PermissionValidatorService {
670
699
  hasPermission(code) {
671
700
  return hasPermission(code, this._permissions());
672
701
  }
702
+ /** Check if user has ANY of the specified permissions (OR logic) */
703
+ hasAnyPermission(code) {
704
+ return hasAnyPermission(code, this._permissions());
705
+ }
673
706
  isPermissionsLoaded() {
674
707
  return this._isLoaded();
675
708
  }
@@ -681,6 +714,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
681
714
  args: [{ providedIn: 'root' }]
682
715
  }] });
683
716
 
717
+ class TranslateService {
718
+ localizationState = inject(LOCALIZATION_STATE_SERVICE, {
719
+ optional: true,
720
+ });
721
+ registry = inject(FALLBACK_MESSAGES_REGISTRY, {
722
+ optional: false,
723
+ });
724
+ translate(key, params) {
725
+ if (this.localizationState === null) {
726
+ const value = this.registry[key];
727
+ return this.interpolate(value, params);
728
+ }
729
+ const translations = this.localizationState.translations();
730
+ let value = translations[key];
731
+ if (!value) {
732
+ const fallbacks = this.localizationState.fallbackMessages();
733
+ value = fallbacks[key] ?? key;
734
+ }
735
+ return this.interpolate(value, params);
736
+ }
737
+ translateSignal(key, params) {
738
+ return computed(() => {
739
+ return this.translate(key, params);
740
+ });
741
+ }
742
+ interpolate(template, params) {
743
+ if (!params || !template)
744
+ return template;
745
+ return template.replace(/\{\{(\w+)\}\}/g, (match, paramKey) => {
746
+ const value = params[paramKey];
747
+ return value !== undefined ? String(value) : match;
748
+ });
749
+ }
750
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
751
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, providedIn: 'root' });
752
+ }
753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, decorators: [{
754
+ type: Injectable,
755
+ args: [{ providedIn: 'root' }]
756
+ }] });
757
+
684
758
  class EditModeElementChangerDirective {
685
759
  el = inject(ElementRef);
686
760
  ngControl = inject(NgControl, { optional: true });
@@ -940,38 +1014,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
940
1014
  }], 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"] }] } });
941
1015
 
942
1016
  class TranslatePipe {
943
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
944
- fallbackMessages = inject(FALLBACK_MESSAGES_REGISTRY, { optional: true });
1017
+ translateAdapter = inject(TranslateService);
945
1018
  transform(key, params, fallback) {
946
1019
  if (!key)
947
1020
  return fallback ?? '';
948
- if (!this.translateAdapter) {
949
- const result = this.getFromFallback(key, params ?? undefined);
950
- return fallback !== undefined && result === key ? fallback : result;
951
- }
952
- this.translateAdapter.languageCode();
953
1021
  const result = this.translateAdapter.translate(key, params ?? undefined);
954
1022
  return fallback !== undefined && result === key ? fallback : result;
955
1023
  }
956
- getFromFallback(template, params) {
957
- // Try to get from fallback messages registry first
958
- if (this.fallbackMessages) {
959
- const value = this.fallbackMessages[template];
960
- if (value) {
961
- return this.interpolate(value, params);
962
- }
963
- }
964
- // Fall back to interpolating the key itself
965
- return this.interpolate(template, params);
966
- }
967
- interpolate(template, params) {
968
- if (!params)
969
- return template;
970
- return template.replace(/\{\{(\w+)\}\}/g, (match, paramKey) => {
971
- const value = params[paramKey];
972
- return value !== undefined ? String(value) : match;
973
- });
974
- }
975
1024
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
976
1025
  static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate", pure: false });
977
1026
  }
@@ -983,21 +1032,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
983
1032
  }]
984
1033
  }] });
985
1034
 
986
- async function tryLoadLocalizationServices(injector) {
987
- try {
988
- // @ts-ignore - ng-localization is optional and loaded dynamically at runtime
989
- const locModule = await import('@flusys/ng-localization');
990
- const config = injector.get(locModule.LOCALIZATION_CONFIG, null);
991
- if (!config)
992
- return null;
993
- return {
994
- stateService: injector.get(locModule.LocalizationStateService),
995
- apiService: injector.get(locModule.LocalizationApiService),
996
- };
1035
+ /**
1036
+ * HTTP context token to skip global error toast.
1037
+ * Use when component handles error display locally.
1038
+ *
1039
+ * @example
1040
+ * ```typescript
1041
+ * this.http.post(url, data, {
1042
+ * context: new HttpContext().set(SKIP_ERROR_TOAST, true)
1043
+ * });
1044
+ * ```
1045
+ */
1046
+ const SKIP_ERROR_TOAST = new HttpContextToken(() => false);
1047
+ /**
1048
+ * Error Catching Interceptor
1049
+ * Displays error messages for failed HTTP requests using backend response.
1050
+ *
1051
+ * Features:
1052
+ * - Uses backend's error title (error.error.error) as summary
1053
+ * - Uses backend's error message (error.error.message) as detail
1054
+ * - Falls back to sensible defaults based on status code
1055
+ * - Skip with SKIP_ERROR_TOAST context token for local handling
1056
+ *
1057
+ * NOTE: This interceptor only handles error display.
1058
+ * For 401 handling and token refresh, use tokenRefreshInterceptor from @flusys/ng-auth.
1059
+ *
1060
+ * @example
1061
+ * ```typescript
1062
+ * // In app.config.ts
1063
+ * provideHttpClient(
1064
+ * withFetch(),
1065
+ * withInterceptors([
1066
+ * authInterceptor, // from @flusys/ng-auth
1067
+ * tokenRefreshInterceptor, // from @flusys/ng-auth
1068
+ * errorCatchingInterceptor, // from @flusys/ng-core
1069
+ * apiLoaderInterceptor, // from @flusys/ng-core
1070
+ * ])
1071
+ * )
1072
+ * ```
1073
+ */
1074
+ function errorCatchingInterceptor(req, next) {
1075
+ const messageService = inject(MessageService);
1076
+ const platformId = inject(PLATFORM_ID);
1077
+ const translateAdapter = inject(TranslateService, { optional: true });
1078
+ const isBrowser = isPlatformBrowser(platformId);
1079
+ // Skip error display for check-login (expected to fail when not logged in)
1080
+ const isCheckLogin = req.url.includes('/check-login');
1081
+ // Skip if component handles error locally
1082
+ const skipErrorToast = req.context.get(SKIP_ERROR_TOAST);
1083
+ return next(req).pipe(catchError$1((error) => {
1084
+ // Skip 401 errors - handled by tokenRefreshInterceptor in ng-auth
1085
+ // Skip 403 errors - handled by permission denied UI in components
1086
+ // Skip check-login errors - expected behavior
1087
+ // Skip if component handles error locally
1088
+ if (error.status === 401 ||
1089
+ error.status === 403 ||
1090
+ isCheckLogin ||
1091
+ skipErrorToast) {
1092
+ return throwError(() => error);
1093
+ }
1094
+ // Display error message for other errors
1095
+ if (isBrowser) {
1096
+ const { summary, detail } = extractErrorInfo(error, translateAdapter);
1097
+ messageService.add({
1098
+ severity: 'error',
1099
+ summary,
1100
+ detail,
1101
+ });
1102
+ }
1103
+ return throwError(() => error);
1104
+ }));
1105
+ }
1106
+ /**
1107
+ * Extract error summary and detail from HTTP error response.
1108
+ * Uses backend response format: { message, messageKey, messageVariables, error, statusCode }
1109
+ * If messageKey is present and translate adapter is available, translates the message.
1110
+ */
1111
+ function extractErrorInfo(error, translateAdapter) {
1112
+ // Check if we have a messageKey for translation
1113
+ const messageKey = error.error?.messageKey;
1114
+ const messageVariables = error.error?.messageVariables;
1115
+ let detail;
1116
+ if (messageKey && translateAdapter) {
1117
+ // Use translated message if available
1118
+ detail = translateAdapter.translate(messageKey, messageVariables);
1119
+ }
1120
+ else if (typeof error.error?.message === 'string') {
1121
+ detail = error.error.message;
1122
+ }
1123
+ else if (Array.isArray(error.error?.message)) {
1124
+ // NestJS validation errors come as array
1125
+ detail = error.error.message[0];
1126
+ }
1127
+ else if (error.message) {
1128
+ detail = error.message;
1129
+ }
1130
+ else {
1131
+ detail = translateAdapter
1132
+ ? translateAdapter.translate('shared.unexpected.error')
1133
+ : 'shared.unexpected.error';
1134
+ }
1135
+ // Extract summary/title from backend response or use default based on status
1136
+ let summary;
1137
+ if (typeof error.error?.error === 'string') {
1138
+ // Backend sends error type (e.g., "Not Found", "Bad Request", "Validation Error")
1139
+ summary = error.error.error;
1140
+ }
1141
+ else {
1142
+ // Fallback to status-based title
1143
+ summary = getDefaultSummary(error.status, translateAdapter);
1144
+ }
1145
+ return { summary, detail };
1146
+ }
1147
+ /**
1148
+ * Get default error summary based on HTTP status code.
1149
+ */
1150
+ function getDefaultSummary(status, translateAdapter) {
1151
+ const keyMap = {
1152
+ 400: 'shared.error.bad.request',
1153
+ 404: 'shared.error.not.found',
1154
+ 409: 'shared.error.conflict',
1155
+ 422: 'shared.error.validation.error',
1156
+ 500: 'shared.error.server.error',
1157
+ 502: 'shared.error.service.unavailable',
1158
+ 503: 'shared.error.service.unavailable',
1159
+ 504: 'shared.error.service.unavailable',
1160
+ };
1161
+ const key = keyMap[status] || 'shared.error';
1162
+ if (translateAdapter) {
1163
+ return translateAdapter.translate(key);
997
1164
  }
998
- catch {
1165
+ // Fallback to key when no translator available
1166
+ return key;
1167
+ }
1168
+
1169
+ /**
1170
+ * Resolver to load translations for specific modules.
1171
+ * Works with or without @flusys/ng-localization provider.
1172
+ *
1173
+ * When localization provider is enabled:
1174
+ * - Registers fallback messages in state
1175
+ * - Fetches translations from API
1176
+ * - Merges them into state
1177
+ *
1178
+ * When localization provider is not enabled:
1179
+ * - Registers fallback messages in FALLBACK_MESSAGES_REGISTRY
1180
+ * - TranslatePipe uses registry for translations
1181
+ * - No API calls
1182
+ *
1183
+ * @param config - Module config with modules array and optional fallback messages
1184
+ * @returns ResolveFn that loads translations before route activation
1185
+ *
1186
+ * @example
1187
+ * // With fallback messages (recommended)
1188
+ * { resolve: { t: resolveTranslationModule({ modules: ['auth'], fallbackMessages: AUTH_MESSAGES }) } }
1189
+ *
1190
+ * @example
1191
+ * // Without fallback (API translations only, requires localization provider)
1192
+ * { resolve: { t: resolveTranslationModule({ modules: ['auth', 'iam'] }) } }
1193
+ */
1194
+ async function tryLoadLocalizationServices(injector) {
1195
+ const stateService = injector.get(LOCALIZATION_STATE_SERVICE, null);
1196
+ const apiService = injector.get(LOCALIZATION_API_SERVICE, null);
1197
+ if (!stateService || !apiService)
999
1198
  return null;
1000
- }
1199
+ return {
1200
+ stateService,
1201
+ apiService,
1202
+ };
1001
1203
  }
1002
1204
  function resolveTranslationModule(config) {
1003
1205
  return async () => {
@@ -1019,6 +1221,10 @@ function resolveTranslationModule(config) {
1019
1221
  if (registry) {
1020
1222
  Object.assign(registry, fallbackMessages);
1021
1223
  }
1224
+ else if (isDevMode()) {
1225
+ console.warn('resolveTranslationModule: FALLBACK_MESSAGES_REGISTRY not provided. ' +
1226
+ 'Add provideFallbackMessagesRegistry() to your app providers.');
1227
+ }
1022
1228
  }
1023
1229
  }
1024
1230
  if (!locServices)
@@ -1236,19 +1442,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1236
1442
  }]
1237
1443
  }] });
1238
1444
 
1445
+ // ── Separate default filter into a named constant so it's sharable/testable ──
1446
+ const DEFAULT_FILTER = {
1447
+ pagination: { currentPage: 0, pageSize: 10 },
1448
+ filter: {},
1449
+ select: [],
1450
+ sort: {},
1451
+ };
1239
1452
  class ApiResourceService {
1453
+ // ── Injected once via `inject()` ──────────────────────────────────────────
1454
+ http = inject(HttpClient);
1455
+ _config = inject(APP_CONFIG);
1240
1456
  baseUrl;
1241
- http;
1242
1457
  moduleApiName;
1458
+ // ── Public writable signals ───────────────────────────────────────────────
1243
1459
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1244
- filterData = signal({
1245
- pagination: { currentPage: 0, pageSize: 10 },
1246
- filter: {},
1247
- select: [],
1248
- sort: {},
1249
- }, ...(ngDevMode ? [{ debugName: "filterData" }] : []));
1460
+ filterData = signal({ ...DEFAULT_FILTER }, ...(ngDevMode ? [{ debugName: "filterData" }] : []));
1461
+ // ── Private activation gate: avoids an initial fetch before fetchList() ──
1250
1462
  _active = signal(false, ...(ngDevMode ? [{ debugName: "_active" }] : []));
1463
+ // ── Resource (reactive list fetching with abort support) ──────────────────
1251
1464
  listResource;
1465
+ // ── Derived/computed signals (pure reads, no side-effects) ────────────────
1252
1466
  isLoading = computed(() => this.listResource.isLoading(), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1253
1467
  data = computed(() => this.listResource.value()?.data ?? [], ...(ngDevMode ? [{ debugName: "data" }] : []));
1254
1468
  total = computed(() => this.listResource.value()?.meta?.total ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : []));
@@ -1261,52 +1475,64 @@ class ApiResourceService {
1261
1475
  }, ...(ngDevMode ? [{ debugName: "hasMore" }] : []));
1262
1476
  error = computed(() => this.listResource.error(), ...(ngDevMode ? [{ debugName: "error" }] : []));
1263
1477
  status = computed(() => this.listResource.status(), ...(ngDevMode ? [{ debugName: "status" }] : []));
1264
- constructor(moduleApiName, http, serviceName) {
1265
- this.moduleApiName = moduleApiName || serviceName || 'api';
1266
- const config = inject(APP_CONFIG);
1267
- const serviceBaseUrl = serviceName ? getServiceUrl(config, serviceName) : config.apiBaseUrl;
1268
- this.baseUrl = moduleApiName ? `${serviceBaseUrl}/${moduleApiName}` : serviceBaseUrl;
1269
- this.http = http;
1478
+ constructor(moduleApiName, serviceName) {
1479
+ this.moduleApiName = moduleApiName;
1480
+ // ── Resolve base URL once at construction, not per-request ───────────────
1481
+ const serviceBaseUrl = serviceName
1482
+ ? getServiceUrl(this._config, serviceName)
1483
+ : this._config.apiBaseUrl;
1484
+ this.baseUrl = moduleApiName
1485
+ ? `${serviceBaseUrl}/${moduleApiName}`
1486
+ : serviceBaseUrl;
1270
1487
  this.listResource = resource({ ...(ngDevMode ? { debugName: "listResource" } : {}), params: () => {
1488
+ // Short-circuit: skip the loader entirely when not yet activated
1271
1489
  if (!this._active())
1272
1490
  return undefined;
1273
1491
  return { search: this.searchTerm(), filter: this.filterData() };
1274
1492
  },
1275
- loader: ({ params, abortSignal }) => {
1276
- const obs = this._getAllObs(params.filter, params.search);
1277
- return new Promise((resolve, reject) => {
1278
- const sub = obs.subscribe({ next: resolve, error: reject });
1279
- abortSignal.addEventListener('abort', () => sub.unsubscribe());
1493
+ loader: ({ params, abortSignal }) =>
1494
+ // ── Inline promise keeps the closure tight; no temp variable needed ─
1495
+ new Promise((resolve, reject) => {
1496
+ const sub = this._getAllObs(params.filter, params.search).subscribe({
1497
+ next: resolve,
1498
+ error: reject,
1280
1499
  });
1281
- } });
1500
+ abortSignal.addEventListener('abort', () => sub.unsubscribe());
1501
+ }) });
1282
1502
  }
1503
+ // ── Single, memoised header builder – avoids object churn per call ────────
1283
1504
  getHttpOptions(endpoint, params) {
1284
1505
  return {
1285
- headers: new HttpHeaders({ 'x-loader-tag': `${this.moduleApiName}/${endpoint}` }),
1506
+ headers: new HttpHeaders({
1507
+ 'x-loader-tag': `${this.moduleApiName}/${endpoint}`,
1508
+ }),
1286
1509
  ...(params ? { params } : {}),
1287
1510
  };
1288
1511
  }
1512
+ // ── Private observable – shared by the resource loader and the public getAll() ──
1289
1513
  _getAllObs(filter, search = '') {
1290
- let params = new HttpParams();
1291
- if (search)
1292
- params = params.append('q', search);
1514
+ const params = search ? new HttpParams().append('q', search) : undefined;
1293
1515
  return this.http.post(`${this.baseUrl}/get-all`, filter, this.getHttpOptions('get-all', params));
1294
1516
  }
1517
+ // ── Activate guard: all list-management helpers call this first ───────────
1518
+ _activate() {
1519
+ this._active.set(true);
1520
+ }
1295
1521
  // ==========================================================================
1296
- // List Management
1522
+ // List management
1297
1523
  // ==========================================================================
1298
1524
  fetchList(search = '', filter) {
1299
1525
  this.searchTerm.set(search);
1300
1526
  if (filter)
1301
1527
  this.filterData.update((prev) => ({ ...prev, ...filter }));
1302
- this._active.set(true);
1528
+ this._activate();
1303
1529
  }
1304
1530
  setPagination(pagination) {
1305
- this._active.set(true);
1531
+ this._activate();
1306
1532
  this.filterData.update((prev) => ({ ...prev, pagination }));
1307
1533
  }
1308
1534
  nextPage() {
1309
- this._active.set(true);
1535
+ this._activate();
1310
1536
  this.filterData.update((prev) => ({
1311
1537
  ...prev,
1312
1538
  pagination: {
@@ -1316,7 +1542,7 @@ class ApiResourceService {
1316
1542
  }));
1317
1543
  }
1318
1544
  resetPagination() {
1319
- this._active.set(true);
1545
+ this._activate();
1320
1546
  this.filterData.update((prev) => ({
1321
1547
  ...prev,
1322
1548
  pagination: { currentPage: 0, pageSize: prev.pagination?.pageSize ?? 10 },
@@ -1326,131 +1552,107 @@ class ApiResourceService {
1326
1552
  this.listResource.reload();
1327
1553
  }
1328
1554
  // ==========================================================================
1329
- // Promise API (IApiService)
1555
+ // Promise API (IApiService implementation)
1330
1556
  // ==========================================================================
1557
+ // ── DRY helper: all promise methods are one-liners via this ───────────────
1558
+ _post(endpoint, body, params) {
1559
+ return firstValueFrom(this.http.post(`${this.baseUrl}/${endpoint}`, body, this.getHttpOptions(endpoint, params)));
1560
+ }
1331
1561
  insert(dto) {
1332
- return firstValueFrom(this.http.post(`${this.baseUrl}/insert`, dto, this.getHttpOptions('insert')));
1562
+ return this._post('insert', dto);
1333
1563
  }
1334
1564
  insertMany(dtos) {
1335
- return firstValueFrom(this.http.post(`${this.baseUrl}/insert-many`, dtos, this.getHttpOptions('insert-many')));
1565
+ return this._post('insert-many', dtos);
1336
1566
  }
1337
1567
  findById(id, select) {
1338
- return firstValueFrom(this.http.post(`${this.baseUrl}/get/${id}`, { select }, this.getHttpOptions(`get/${id}`)));
1568
+ return this._post(`get/${id}`, { select });
1339
1569
  }
1340
1570
  getAll(filter, search = '') {
1341
1571
  return firstValueFrom(this._getAllObs(filter, search));
1342
1572
  }
1343
1573
  update(dto) {
1344
- return firstValueFrom(this.http.post(`${this.baseUrl}/update`, dto, this.getHttpOptions('update')));
1574
+ return this._post('update', dto);
1345
1575
  }
1346
1576
  updateMany(dtos) {
1347
- return firstValueFrom(this.http.post(`${this.baseUrl}/update-many`, dtos, this.getHttpOptions('update-many')));
1577
+ return this._post('update-many', dtos);
1348
1578
  }
1349
- findByIds(ids, select) {
1350
- return firstValueFrom(this.http.post(`${this.baseUrl}/get-by-ids`, { ids, select }, this.getHttpOptions('get-by-ids')));
1579
+ delete(deleteDto) {
1580
+ return this._post('delete', deleteDto);
1351
1581
  }
1352
- bulkUpsert(toInsert, toUpdate) {
1353
- return firstValueFrom(this.http.post(`${this.baseUrl}/bulk-upsert`, [...toInsert, ...toUpdate], this.getHttpOptions('bulk-upsert'))).then((res) => res.data ?? []);
1582
+ findByIds(ids, select) {
1583
+ return this._post('get-by-ids', { ids, select });
1354
1584
  }
1355
1585
  getByFilter(filter) {
1356
- return firstValueFrom(this.http.post(`${this.baseUrl}/get-by-filter`, filter, this.getHttpOptions('get-by-filter')));
1586
+ return this._post('get-by-filter', filter);
1357
1587
  }
1358
- delete(deleteDto) {
1359
- return firstValueFrom(this.http.post(`${this.baseUrl}/delete`, deleteDto, this.getHttpOptions('delete')));
1588
+ async bulkUpsert(toInsert, toUpdate) {
1589
+ const res = await this._post('bulk-upsert', [
1590
+ ...toInsert,
1591
+ ...toUpdate,
1592
+ ]);
1593
+ return res.data ?? [];
1360
1594
  }
1361
1595
  }
1362
1596
 
1363
- /**
1364
- * Base class for form controls that support ALL Angular form patterns:
1365
- *
1366
- * 1. **Template-driven forms:** `[(value)]="mySignal"` or `[(ngModel)]="myValue"`
1367
- * 2. **Reactive forms:** `[formControl]="ctrl"` or `formControlName="field"`
1368
- * 3. **Signal forms (Angular 21+):** `[formField]="formTree.field"`
1369
- *
1370
- * Implements both `ControlValueAccessor` (reactive forms) and `FormValueControl` (signal forms).
1371
- *
1372
- * @example
1373
- * ```typescript
1374
- * @Component({
1375
- * providers: [provideValueAccessor(MySelectComponent)]
1376
- * })
1377
- * export class MySelectComponent extends BaseFormControl<string | null> {
1378
- * override readonly value = model<string | null>(null);
1379
- *
1380
- * constructor() {
1381
- * super();
1382
- * this.initializeFormControl();
1383
- * }
1384
- * }
1385
- *
1386
- * // Usage:
1387
- * // Template-driven: <my-select [(value)]="selectedId" />
1388
- * // Reactive forms: <my-select [formControl]="myControl" />
1389
- * // Signal forms: <my-select [formField]="formTree.myField" />
1390
- * ```
1391
- */
1392
1597
  class BaseFormControl {
1393
1598
  injector = inject(Injector);
1394
- /**
1395
- * Disabled state model - bound to form control disabled state.
1396
- * Using model() to satisfy FormValueControl interface requirements.
1397
- */
1398
1599
  disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1399
- /**
1400
- * Touched state model - for validation styling.
1401
- * Using model() to satisfy FormValueControl interface requirements.
1402
- */
1403
1600
  touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
1601
+ // ── CVA callbacks ─────────────────────────────────────────────────────────
1602
+ // Initialised to no-ops so subclasses never need null-guards
1404
1603
  onChange = () => { };
1405
1604
  onTouched = () => { };
1406
- isWritingValue = false;
1605
+ // ── Write-guard: prevents echo loop (writeValue → effect → onChange) ──────
1606
+ // Using a counter instead of a boolean handles the (rare) re-entrant case
1607
+ writeDepth = 0;
1407
1608
  /**
1408
- * Initialize the form control synchronization for ControlValueAccessor.
1409
- * Must be called in the subclass constructor after super().
1609
+ * Wire up the ControlValueAccessor ↔ signal bridge.
1610
+ * Call once at the end of the subclass constructor.
1611
+ *
1612
+ * Signal-form consumers ([formField]) don't need this — Angular's
1613
+ * FormField directive binds directly to `value`.
1410
1614
  *
1411
- * Note: For signal forms ([formField]), this is not needed as
1412
- * the FormField directive binds directly to the value model.
1615
+ * @example
1616
+ * constructor() { super(); this.initializeFormControl(); }
1413
1617
  */
1414
1618
  initializeFormControl() {
1415
1619
  runInInjectionContext(this.injector, () => {
1416
1620
  effect(() => {
1417
- const currentValue = this.value();
1418
- if (!this.isWritingValue) {
1419
- untracked(() => this.onChange(currentValue));
1621
+ const next = this.value();
1622
+ // Skip propagation while writeValue() is on the call stack
1623
+ if (this.writeDepth === 0) {
1624
+ untracked(() => this.onChange(next));
1420
1625
  }
1421
1626
  });
1422
1627
  });
1423
1628
  }
1424
- // ============================================
1425
- // ControlValueAccessor Implementation
1426
- // (For reactive forms: formControl, formControlName)
1427
- // ============================================
1428
- /** ControlValueAccessor: Write value from form control to signal */
1629
+ // ==========================================================================
1630
+ // ControlValueAccessor
1631
+ // ==========================================================================
1429
1632
  writeValue(value) {
1430
- this.isWritingValue = true;
1431
- this.value.set(value);
1432
- this.isWritingValue = false;
1633
+ this.writeDepth++;
1634
+ try {
1635
+ this.value.set(value);
1636
+ }
1637
+ finally {
1638
+ // Always decrement — even if set() throws — so the guard never sticks
1639
+ this.writeDepth--;
1640
+ }
1433
1641
  }
1434
- /** ControlValueAccessor: Register change callback */
1435
1642
  registerOnChange(fn) {
1436
1643
  this.onChange = fn;
1437
1644
  }
1438
- /** ControlValueAccessor: Register touched callback */
1439
1645
  registerOnTouched(fn) {
1440
1646
  this.onTouched = fn;
1441
1647
  }
1442
- /** ControlValueAccessor: Set disabled state from form control */
1443
1648
  setDisabledState(isDisabled) {
1444
1649
  this.disabled.set(isDisabled);
1445
1650
  }
1446
- // ============================================
1447
- // FormValueControl Implementation
1448
- // (For signal forms: [formField])
1449
- // ============================================
1450
- // The FormValueControl interface only requires:
1451
- // - value: ModelSignal<T> (already declared as abstract above)
1452
- // The FormField directive automatically binds to the value model.
1453
- /** Mark the control as touched (call on blur or panel close) */
1651
+ // ==========================================================================
1652
+ // FormValueControl (signal forms)
1653
+ // `value: ModelSignal<T>` satisfies the interface — nothing else needed.
1654
+ // ==========================================================================
1655
+ /** Call on blur / panel-close to propagate touched state. */
1454
1656
  markAsTouched() {
1455
1657
  if (!this.touched()) {
1456
1658
  this.touched.set(true);
@@ -1479,7 +1681,7 @@ function provideValueAccessor(component) {
1479
1681
  return {
1480
1682
  provide: NG_VALUE_ACCESSOR,
1481
1683
  useExisting: forwardRef(() => component),
1482
- multi: true
1684
+ multi: true,
1483
1685
  };
1484
1686
  }
1485
1687
 
@@ -1607,7 +1809,7 @@ function checkScrollPagination(event, config) {
1607
1809
 
1608
1810
  class FileUploaderComponent {
1609
1811
  messageService = inject(MessageService);
1610
- translateAdapter = inject(TRANSLATE_ADAPTER, {
1812
+ translateAdapter = inject(TranslateService, {
1611
1813
  optional: true,
1612
1814
  });
1613
1815
  fileProvider = inject(FILE_PROVIDER, { optional: true });
@@ -2069,7 +2271,7 @@ const DEFAULT_PAGE_SIZE$1 = 20;
2069
2271
  const DEFAULT_SELECTOR_PAGE_SIZE = 50;
2070
2272
  class FileSelectorDialogComponent {
2071
2273
  destroyRef = inject(DestroyRef);
2072
- translateAdapter = inject(TRANSLATE_ADAPTER, {
2274
+ translateAdapter = inject(TranslateService, {
2073
2275
  optional: true,
2074
2276
  });
2075
2277
  fileProvider = inject(FILE_PROVIDER, { optional: true });
@@ -2965,7 +3167,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2965
3167
  class LazyMultiSelectComponent extends BaseFormControl {
2966
3168
  document = inject(DOCUMENT$1);
2967
3169
  appRef = inject(ApplicationRef);
2968
- translateAdapter = inject(TRANSLATE_ADAPTER, {
3170
+ translateAdapter = inject(TranslateService, {
2969
3171
  optional: true,
2970
3172
  });
2971
3173
  onDocumentClickBound = this.handleDocumentClick.bind(this);
@@ -3171,7 +3373,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
3171
3373
  */
3172
3374
  class LazySelectComponent extends BaseFormControl {
3173
3375
  destroyRef = inject(DestroyRef);
3174
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
3376
+ translateAdapter = inject(TranslateService, {
3377
+ optional: true,
3378
+ });
3175
3379
  onScrollBound = this.onScroll.bind(this);
3176
3380
  scrollTargetEl = null;
3177
3381
  isDestroyed = false;
@@ -3295,9 +3499,7 @@ class BaseUserSelectComponent {
3295
3499
  destroyRef = inject(DestroyRef);
3296
3500
  injector = inject(Injector);
3297
3501
  userProvider = inject(USER_PROVIDER);
3298
- translateAdapter = inject(TRANSLATE_ADAPTER, {
3299
- optional: true,
3300
- });
3502
+ translateAdapter = inject(TranslateService);
3301
3503
  abortController = null;
3302
3504
  // Inputs
3303
3505
  loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
@@ -3775,7 +3977,7 @@ class BaseFormPage {
3775
3977
  route = inject(ActivatedRoute);
3776
3978
  messageService = inject(MessageService);
3777
3979
  destroyRef = inject(DestroyRef);
3778
- translateAdapter = inject(TRANSLATE_ADAPTER, {
3980
+ translateAdapter = inject(TranslateService, {
3779
3981
  optional: true,
3780
3982
  });
3781
3983
  routeParams = toSignal(this.route.paramMap);
@@ -3911,7 +4113,9 @@ class BaseListPage {
3911
4113
  appConfig = inject(APP_CONFIG);
3912
4114
  confirmationService = inject(ConfirmationService);
3913
4115
  destroyRef = inject(DestroyRef);
3914
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
4116
+ translateAdapter = inject(TranslateService, {
4117
+ optional: true,
4118
+ });
3915
4119
  /** Items list */
3916
4120
  items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
3917
4121
  /** Loading state */
@@ -3961,25 +4165,41 @@ class BaseListPage {
3961
4165
  * Show success toast message
3962
4166
  */
3963
4167
  showSuccess(detail, summary) {
3964
- this.messageService.add({ severity: 'success', summary: summary ?? this.t('shared.success'), detail });
4168
+ this.messageService.add({
4169
+ severity: 'success',
4170
+ summary: summary ?? this.t('shared.success'),
4171
+ detail,
4172
+ });
3965
4173
  }
3966
4174
  /**
3967
4175
  * Show error toast message
3968
4176
  */
3969
4177
  showError(detail, summary) {
3970
- this.messageService.add({ severity: 'error', summary: summary ?? this.t('shared.error'), detail });
4178
+ this.messageService.add({
4179
+ severity: 'error',
4180
+ summary: summary ?? this.t('shared.error'),
4181
+ detail,
4182
+ });
3971
4183
  }
3972
4184
  /**
3973
4185
  * Show info toast message
3974
4186
  */
3975
4187
  showInfo(detail, summary) {
3976
- this.messageService.add({ severity: 'info', summary: summary ?? this.t('shared.info'), detail });
4188
+ this.messageService.add({
4189
+ severity: 'info',
4190
+ summary: summary ?? this.t('shared.info'),
4191
+ detail,
4192
+ });
3977
4193
  }
3978
4194
  /**
3979
4195
  * Show warning toast message
3980
4196
  */
3981
4197
  showWarn(detail, summary) {
3982
- this.messageService.add({ severity: 'warn', summary: summary ?? this.t('shared.warning'), detail });
4198
+ this.messageService.add({
4199
+ severity: 'warn',
4200
+ summary: summary ?? this.t('shared.warning'),
4201
+ detail,
4202
+ });
3983
4203
  }
3984
4204
  /**
3985
4205
  * Translate a key using the adapter, fallback to key if not available.
@@ -4006,7 +4226,9 @@ class BaseListPage {
4006
4226
  .subscribe({
4007
4227
  next: (res) => {
4008
4228
  const r = res;
4009
- this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
4229
+ this.showSuccess(r?.messageKey
4230
+ ? this.t(r.messageKey, r.messageVariables)
4231
+ : (r?.message ?? ''));
4010
4232
  this.loadData();
4011
4233
  },
4012
4234
  });
@@ -4029,7 +4251,9 @@ class BaseListPage {
4029
4251
  accept: async () => {
4030
4252
  const res = await deleteApiCall(idGetter(item));
4031
4253
  const r = res;
4032
- this.showSuccess(r?.messageKey ? this.t(r.messageKey, r.messageVariables) : (r?.message ?? ''));
4254
+ this.showSuccess(r?.messageKey
4255
+ ? this.t(r.messageKey, r.messageVariables)
4256
+ : (r?.message ?? ''));
4033
4257
  await this.loadData();
4034
4258
  },
4035
4259
  });
@@ -4114,5 +4338,5 @@ function getUserTimezone() {
4114
4338
  * Generated bundle index. Do not edit.
4115
4339
  */
4116
4340
 
4117
- 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 };
4341
+ 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 };
4118
4342
  //# sourceMappingURL=flusys-ng-shared.mjs.map