@elite.framework/ng.core 2.0.30 → 2.0.32

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.
@@ -4,15 +4,14 @@ import { throwError, catchError, map as map$1, Subject, BehaviorSubject, combine
4
4
  import * as i1 from '@angular/common/http';
5
5
  import { HttpParams, HttpClient, HttpErrorResponse } from '@angular/common/http';
6
6
  import { CORE_OPTIONS, INCUDE_LOCALIZATION_RESOURCES_TOKEN, localizations$, DISABLE_PROJECT_NAME, OTHERS_GROUP, TENANT_KEY, TENANT_NOT_FOUND_BY_NAME, CHECK_AUTHENTICATION_STATE_FN_KEY, APP_INIT_ERROR_HANDLERS } from '@elite.framework/ng.core/tokens';
7
- import { InternalStore, isFunction, transformStringToArray, isPlainObject, createLocalizer, createLocalizerWithFallback, interpolate, createTreeFromList, BaseTreeNode, createGroupMap, pushValueTo, noop, getPathName, deepMerge, createTokenParser } from '@elite.framework/ng.core/utils';
7
+ import { InternalStore, createLocalizer, createLocalizerWithFallback, interpolate, isFunction, transformStringToArray, isPlainObject, createTreeFromList, BaseTreeNode, createGroupMap, pushValueTo, noop, getPathName, deepMerge, createTokenParser } from '@elite.framework/ng.core/utils';
8
8
  import { map, switchMap, tap, take, filter, mapTo, takeUntil, catchError as catchError$1 } from 'rxjs/operators';
9
9
  import { isPlatformBrowser, DOCUMENT, registerLocaleData } from '@angular/common';
10
- import * as i1$1 from '@ngx-translate/core';
11
10
  import Swal from 'sweetalert2';
11
+ import compare from 'just-compare';
12
12
  import * as i2 from '@angular/router';
13
13
  import { RouterStateSnapshot, TitleStrategy, NavigationStart, NavigationError, NavigationEnd, NavigationCancel, Router, PRIMARY_OUTLET } from '@angular/router';
14
14
  import { DEFAULT_REDIRECT_KEY, NavItem, UserMenu } from '@elite.framework/ng.core/models';
15
- import compare from 'just-compare';
16
15
  import { toSignal } from '@angular/core/rxjs-interop';
17
16
  import { Title } from '@angular/platform-browser';
18
17
  import clone from 'just-clone';
@@ -494,95 +493,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImpor
494
493
  args: [REQUEST]
495
494
  }] }] });
496
495
 
