@elite.framework/ng.core 2.0.29 → 2.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/elite.framework-ng.core-interceptors.mjs +5 -5
- package/fesm2022/elite.framework-ng.core-interceptors.mjs.map +1 -1
- package/fesm2022/elite.framework-ng.core-models.mjs.map +1 -1
- package/fesm2022/elite.framework-ng.core-services.mjs +1156 -1143
- package/fesm2022/elite.framework-ng.core-services.mjs.map +1 -1
- package/models/index.d.ts +0 -1
- package/package.json +1 -1
- package/services/index.d.ts +174 -170
|
@@ -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,
|
|
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: '/
|
|
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: '/
|
|
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
|
|
730
|
+
class SessionStateService {
|
|
815
731
|
configState;
|
|
816
|
-
|
|
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
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
749
|
+
this.store.sliceUpdate(state => state).subscribe(this.updateLocalStorage);
|
|
838
750
|
}
|
|
839
|
-
|
|
840
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
return true;
|
|
845
|
-
return this.getGrantedPolicy$(permissionName);
|
|
763
|
+
onLanguageChange$() {
|
|
764
|
+
return this.store.sliceUpdate(state => state.language);
|
|
846
765
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
return true;
|
|
850
|
-
return this.getGrantedPolicy(permissionName);
|
|
766
|
+
onTenantChange$() {
|
|
767
|
+
return this.store.sliceUpdate(state => state.tenant);
|
|
851
768
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return this.isPolicyGranted(key, policies);
|
|
769
|
+
getLanguage() {
|
|
770
|
+
return this.store.state.language;
|
|
855
771
|
}
|
|
856
|
-
|
|
857
|
-
return this.
|
|
772
|
+
getLanguage$() {
|
|
773
|
+
return this.store.sliceState(state => state.language);
|
|
858
774
|
}
|
|
859
|
-
|
|
860
|
-
return this.
|
|
775
|
+
getTenant() {
|
|
776
|
+
return this.store.state.tenant;
|
|
861
777
|
}
|
|
862
|
-
|
|
863
|
-
return
|
|
778
|
+
getTenant$() {
|
|
779
|
+
return this.store.sliceState(state => state.tenant);
|
|
864
780
|
}
|
|
865
|
-
|
|
866
|
-
|
|
781
|
+
setTenant(tenant) {
|
|
782
|
+
if (compare(tenant, this.store.state.tenant))
|
|
783
|
+
return;
|
|
784
|
+
this.store.set({ ...this.store.state, tenant });
|
|
867
785
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
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
|
-
|
|
893
|
-
return this.
|
|
801
|
+
get application() {
|
|
802
|
+
return this.store.state.application;
|
|
894
803
|
}
|
|
895
|
-
|
|
896
|
-
return this.
|
|
804
|
+
get user() {
|
|
805
|
+
return this.store.state.user;
|
|
897
806
|
}
|
|
898
|
-
|
|
899
|
-
return this.
|
|
807
|
+
get userId() {
|
|
808
|
+
return this.user ? this.user.id : 0;
|
|
900
809
|
}
|
|
901
|
-
|
|
902
|
-
return this.
|
|
810
|
+
get tenant() {
|
|
811
|
+
return this.store.state.tenant;
|
|
903
812
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
954
|
-
|
|
955
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1056
|
-
|
|
1057
|
-
this.log('WARN', message, args, 'color: orange');
|
|
1058
|
-
}
|
|
858
|
+
get impersonatorUser() {
|
|
859
|
+
return this.store.state.impersonatorUser;
|
|
1059
860
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
-
|
|
1070
|
-
this.
|
|
864
|
+
get impersonatorTenant() {
|
|
865
|
+
return this.store.state.impersonatorTenant;
|
|
1071
866
|
}
|
|
1072
|
-
|
|
1073
|
-
return
|
|
867
|
+
get impersonatorTenancyName() {
|
|
868
|
+
return this.impersonatorTenant ? this.impersonatorTenant?.tenancyName ?? '' : '';
|
|
1074
869
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
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
|
-
|
|
1089
|
-
//
|
|
1090
|
-
|
|
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:
|
|
1113
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type:
|
|
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:
|
|
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
|
|
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
|
-
*
|
|
1125
|
-
*
|
|
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
|
-
|
|
1128
|
-
|
|
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
|
-
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
1182
|
-
|
|
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
|
-
|
|
947
|
+
return local;
|
|
948
|
+
}), filter(Boolean))
|
|
949
|
+
.subscribe(val => this.localizations$.next(val));
|
|
1186
950
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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 (
|
|
1202
|
-
|
|
1203
|
-
|
|
1031
|
+
if (typeof key !== 'string') {
|
|
1032
|
+
defaultValue = key.defaultValue;
|
|
1033
|
+
key = key.key;
|
|
1204
1034
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
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 (
|
|
1210
|
-
|
|
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 (
|
|
1213
|
-
|
|
1051
|
+
if (!sourceName) {
|
|
1052
|
+
warn('Localization source name is not specified and the defaultResourceName was not defined!');
|
|
1053
|
+
return defaultValue || sourceKey;
|
|
1214
1054
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
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
|
-
|
|
1219
|
-
|
|
1060
|
+
let localization = source[sourceKey];
|
|
1061
|
+
if (typeof localization === 'undefined') {
|
|
1062
|
+
return defaultValue || sourceKey;
|
|
1220
1063
|
}
|
|
1221
|
-
|
|
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
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
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
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
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(',');
|
|
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;
|
|
1298
1114
|
}
|
|
1299
|
-
|
|
1300
|
-
return
|
|
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(',');
|
|
1115
|
+
t(key) {
|
|
1116
|
+
return this.translate.instant(key);
|
|
1309
1117
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
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
|
-
}
|
|
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
|
|
1361
1128
|
});
|
|
1362
|
-
params.filters = groups;
|
|
1363
|
-
}
|
|
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;
|
|
1401
1129
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
direction
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
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();
|
|
1436
|
-
}
|
|
1437
|
-
else {
|
|
1438
|
-
group.propertyName = baseStr.trim();
|
|
1439
|
-
}
|
|
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
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
return group;
|
|
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
|
|
1459
1139
|
});
|
|
1460
1140
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
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];
|
|
1477
|
-
}
|
|
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();
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
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
|
-
});
|
|
1501
|
-
}
|
|
1502
|
-
return item;
|
|
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
|
|
1503
1149
|
});
|
|
1504
1150
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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];
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
return agg;
|
|
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
|
|
1523
1158
|
});
|
|
1524
1159
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
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
|
|
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
|
|
1565
1168
|
};
|
|
1169
|
+
return Swal.fire(translated);
|
|
1566
1170
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
expand: model.expand,
|
|
1578
|
-
includeCount: model.pagination?.includeCount
|
|
1579
|
-
};
|
|
1580
|
-
return params;
|
|
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) });
|
|
1581
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' });
|
|
1582
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 }] });
|
|
1583
1189
|
|
|
1584
|
-
class
|
|
1190
|
+
class PermissionCheckerService {
|
|
1585
1191
|
configState;
|
|
1586
|
-
|
|
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) {
|
|
1192
|
+
constructor(configState) {
|
|
1593
1193
|
this.configState = configState;
|
|
1594
|
-
this.localStorageService = localStorageService;
|
|
1595
|
-
this.init();
|
|
1596
|
-
this.setInitialLanguage();
|
|
1597
1194
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
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));
|
|
1602
1206
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
.subscribe(lang => {
|
|
1611
|
-
if (lang.includes(';')) {
|
|
1612
|
-
lang = lang.split(';')[0];
|
|
1613
|
-
}
|
|
1614
|
-
this.setLanguage(lang);
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
onLanguageChange$() {
|
|
1618
|
-
return this.store.sliceUpdate(state => state.language);
|
|
1619
|
-
}
|
|
1620
|
-
onTenantChange$() {
|
|
1621
|
-
return this.store.sliceUpdate(state => state.tenant);
|
|
1622
|
-
}
|
|
1623
|
-
getLanguage() {
|
|
1624
|
-
return this.store.state.language;
|
|
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));
|
|
1212
|
+
}
|
|
1213
|
+
return this.getPolicy(key, grantedPolicies);
|
|
1625
1214
|
}
|
|
1626
|
-
|
|
1627
|
-
return this.
|
|
1215
|
+
getGrantedPolicy$(key) {
|
|
1216
|
+
return this.getStream().pipe(map$1(grantedPolicies => this.isPolicyGranted(key, grantedPolicies)));
|
|
1628
1217
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1218
|
+
isGrantedAsync(permissionName) {
|
|
1219
|
+
if (!permissionName)
|
|
1220
|
+
return true;
|
|
1221
|
+
return this.getGrantedPolicy$(permissionName);
|
|
1631
1222
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1223
|
+
isGranted(permissionName) {
|
|
1224
|
+
if (!permissionName)
|
|
1225
|
+
return true;
|
|
1226
|
+
return this.getGrantedPolicy(permissionName);
|
|
1634
1227
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
this.store.set({ ...this.store.state, tenant });
|
|
1228
|
+
getGrantedPolicy(key) {
|
|
1229
|
+
const policies = this.getSnapshot();
|
|
1230
|
+
return this.isPolicyGranted(key, policies);
|
|
1639
1231
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
return;
|
|
1643
|
-
this.store.set({ ...this.store.state, ...session });
|
|
1232
|
+
getStream() {
|
|
1233
|
+
return this.configState.getAll$().pipe(map$1(this.mapToPolicies));
|
|
1644
1234
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
if (language !== currentLanguage) {
|
|
1648
|
-
this.store.patch({ language });
|
|
1649
|
-
}
|
|
1650
|
-
const currentAttribute = this.document.documentElement.getAttribute('lang');
|
|
1651
|
-
if (language !== currentAttribute) {
|
|
1652
|
-
this.document.documentElement.setAttribute('lang', language);
|
|
1653
|
-
}
|
|
1235
|
+
getSnapshot() {
|
|
1236
|
+
return this.mapToPolicies(this.configState.getAll());
|
|
1654
1237
|
}
|
|
1655
|
-
|
|
1656
|
-
return
|
|
1238
|
+
mapToPolicies(applicationConfiguration) {
|
|
1239
|
+
return applicationConfiguration?.auth?.grantedPolicies || {};
|
|
1657
1240
|
}
|
|
1658
|
-
|
|
1659
|
-
return
|
|
1241
|
+
getPolicy(key, grantedPolicies) {
|
|
1242
|
+
return grantedPolicies[key] || false;
|
|
1660
1243
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
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);
|
|
1663
1258
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
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;
|
|
1666
1267
|
}
|
|
1667
|
-
|
|
1668
|
-
return this.
|
|
1268
|
+
canActivate(route, state) {
|
|
1269
|
+
return this.hasPermissions(route, state);
|
|
1669
1270
|
}
|
|
1670
|
-
|
|
1671
|
-
return this.
|
|
1672
|
-
|
|
1673
|
-
} */
|
|
1674
|
-
get tenancyName() {
|
|
1675
|
-
return this.store.state.tenant ? this.tenant?.tenancyName : '';
|
|
1271
|
+
canActivateChild(route, state) {
|
|
1272
|
+
return this.hasPermissions(route, state);
|
|
1676
1273
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
return this.branch ? this.branch.id : 0;
|
|
1274
|
+
canLoad(route) {
|
|
1275
|
+
return this.hasPermissions(route);
|
|
1680
1276
|
}
|
|
1681
|
-
|
|
1682
|
-
return this.
|
|
1277
|
+
canMatch(route) {
|
|
1278
|
+
return this.hasPermissions(route);
|
|
1683
1279
|
}
|
|
1684
|
-
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1687
|
-
|
|
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));
|
|
1288
|
+
}
|
|
1289
|
+
else {
|
|
1290
|
+
only.push(...transformStringToArray(data.only));
|
|
1291
|
+
}
|
|
1688
1292
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1293
|
+
if (data?.except) {
|
|
1294
|
+
if (isFunction(data.except)) {
|
|
1295
|
+
const result = data.except(route, state);
|
|
1296
|
+
except.push(...transformStringToArray(result));
|
|
1297
|
+
}
|
|
1298
|
+
else {
|
|
1299
|
+
except.push(...transformStringToArray(data.except));
|
|
1300
|
+
}
|
|
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;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
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
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
this.handleRedirect(permissions, permissions.only[0], route, state);
|
|
1693
1325
|
return false;
|
|
1694
1326
|
}
|
|
1695
|
-
// this.configState.SetTenantIdCookie(tenantId);
|
|
1696
|
-
location.reload();
|
|
1697
1327
|
return true;
|
|
1698
1328
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
if (!
|
|
1702
|
-
return
|
|
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
|
|
1703
1339
|
}
|
|
1704
|
-
if (
|
|
1705
|
-
|
|
1340
|
+
else if (Array.isArray(resolvedRedirect)) {
|
|
1341
|
+
this.router.navigate(resolvedRedirect); // array is fine
|
|
1706
1342
|
}
|
|
1707
|
-
else if (
|
|
1708
|
-
|
|
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)]);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
console.warn('Unknown redirect type', resolvedRedirect);
|
|
1709
1360
|
}
|
|
1710
|
-
return true;
|
|
1711
1361
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
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' });
|
|
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 }] });
|
|
1369
|
+
|
|
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;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
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;
|
|
1410
|
+
}
|
|
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
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
debug(message, ...args) {
|
|
1422
|
+
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
1423
|
+
this.log('DEBUG', message, args, 'color: blue');
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
info(message, ...args) {
|
|
1427
|
+
if (this.shouldLog(LogLevel.INFO)) {
|
|
1428
|
+
this.log('INFO', message, args, 'color: green');
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
warn(message, ...args) {
|
|
1432
|
+
if (this.shouldLog(LogLevel.WARN)) {
|
|
1433
|
+
this.log('WARN', message, args, 'color: orange');
|
|
1434
|
+
}
|
|
1714
1435
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
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
|
+
}
|
|
1717
1444
|
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1445
|
+
setLogLevel(level) {
|
|
1446
|
+
this.level = level;
|
|
1720
1447
|
}
|
|
1721
|
-
|
|
1722
|
-
return this.
|
|
1448
|
+
shouldLog(level) {
|
|
1449
|
+
return level >= this.level && this.enableConsole;
|
|
1723
1450
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
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);
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
console.log(`%c${logMessage}`, style);
|
|
1462
|
+
}
|
|
1726
1463
|
}
|
|
1727
|
-
|
|
1728
|
-
//
|
|
1729
|
-
|
|
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
|
+
}
|
|
1730
1487
|
}
|
|
1731
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type:
|
|
1732
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type:
|
|
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' });
|
|
1733
1490
|
}
|
|
1734
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type:
|
|
1491
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: LoggerService, decorators: [{
|
|
1735
1492
|
type: Injectable,
|
|
1736
1493
|
args: [{
|
|
1737
|
-
providedIn: 'root'
|
|
1494
|
+
providedIn: 'root'
|
|
1738
1495
|
}]
|
|
1739
|
-
}], ctorParameters: () => [
|
|
1496
|
+
}], ctorParameters: () => [] });
|
|
1740
1497
|
|
|
1741
|
-
class
|
|
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());
|
|
1498
|
+
class IdParserService {
|
|
1749
1499
|
/**
|
|
1750
|
-
*
|
|
1751
|
-
*
|
|
1752
|
-
* it's actually not. This could be invoked any time, and the latestLang could be different from the
|
|
1753
|
-
* sessionState.getLanguage() value.
|
|
1500
|
+
* Extract IDs from a model based on comma-separated field names.
|
|
1501
|
+
* Example: "id,biId" => [model.id, model.biId]
|
|
1754
1502
|
*/
|
|
1755
|
-
|
|
1756
|
-
|
|
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]);
|
|
1757
1511
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
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;
|
|
1760
1562
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
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('&');
|
|
1763
1598
|
}
|
|
1764
|
-
|
|
1765
|
-
return
|
|
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(' ');
|
|
1622
|
+
}
|
|
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(',');
|
|
1649
|
+
}
|
|
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(',');
|
|
1766
1674
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
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(',');
|
|
1776
1685
|
}
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
const
|
|
1780
|
-
|
|
1781
|
-
const
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
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 });
|
|
1787
1699
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1700
|
+
lastIndex = match.index + match[0].length;
|
|
1701
|
+
}
|
|
1702
|
+
// Add the last segment
|
|
1703
|
+
const lastSegment = decodedString.substring(lastIndex).trim();
|
|
1704
|
+
if (lastSegment) {
|
|
1705
|
+
segments.push({ condition: lastSegment, operator: undefined });
|
|
1706
|
+
}
|
|
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';
|
|
1792
1727
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
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;
|
|
1739
|
+
}
|
|
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);
|
|
1799
1756
|
});
|
|
1800
1757
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
return conditions;
|
|
1804
1777
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
const
|
|
1809
|
-
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
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
|
|
1815
1788
|
});
|
|
1816
|
-
|
|
1817
|
-
});
|
|
1818
|
-
this.uiLocalizations$.next(localizationMap);
|
|
1789
|
+
}
|
|
1819
1790
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
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
|
+
});
|
|
1825
1836
|
}
|
|
1826
|
-
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
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];
|
|
1853
|
+
}
|
|
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();
|
|
1860
|
+
}
|
|
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
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
return item;
|
|
1832
1879
|
});
|
|
1833
1880
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
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);
|
|
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;
|
|
1899
|
+
});
|
|
1878
1900
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
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
|
-
|
|
1925
|
-
static
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
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 {
|
|
@@ -2244,38 +2244,73 @@ class AbstractNavTreeService extends AbstractTreeService {
|
|
|
2244
2244
|
this.injector = injector;
|
|
2245
2245
|
// TODO Merge Dynamic Nav Too!
|
|
2246
2246
|
const configState = this.injector.get(ConfigStateService);
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
this.
|
|
2247
|
+
this.subscription = configState
|
|
2248
|
+
.createOnUpdateStream((state) => state)
|
|
2249
|
+
.subscribe(() => this.refresh());
|
|
2250
|
+
this.permissionService = injector.get(PermissionCheckerService);
|
|
2251
|
+
this.othersGroup = injector.get(OTHERS_GROUP);
|
|
2252
|
+
this.compareFunc = injector.get(SORT_COMPARE_FUNC);
|
|
2253
|
+
}
|
|
2254
|
+
isGranted({ requiredPolicy }) {
|
|
2255
|
+
return this.permissionService.getGrantedPolicy(requiredPolicy);
|
|
2256
|
+
}
|
|
2257
|
+
hasChildren(identifier) {
|
|
2258
|
+
const node = this.find(item => item[this.id] === identifier);
|
|
2259
|
+
return Boolean(node?.children?.length);
|
|
2260
|
+
}
|
|
2261
|
+
hasInvisibleChild(identifier) {
|
|
2262
|
+
const node = this.find(item => item[this.id] === identifier);
|
|
2263
|
+
return node?.children?.some(child => child.invisible) || false;
|
|
2264
|
+
}
|
|
2265
|
+
/* istanbul ignore next */
|
|
2266
|
+
ngOnDestroy() {
|
|
2267
|
+
this.subscription.unsubscribe();
|
|
2268
|
+
}
|
|
2269
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2270
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService });
|
|
2271
|
+
}
|
|
2272
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService, decorators: [{
|
|
2273
|
+
type: Injectable
|
|
2274
|
+
}], ctorParameters: () => [{ type: i0.Injector }] });
|
|
2275
|
+
class RoutesService extends AbstractNavTreeService {
|
|
2276
|
+
/**
|
|
2277
|
+
*
|
|
2278
|
+
*/
|
|
2279
|
+
constructor(injector) {
|
|
2280
|
+
super(injector);
|
|
2281
|
+
const configState = this.injector.get(ConfigStateService);
|
|
2282
|
+
configState.getAll$().pipe(map$1(appState => appState?.navigation)).subscribe(navigation => {
|
|
2251
2283
|
if (!navigation?.menus) {
|
|
2252
2284
|
this.refresh();
|
|
2253
2285
|
return;
|
|
2254
2286
|
}
|
|
2255
2287
|
const dynamicRoutes = this.convertNavigationToRoutes(navigation.menus);
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2288
|
+
dynamicRoutes.forEach(newRoute => {
|
|
2289
|
+
// تحقق إذا يوجد route بنفس key أو path
|
|
2290
|
+
const existing = this.flat.find(r => (r.key && r.key === newRoute.key) || r.path === newRoute.path);
|
|
2291
|
+
if (existing) {
|
|
2292
|
+
// استبدال القديم باستخدام patch
|
|
2293
|
+
this.patch(existing[this.id], newRoute);
|
|
2294
|
+
}
|
|
2295
|
+
else {
|
|
2296
|
+
// إضافة route جديد
|
|
2297
|
+
this.add([newRoute]);
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2259
2300
|
});
|
|
2260
|
-
this.permissionService = injector.get(PermissionCheckerService);
|
|
2261
|
-
this.othersGroup = injector.get(OTHERS_GROUP);
|
|
2262
|
-
this.compareFunc = injector.get(SORT_COMPARE_FUNC);
|
|
2263
2301
|
}
|
|
2264
2302
|
// Add helper method to convert NavigationMenuItem to Route
|
|
2265
2303
|
convertNavigationToRoutes(menuItems, parentName) {
|
|
2266
|
-
const seenKeys = new Set();
|
|
2267
2304
|
return menuItems
|
|
2268
2305
|
.filter(item => item.isEnabled &&
|
|
2269
|
-
item.isVisible
|
|
2270
|
-
!!item.key &&
|
|
2271
|
-
!seenKeys.has(item.key) && seenKeys.add(item.key))
|
|
2306
|
+
item.isVisible)
|
|
2272
2307
|
.map(item => {
|
|
2273
2308
|
const route = {
|
|
2274
|
-
name: item.
|
|
2309
|
+
name: item.displayName ?? '',
|
|
2310
|
+
key: item.name ?? '',
|
|
2275
2311
|
parentName,
|
|
2276
2312
|
path: this.extractPathFromUrl(item.url) || item.name.toLowerCase(),
|
|
2277
2313
|
requiredPolicy: item.permission || undefined,
|
|
2278
|
-
key: item.key,
|
|
2279
2314
|
order: item.order,
|
|
2280
2315
|
invisible: !item.isVisible,
|
|
2281
2316
|
iconClass: item.icon || undefined,
|
|
@@ -2300,28 +2335,6 @@ class AbstractNavTreeService extends AbstractTreeService {
|
|
|
2300
2335
|
return url; // Return as-is if not a valid URL
|
|
2301
2336
|
}
|
|
2302
2337
|
}
|
|
2303
|
-
isGranted({ requiredPolicy }) {
|
|
2304
|
-
return this.permissionService.getGrantedPolicy(requiredPolicy);
|
|
2305
|
-
}
|
|
2306
|
-
hasChildren(identifier) {
|
|
2307
|
-
const node = this.find(item => item[this.id] === identifier);
|
|
2308
|
-
return Boolean(node?.children?.length);
|
|
2309
|
-
}
|
|
2310
|
-
hasInvisibleChild(identifier) {
|
|
2311
|
-
const node = this.find(item => item[this.id] === identifier);
|
|
2312
|
-
return node?.children?.some(child => child.invisible) || false;
|
|
2313
|
-
}
|
|
2314
|
-
/* istanbul ignore next */
|
|
2315
|
-
ngOnDestroy() {
|
|
2316
|
-
this.subscription.unsubscribe();
|
|
2317
|
-
}
|
|
2318
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2319
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService });
|
|
2320
|
-
}
|
|
2321
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AbstractNavTreeService, decorators: [{
|
|
2322
|
-
type: Injectable
|
|
2323
|
-
}], ctorParameters: () => [{ type: i0.Injector }] });
|
|
2324
|
-
class RoutesService extends AbstractNavTreeService {
|
|
2325
2338
|
hasPathOrChild(item) {
|
|
2326
2339
|
return Boolean(item.path) || this.hasChildren(item.name);
|
|
2327
2340
|
}
|
|
@@ -2331,13 +2344,13 @@ class RoutesService extends AbstractNavTreeService {
|
|
|
2331
2344
|
get groupedVisible$() {
|
|
2332
2345
|
return this.visible$.pipe(map$1(items => items.filter(item => this.hasPathOrChild(item))), map$1(visible => this.createGroupedTree(visible)));
|
|
2333
2346
|
}
|
|
2334
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: RoutesService, deps:
|
|
2347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: RoutesService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2335
2348
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: RoutesService, providedIn: 'root' });
|
|
2336
2349
|
}
|
|
2337
2350
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: RoutesService, decorators: [{
|
|
2338
2351
|
type: Injectable,
|
|
2339
2352
|
args: [{ providedIn: 'root' }]
|
|
2340
|
-
}] });
|
|
2353
|
+
}], ctorParameters: () => [{ type: i0.Injector }] });
|
|
2341
2354
|
|
|
2342
2355
|
class RoutesHandler {
|
|
2343
2356
|
routes;
|