497
- class SwalService {
498
- translate;
499
- defaultClass = {
500
- container: 'swal2-container'
501
- };
502
- constructor(translate) {
503
- this.translate = translate;
504
- }
505
- t(key) {
506
- return this.translate.instant(key);
507
- }
508
- /** رسالة تأكيد */
509
- confirm(title = this.t('Confirm.Title'), text = this.t('Confirm.Text'), confirmButtonText = this.t('Confirm.Yes'), cancelButtonText = this.t('Confirm.Cancel')) {
510
- return Swal.fire({
511
- icon: 'warning',
512
- title,
513
- text,
514
- showCancelButton: true,
515
- confirmButtonText,
516
- cancelButtonText,
517
- customClass: this.defaultClass
518
- });
519
- }
520
- /** رسالة نجاح */
521
- success(title, text, timer = 2000) {
522
- return Swal.fire({
523
- icon: 'success',
524
- title: this.t(title),
525
- text: text ? this.t(text) : undefined,
526
- timer,
527
- showConfirmButton: false,
528
- customClass: this.defaultClass
529
- });
530
- }
531
- /** رسالة خطأ */
532
- error(title, text, footer) {
533
- return Swal.fire({
534
- icon: 'error',
535
- title: this.t(title),
536
- text: text ? this.t(text) : undefined,
537
- footer: footer ? this.t(footer) : undefined,
538
- customClass: this.defaultClass
539
- });
540
- }
541
- /** رسالة معلومات */
542
- info(title, text) {
543
- return Swal.fire({
544
- icon: 'info',
545
- title: this.t(title),
546
- text: text ? this.t(text) : undefined,
547
- customClass: this.defaultClass
548
- });
549
- }
550
- /** أي خيارات مخصصة */
551
- alert(options) {
552
- const translated = {
553
- ...options,
554
- title: options.title ? this.t(options.title) : undefined,
555
- text: options.text ? this.t(options.text) : undefined,
556
- footer: options.footer ? this.t(options.footer) : undefined,
557
- customClass: this.defaultClass
558
- };
559
- return Swal.fire(translated);
560
- }
561
- /** Toast */
562
- toast(title, icon = 'info', position = 'top-end', timer = 3000) {
563
- const Toast = Swal.mixin({
564
- toast: true,
565
- position,
566
- timer,
567
- showConfirmButton: false,
568
- customClass: this.defaultClass
569
- });
570
- Toast.fire({ icon, title: this.t(title) });
571
- }
572
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, deps: [{ token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
573
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, providedIn: 'root' });
574
- }
575
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, decorators: [{
576
- type: Injectable,
577
- args: [{ providedIn: 'root' }]
578
- }], ctorParameters: () => [{ type: i1$1.TranslateService }] });
579
-
580
496
  class ApplicationLocalizationService {
581
497
  restService;
582
498
  apiName = 'framework';
583
499
  get = (input, config) => this.restService.request({
584
500
  method: 'GET',
585
- url: '/api/asp-net-core/framework-application-localizations',
501
+ url: '/asp-net-core/framework-application-localizations',
586
502
  params: { cultureName: input.cultureName, onlyDynamics: input.onlyDynamics },
587
503
  }, { apiName: this.apiName, ...config });
588
504
  constructor(restService) {
@@ -605,7 +521,7 @@ class ApplicationConfigurationService {
605
521
  get = (options, config) => this.restService.request({
606
522
  method: 'GET',
607
523
  // url: '/api/framework/application-configuration',
608
- url: '/api/asp-net-core/framework-application-configurations',
524
+ url: '/asp-net-core/framework-application-configurations',
609
525
  params: { includeLocalizationResources: options.includeLocalizationResources },
610
526
  }, { apiName: this.apiName, ...config });
611
527
  constructor(restService) {
@@ -811,1150 +727,1234 @@ function splitKeys(keys) {
811
727
  return keys;
812
728
  }
813
729
 
814
- class PermissionCheckerService {
730
+ class SessionStateService {
815
731
  configState;
816
- constructor(configState) {
732
+ localStorageService;
733
+ store = new InternalStore({});
734
+ document = inject(DOCUMENT);
735
+ updateLocalStorage = () => {
736
+ this.localStorageService.set('abpSession', JSON.stringify(this.store.state), { expires: new Date(new Date().getTime() + 365 * 86400000), path: '/' });
737
+ };
738
+ constructor(configState, localStorageService) {
817
739
  this.configState = configState;
740
+ this.localStorageService = localStorageService;
741
+ this.init();
742
+ this.setInitialLanguage();
818
743
  }
819
- isPolicyGranted(key, grantedPolicies) {
820
- if (!key)
821
- return true;
822
- const orRegexp = /\|\|/g;
823
- const andRegexp = /&&/g;
824
- // TODO: Allow combination of ANDs & ORs
825
- if (orRegexp.test(key)) {
826
- const keys = key.split('||').filter(Boolean);
827
- if (keys.length < 2)
828
- return false;
829
- return keys.some(k => this.getPolicy(k.trim(), grantedPolicies));
830
- }
831
- else if (andRegexp.test(key)) {
832
- const keys = key.split('&&').filter(Boolean);
833
- if (keys.length < 2)
834
- return false;
835
- return keys.every(k => this.getPolicy(k.trim(), grantedPolicies));
744
+ init() {
745
+ const session = this.localStorageService.get('abpSession');
746
+ if (session) {
747
+ this.store.set(JSON.parse(session));
836
748
  }
837
- return this.getPolicy(key, grantedPolicies);
749
+ this.store.sliceUpdate(state => state).subscribe(this.updateLocalStorage);
838
750
  }
839
- getGrantedPolicy$(key) {
840
- return this.getStream().pipe(map$1(grantedPolicies => this.isPolicyGranted(key, grantedPolicies)));
751
+ setInitialLanguage() {
752
+ const appLanguage = this.getLanguage();
753
+ this.configState
754
+ .getDeep$('localization.currentCulture.cultureName')
755
+ .pipe(filter(cultureName => !!cultureName), take(1))
756
+ .subscribe(lang => {
757
+ if (lang.includes(';')) {
758
+ lang = lang.split(';')[0];
759
+ }
760
+ this.setLanguage(lang);
761
+ });
841
762
  }
842
- isGrantedAsync(permissionName) {
843
- if (!permissionName)
844
- return true;
845
- return this.getGrantedPolicy$(permissionName);
763
+ onLanguageChange$() {
764
+ return this.store.sliceUpdate(state => state.language);
846
765
  }
847
- isGranted(permissionName) {
848
- if (!permissionName)
849
- return true;
850
- return this.getGrantedPolicy(permissionName);
766
+ onTenantChange$() {
767
+ return this.store.sliceUpdate(state => state.tenant);
851
768
  }
852
- getGrantedPolicy(key) {
853
- const policies = this.getSnapshot();
854
- return this.isPolicyGranted(key, policies);
769
+ getLanguage() {
770
+ return this.store.state.language;
855
771
  }
856
- getStream() {
857
- return this.configState.getAll$().pipe(map$1(this.mapToPolicies));
772
+ getLanguage$() {
773
+ return this.store.sliceState(state => state.language);
858
774
  }
859
- getSnapshot() {
860
- return this.mapToPolicies(this.configState.getAll());
775
+ getTenant() {
776
+ return this.store.state.tenant;
861
777
  }
862
- mapToPolicies(applicationConfiguration) {
863
- return applicationConfiguration?.auth?.grantedPolicies || {};
778
+ getTenant$() {
779
+ return this.store.sliceState(state => state.tenant);
864
780
  }
865
- getPolicy(key, grantedPolicies) {
866
- return grantedPolicies[key] || false;
781
+ setTenant(tenant) {
782
+ if (compare(tenant, this.store.state.tenant))
783
+ return;
784
+ this.store.set({ ...this.store.state, tenant });
867
785
  }
868
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, deps: [{ token: ConfigStateService }], target: i0.ɵɵFactoryTarget.Injectable });
869
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, providedIn: 'root' });
870
- }
871
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, decorators: [{
872
- type: Injectable,
873
- args: [{
874
- providedIn: 'root'
875
- }]
876
- }], ctorParameters: () => [{ type: ConfigStateService }] });
877
-
878
- const ngxPermissionsGuard = (route, state) => {
879
- const guard = inject(NgxPermissionsGuard);
880
- if (state instanceof RouterStateSnapshot) {
881
- return guard.hasPermissions(route, state);
786
+ setState(session) {
787
+ if (compare(session, this.store.state))
788
+ return;
789
+ this.store.set({ ...this.store.state, ...session });
882
790
  }
883
- return guard.hasPermissions(route);
884
- };
885
- class NgxPermissionsGuard {
886
- permissions;
887
- router;
888
- constructor(permissions, router) {
889
- this.permissions = permissions;
890
- this.router = router;
791
+ setLanguage(language) {
792
+ const currentLanguage = this.store.state.language;
793
+ if (language !== currentLanguage) {
794
+ this.store.patch({ language });
795
+ }
796
+ const currentAttribute = this.document.documentElement.getAttribute('lang');
797
+ if (language !== currentAttribute) {
798
+ this.document.documentElement.setAttribute('lang', language);
799
+ }
891
800
  }
892
- canActivate(route, state) {
893
- return this.hasPermissions(route, state);
801
+ get application() {
802
+ return this.store.state.application;
894
803
  }
895
- canActivateChild(route, state) {
896
- return this.hasPermissions(route, state);
804
+ get user() {
805
+ return this.store.state.user;
897
806
  }
898
- canLoad(route) {
899
- return this.hasPermissions(route);
807
+ get userId() {
808
+ return this.user ? this.user.id : 0;
900
809
  }
901
- canMatch(route) {
902
- return this.hasPermissions(route);
810
+ get tenant() {
811
+ return this.store.state.tenant;
903
812
  }
904
- async hasPermissions(route, state) {
905
- const data = route.data?.['permissions'];
906
- const only = [];
907
- const except = [];
908
- if (data?.only) {
909
- if (isFunction(data.only)) {
910
- const result = data.only(route, state);
911
- only.push(...transformStringToArray(result));
912
- }
913
- else {
914
- only.push(...transformStringToArray(data.only));
915
- }
813
+ get branch() {
814
+ return this.store.state.branch;
815
+ }
816
+ /* get branchName(): BranchLoginInfoDto {
817
+ return this._branch ? this.branch.Name : '';
818
+
819
+ } */
820
+ get tenancyName() {
821
+ return this.store.state.tenant ? this.tenant?.tenancyName : '';
822
+ }
823
+ get branchId() {
824
+ // return this.branch ? this.branch.id : this.configState.Session?.branchId ? this.configState.Session.branchId:null;
825
+ return this.branch ? this.branch.id : 0;
826
+ }
827
+ get tenantId() {
828
+ return this.tenant ? this.tenant.id : 0;
829
+ }
830
+ getShownLoginName() {
831
+ const userName = this.store.state.user?.userName ?? '';
832
+ if (!this.multiTenancySide) {
833
+ return userName;
916
834
  }
917
- if (data?.except) {
918
- if (isFunction(data.except)) {
919
- const result = data.except(route, state);
920
- except.push(...transformStringToArray(result));
921
- }
922
- else {
923
- except.push(...transformStringToArray(data.except));
924
- }
925
- }
926
- const permissions = {
927
- only,
928
- except,
929
- redirectTo: data?.redirectTo
930
- };
931
- // --- rest of the guard logic ---
932
- // EXCEPT check
933
- if (permissions.except?.length) {
934
- for (const perm of permissions.except) {
935
- if (await this.permissions.isGranted(perm)) {
936
- this.handleRedirect(permissions, perm, route, state);
937
- return false;
938
- }
939
- }
940
- }
941
- // ONLY check
942
- if (permissions.only?.length) {
943
- for (const perm of permissions.only) {
944
- if (await this.permissions.isGrantedAsync(perm)) {
945
- return true;
946
- }
947
- }
948
- this.handleRedirect(permissions, permissions.only[0], route, state);
835
+ return (this.store.state.tenant ? this.store.state.tenant.tenancyName : '.') + '\\' + userName;
836
+ }
837
+ changeTenantIfNeeded(tenantId) {
838
+ if (this.isCurrentTenant(tenantId)) {
949
839
  return false;
950
840
  }
841
+ // this.configState.SetTenantIdCookie(tenantId);
842
+ location.reload();
951
843
  return true;
952
844
  }
953
- handleRedirect(permissions, failedPermission, route, state) {
954
- const redirectTo = permissions.redirectTo;
955
- if (!redirectTo)
956
- return;
957
- // If redirectTo is a function
958
- const resolvedRedirect = isFunction(redirectTo)
959
- ? redirectTo(failedPermission, route, state)
960
- : redirectTo;
961
- if (typeof resolvedRedirect === 'string') {
962
- this.router.navigate([resolvedRedirect]); // wrap string in array
963
- }
964
- else if (Array.isArray(resolvedRedirect)) {
965
- this.router.navigate(resolvedRedirect); // array is fine
966
- }
967
- else if (isPlainObject(resolvedRedirect)) {
968
- // Object: check for failedPermission key first
969
- const target = resolvedRedirect[failedPermission] ?? resolvedRedirect[DEFAULT_REDIRECT_KEY];
970
- if (target) {
971
- if (typeof target === 'string') {
972
- this.router.navigate([target]);
973
- }
974
- else if (Array.isArray(target)) {
975
- this.router.navigate(target);
976
- }
977
- else if (isFunction(target)) {
978
- this.router.navigate([target(failedPermission, route, state)]);
979
- }
980
- }
981
- }
982
- else {
983
- console.warn('Unknown redirect type', resolvedRedirect);
984
- }
985
- }
986
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, deps: [{ token: PermissionCheckerService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Injectable });
987
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, providedIn: 'root' });
988
- }
989
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, decorators: [{
990
- type: Injectable,
991
- args: [{ providedIn: 'root' }]
992
- }], ctorParameters: () => [{ type: PermissionCheckerService }, { type: i2.Router }] });
993
-
994
- // logger.interface.ts
995
- var LogLevel;
996
- (function (LogLevel) {
997
- LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
998
- LogLevel[LogLevel["INFO"] = 1] = "INFO";
999
- LogLevel[LogLevel["WARN"] = 2] = "WARN";
1000
- LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
1001
- LogLevel[LogLevel["OFF"] = 4] = "OFF";
1002
- })(LogLevel || (LogLevel = {}));
1003
-
1004
- // logger.service.ts
1005
- // Remove the separate LoggerConfig interface since we're using EnvironmentConfig
1006
- // and extend EnvironmentConfig to include logger-specific properties
1007
- class LoggerService {
1008
- level = LogLevel.DEBUG;
1009
- enableConsole = true;
1010
- prefix = '[App]';
1011
- timestamp = true;
1012
- environmentService = inject(EnvironmentService);
1013
- environment = this.environmentService?.getEnvironment();
1014
- constructor() {
1015
- // Use environment configuration if provided
1016
- if (this.environment) {
1017
- this.configureFromEnvironment(this.environment);
1018
- }
1019
- // Fallback: In production, default to WARN level
1020
- if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
1021
- this.level = LogLevel.WARN;
1022
- this.enableConsole = false;
1023
- }
1024
- }
1025
- configureFromEnvironment(env) {
1026
- // Map string log levels from environment to LogLevel enum
1027
- this.level = this.mapLogLevel(env.logLevel);
1028
- // Use production flag to determine console logging
1029
- this.enableConsole = !env.production;
1030
- // Set prefix from environment if available
1031
- this.prefix = `[${env['appName'] || 'App'}]`;
1032
- // Enable timestamp in development, disable in production for performance
1033
- this.timestamp = !env.production;
1034
- }
1035
- mapLogLevel(level) {
1036
- switch (level) {
1037
- case 'debug': return LogLevel.DEBUG;
1038
- case 'info': return LogLevel.INFO;
1039
- case 'warn': return LogLevel.WARN;
1040
- case 'error': return LogLevel.ERROR;
1041
- case 'off': return LogLevel.OFF;
1042
- default: return LogLevel.WARN;
845
+ isCurrentTenant(tenantId) {
846
+ let isTenant = tenantId ?? 0 > 0;
847
+ if (!isTenant && !this.tenant) { // this is host
848
+ return true;
1043
849
  }
1044
- }
1045
- debug(message, ...args) {
1046
- if (this.shouldLog(LogLevel.DEBUG)) {
1047
- this.log('DEBUG', message, args, 'color: blue');
850
+ if (!tenantId && this.tenant) {
851
+ return false;
1048
852
  }
1049
- }
1050
- info(message, ...args) {
1051
- if (this.shouldLog(LogLevel.INFO)) {
1052
- this.log('INFO', message, args, 'color: green');
853
+ else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) {
854
+ return false;
1053
855
  }
856
+ return true;
1054
857
  }
1055
- warn(message, ...args) {
1056
- if (this.shouldLog(LogLevel.WARN)) {
1057
- this.log('WARN', message, args, 'color: orange');
1058
- }
858
+ get impersonatorUser() {
859
+ return this.store.state.impersonatorUser;
1059
860
  }
1060
- error(message, ...args) {
1061
- if (this.shouldLog(LogLevel.ERROR)) {
1062
- this.log('ERROR', message, args, 'color: red');
1063
- // Send to external services only for errors in production
1064
- if (this.environment?.production) {
1065
- this.sendToExternalService('ERROR', message, args);
1066
- }
1067
- }
861
+ get impersonatorUserId() {
862
+ return this.impersonatorUser ? this.impersonatorUser.id : 0;
1068
863
  }
1069
- setLogLevel(level) {
1070
- this.level = level;
864
+ get impersonatorTenant() {
865
+ return this.store.state.impersonatorTenant;
1071
866
  }
1072
- shouldLog(level) {
1073
- return level >= this.level && this.enableConsole;
867
+ get impersonatorTenancyName() {
868
+ return this.impersonatorTenant ? this.impersonatorTenant?.tenancyName ?? '' : '';
1074
869
  }
1075
- log(level, message, args, style) {
1076
- if (!this.enableConsole)
1077
- return;
1078
- const timestamp = this.timestamp ? new Date().toISOString() : '';
1079
- const formattedPrefix = this.prefix ? `${this.prefix}` : '';
1080
- const logMessage = `${timestamp} ${formattedPrefix} [${level}] ${message}`;
1081
- if (args.length > 0) {
1082
- console.log(`%c${logMessage}`, style, ...args);
1083
- }
1084
- else {
1085
- console.log(`%c${logMessage}`, style);
1086
- }
870
+ get impersonatorTenantId() {
871
+ return this.impersonatorTenant ? this.impersonatorTenant.id : 0;
1087
872
  }
1088
- sendToExternalService(level, message, args) {
1089
- // Integrate with external logging services from environment
1090
- try {
1091
- const sentryUrl = this.environment?.externalServices?.['sentry'];
1092
- const logRocketUrl = this.environment?.externalServices?.['logRocket'];
1093
- // Example: Send to configured external services
1094
- if (sentryUrl) {
1095
- // this.sendToSentry(level, message, args);
1096
- }
1097
- if (logRocketUrl) {
1098
- // this.sendToLogRocket(level, message, args);
1099
- }
1100
- // Send to backend if apiUrl is available
1101
- if (this.environment?.apis.default.url) {
1102
- // this.http.post(`${this.environment.apiUrl}/logs`, {
1103
- // level, message, args, timestamp: new Date()
1104
- // }).subscribe();
1105
- }
1106
- }
1107
- catch (error) {
1108
- // Don't use this.error here to avoid infinite loops
1109
- console.error('Failed to send log to external service:', error);
1110
- }
873
+ get multiTenancySide() {
874
+ // return this.configState.session?.multiTenancySide;
875
+ return 0;
1111
876
  }
1112
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1113
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, providedIn: 'root' });
877
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, deps: [{ token: ConfigStateService }, { token: SsrCookieService }], target: i0.ɵɵFactoryTarget.Injectable });
878
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, providedIn: 'root' });
1114
879
  }
1115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, decorators: [{
880
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, decorators: [{
1116
881
  type: Injectable,
1117
882
  args: [{
1118
- providedIn: 'root'
883
+ providedIn: 'root',
1119
884
  }]
1120
- }], ctorParameters: () => [] });
885
+ }], ctorParameters: () => [{ type: ConfigStateService }, { type: SsrCookieService }] });
1121
886
 
1122
- class IdParserService {
887
+ class LocalizationService {
888
+ sessionState;
889
+ injector;
890
+ configState;
891
+ latestLang = 'ar';
892
+ _languageChange$ = new Subject();
893
+ uiLocalizations$ = new BehaviorSubject(new Map());
894
+ localizations$ = new BehaviorSubject(new Map());
1123
895
  /**
1124
- * Extract IDs from a model based on comma-separated field names.
1125
- * Example: "id,biId" => [model.id, model.biId]
896
+ * Returns currently selected language
897
+ * Even though this looks like it's redundant to return the same value as `getLanguage()`,
898
+ * it's actually not. This could be invoked any time, and the latestLang could be different from the
899
+ * sessionState.getLanguage() value.
1126
900
  */
1127
- parseIds(model, idField) {
1128
- if (!idField)
1129
- return null;
1130
- const fields = idField.split(',').map(f => f.trim()).filter(Boolean);
1131
- if (fields.length === 1) {
1132
- return model[fields[0]];
1133
- }
1134
- return fields.map(f => model[f]);
901
+ get currentLanguage() {
902
+ return this.latestLang || this.sessionState.getLanguage();
1135
903
  }
1136
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1137
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, providedIn: 'root' });
1138
- }
1139
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, decorators: [{
1140
- type: Injectable,
1141
- args: [{ providedIn: 'root' }]
1142
- }] });
1143
-
1144
- class QueryParser {
1145
- static parse(queryString) {
1146
- const params = {
1147
- filters: [],
1148
- orderBy: [],
1149
- groupBy: [],
1150
- select: [],
1151
- aggregates: []
1152
- };
1153
- if (!queryString)
1154
- return params;
1155
- const queryParts = queryString.split('&');
1156
- for (const part of queryParts) {
1157
- if (part.startsWith('$filter=')) {
1158
- this.parseFilter(part.substring('$filter='.length), params);
1159
- }
1160
- else if (part.startsWith('$orderby=')) {
1161
- this.parseOrderBy(part.substring('$orderby='.length), params);
1162
- }
1163
- else if (part.startsWith('$groupby=')) {
1164
- this.parseGroupBy(part.substring('$groupby='.length), params);
1165
- }
1166
- else if (part.startsWith('$select=')) {
1167
- this.parseSelect(part.substring('$select='.length), params);
1168
- }
1169
- else if (part.startsWith('$aggregate=')) {
1170
- this.parseAggregate(part.substring('$aggregate='.length), params);
1171
- }
1172
- else if (part.startsWith('$top=')) {
1173
- params.top = Number(part.substring('$top='.length));
1174
- }
1175
- else if (part.startsWith('$skip=')) {
1176
- params.skip = Number(part.substring('$skip='.length));
1177
- }
1178
- else if (part.startsWith('$expand=')) {
1179
- params.expand = part.substring('$expand='.length).split(',').map(f => f.trim());
904
+ get currentLang() {
905
+ return this.latestLang || this.sessionState.getLanguage();
906
+ }
907
+ get currentLang$() {
908
+ return this.sessionState.getLanguage$();
909
+ }
910
+ get languageChange$() {
911
+ return this._languageChange$.asObservable();
912
+ }
913
+ constructor(sessionState, injector, otherInstance, configState) {
914
+ this.sessionState = sessionState;
915
+ this.injector = injector;
916
+ this.configState = configState;
917
+ if (otherInstance)
918
+ throw new Error('LocalizationService should have only one instance.');
919
+ this.listenToSetLanguage();
920
+ this.initLocalizationValues();
921
+ this.latestLang = this.sessionState.getLanguage();
922
+ }
923
+ initLocalizationValues() {
924
+ localizations$.subscribe(val => this.addLocalization(val));
925
+ const legacyResources$ = this.configState.getDeep$('localization.values');
926
+ const remoteLocalizations$ = this.configState.getDeep$('localization.resources');
927
+ const currentLanguage$ = this.sessionState.getLanguage$();
928
+ const uiLocalizations$ = combineLatest([currentLanguage$, this.uiLocalizations$]).pipe(map(([currentLang, localizations]) => localizations.get(currentLang)));
929
+ combineLatest([legacyResources$, remoteLocalizations$, uiLocalizations$])
930
+ .pipe(map(([legacy, resource, local]) => {
931
+ if (!resource) {
932
+ return null;
1180
933
  }
1181
- else if (part.startsWith('$count=')) {
1182
- params.includeCount = part.substring('$count='.length).toLowerCase() === 'true';
934
+ const remote = combineLegacyandNewResources(legacy || {}, resource);
935
+ if (remote) {
936
+ if (!local) {
937
+ local = new Map();
938
+ }
939
+ Object.entries(remote).forEach(entry => {
940
+ const resourceName = entry[0];
941
+ const remoteTexts = entry[1];
942
+ let resource = local?.get(resourceName) || {};
943
+ resource = { ...resource, ...remoteTexts };
944
+ local?.set(resourceName, resource);
945
+ });
1183
946
  }
1184
- }
1185
- return params;
947
+ return local;
948
+ }), filter(Boolean))
949
+ .subscribe(val => this.localizations$.next(val));
1186
950
  }
1187
- static toString(params) {
1188
- const parts = [];
1189
- if (params.filters?.length) {
1190
- const filterStr = this.buildFilterString(params.filters);
1191
- parts.push(`$filter=${encodeURIComponent(filterStr)}`);
1192
- }
1193
- if (params.orderBy?.length) {
1194
- const orderByStr = params.orderBy.map(o => `${o.field} ${o.direction}`).join(',');
1195
- parts.push(`$orderby=${encodeURIComponent(orderByStr)}`);
1196
- }
1197
- if (params.groupBy?.length) {
1198
- const groupByStr = this.buildGroupByString(params.groupBy);
1199
- parts.push(`$groupby=${encodeURIComponent(groupByStr)}`);
951
+ addLocalization(localizations) {
952
+ if (!localizations)
953
+ return;
954
+ const localizationMap = this.uiLocalizations$.value;
955
+ localizations.forEach(loc => {
956
+ const cultureMap = localizationMap.get(loc.culture) || new Map();
957
+ loc.resources.forEach(res => {
958
+ let resource = cultureMap.get(res.resourceName) || {};
959
+ resource = { ...resource, ...res.texts };
960
+ cultureMap.set(res.resourceName, resource);
961
+ });
962
+ localizationMap.set(loc.culture, cultureMap);
963
+ });
964
+ this.uiLocalizations$.next(localizationMap);
965
+ }
966
+ listenToSetLanguage() {
967
+ this.sessionState
968
+ .onLanguageChange$()
969
+ .pipe(filter(lang => this.configState.getDeep('localization.currentCulture.cultureName') !== lang), switchMap((lang) => this.configState.refreshLocalization(lang).pipe(map(() => lang))), filter(Boolean), switchMap((lang) => from(this.registerLocale(lang).then(() => lang))))
970
+ .subscribe((lang) => this._languageChange$.next(lang));
971
+ }
972
+ registerLocale(locale) {
973
+ const { registerLocaleFn } = this.injector.get(CORE_OPTIONS);
974
+ return registerLocaleFn(locale).then(module => {
975
+ if (module?.default)
976
+ registerLocaleData(module.default);
977
+ this.latestLang = locale;
978
+ });
979
+ }
980
+ /**
981
+ * Returns an observable localized text with the given interpolation parameters in current language.
982
+ * @param key Localizaton key to replace with localized text
983
+ * @param interpolateParams Values to interpolate
984
+ */
985
+ get(key, ...interpolateParams) {
986
+ return this.configState
987
+ .getAll$()
988
+ .pipe(map(state => this.getLocalization(state, key, ...interpolateParams)));
989
+ }
990
+ getResource(resourceName) {
991
+ return this.localizations$.value.get(resourceName);
992
+ }
993
+ getResource$(resourceName) {
994
+ return this.localizations$.pipe(map(res => res.get(resourceName)));
995
+ }
996
+ /**
997
+ * Returns localized text with the given interpolation parameters in current language.
998
+ * @param key Localization key to replace with localized text
999
+ // const translateService = inject(TranslateService);// from '@ngx-translate/core'; // use it for fallback
1000
+ * @param interpolateParams Values to intepolate.
1001
+ */
1002
+ instant(key, ...interpolateParams) {
1003
+ return this.getLocalization(this.configState.getAll(), key, ...interpolateParams);
1004
+ }
1005
+ localize(resourceName, key, defaultValue) {
1006
+ return this.configState.getOne$('localization').pipe(map(createLocalizer), map((localize) => localize(resourceName, key, defaultValue)));
1007
+ }
1008
+ get defaultResourceName() {
1009
+ return this.configState.getAll().localization ? this.configState.getAll().localization.defaultResourceName : 'FrameworkLocalization';
1010
+ }
1011
+ l(key) {
1012
+ const localization = this.configState.getOne('localization');
1013
+ return createLocalizer(localization)(this.defaultResourceName ?? '', key, key);
1014
+ }
1015
+ localizeSync(resourceName, key, defaultValue) {
1016
+ const localization = this.configState.getOne('localization');
1017
+ return createLocalizer(localization)(resourceName, key, defaultValue);
1018
+ }
1019
+ localizeWithFallback(resourceNames, keys, defaultValue) {
1020
+ return this.configState.getOne$('localization').pipe(map(createLocalizerWithFallback), map((localizeWithFallback) => localizeWithFallback(resourceNames, keys, defaultValue)));
1021
+ }
1022
+ localizeWithFallbackSync(resourceNames, keys, defaultValue) {
1023
+ const localization = this.configState.getOne('localization');
1024
+ return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue);
1025
+ }
1026
+ getLocalization(state, key, ...interpolateParams) {
1027
+ let defaultValue = '';
1028
+ if (!key) {
1029
+ return defaultValue;
1200
1030
  }
1201
- if (params.select?.length) {
1202
- const selectStr = this.buildSelectString(params.select);
1203
- parts.push(`$select=${encodeURIComponent(selectStr)}`);
1031
+ if (typeof key !== 'string') {
1032
+ defaultValue = key.defaultValue;
1033
+ key = key.key;
1204
1034
  }
1205
- if (params.aggregates?.length) {
1206
- const aggregateStr = this.buildAggregateString(params.aggregates);
1207
- parts.push(`$aggregate=${encodeURIComponent(aggregateStr)}`);
1035
+ const keys = key.split('::');
1036
+ const warn = (message) => {
1037
+ if (isDevMode())
1038
+ console.warn(message);
1039
+ };
1040
+ if (keys.length < 2) {
1041
+ warn('The localization source separator (::) not found.');
1042
+ return defaultValue || key;
1208
1043
  }
1209
- if (params.top !== undefined) {
1210
- parts.push(`$top=${params.top}`);
1044
+ if (!state.localization)
1045
+ return defaultValue || keys[1];
1046
+ const sourceName = keys[0] || state.localization.defaultResourceName;
1047
+ const sourceKey = keys[1];
1048
+ if (sourceName === '_') {
1049
+ return defaultValue || sourceKey;
1211
1050
  }
1212
- if (params.skip !== undefined) {
1213
- parts.push(`$skip=${params.skip}`);
1051
+ if (!sourceName) {
1052
+ warn('Localization source name is not specified and the defaultResourceName was not defined!');
1053
+ return defaultValue || sourceKey;
1214
1054
  }
1215
- if (params.expand?.length) {
1216
- parts.push(`$expand=${encodeURIComponent(params.expand.join(','))}`);
1055
+ const source = this.localizations$.value.get(sourceName);
1056
+ if (!source) {
1057
+ warn('Could not find localization source: ' + sourceName);
1058
+ return defaultValue || sourceKey;
1217
1059
  }
1218
- if (params.includeCount !== undefined) {
1219
- parts.push(`$count=${params.includeCount}`);
1060
+ let localization = source[sourceKey];
1061
+ if (typeof localization === 'undefined') {
1062
+ return defaultValue || sourceKey;
1220
1063
  }
1221
- return parts.join('&');
1064
+ interpolateParams = interpolateParams.filter(params => params != null);
1065
+ if (localization)
1066
+ localization = interpolate(localization, interpolateParams);
1067
+ if (typeof localization !== 'string')
1068
+ localization = '';
1069
+ return localization || defaultValue || key;
1222
1070
  }
1223
- static buildFilterString(filters) {
1224
- return filters.map((group, index) => {
1225
- const groupStr = group.conditions
1226
- .map(condition => {
1227
- let val = condition.value;
1228
- if (typeof val === 'string') {
1229
- val = `'${val.replace(/'/g, "''")}'`;
1230
- }
1231
- else if (typeof val === 'boolean') {
1232
- val = val ? 'true' : 'false';
1233
- }
1234
- return `${condition.field} ${condition.operator} ${val}`;
1235
- })
1236
- .join(` ${group.logicalOperator} `);
1237
- // For the first group, no groupLogicalOperator is needed
1238
- if (index === 0) {
1239
- return group.conditions.length > 1 ? `(${groupStr})` : groupStr;
1240
- }
1241
- else {
1242
- const joinOp = group.groupLogicalOperator || 'and';
1243
- return group.conditions.length > 1 ? `${joinOp} (${groupStr})` : `${joinOp} ${groupStr}`;
1244
- }
1245
- }).join(' ');
1071
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, deps: [{ token: SessionStateService }, { token: i0.Injector }, { token: LocalizationService, optional: true, skipSelf: true }, { token: ConfigStateService }], target: i0.ɵɵFactoryTarget.Injectable });
1072
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, providedIn: 'root' });
1073
+ }
1074
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, decorators: [{
1075
+ type: Injectable,
1076
+ args: [{ providedIn: 'root' }]
1077
+ }], ctorParameters: () => [{ type: SessionStateService }, { type: i0.Injector }, { type: LocalizationService, decorators: [{
1078
+ type: Optional
1079
+ }, {
1080
+ type: SkipSelf
1081
+ }] }, { type: ConfigStateService }] });
1082
+ function recursivelyMergeBaseResources(baseResourceName, source) {
1083
+ const item = source[baseResourceName];
1084
+ if (item.baseResources.length === 0) {
1085
+ return item;
1246
1086
  }
1247
- static buildGroupByString(groupBy) {
1248
- return groupBy
1249
- .sort((a, b) => (a.order || 0) - (b.order || 0))
1250
- .map(group => {
1251
- let groupStr = group.propertyName;
1252
- // Add display name if provided
1253
- if (group.displayName) {
1254
- groupStr += ` as ${group.displayName}`;
1255
- }
1256
- // Add metadata for UI
1257
- const metadata = [];
1258
- if (group.showTotal !== undefined) {
1259
- metadata.push(`showTotal:${group.showTotal}`);
1260
- }
1261
- if (group.totalBackground) {
1262
- metadata.push(`totalBg:${group.totalBackground}`);
1263
- }
1264
- if (group.groupHeaderBackground) {
1265
- metadata.push(`headerBg:${group.groupHeaderBackground}`);
1266
- }
1267
- if (metadata.length > 0) {
1268
- groupStr += `(${metadata.join(';')})`;
1269
- }
1270
- return groupStr;
1271
- })
1272
- .join(',');
1087
+ return item.baseResources.reduce((acc, baseResource) => {
1088
+ const baseItem = recursivelyMergeBaseResources(baseResource, source);
1089
+ const texts = { ...baseItem.texts, ...item.texts };
1090
+ return { ...acc, texts };
1091
+ }, item);
1092
+ }
1093
+ function mergeResourcesWithBaseResource(resource) {
1094
+ const entities = Object.keys(resource).map(key => {
1095
+ const newValue = recursivelyMergeBaseResources(key, resource);
1096
+ return [key, newValue];
1097
+ });
1098
+ return entities.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
1099
+ }
1100
+ function combineLegacyandNewResources(legacy, resource) {
1101
+ const mergedResource = mergeResourcesWithBaseResource(resource);
1102
+ return Object.entries(mergedResource).reduce((acc, [key, value]) => {
1103
+ return { ...acc, [key]: value.texts };
1104
+ }, legacy);
1105
+ }
1106
+
1107
+ class SwalService {
1108
+ translate;
1109
+ defaultClass = {
1110
+ container: 'swal2-container'
1111
+ };
1112
+ constructor(translate) {
1113
+ this.translate = translate;
1273
1114
  }
1274
- static buildSelectString(select) {
1275
- return select
1276
- .sort((a, b) => (a.order || 0) - (b.order || 0))
1277
- .map(item => {
1278
- let selectStr = item.propertyName;
1279
- // Add display name if provided
1280
- if (item.displayName) {
1281
- selectStr += ` as ${item.displayName}`;
1282
- }
1283
- // Add aggregate function if provided
1284
- if (item.aggregateFunction) {
1285
- selectStr += ` with ${item.aggregateFunction}`;
1286
- }
1287
- // Add visibility metadata
1288
- const metadata = [];
1289
- if (item.isVisible !== undefined) {
1290
- metadata.push(`visible:${item.isVisible}`);
1291
- }
1292
- if (metadata.length > 0) {
1293
- selectStr += `(${metadata.join(';')})`;
1294
- }
1295
- return selectStr;
1296
- })
1297
- .join(',');
1115
+ t(key) {
1116
+ return this.translate.instant(key);
1298
1117
  }
1299
- static buildAggregateString(aggregates) {
1300
- return aggregates
1301
- .map(agg => {
1302
- let aggStr = `${agg.aggregateFunction}(${agg.propertyName})`;
1303
- if (agg.alias) {
1304
- aggStr += ` as ${agg.alias}`;
1305
- }
1306
- return aggStr;
1307
- })
1308
- .join(',');
1118
+ /** رسالة تأكيد */
1119
+ confirm(title = this.t('Confirm.Title'), text = this.t('Confirm.Text'), confirmButtonText = this.t('Confirm.Yes'), cancelButtonText = this.t('Confirm.Cancel')) {
1120
+ return Swal.fire({
1121
+ icon: 'warning',
1122
+ title,
1123
+ text,
1124
+ showCancelButton: true,
1125
+ confirmButtonText,
1126
+ cancelButtonText,
1127
+ customClass: this.defaultClass
1128
+ });
1309
1129
  }
1310
- static parseFilter(filterString, params) {
1311
- const decodedString = decodeURIComponent(filterString);
1312
- const groups = [];
1313
- // Split by top-level AND/OR operators
1314
- const topLevelPattern = /(?:^|\s)(and|or)\s+(?=(?:[^()]*\([^()]*\))*[^()]*$)/gi;
1315
- const segments = [];
1316
- let lastIndex = 0;
1317
- let match;
1318
- while ((match = topLevelPattern.exec(decodedString)) !== null) {
1319
- const operator = match[1];
1320
- const segment = decodedString.substring(lastIndex, match.index).trim();
1321
- if (segment) {
1322
- segments.push({ condition: segment, operator });
1323
- }
1324
- lastIndex = match.index + match[0].length;
1325
- }
1326
- // Add the last segment
1327
- const lastSegment = decodedString.substring(lastIndex).trim();
1328
- if (lastSegment) {
1329
- segments.push({ condition: lastSegment, operator: undefined });
1330
- }
1331
- // Parse each segment
1332
- segments.forEach((segment, index) => {
1333
- let conditions = [];
1334
- // Check if it's a group with parentheses
1335
- if (segment.condition.startsWith('(') && segment.condition.endsWith(')')) {
1336
- const innerCondition = segment.condition.slice(1, -1).trim();
1337
- conditions = this.parseConditions(innerCondition);
1338
- }
1339
- else {
1340
- // Individual condition
1341
- conditions = this.parseConditions(segment.condition);
1342
- }
1343
- if (conditions.length > 0) {
1344
- const group = {
1345
- logicalOperator: 'and',
1346
- conditions: conditions
1347
- };
1348
- // For groups after the first, set the groupLogicalOperator
1349
- if (index > 0) {
1350
- group.groupLogicalOperator = segment.operator?.toLowerCase() || 'and';
1351
- }
1352
- // Detect the logical operator used within the group
1353
- if (conditions.length === 1 && segment.condition.includes(' or ')) {
1354
- group.logicalOperator = 'or';
1355
- }
1356
- else if (conditions.length > 1) {
1357
- group.logicalOperator = segment.condition.toLowerCase().includes(' or ') ? 'or' : 'and';
1358
- }
1359
- groups.push(group);
1360
- }
1130
+ /** رسالة نجاح */
1131
+ success(title, text, timer = 2000) {
1132
+ return Swal.fire({
1133
+ icon: 'success',
1134
+ title: this.t(title),
1135
+ text: text ? this.t(text) : undefined,
1136
+ timer,
1137
+ showConfirmButton: false,
1138
+ customClass: this.defaultClass
1361
1139
  });
1362
- params.filters = groups;
1363
1140
  }
1364
- static parseConditions(conditionString) {
1365
- const conditions = [];
1366
- // Enhanced pattern to handle more operators and complex values
1367
- const pattern = /(\w+(?:\.\w+)*)\s+(eq|ne|gt|ge|lt|le|contains|startswith|endswith|in|notin)\s+('[^']*'|\[[^\]]*\]|\d+|true|false|null)/gi;
1368
- let match;
1369
- while ((match = pattern.exec(conditionString)) !== null) {
1370
- const [, field, operator, rawValue] = match;
1371
- let value = rawValue.trim();
1372
- if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
1373
- value = rawValue.slice(1, -1).replace(/''/g, "'");
1374
- }
1375
- else if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
1376
- // Handle array values for IN/NOT IN operators
1377
- value = rawValue.slice(1, -1).split(',').map(v => {
1378
- const trimmed = v.trim().replace(/^'|'$/g, '');
1379
- return isNaN(Number(trimmed)) ? trimmed : Number(trimmed);
1380
- });
1381
- }
1382
- else if (!isNaN(Number(rawValue))) {
1383
- value = Number(rawValue);
1384
- }
1385
- else if (rawValue.toLowerCase() === 'true') {
1386
- value = true;
1387
- }
1388
- else if (rawValue.toLowerCase() === 'false') {
1389
- value = false;
1390
- }
1391
- else if (rawValue.toLowerCase() === 'null') {
1392
- value = null;
1393
- }
1394
- conditions.push({
1395
- field: field.trim(),
1396
- operator: operator.trim().toLowerCase(),
1397
- value
1398
- });
1399
- }
1400
- return conditions;
1141
+ /** رسالة خطأ */
1142
+ error(title, text, footer) {
1143
+ return Swal.fire({
1144
+ icon: 'error',
1145
+ title: this.t(title),
1146
+ text: text ? this.t(text) : undefined,
1147
+ footer: footer ? this.t(footer) : undefined,
1148
+ customClass: this.defaultClass
1149
+ });
1401
1150
  }
1402
- static parseOrderBy(orderByString, params) {
1403
- const decodedString = decodeURIComponent(orderByString);
1404
- const clauses = decodedString.split(',');
1405
- for (const clause of clauses) {
1406
- const parts = clause.trim().split(/\s+/);
1407
- const field = parts[0];
1408
- const direction = parts[1]?.toLowerCase() || 'asc';
1409
- params.orderBy?.push({
1410
- field,
1411
- direction
1412
- });
1151
+ /** رسالة معلومات */
1152
+ info(title, text) {
1153
+ return Swal.fire({
1154
+ icon: 'info',
1155
+ title: this.t(title),
1156
+ text: text ? this.t(text) : undefined,
1157
+ customClass: this.defaultClass
1158
+ });
1159
+ }
1160
+ /** أي خيارات مخصصة */
1161
+ alert(options) {
1162
+ const translated = {
1163
+ ...options,
1164
+ title: options.title ? this.t(options.title) : undefined,
1165
+ text: options.text ? this.t(options.text) : undefined,
1166
+ footer: options.footer ? this.t(options.footer) : undefined,
1167
+ customClass: this.defaultClass
1168
+ };
1169
+ return Swal.fire(translated);
1170
+ }
1171
+ /** Toast */
1172
+ toast(title, icon = 'info', position = 'top-end', timer = 3000) {
1173
+ const Toast = Swal.mixin({
1174
+ toast: true,
1175
+ position,
1176
+ timer,
1177
+ showConfirmButton: false,
1178
+ customClass: this.defaultClass
1179
+ });
1180
+ Toast.fire({ icon, title: this.t(title) });
1181
+ }
1182
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, deps: [{ token: LocalizationService }], target: i0.ɵɵFactoryTarget.Injectable });
1183
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, providedIn: 'root' });
1184
+ }
1185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SwalService, decorators: [{
1186
+ type: Injectable,
1187
+ args: [{ providedIn: 'root' }]
1188
+ }], ctorParameters: () => [{ type: LocalizationService }] });
1189
+
1190
+ class PermissionCheckerService {
1191
+ configState;
1192
+ constructor(configState) {
1193
+ this.configState = configState;
1194
+ }
1195
+ isPolicyGranted(key, grantedPolicies) {
1196
+ if (!key)
1197
+ return true;
1198
+ const orRegexp = /\|\|/g;
1199
+ const andRegexp = /&&/g;
1200
+ // TODO: Allow combination of ANDs & ORs
1201
+ if (orRegexp.test(key)) {
1202
+ const keys = key.split('||').filter(Boolean);
1203
+ if (keys.length < 2)
1204
+ return false;
1205
+ return keys.some(k => this.getPolicy(k.trim(), grantedPolicies));
1206
+ }
1207
+ else if (andRegexp.test(key)) {
1208
+ const keys = key.split('&&').filter(Boolean);
1209
+ if (keys.length < 2)
1210
+ return false;
1211
+ return keys.every(k => this.getPolicy(k.trim(), grantedPolicies));
1413
1212
  }
1213
+ return this.getPolicy(key, grantedPolicies);
1414
1214
  }
1415
- static parseGroupBy(groupByString, params) {
1416
- const decodedString = decodeURIComponent(groupByString);
1417
- const groups = decodedString.split(',');
1418
- params.groupBy = groups.map((groupStr, index) => {
1419
- const group = {
1420
- propertyName: '',
1421
- order: index
1422
- };
1423
- // Check for metadata in parentheses
1424
- const metadataMatch = groupStr.match(/^(.*?)\((.*)\)$/);
1425
- let baseStr = groupStr;
1426
- let metadataStr = '';
1427
- if (metadataMatch) {
1428
- baseStr = metadataMatch[1];
1429
- metadataStr = metadataMatch[2];
1430
- }
1431
- // Check for display name alias
1432
- const aliasMatch = baseStr.match(/^(.*?)\s+as\s+(.*)$/);
1433
- if (aliasMatch) {
1434
- group.propertyName = aliasMatch[1].trim();
1435
- group.displayName = aliasMatch[2].trim();
1215
+ getGrantedPolicy$(key) {
1216
+ return this.getStream().pipe(map$1(grantedPolicies => this.isPolicyGranted(key, grantedPolicies)));
1217
+ }
1218
+ isGrantedAsync(permissionName) {
1219
+ if (!permissionName)
1220
+ return true;
1221
+ return this.getGrantedPolicy$(permissionName);
1222
+ }
1223
+ isGranted(permissionName) {
1224
+ if (!permissionName)
1225
+ return true;
1226
+ return this.getGrantedPolicy(permissionName);
1227
+ }
1228
+ getGrantedPolicy(key) {
1229
+ const policies = this.getSnapshot();
1230
+ return this.isPolicyGranted(key, policies);
1231
+ }
1232
+ getStream() {
1233
+ return this.configState.getAll$().pipe(map$1(this.mapToPolicies));
1234
+ }
1235
+ getSnapshot() {
1236
+ return this.mapToPolicies(this.configState.getAll());
1237
+ }
1238
+ mapToPolicies(applicationConfiguration) {
1239
+ return applicationConfiguration?.auth?.grantedPolicies || {};
1240
+ }
1241
+ getPolicy(key, grantedPolicies) {
1242
+ return grantedPolicies[key] || false;
1243
+ }
1244
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, deps: [{ token: ConfigStateService }], target: i0.ɵɵFactoryTarget.Injectable });
1245
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, providedIn: 'root' });
1246
+ }
1247
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: PermissionCheckerService, decorators: [{
1248
+ type: Injectable,
1249
+ args: [{
1250
+ providedIn: 'root'
1251
+ }]
1252
+ }], ctorParameters: () => [{ type: ConfigStateService }] });
1253
+
1254
+ const ngxPermissionsGuard = (route, state) => {
1255
+ const guard = inject(NgxPermissionsGuard);
1256
+ if (state instanceof RouterStateSnapshot) {
1257
+ return guard.hasPermissions(route, state);
1258
+ }
1259
+ return guard.hasPermissions(route);
1260
+ };
1261
+ class NgxPermissionsGuard {
1262
+ permissions;
1263
+ router;
1264
+ constructor(permissions, router) {
1265
+ this.permissions = permissions;
1266
+ this.router = router;
1267
+ }
1268
+ canActivate(route, state) {
1269
+ return this.hasPermissions(route, state);
1270
+ }
1271
+ canActivateChild(route, state) {
1272
+ return this.hasPermissions(route, state);
1273
+ }
1274
+ canLoad(route) {
1275
+ return this.hasPermissions(route);
1276
+ }
1277
+ canMatch(route) {
1278
+ return this.hasPermissions(route);
1279
+ }
1280
+ async hasPermissions(route, state) {
1281
+ const data = route.data?.['permissions'];
1282
+ const only = [];
1283
+ const except = [];
1284
+ if (data?.only) {
1285
+ if (isFunction(data.only)) {
1286
+ const result = data.only(route, state);
1287
+ only.push(...transformStringToArray(result));
1436
1288
  }
1437
1289
  else {
1438
- group.propertyName = baseStr.trim();
1290
+ only.push(...transformStringToArray(data.only));
1439
1291
  }
1440
- // Parse metadata
1441
- if (metadataStr) {
1442
- const metadataParts = metadataStr.split(';');
1443
- metadataParts.forEach(part => {
1444
- const [key, value] = part.split(':');
1445
- switch (key) {
1446
- case 'showTotal':
1447
- group.showTotal = value.toLowerCase() === 'true';
1448
- break;
1449
- case 'totalBg':
1450
- group.totalBackground = value;
1451
- break;
1452
- case 'headerBg':
1453
- group.groupHeaderBackground = value;
1454
- break;
1455
- }
1456
- });
1292
+ }
1293
+ if (data?.except) {
1294
+ if (isFunction(data.except)) {
1295
+ const result = data.except(route, state);
1296
+ except.push(...transformStringToArray(result));
1457
1297
  }
1458
- return group;
1459
- });
1460
- }
1461
- static parseSelect(selectString, params) {
1462
- const decodedString = decodeURIComponent(selectString);
1463
- const items = decodedString.split(',');
1464
- params.select = items.map((itemStr, index) => {
1465
- const item = {
1466
- propertyName: '',
1467
- isVisible: true,
1468
- order: index
1469
- };
1470
- // Check for metadata in parentheses
1471
- const metadataMatch = itemStr.match(/^(.*?)\((.*)\)$/);
1472
- let baseStr = itemStr;
1473
- let metadataStr = '';
1474
- if (metadataMatch) {
1475
- baseStr = metadataMatch[1];
1476
- metadataStr = metadataMatch[2];
1298
+ else {
1299
+ except.push(...transformStringToArray(data.except));
1477
1300
  }
1478
- // Check for display name alias and aggregate function
1479
- const aliasMatch = baseStr.match(/^(.*?)(?:\s+as\s+(.*?))?(?:\s+with\s+(\w+))?$/);
1480
- if (aliasMatch) {
1481
- item.propertyName = aliasMatch[1].trim();
1482
- if (aliasMatch[2]) {
1483
- item.displayName = aliasMatch[2].trim();
1484
- }
1485
- if (aliasMatch[3]) {
1486
- item.aggregateFunction = aliasMatch[3].trim();
1301
+ }
1302
+ const permissions = {
1303
+ only,
1304
+ except,
1305
+ redirectTo: data?.redirectTo
1306
+ };
1307
+ // --- rest of the guard logic ---
1308
+ // EXCEPT check
1309
+ if (permissions.except?.length) {
1310
+ for (const perm of permissions.except) {
1311
+ if (await this.permissions.isGranted(perm)) {
1312
+ this.handleRedirect(permissions, perm, route, state);
1313
+ return false;
1487
1314
  }
1488
1315
  }
1489
- else {
1490
- item.propertyName = baseStr.trim();
1491
- }
1492
- // Parse metadata
1493
- if (metadataStr) {
1494
- const metadataParts = metadataStr.split(';');
1495
- metadataParts.forEach(part => {
1496
- const [key, value] = part.split(':');
1497
- if (key === 'visible') {
1498
- item.isVisible = value.toLowerCase() === 'true';
1499
- }
1500
- });
1316
+ }
1317
+ // ONLY check
1318
+ if (permissions.only?.length) {
1319
+ for (const perm of permissions.only) {
1320
+ if (await this.permissions.isGrantedAsync(perm)) {
1321
+ return true;
1322
+ }
1501
1323
  }
1502
- return item;
1503
- });
1324
+ this.handleRedirect(permissions, permissions.only[0], route, state);
1325
+ return false;
1326
+ }
1327
+ return true;
1504
1328
  }
1505
- static parseAggregate(aggregateString, params) {
1506
- const decodedString = decodeURIComponent(aggregateString);
1507
- const aggregates = decodedString.split(',');
1508
- params.aggregates = aggregates.map(aggStr => {
1509
- const agg = {
1510
- propertyName: '',
1511
- aggregateFunction: ''
1512
- };
1513
- // Match pattern: function(property) as alias
1514
- const match = aggStr.match(/^(\w+)\((\w+)\)(?:\s+as\s+(\w+))?$/);
1515
- if (match) {
1516
- agg.aggregateFunction = match[1];
1517
- agg.propertyName = match[2];
1518
- if (match[3]) {
1519
- agg.alias = match[3];
1329
+ handleRedirect(permissions, failedPermission, route, state) {
1330
+ const redirectTo = permissions.redirectTo;
1331
+ if (!redirectTo)
1332
+ return;
1333
+ // If redirectTo is a function
1334
+ const resolvedRedirect = isFunction(redirectTo)
1335
+ ? redirectTo(failedPermission, route, state)
1336
+ : redirectTo;
1337
+ if (typeof resolvedRedirect === 'string') {
1338
+ this.router.navigate([resolvedRedirect]); // wrap string in array
1339
+ }
1340
+ else if (Array.isArray(resolvedRedirect)) {
1341
+ this.router.navigate(resolvedRedirect); // array is fine
1342
+ }
1343
+ else if (isPlainObject(resolvedRedirect)) {
1344
+ // Object: check for failedPermission key first
1345
+ const target = resolvedRedirect[failedPermission] ?? resolvedRedirect[DEFAULT_REDIRECT_KEY];
1346
+ if (target) {
1347
+ if (typeof target === 'string') {
1348
+ this.router.navigate([target]);
1349
+ }
1350
+ else if (Array.isArray(target)) {
1351
+ this.router.navigate(target);
1352
+ }
1353
+ else if (isFunction(target)) {
1354
+ this.router.navigate([target(failedPermission, route, state)]);
1520
1355
  }
1521
1356
  }
1522
- return agg;
1523
- });
1524
- }
1525
- // Helper method to convert QueryParameters to ODataQueryParameters (C# equivalent)
1526
- static toODataQueryParameters(params) {
1527
- return {
1528
- Filters: params.filters.map(group => ({
1529
- LogicalOperator: group.logicalOperator,
1530
- GroupLogicalOperator: group.groupLogicalOperator,
1531
- Conditions: group.conditions.map(cond => ({
1532
- Field: cond.field,
1533
- Operator: cond.operator,
1534
- Value: cond.value
1535
- }))
1536
- })),
1537
- OrderBy: params.orderBy?.map(order => ({
1538
- Field: order.field,
1539
- Direction: order.direction
1540
- })) || [],
1541
- GroupBy: params.groupBy?.map(group => ({
1542
- PropertyName: group.propertyName,
1543
- DisplayName: group.displayName,
1544
- ShowTotal: group.showTotal ?? true,
1545
- TotalBackground: group.totalBackground,
1546
- GroupHeaderBackground: group.groupHeaderBackground,
1547
- Order: group.order
1548
- })) || [],
1549
- Select: params.select?.map(sel => ({
1550
- PropertyName: sel.propertyName,
1551
- DisplayName: sel.displayName,
1552
- AggregateFunction: sel.aggregateFunction,
1553
- IsVisible: sel.isVisible ?? true,
1554
- Order: sel.order
1555
- })) || [],
1556
- Aggregates: params.aggregates?.map(agg => ({
1557
- PropertyName: agg.propertyName,
1558
- AggregateFunction: agg.aggregateFunction,
1559
- Alias: agg.alias
1560
- })) || [],
1561
- Top: params.top,
1562
- Skip: params.skip,
1563
- Expand: params.expand,
1564
- IncludeCount: params.includeCount
1565
- };
1566
- }
1567
- // Helper method to create query parameters from UI model
1568
- static fromUIModel(model) {
1569
- const params = {
1570
- filters: model.advancedFilters || [],
1571
- orderBy: model.sorting || [],
1572
- groupBy: model.grouping || [],
1573
- select: model.columns || [],
1574
- aggregates: model.aggregates || [],
1575
- top: model.pagination?.top,
1576
- skip: model.pagination?.skip,
1577
- expand: model.expand,
1578
- includeCount: model.pagination?.includeCount
1579
- };
1580
- return params;
1357
+ }
1358
+ else {
1359
+ console.warn('Unknown redirect type', resolvedRedirect);
1360
+ }
1581
1361
  }
1362
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, deps: [{ token: PermissionCheckerService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Injectable });
1363
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, providedIn: 'root' });
1582
1364
  }
1365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: NgxPermissionsGuard, decorators: [{
1366
+ type: Injectable,
1367
+ args: [{ providedIn: 'root' }]
1368
+ }], ctorParameters: () => [{ type: PermissionCheckerService }, { type: i2.Router }] });
1583
1369
 
1584
- class SessionStateService {
1585
- configState;
1586
- localStorageService;
1587
- store = new InternalStore({});
1588
- document = inject(DOCUMENT);
1589
- updateLocalStorage = () => {
1590
- this.localStorageService.set('abpSession', JSON.stringify(this.store.state), { expires: new Date(new Date().getTime() + 365 * 86400000), path: '/' });
1591
- };
1592
- constructor(configState, localStorageService) {
1593
- this.configState = configState;
1594
- this.localStorageService = localStorageService;
1595
- this.init();
1596
- this.setInitialLanguage();
1597
- }
1598
- init() {
1599
- const session = this.localStorageService.get('abpSession');
1600
- if (session) {
1601
- this.store.set(JSON.parse(session));
1370
+ // logger.interface.ts
1371
+ var LogLevel;
1372
+ (function (LogLevel) {
1373
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
1374
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
1375
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
1376
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
1377
+ LogLevel[LogLevel["OFF"] = 4] = "OFF";
1378
+ })(LogLevel || (LogLevel = {}));
1379
+
1380
+ // logger.service.ts
1381
+ // Remove the separate LoggerConfig interface since we're using EnvironmentConfig
1382
+ // and extend EnvironmentConfig to include logger-specific properties
1383
+ class LoggerService {
1384
+ level = LogLevel.DEBUG;
1385
+ enableConsole = true;
1386
+ prefix = '[App]';
1387
+ timestamp = true;
1388
+ environmentService = inject(EnvironmentService);
1389
+ environment = this.environmentService?.getEnvironment();
1390
+ constructor() {
1391
+ // Use environment configuration if provided
1392
+ if (this.environment) {
1393
+ this.configureFromEnvironment(this.environment);
1394
+ }
1395
+ // Fallback: In production, default to WARN level
1396
+ if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
1397
+ this.level = LogLevel.WARN;
1398
+ this.enableConsole = false;
1602
1399
  }
1603
- this.store.sliceUpdate(state => state).subscribe(this.updateLocalStorage);
1604
- }
1605
- setInitialLanguage() {
1606
- const appLanguage = this.getLanguage();
1607
- this.configState
1608
- .getDeep$('localization.currentCulture.cultureName')
1609
- .pipe(filter(cultureName => !!cultureName), take(1))
1610
- .subscribe(lang => {
1611
- if (lang.includes(';')) {
1612
- lang = lang.split(';')[0];
1613
- }
1614
- this.setLanguage(lang);
1615
- });
1616
1400
  }
1617
- onLanguageChange$() {
1618
- return this.store.sliceUpdate(state => state.language);
1401
+ configureFromEnvironment(env) {
1402
+ // Map string log levels from environment to LogLevel enum
1403
+ this.level = this.mapLogLevel(env.logLevel);
1404
+ // Use production flag to determine console logging
1405
+ this.enableConsole = !env.production;
1406
+ // Set prefix from environment if available
1407
+ this.prefix = `[${env['appName'] || 'App'}]`;
1408
+ // Enable timestamp in development, disable in production for performance
1409
+ this.timestamp = !env.production;
1619
1410
  }
1620
- onTenantChange$() {
1621
- return this.store.sliceUpdate(state => state.tenant);
1411
+ mapLogLevel(level) {
1412
+ switch (level) {
1413
+ case 'debug': return LogLevel.DEBUG;
1414
+ case 'info': return LogLevel.INFO;
1415
+ case 'warn': return LogLevel.WARN;
1416
+ case 'error': return LogLevel.ERROR;
1417
+ case 'off': return LogLevel.OFF;
1418
+ default: return LogLevel.WARN;
1419
+ }
1622
1420
  }
1623
- getLanguage() {
1624
- return this.store.state.language;
1421
+ debug(message, ...args) {
1422
+ if (this.shouldLog(LogLevel.DEBUG)) {
1423
+ this.log('DEBUG', message, args, 'color: blue');
1424
+ }
1625
1425
  }
1626
- getLanguage$() {
1627
- return this.store.sliceState(state => state.language);
1426
+ info(message, ...args) {
1427
+ if (this.shouldLog(LogLevel.INFO)) {
1428
+ this.log('INFO', message, args, 'color: green');
1429
+ }
1628
1430
  }
1629
- getTenant() {
1630
- return this.store.state.tenant;
1431
+ warn(message, ...args) {
1432
+ if (this.shouldLog(LogLevel.WARN)) {
1433
+ this.log('WARN', message, args, 'color: orange');
1434
+ }
1631
1435
  }
1632
- getTenant$() {
1633
- return this.store.sliceState(state => state.tenant);
1436
+ error(message, ...args) {
1437
+ if (this.shouldLog(LogLevel.ERROR)) {
1438
+ this.log('ERROR', message, args, 'color: red');
1439
+ // Send to external services only for errors in production
1440
+ if (this.environment?.production) {
1441
+ this.sendToExternalService('ERROR', message, args);
1442
+ }
1443
+ }
1634
1444
  }
1635
- setTenant(tenant) {
1636
- if (compare(tenant, this.store.state.tenant))
1637
- return;
1638
- this.store.set({ ...this.store.state, tenant });
1445
+ setLogLevel(level) {
1446
+ this.level = level;
1639
1447
  }
1640
- setState(session) {
1641
- if (compare(session, this.store.state))
1642
- return;
1643
- this.store.set({ ...this.store.state, ...session });
1448
+ shouldLog(level) {
1449
+ return level >= this.level && this.enableConsole;
1644
1450
  }
1645
- setLanguage(language) {
1646
- const currentLanguage = this.store.state.language;
1647
- if (language !== currentLanguage) {
1648
- this.store.patch({ language });
1451
+ log(level, message, args, style) {
1452
+ if (!this.enableConsole)
1453
+ return;
1454
+ const timestamp = this.timestamp ? new Date().toISOString() : '';
1455
+ const formattedPrefix = this.prefix ? `${this.prefix}` : '';
1456
+ const logMessage = `${timestamp} ${formattedPrefix} [${level}] ${message}`;
1457
+ if (args.length > 0) {
1458
+ console.log(`%c${logMessage}`, style, ...args);
1649
1459
  }
1650
- const currentAttribute = this.document.documentElement.getAttribute('lang');
1651
- if (language !== currentAttribute) {
1652
- this.document.documentElement.setAttribute('lang', language);
1460
+ else {
1461
+ console.log(`%c${logMessage}`, style);
1653
1462
  }
1654
1463
  }
1655
- get application() {
1656
- return this.store.state.application;
1464
+ sendToExternalService(level, message, args) {
1465
+ // Integrate with external logging services from environment
1466
+ try {
1467
+ const sentryUrl = this.environment?.externalServices?.['sentry'];
1468
+ const logRocketUrl = this.environment?.externalServices?.['logRocket'];
1469
+ // Example: Send to configured external services
1470
+ if (sentryUrl) {
1471
+ // this.sendToSentry(level, message, args);
1472
+ }
1473
+ if (logRocketUrl) {
1474
+ // this.sendToLogRocket(level, message, args);
1475
+ }
1476
+ // Send to backend if apiUrl is available
1477
+ if (this.environment?.apis.default.url) {
1478
+ // this.http.post(`${this.environment.apiUrl}/logs`, {
1479
+ // level, message, args, timestamp: new Date()
1480
+ // }).subscribe();
1481
+ }
1482
+ }
1483
+ catch (error) {
1484
+ // Don't use this.error here to avoid infinite loops
1485
+ console.error('Failed to send log to external service:', error);
1486
+ }
1657
1487
  }
1658
- get user() {
1659
- return this.store.state.user;
1488
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1489
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, providedIn: 'root' });
1490
+ }
1491
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, decorators: [{
1492
+ type: Injectable,
1493
+ args: [{
1494
+ providedIn: 'root'
1495
+ }]
1496
+ }], ctorParameters: () => [] });
1497
+
1498
+ class IdParserService {
1499
+ /**
1500
+ * Extract IDs from a model based on comma-separated field names.
1501
+ * Example: "id,biId" => [model.id, model.biId]
1502
+ */
1503
+ parseIds(model, idField) {
1504
+ if (!idField)
1505
+ return null;
1506
+ const fields = idField.split(',').map(f => f.trim()).filter(Boolean);
1507
+ if (fields.length === 1) {
1508
+ return model[fields[0]];
1509
+ }
1510
+ return fields.map(f => model[f]);
1660
1511
  }
1661
- get userId() {
1662
- return this.user ? this.user.id : 0;
1512
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1513
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, providedIn: 'root' });
1514
+ }
1515
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: IdParserService, decorators: [{
1516
+ type: Injectable,
1517
+ args: [{ providedIn: 'root' }]
1518
+ }] });
1519
+
1520
+ class QueryParser {
1521
+ static parse(queryString) {
1522
+ const params = {
1523
+ filters: [],
1524
+ orderBy: [],
1525
+ groupBy: [],
1526
+ select: [],
1527
+ aggregates: []
1528
+ };
1529
+ if (!queryString)
1530
+ return params;
1531
+ const queryParts = queryString.split('&');
1532
+ for (const part of queryParts) {
1533
+ if (part.startsWith('$filter=')) {
1534
+ this.parseFilter(part.substring('$filter='.length), params);
1535
+ }
1536
+ else if (part.startsWith('$orderby=')) {
1537
+ this.parseOrderBy(part.substring('$orderby='.length), params);
1538
+ }
1539
+ else if (part.startsWith('$groupby=')) {
1540
+ this.parseGroupBy(part.substring('$groupby='.length), params);
1541
+ }
1542
+ else if (part.startsWith('$select=')) {
1543
+ this.parseSelect(part.substring('$select='.length), params);
1544
+ }
1545
+ else if (part.startsWith('$aggregate=')) {
1546
+ this.parseAggregate(part.substring('$aggregate='.length), params);
1547
+ }
1548
+ else if (part.startsWith('$top=')) {
1549
+ params.top = Number(part.substring('$top='.length));
1550
+ }
1551
+ else if (part.startsWith('$skip=')) {
1552
+ params.skip = Number(part.substring('$skip='.length));
1553
+ }
1554
+ else if (part.startsWith('$expand=')) {
1555
+ params.expand = part.substring('$expand='.length).split(',').map(f => f.trim());
1556
+ }
1557
+ else if (part.startsWith('$count=')) {
1558
+ params.includeCount = part.substring('$count='.length).toLowerCase() === 'true';
1559
+ }
1560
+ }
1561
+ return params;
1663
1562
  }
1664
- get tenant() {
1665
- return this.store.state.tenant;
1563
+ static toString(params) {
1564
+ const parts = [];
1565
+ if (params.filters?.length) {
1566
+ const filterStr = this.buildFilterString(params.filters);
1567
+ parts.push(`$filter=${encodeURIComponent(filterStr)}`);
1568
+ }
1569
+ if (params.orderBy?.length) {
1570
+ const orderByStr = params.orderBy.map(o => `${o.field} ${o.direction}`).join(',');
1571
+ parts.push(`$orderby=${encodeURIComponent(orderByStr)}`);
1572
+ }
1573
+ if (params.groupBy?.length) {
1574
+ const groupByStr = this.buildGroupByString(params.groupBy);
1575
+ parts.push(`$groupby=${encodeURIComponent(groupByStr)}`);
1576
+ }
1577
+ if (params.select?.length) {
1578
+ const selectStr = this.buildSelectString(params.select);
1579
+ parts.push(`$select=${encodeURIComponent(selectStr)}`);
1580
+ }
1581
+ if (params.aggregates?.length) {
1582
+ const aggregateStr = this.buildAggregateString(params.aggregates);
1583
+ parts.push(`$aggregate=${encodeURIComponent(aggregateStr)}`);
1584
+ }
1585
+ if (params.top !== undefined) {
1586
+ parts.push(`$top=${params.top}`);
1587
+ }
1588
+ if (params.skip !== undefined) {
1589
+ parts.push(`$skip=${params.skip}`);
1590
+ }
1591
+ if (params.expand?.length) {
1592
+ parts.push(`$expand=${encodeURIComponent(params.expand.join(','))}`);
1593
+ }
1594
+ if (params.includeCount !== undefined) {
1595
+ parts.push(`$count=${params.includeCount}`);
1596
+ }
1597
+ return parts.join('&');
1666
1598
  }
1667
- get branch() {
1668
- return this.store.state.branch;
1599
+ static buildFilterString(filters) {
1600
+ return filters.map((group, index) => {
1601
+ const groupStr = group.conditions
1602
+ .map(condition => {
1603
+ let val = condition.value;
1604
+ if (typeof val === 'string') {
1605
+ val = `'${val.replace(/'/g, "''")}'`;
1606
+ }
1607
+ else if (typeof val === 'boolean') {
1608
+ val = val ? 'true' : 'false';
1609
+ }
1610
+ return `${condition.field} ${condition.operator} ${val}`;
1611
+ })
1612
+ .join(` ${group.logicalOperator} `);
1613
+ // For the first group, no groupLogicalOperator is needed
1614
+ if (index === 0) {
1615
+ return group.conditions.length > 1 ? `(${groupStr})` : groupStr;
1616
+ }
1617
+ else {
1618
+ const joinOp = group.groupLogicalOperator || 'and';
1619
+ return group.conditions.length > 1 ? `${joinOp} (${groupStr})` : `${joinOp} ${groupStr}`;
1620
+ }
1621
+ }).join(' ');
1669
1622
  }
1670
- /* get branchName(): BranchLoginInfoDto {
1671
- return this._branch ? this.branch.Name : '';
1672
-
1673
- } */
1674
- get tenancyName() {
1675
- return this.store.state.tenant ? this.tenant?.tenancyName : '';
1623
+ static buildGroupByString(groupBy) {
1624
+ return groupBy
1625
+ .sort((a, b) => (a.order || 0) - (b.order || 0))
1626
+ .map(group => {
1627
+ let groupStr = group.propertyName;
1628
+ // Add display name if provided
1629
+ if (group.displayName) {
1630
+ groupStr += ` as ${group.displayName}`;
1631
+ }
1632
+ // Add metadata for UI
1633
+ const metadata = [];
1634
+ if (group.showTotal !== undefined) {
1635
+ metadata.push(`showTotal:${group.showTotal}`);
1636
+ }
1637
+ if (group.totalBackground) {
1638
+ metadata.push(`totalBg:${group.totalBackground}`);
1639
+ }
1640
+ if (group.groupHeaderBackground) {
1641
+ metadata.push(`headerBg:${group.groupHeaderBackground}`);
1642
+ }
1643
+ if (metadata.length > 0) {
1644
+ groupStr += `(${metadata.join(';')})`;
1645
+ }
1646
+ return groupStr;
1647
+ })
1648
+ .join(',');
1676
1649
  }
1677
- get branchId() {
1678
- // return this.branch ? this.branch.id : this.configState.Session?.branchId ? this.configState.Session.branchId:null;
1679
- return this.branch ? this.branch.id : 0;
1650
+ static buildSelectString(select) {
1651
+ return select
1652
+ .sort((a, b) => (a.order || 0) - (b.order || 0))
1653
+ .map(item => {
1654
+ let selectStr = item.propertyName;
1655
+ // Add display name if provided
1656
+ if (item.displayName) {
1657
+ selectStr += ` as ${item.displayName}`;
1658
+ }
1659
+ // Add aggregate function if provided
1660
+ if (item.aggregateFunction) {
1661
+ selectStr += ` with ${item.aggregateFunction}`;
1662
+ }
1663
+ // Add visibility metadata
1664
+ const metadata = [];
1665
+ if (item.isVisible !== undefined) {
1666
+ metadata.push(`visible:${item.isVisible}`);
1667
+ }
1668
+ if (metadata.length > 0) {
1669
+ selectStr += `(${metadata.join(';')})`;
1670
+ }
1671
+ return selectStr;
1672
+ })
1673
+ .join(',');
1680
1674
  }
1681
- get tenantId() {
1682
- return this.tenant ? this.tenant.id : 0;
1675
+ static buildAggregateString(aggregates) {
1676
+ return aggregates
1677
+ .map(agg => {
1678
+ let aggStr = `${agg.aggregateFunction}(${agg.propertyName})`;
1679
+ if (agg.alias) {
1680
+ aggStr += ` as ${agg.alias}`;
1681
+ }
1682
+ return aggStr;
1683
+ })
1684
+ .join(',');
1683
1685
  }
1684
- getShownLoginName() {
1685
- const userName = this.store.state.user?.userName ?? '';
1686
- if (!this.multiTenancySide) {
1687
- return userName;
1686
+ static parseFilter(filterString, params) {
1687
+ const decodedString = decodeURIComponent(filterString);
1688
+ const groups = [];
1689
+ // Split by top-level AND/OR operators
1690
+ const topLevelPattern = /(?:^|\s)(and|or)\s+(?=(?:[^()]*\([^()]*\))*[^()]*$)/gi;
1691
+ const segments = [];
1692
+ let lastIndex = 0;
1693
+ let match;
1694
+ while ((match = topLevelPattern.exec(decodedString)) !== null) {
1695
+ const operator = match[1];
1696
+ const segment = decodedString.substring(lastIndex, match.index).trim();
1697
+ if (segment) {
1698
+ segments.push({ condition: segment, operator });
1699
+ }
1700
+ lastIndex = match.index + match[0].length;
1688
1701
  }
1689
- return (this.store.state.tenant ? this.store.state.tenant.tenancyName : '.') + '\\' + userName;
1690
- }
1691
- changeTenantIfNeeded(tenantId) {
1692
- if (this.isCurrentTenant(tenantId)) {
1693
- return false;
1702
+ // Add the last segment
1703
+ const lastSegment = decodedString.substring(lastIndex).trim();
1704
+ if (lastSegment) {
1705
+ segments.push({ condition: lastSegment, operator: undefined });
1694
1706
  }
1695
- // this.configState.SetTenantIdCookie(tenantId);
1696
- location.reload();
1697
- return true;
1707
+ // Parse each segment
1708
+ segments.forEach((segment, index) => {
1709
+ let conditions = [];
1710
+ // Check if it's a group with parentheses
1711
+ if (segment.condition.startsWith('(') && segment.condition.endsWith(')')) {
1712
+ const innerCondition = segment.condition.slice(1, -1).trim();
1713
+ conditions = this.parseConditions(innerCondition);
1714
+ }
1715
+ else {
1716
+ // Individual condition
1717
+ conditions = this.parseConditions(segment.condition);
1718
+ }
1719
+ if (conditions.length > 0) {
1720
+ const group = {
1721
+ logicalOperator: 'and',
1722
+ conditions: conditions
1723
+ };
1724
+ // For groups after the first, set the groupLogicalOperator
1725
+ if (index > 0) {
1726
+ group.groupLogicalOperator = segment.operator?.toLowerCase() || 'and';
1727
+ }
1728
+ // Detect the logical operator used within the group
1729
+ if (conditions.length === 1 && segment.condition.includes(' or ')) {
1730
+ group.logicalOperator = 'or';
1731
+ }
1732
+ else if (conditions.length > 1) {
1733
+ group.logicalOperator = segment.condition.toLowerCase().includes(' or ') ? 'or' : 'and';
1734
+ }
1735
+ groups.push(group);
1736
+ }
1737
+ });
1738
+ params.filters = groups;
1698
1739
  }
1699
- isCurrentTenant(tenantId) {
1700
- let isTenant = tenantId ?? 0 > 0;
1701
- if (!isTenant && !this.tenant) { // this is host
1702
- return true;
1703
- }
1704
- if (!tenantId && this.tenant) {
1705
- return false;
1706
- }
1707
- else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) {
1708
- return false;
1740
+ static parseConditions(conditionString) {
1741
+ const conditions = [];
1742
+ // Enhanced pattern to handle more operators and complex values
1743
+ const pattern = /(\w+(?:\.\w+)*)\s+(eq|ne|gt|ge|lt|le|contains|startswith|endswith|in|notin)\s+('[^']*'|\[[^\]]*\]|\d+|true|false|null)/gi;
1744
+ let match;
1745
+ while ((match = pattern.exec(conditionString)) !== null) {
1746
+ const [, field, operator, rawValue] = match;
1747
+ let value = rawValue.trim();
1748
+ if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
1749
+ value = rawValue.slice(1, -1).replace(/''/g, "'");
1750
+ }
1751
+ else if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
1752
+ // Handle array values for IN/NOT IN operators
1753
+ value = rawValue.slice(1, -1).split(',').map(v => {
1754
+ const trimmed = v.trim().replace(/^'|'$/g, '');
1755
+ return isNaN(Number(trimmed)) ? trimmed : Number(trimmed);
1756
+ });
1757
+ }
1758
+ else if (!isNaN(Number(rawValue))) {
1759
+ value = Number(rawValue);
1760
+ }
1761
+ else if (rawValue.toLowerCase() === 'true') {
1762
+ value = true;
1763
+ }
1764
+ else if (rawValue.toLowerCase() === 'false') {
1765
+ value = false;
1766
+ }
1767
+ else if (rawValue.toLowerCase() === 'null') {
1768
+ value = null;
1769
+ }
1770
+ conditions.push({
1771
+ field: field.trim(),
1772
+ operator: operator.trim().toLowerCase(),
1773
+ value
1774
+ });
1709
1775
  }
1710
- return true;
1711
- }
1712
- get impersonatorUser() {
1713
- return this.store.state.impersonatorUser;
1714
- }
1715
- get impersonatorUserId() {
1716
- return this.impersonatorUser ? this.impersonatorUser.id : 0;
1717
- }
1718
- get impersonatorTenant() {
1719
- return this.store.state.impersonatorTenant;
1720
- }
1721
- get impersonatorTenancyName() {
1722
- return this.impersonatorTenant ? this.impersonatorTenant?.tenancyName ?? '' : '';
1723
- }
1724
- get impersonatorTenantId() {
1725
- return this.impersonatorTenant ? this.impersonatorTenant.id : 0;
1726
- }
1727
- get multiTenancySide() {
1728
- // return this.configState.session?.multiTenancySide;
1729
- return 0;
1730
- }
1731
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, deps: [{ token: ConfigStateService }, { token: SsrCookieService }], target: i0.ɵɵFactoryTarget.Injectable });
1732
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, providedIn: 'root' });
1733
- }
1734
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: SessionStateService, decorators: [{
1735
- type: Injectable,
1736
- args: [{
1737
- providedIn: 'root',
1738
- }]
1739
- }], ctorParameters: () => [{ type: ConfigStateService }, { type: SsrCookieService }] });
1740
-
1741
- class LocalizationService {
1742
- sessionState;
1743
- injector;
1744
- configState;
1745
- latestLang = 'ar';
1746
- _languageChange$ = new Subject();
1747
- uiLocalizations$ = new BehaviorSubject(new Map());
1748
- localizations$ = new BehaviorSubject(new Map());
1749
- /**
1750
- * Returns currently selected language
1751
- * Even though this looks like it's redundant to return the same value as `getLanguage()`,
1752
- * it's actually not. This could be invoked any time, and the latestLang could be different from the
1753
- * sessionState.getLanguage() value.
1754
- */
1755
- get currentLanguage() {
1756
- return this.latestLang || this.sessionState.getLanguage();
1757
- }
1758
- get currentLang() {
1759
- return this.latestLang || this.sessionState.getLanguage();
1760
- }
1761
- get currentLang$() {
1762
- return this.sessionState.getLanguage$();
1776
+ return conditions;
1763
1777
  }
1764
- get languageChange$() {
1765
- return this._languageChange$.asObservable();
1778
+ static parseOrderBy(orderByString, params) {
1779
+ const decodedString = decodeURIComponent(orderByString);
1780
+ const clauses = decodedString.split(',');
1781
+ for (const clause of clauses) {
1782
+ const parts = clause.trim().split(/\s+/);
1783
+ const field = parts[0];
1784
+ const direction = parts[1]?.toLowerCase() || 'asc';
1785
+ params.orderBy?.push({
1786
+ field,
1787
+ direction
1788
+ });
1789
+ }
1766
1790
  }
1767
- constructor(sessionState, injector, otherInstance, configState) {
1768
- this.sessionState = sessionState;
1769
- this.injector = injector;
1770
- this.configState = configState;
1771
- if (otherInstance)
1772
- throw new Error('LocalizationService should have only one instance.');
1773
- this.listenToSetLanguage();
1774
- this.initLocalizationValues();
1775
- this.latestLang = this.sessionState.getLanguage();
1791
+ static parseGroupBy(groupByString, params) {
1792
+ const decodedString = decodeURIComponent(groupByString);
1793
+ const groups = decodedString.split(',');
1794
+ params.groupBy = groups.map((groupStr, index) => {
1795
+ const group = {
1796
+ propertyName: '',
1797
+ order: index
1798
+ };
1799
+ // Check for metadata in parentheses
1800
+ const metadataMatch = groupStr.match(/^(.*?)\((.*)\)$/);
1801
+ let baseStr = groupStr;
1802
+ let metadataStr = '';
1803
+ if (metadataMatch) {
1804
+ baseStr = metadataMatch[1];
1805
+ metadataStr = metadataMatch[2];
1806
+ }
1807
+ // Check for display name alias
1808
+ const aliasMatch = baseStr.match(/^(.*?)\s+as\s+(.*)$/);
1809
+ if (aliasMatch) {
1810
+ group.propertyName = aliasMatch[1].trim();
1811
+ group.displayName = aliasMatch[2].trim();
1812
+ }
1813
+ else {
1814
+ group.propertyName = baseStr.trim();
1815
+ }
1816
+ // Parse metadata
1817
+ if (metadataStr) {
1818
+ const metadataParts = metadataStr.split(';');
1819
+ metadataParts.forEach(part => {
1820
+ const [key, value] = part.split(':');
1821
+ switch (key) {
1822
+ case 'showTotal':
1823
+ group.showTotal = value.toLowerCase() === 'true';
1824
+ break;
1825
+ case 'totalBg':
1826
+ group.totalBackground = value;
1827
+ break;
1828
+ case 'headerBg':
1829
+ group.groupHeaderBackground = value;
1830
+ break;
1831
+ }
1832
+ });
1833
+ }
1834
+ return group;
1835
+ });
1776
1836
  }
1777
- initLocalizationValues() {
1778
- localizations$.subscribe(val => this.addLocalization(val));
1779
- const legacyResources$ = this.configState.getDeep$('localization.values');
1780
- const remoteLocalizations$ = this.configState.getDeep$('localization.resources');
1781
- const currentLanguage$ = this.sessionState.getLanguage$();
1782
- const uiLocalizations$ = combineLatest([currentLanguage$, this.uiLocalizations$]).pipe(map(([currentLang, localizations]) => localizations.get(currentLang)));
1783
- combineLatest([legacyResources$, remoteLocalizations$, uiLocalizations$])
1784
- .pipe(map(([legacy, resource, local]) => {
1785
- if (!resource) {
1786
- return null;
1837
+ static parseSelect(selectString, params) {
1838
+ const decodedString = decodeURIComponent(selectString);
1839
+ const items = decodedString.split(',');
1840
+ params.select = items.map((itemStr, index) => {
1841
+ const item = {
1842
+ propertyName: '',
1843
+ isVisible: true,
1844
+ order: index
1845
+ };
1846
+ // Check for metadata in parentheses
1847
+ const metadataMatch = itemStr.match(/^(.*?)\((.*)\)$/);
1848
+ let baseStr = itemStr;
1849
+ let metadataStr = '';
1850
+ if (metadataMatch) {
1851
+ baseStr = metadataMatch[1];
1852
+ metadataStr = metadataMatch[2];
1787
1853
  }
1788
- const remote = combineLegacyandNewResources(legacy || {}, resource);
1789
- if (remote) {
1790
- if (!local) {
1791
- local = new Map();
1854
+ // Check for display name alias and aggregate function
1855
+ const aliasMatch = baseStr.match(/^(.*?)(?:\s+as\s+(.*?))?(?:\s+with\s+(\w+))?$/);
1856
+ if (aliasMatch) {
1857
+ item.propertyName = aliasMatch[1].trim();
1858
+ if (aliasMatch[2]) {
1859
+ item.displayName = aliasMatch[2].trim();
1792
1860
  }
1793
- Object.entries(remote).forEach(entry => {
1794
- const resourceName = entry[0];
1795
- const remoteTexts = entry[1];
1796
- let resource = local?.get(resourceName) || {};
1797
- resource = { ...resource, ...remoteTexts };
1798
- local?.set(resourceName, resource);
1861
+ if (aliasMatch[3]) {
1862
+ item.aggregateFunction = aliasMatch[3].trim();
1863
+ }
1864
+ }
1865
+ else {
1866
+ item.propertyName = baseStr.trim();
1867
+ }
1868
+ // Parse metadata
1869
+ if (metadataStr) {
1870
+ const metadataParts = metadataStr.split(';');
1871
+ metadataParts.forEach(part => {
1872
+ const [key, value] = part.split(':');
1873
+ if (key === 'visible') {
1874
+ item.isVisible = value.toLowerCase() === 'true';
1875
+ }
1799
1876
  });
1800
1877
  }
1801
- return local;
1802
- }), filter(Boolean))
1803
- .subscribe(val => this.localizations$.next(val));
1804
- }
1805
- addLocalization(localizations) {
1806
- if (!localizations)
1807
- return;
1808
- const localizationMap = this.uiLocalizations$.value;
1809
- localizations.forEach(loc => {
1810
- const cultureMap = localizationMap.get(loc.culture) || new Map();
1811
- loc.resources.forEach(res => {
1812
- let resource = cultureMap.get(res.resourceName) || {};
1813
- resource = { ...resource, ...res.texts };
1814
- cultureMap.set(res.resourceName, resource);
1815
- });
1816
- localizationMap.set(loc.culture, cultureMap);
1878
+ return item;
1817
1879
  });
1818
- this.uiLocalizations$.next(localizationMap);
1819
- }
1820
- listenToSetLanguage() {
1821
- this.sessionState
1822
- .onLanguageChange$()
1823
- .pipe(filter(lang => this.configState.getDeep('localization.currentCulture.cultureName') !== lang), switchMap((lang) => this.configState.refreshLocalization(lang).pipe(map(() => lang))), filter(Boolean), switchMap((lang) => from(this.registerLocale(lang).then(() => lang))))
1824
- .subscribe((lang) => this._languageChange$.next(lang));
1825
1880
  }
1826
- registerLocale(locale) {
1827
- const { registerLocaleFn } = this.injector.get(CORE_OPTIONS);
1828
- return registerLocaleFn(locale).then(module => {
1829
- if (module?.default)
1830
- registerLocaleData(module.default);
1831
- this.latestLang = locale;
1881
+ static parseAggregate(aggregateString, params) {
1882
+ const decodedString = decodeURIComponent(aggregateString);
1883
+ const aggregates = decodedString.split(',');
1884
+ params.aggregates = aggregates.map(aggStr => {
1885
+ const agg = {
1886
+ propertyName: '',
1887
+ aggregateFunction: ''
1888
+ };
1889
+ // Match pattern: function(property) as alias
1890
+ const match = aggStr.match(/^(\w+)\((\w+)\)(?:\s+as\s+(\w+))?$/);
1891
+ if (match) {
1892
+ agg.aggregateFunction = match[1];
1893
+ agg.propertyName = match[2];
1894
+ if (match[3]) {
1895
+ agg.alias = match[3];
1896
+ }
1897
+ }
1898
+ return agg;
1832
1899
  });
1833
1900
  }
1834
- /**
1835
- * Returns an observable localized text with the given interpolation parameters in current language.
1836
- * @param key Localizaton key to replace with localized text
1837
- * @param interpolateParams Values to interpolate
1838
- */
1839
- get(key, ...interpolateParams) {
1840
- return this.configState
1841
- .getAll$()
1842
- .pipe(map(state => this.getLocalization(state, key, ...interpolateParams)));
1843
- }
1844
- getResource(resourceName) {
1845
- return this.localizations$.value.get(resourceName);
1846
- }
1847
- getResource$(resourceName) {
1848
- return this.localizations$.pipe(map(res => res.get(resourceName)));
1849
- }
1850
- /**
1851
- * Returns localized text with the given interpolation parameters in current language.
1852
- * @param key Localization key to replace with localized text
1853
- * @param interpolateParams Values to intepolate.
1854
- */
1855
- instant(key, ...interpolateParams) {
1856
- return this.getLocalization(this.configState.getAll(), key, ...interpolateParams);
1857
- }
1858
- localize(resourceName, key, defaultValue) {
1859
- return this.configState.getOne$('localization').pipe(map(createLocalizer), map((localize) => localize(resourceName, key, defaultValue)));
1860
- }
1861
- get defaultResourceName() {
1862
- return this.configState.getAll().localization ? this.configState.getAll().localization.defaultResourceName : 'FrameworkLocalization';
1863
- }
1864
- l(key) {
1865
- const localization = this.configState.getOne('localization');
1866
- return createLocalizer(localization)(this.defaultResourceName ?? '', key, key);
1867
- }
1868
- localizeSync(resourceName, key, defaultValue) {
1869
- const localization = this.configState.getOne('localization');
1870
- return createLocalizer(localization)(resourceName, key, defaultValue);
1871
- }
1872
- localizeWithFallback(resourceNames, keys, defaultValue) {
1873
- return this.configState.getOne$('localization').pipe(map(createLocalizerWithFallback), map((localizeWithFallback) => localizeWithFallback(resourceNames, keys, defaultValue)));
1874
- }
1875
- localizeWithFallbackSync(resourceNames, keys, defaultValue) {
1876
- const localization = this.configState.getOne('localization');
1877
- return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue);
1878
- }
1879
- getLocalization(state, key, ...interpolateParams) {
1880
- let defaultValue = '';
1881
- if (!key) {
1882
- return defaultValue;
1883
- }
1884
- if (typeof key !== 'string') {
1885
- defaultValue = key.defaultValue;
1886
- key = key.key;
1887
- }
1888
- const keys = key.split('::');
1889
- const warn = (message) => {
1890
- if (isDevMode())
1891
- console.warn(message);
1901
+ // Helper method to convert QueryParameters to ODataQueryParameters (C# equivalent)
1902
+ static toODataQueryParameters(params) {
1903
+ return {
1904
+ Filters: params.filters.map(group => ({
1905
+ LogicalOperator: group.logicalOperator,
1906
+ GroupLogicalOperator: group.groupLogicalOperator,
1907
+ Conditions: group.conditions.map(cond => ({
1908
+ Field: cond.field,
1909
+ Operator: cond.operator,
1910
+ Value: cond.value
1911
+ }))
1912
+ })),
1913
+ OrderBy: params.orderBy?.map(order => ({
1914
+ Field: order.field,
1915
+ Direction: order.direction
1916
+ })) || [],
1917
+ GroupBy: params.groupBy?.map(group => ({
1918
+ PropertyName: group.propertyName,
1919
+ DisplayName: group.displayName,
1920
+ ShowTotal: group.showTotal ?? true,
1921
+ TotalBackground: group.totalBackground,
1922
+ GroupHeaderBackground: group.groupHeaderBackground,
1923
+ Order: group.order
1924
+ })) || [],
1925
+ Select: params.select?.map(sel => ({
1926
+ PropertyName: sel.propertyName,
1927
+ DisplayName: sel.displayName,
1928
+ AggregateFunction: sel.aggregateFunction,
1929
+ IsVisible: sel.isVisible ?? true,
1930
+ Order: sel.order
1931
+ })) || [],
1932
+ Aggregates: params.aggregates?.map(agg => ({
1933
+ PropertyName: agg.propertyName,
1934
+ AggregateFunction: agg.aggregateFunction,
1935
+ Alias: agg.alias
1936
+ })) || [],
1937
+ Top: params.top,
1938
+ Skip: params.skip,
1939
+ Expand: params.expand,
1940
+ IncludeCount: params.includeCount
1892
1941
  };
1893
- if (keys.length < 2) {
1894
- warn('The localization source separator (::) not found.');
1895
- return defaultValue || key;
1896
- }
1897
- if (!state.localization)
1898
- return defaultValue || keys[1];
1899
- const sourceName = keys[0] || state.localization.defaultResourceName;
1900
- const sourceKey = keys[1];
1901
- if (sourceName === '_') {
1902
- return defaultValue || sourceKey;
1903
- }
1904
- if (!sourceName) {
1905
- warn('Localization source name is not specified and the defaultResourceName was not defined!');
1906
- return defaultValue || sourceKey;
1907
- }
1908
- const source = this.localizations$.value.get(sourceName);
1909
- if (!source) {
1910
- warn('Could not find localization source: ' + sourceName);
1911
- return defaultValue || sourceKey;
1912
- }
1913
- let localization = source[sourceKey];
1914
- if (typeof localization === 'undefined') {
1915
- return defaultValue || sourceKey;
1916
- }
1917
- interpolateParams = interpolateParams.filter(params => params != null);
1918
- if (localization)
1919
- localization = interpolate(localization, interpolateParams);
1920
- if (typeof localization !== 'string')
1921
- localization = '';
1922
- return localization || defaultValue || key;
1923
1942
  }
1924
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, deps: [{ token: SessionStateService }, { token: i0.Injector }, { token: LocalizationService, optional: true, skipSelf: true }, { token: ConfigStateService }], target: i0.ɵɵFactoryTarget.Injectable });
1925
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, providedIn: 'root' });
1926
- }
1927
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LocalizationService, decorators: [{
1928
- type: Injectable,
1929
- args: [{ providedIn: 'root' }]
1930
- }], ctorParameters: () => [{ type: SessionStateService }, { type: i0.Injector }, { type: LocalizationService, decorators: [{
1931
- type: Optional
1932
- }, {
1933
- type: SkipSelf
1934
- }] }, { type: ConfigStateService }] });
1935
- function recursivelyMergeBaseResources(baseResourceName, source) {
1936
- const item = source[baseResourceName];
1937
- if (item.baseResources.length === 0) {
1938
- return item;
1943
+ // Helper method to create query parameters from UI model
1944
+ static fromUIModel(model) {
1945
+ const params = {
1946
+ filters: model.advancedFilters || [],
1947
+ orderBy: model.sorting || [],
1948
+ groupBy: model.grouping || [],
1949
+ select: model.columns || [],
1950
+ aggregates: model.aggregates || [],
1951
+ top: model.pagination?.top,
1952
+ skip: model.pagination?.skip,
1953
+ expand: model.expand,
1954
+ includeCount: model.pagination?.includeCount
1955
+ };
1956
+ return params;
1939
1957
  }
1940
- return item.baseResources.reduce((acc, baseResource) => {
1941
- const baseItem = recursivelyMergeBaseResources(baseResource, source);
1942
- const texts = { ...baseItem.texts, ...item.texts };
1943
- return { ...acc, texts };
1944
- }, item);
1945
- }
1946
- function mergeResourcesWithBaseResource(resource) {
1947
- const entities = Object.keys(resource).map(key => {
1948
- const newValue = recursivelyMergeBaseResources(key, resource);
1949
- return [key, newValue];
1950
- });
1951
- return entities.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
1952
- }
1953
- function combineLegacyandNewResources(legacy, resource) {
1954
- const mergedResource = mergeResourcesWithBaseResource(resource);
1955
- return Object.entries(mergedResource).reduce((acc, [key, value]) => {
1956
- return { ...acc, [key]: value.texts };
1957
- }, legacy);
1958
1958
  }
1959
1959
 
1960
1960
  class LocalStorageListenerService {
@@ -2994,7 +2994,7 @@ async function getInitialData() {
2994
2994
  if (!options.skipInitAuthService && authService) {
2995
2995
  await authService.init();
2996
2996
  }
2997
- if (options.skipGetAppConfiguration)
2997
+ if (options.skipGetAppConfiguration || authService?.isAuthenticated == false)
2998
2998
  return;
2999
2999
  const result$ = configState.refreshAppState().pipe(tap(() => checkAuthenticationState(injector)), tap(() => {
3000
3000
  const currentTenant = configState.getOne('currentTenant');