@corp-products/app-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/eslint.config.js +48 -0
- package/ng-package.json +7 -0
- package/package.json +20 -0
- package/project.json +29 -0
- package/src/index.ts +9 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.html +7 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.scss +8 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.ts +125 -0
- package/src/lib/core/components/app-breadcrumb/app-breadcrumb.interface.ts +15 -0
- package/src/lib/core/components/app-header/app-header.component.html +26 -0
- package/src/lib/core/components/app-header/app-header.component.scss +0 -0
- package/src/lib/core/components/app-header/app-header.component.ts +48 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.html +20 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.scss +0 -0
- package/src/lib/core/components/app-side-menu/app-side-menu.component.ts +30 -0
- package/src/lib/core/components/app-side-menu/routes-names.ts +28 -0
- package/src/lib/core/components/app-side-menu/side-menu-items.ts +45 -0
- package/src/lib/core/components/app-side-menu/side-menu.ts +12 -0
- package/src/lib/core/components/global-loader/global-loader.component.html +18 -0
- package/src/lib/core/components/global-loader/global-loader.component.scss +0 -0
- package/src/lib/core/components/global-loader/global-loader.component.spec.ts +24 -0
- package/src/lib/core/components/global-loader/global-loader.component.ts +16 -0
- package/src/lib/core/components/index.ts +4 -0
- package/src/lib/core/components/no-access/no-access.component.html +10 -0
- package/src/lib/core/components/no-access/no-access.component.scss +0 -0
- package/src/lib/core/components/no-access/no-access.component.ts +19 -0
- package/src/lib/core/core-config.ts +11 -0
- package/src/lib/core/directives/index.ts +1 -0
- package/src/lib/core/directives/permissions.directive.ts +114 -0
- package/src/lib/core/enums/index.ts +1 -0
- package/src/lib/core/enums/user-permissions.enum.ts +36 -0
- package/src/lib/core/guards/auth.guard.ts +17 -0
- package/src/lib/core/guards/index.ts +2 -0
- package/src/lib/core/guards/permissions.guard.ts +17 -0
- package/src/lib/core/handlers/http-context-handler.ts +9 -0
- package/src/lib/core/handlers/storage-handler.ts +37 -0
- package/src/lib/core/handlers/stortage.ts +7 -0
- package/src/lib/core/interceptors/app-http-config.ts +8 -0
- package/src/lib/core/interceptors/base-http.interceptor.ts +44 -0
- package/src/lib/core/interceptors/global-http-error.interceptor.ts +21 -0
- package/src/lib/core/interceptors/index.ts +3 -0
- package/src/lib/core/interfaces/index.ts +3 -0
- package/src/lib/core/interfaces/jwt-token-decoded.interface.ts +7 -0
- package/src/lib/core/interfaces/list-response.interface.ts +5 -0
- package/src/lib/core/interfaces/router-data.interface.ts +20 -0
- package/src/lib/core/interfaces/user-info.interface.ts +4 -0
- package/src/lib/core/interfaces/user-profile-data.ts +19 -0
- package/src/lib/core/services/auth.service.ts +63 -0
- package/src/lib/core/services/base-http-service/base-http-response.ts +14 -0
- package/src/lib/core/services/base-http-service/base-http-types.ts +42 -0
- package/src/lib/core/services/base-http-service/base-http-utils.ts +48 -0
- package/src/lib/core/services/base-http-service/base-http.service.ts +106 -0
- package/src/lib/core/services/base-http-service/http-context-handler.ts +9 -0
- package/src/lib/core/services/base-http-service/index.ts +4 -0
- package/src/lib/core/services/base-pagination-service/base-pagination.service.ts +59 -0
- package/src/lib/core/services/base-pagination-service/index.ts +3 -0
- package/src/lib/core/services/base-pagination-service/list-options.interface.ts +8 -0
- package/src/lib/core/services/base-pagination-service/pagination.ts +6 -0
- package/src/lib/core/services/enviroment.base.service.ts +16 -0
- package/src/lib/core/services/error-handler.service.ts +74 -0
- package/src/lib/core/services/index.ts +8 -0
- package/src/lib/core/services/jwt-decoder.service.ts +23 -0
- package/src/lib/core/services/loader.service.ts +32 -0
- package/src/lib/core/services/lov.service.ts +22 -0
- package/src/lib/core/services/permissions.service.ts +66 -0
- package/src/lib/core/services/toaster.service.ts +65 -0
- package/tsconfig.json +26 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.lib.prod.json +9 -0
package/README.md
ADDED
package/eslint.config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const nx = require('@nx/eslint-plugin');
|
|
2
|
+
const baseConfig = require('../../eslint.config.js');
|
|
3
|
+
|
|
4
|
+
module.exports = [
|
|
5
|
+
...baseConfig,
|
|
6
|
+
{
|
|
7
|
+
files: ['**/*.json'],
|
|
8
|
+
rules: {
|
|
9
|
+
'@nx/dependency-checks': [
|
|
10
|
+
'error',
|
|
11
|
+
{
|
|
12
|
+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
languageOptions: {
|
|
17
|
+
parser: require('jsonc-eslint-parser'),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
...nx.configs['flat/angular'],
|
|
21
|
+
...nx.configs['flat/angular-template'],
|
|
22
|
+
{
|
|
23
|
+
files: ['**/*.ts'],
|
|
24
|
+
rules: {
|
|
25
|
+
'@angular-eslint/directive-selector': [
|
|
26
|
+
'error',
|
|
27
|
+
{
|
|
28
|
+
type: 'attribute',
|
|
29
|
+
prefix: 'app',
|
|
30
|
+
style: 'camelCase',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
'@angular-eslint/component-selector': [
|
|
34
|
+
'error',
|
|
35
|
+
{
|
|
36
|
+
type: 'element',
|
|
37
|
+
prefix: 'app',
|
|
38
|
+
style: 'kebab-case',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
files: ['**/*.html'],
|
|
45
|
+
// Override or add rules here
|
|
46
|
+
rules: {},
|
|
47
|
+
},
|
|
48
|
+
];
|
package/ng-package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@corp-products/app-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"author": "shireen Omar",
|
|
5
|
+
"description": "shared UI components across our apps",
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"@angular/common": "^18.2.0",
|
|
8
|
+
"@angular/core": "^18.2.0",
|
|
9
|
+
"@corp-products/ui-components": "^0.0.1"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"angular",
|
|
13
|
+
"library"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"sideEffects": false
|
|
20
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "core",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "libs/core/src",
|
|
5
|
+
"prefix": "",
|
|
6
|
+
"projectType": "library",
|
|
7
|
+
"tags": [],
|
|
8
|
+
"targets": {
|
|
9
|
+
"build": {
|
|
10
|
+
"executor": "@nx/angular:package",
|
|
11
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}"],
|
|
12
|
+
"options": {
|
|
13
|
+
"project": "libs/core/ng-package.json"
|
|
14
|
+
},
|
|
15
|
+
"configurations": {
|
|
16
|
+
"production": {
|
|
17
|
+
"tsConfig": "libs/core/tsconfig.lib.prod.json"
|
|
18
|
+
},
|
|
19
|
+
"development": {
|
|
20
|
+
"tsConfig": "libs/core/tsconfig.lib.json"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"defaultConfiguration": "production"
|
|
24
|
+
},
|
|
25
|
+
"lint": {
|
|
26
|
+
"executor": "@nx/eslint:lint"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./lib/core/services/base-pagination-service";
|
|
2
|
+
export * from "./lib/core/components";
|
|
3
|
+
export * from "./lib/core/services";
|
|
4
|
+
export * from "./lib/core/interfaces";
|
|
5
|
+
export * from "./lib/core/guards";
|
|
6
|
+
export * from "./lib/core/interceptors";
|
|
7
|
+
export * from "./lib/core/enums";
|
|
8
|
+
export * from "./lib/core/directives";
|
|
9
|
+
export * from "./lib/core/core-config";
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {CommonModule} from '@angular/common';
|
|
2
|
+
import {Component, OnInit, ViewEncapsulation} from '@angular/core';
|
|
3
|
+
import {ActivatedRoute, NavigationEnd, Router, RouterModule} from '@angular/router';
|
|
4
|
+
import {MenuItem} from 'primeng/api';
|
|
5
|
+
import {Breadcrumb} from 'primeng/breadcrumb';
|
|
6
|
+
import {filter} from 'rxjs';
|
|
7
|
+
import {BreadCrumbExtraData, BreadcrumbItem} from './app-breadcrumb.interface';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'app-breadcrumb',
|
|
11
|
+
templateUrl: './app-breadcrumb.component.html',
|
|
12
|
+
styleUrl: './app-breadcrumb.component.scss',
|
|
13
|
+
standalone: true,
|
|
14
|
+
imports: [CommonModule, Breadcrumb, RouterModule],
|
|
15
|
+
encapsulation: ViewEncapsulation.None,
|
|
16
|
+
})
|
|
17
|
+
export class AppBreadcrumbComponent implements OnInit {
|
|
18
|
+
items: BreadcrumbItem[] | undefined;
|
|
19
|
+
isShownBreadcrumb: boolean;
|
|
20
|
+
|
|
21
|
+
constructor(private activatedRoute: ActivatedRoute, private router: Router) {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
ngOnInit(): void {
|
|
25
|
+
this.router.events
|
|
26
|
+
.pipe(filter(event => event instanceof NavigationEnd))
|
|
27
|
+
.subscribe((e) => {
|
|
28
|
+
// remove duplicates
|
|
29
|
+
this.items = (this._createBreadcrumbs(this.activatedRoute.root) as BreadcrumbItem[]).filter(
|
|
30
|
+
(item, index, self) =>
|
|
31
|
+
index === self.findIndex((t) => t.label === item.label && t.routerLink === item.routerLink)
|
|
32
|
+
);
|
|
33
|
+
this._checkHiddenBreadcrumb();
|
|
34
|
+
this._checkEmpty();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private _checkHiddenBreadcrumb() {
|
|
39
|
+
const activeBreadcrumb = this.items?.find((item) => {
|
|
40
|
+
return item.routerLink === this.router.url
|
|
41
|
+
});
|
|
42
|
+
this.isShownBreadcrumb = !!activeBreadcrumb?.isShownBreadcrumb
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private _createBreadcrumbs(activatedRoute: ActivatedRoute, routerLink = '', breadcrumbs: MenuItem[] = []): any {
|
|
46
|
+
const children: ActivatedRoute[] = activatedRoute.children;
|
|
47
|
+
|
|
48
|
+
if (children.length === 0) {
|
|
49
|
+
return breadcrumbs;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const child of children) {
|
|
53
|
+
const routeURL: string = child.snapshot.url.map(segment => segment.path).join('/');
|
|
54
|
+
if (routeURL !== '') {
|
|
55
|
+
routerLink += `/${routeURL}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let label = child.snapshot.data["breadcrumb"];
|
|
59
|
+
const notClickableBreadcrumb = !!child.snapshot.data["notClickableBreadcrumb"];
|
|
60
|
+
const isShownBreadcrumb = !!child.snapshot.data["isShownBreadcrumb"];
|
|
61
|
+
const extraBreadcrumbs: BreadCrumbExtraData[] = child.snapshot.data["extraBreadcrumbs"] || [];
|
|
62
|
+
if (!(typeof label === 'undefined' || label === null)) {
|
|
63
|
+
const data = activatedRoute.snapshot.firstChild?.data || {};
|
|
64
|
+
// Resolve dynamic breadcrumb label
|
|
65
|
+
if (label && label.startsWith("[") && label.endsWith("]")) {
|
|
66
|
+
label = this._getRecursiveKey(data, label.slice(1, -1).split("."));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Resolve dynamic URL parts
|
|
70
|
+
const resolveDynamicUrl = (route: string): string => {
|
|
71
|
+
return route.replace(/\[([^\]]+)]/g, (_, key) => this._getRecursiveKey(data, key.split(".")) || '');
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mainBreadcrumb: MenuItem = {label, routerLink, data, notClickableBreadcrumb, isShownBreadcrumb};
|
|
75
|
+
|
|
76
|
+
// Process extra breadcrumbs
|
|
77
|
+
const beforeBreadcrumbs: MenuItem[] = [];
|
|
78
|
+
const afterBreadcrumbs: MenuItem[] = [];
|
|
79
|
+
|
|
80
|
+
extraBreadcrumbs.forEach(extra => {
|
|
81
|
+
let extraLabel = extra.label || '';
|
|
82
|
+
if (extraLabel.startsWith("[") && extraLabel.endsWith("]")) {
|
|
83
|
+
extraLabel = this._getRecursiveKey(data, extraLabel.slice(1, -1).split("."));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const extraUrl = resolveDynamicUrl(extra.routerLink || '');
|
|
87
|
+
|
|
88
|
+
const extraBreadcrumb: MenuItem = {...extra, label: extraLabel, routerLink: extraUrl};
|
|
89
|
+
|
|
90
|
+
if (extra.position === "before") {
|
|
91
|
+
beforeBreadcrumbs.push(extraBreadcrumb);
|
|
92
|
+
} else {
|
|
93
|
+
afterBreadcrumbs.push(extraBreadcrumb);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Merge breadcrumbs
|
|
98
|
+
breadcrumbs.push(...beforeBreadcrumbs, mainBreadcrumb, ...afterBreadcrumbs);
|
|
99
|
+
}
|
|
100
|
+
return this._createBreadcrumbs(child, routerLink, breadcrumbs);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private _checkEmpty() {
|
|
105
|
+
if (!this.items) return
|
|
106
|
+
const lastBreadcrumbVisibility = this.items[this.items.length - 1].isShownBreadcrumb;
|
|
107
|
+
this.items = this.items.filter(b => b.label !== '');
|
|
108
|
+
this.items[this.items.length - 1].isShownBreadcrumb = lastBreadcrumbVisibility;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private _getRecursiveKey(obj: Record<string, any>, keys: string[]): any | undefined {
|
|
112
|
+
if (keys.length === 0) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const [currentKey, ...remainingKeys] = keys;
|
|
117
|
+
if (currentKey in obj) {
|
|
118
|
+
if (remainingKeys.length > 0) {
|
|
119
|
+
return this._getRecursiveKey(obj[currentKey], remainingKeys);
|
|
120
|
+
}
|
|
121
|
+
return obj[currentKey];
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {MenuItem} from "primeng/api";
|
|
2
|
+
|
|
3
|
+
export interface BreadcrumbItem {
|
|
4
|
+
notClickableBreadcrumb: boolean;
|
|
5
|
+
label: string;
|
|
6
|
+
routerLink: string;
|
|
7
|
+
isShownBreadcrumb: boolean;
|
|
8
|
+
extraBreadcrumbs?: BreadCrumbExtraData[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BreadCrumbPosition {
|
|
12
|
+
position: 'before' | 'after'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type BreadCrumbExtraData = MenuItem & BreadCrumbPosition;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<div class="px-10 flex justify-between items-center bg-white shadow-md py-3 h-16">
|
|
2
|
+
<div class="d-flex md:hidden w-fit">
|
|
3
|
+
<!-- Burger Icon for Mobile -->
|
|
4
|
+
<app-button icon="font-icon-burger-icon text-[25px]" size="large" variant="text"
|
|
5
|
+
(clickEmitter)="toggleMenu.emit()"/>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="d-flex items-center justify-content-between gap-1">
|
|
8
|
+
<app-button
|
|
9
|
+
*hasPermissions="[PermissionsActions.CREATE];key: UserPermissionsEnum.BOD"
|
|
10
|
+
iconPos="right" icon="font-icon-plus" (clickEmitter)="createButtonClicked.emit()"
|
|
11
|
+
[title]="'actions.create' | translate"/>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="flex items-center gap-1">
|
|
15
|
+
@if (userInfo) {
|
|
16
|
+
<div class="flex flex-col items-end justify-center">
|
|
17
|
+
<p class="text-sm">{{ userInfo.userName }}</p>
|
|
18
|
+
<p class="text-gray-500 mb-0 text-[10px]">{{ userInfo.email }}</p>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<img src="assets/images/user-placeholder.jpg" class="w-9 h-9 object-cover rounded-full" alt=""/>
|
|
22
|
+
}
|
|
23
|
+
<p-menu #menu [model]="items" [popup]="true"></p-menu>
|
|
24
|
+
<button (click)="menu.toggle($event)" class="mb-2"><i class="font-icon-Vector text-[6px]"></i></button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from "@angular/core";
|
|
2
|
+
import { RouterModule } from "@angular/router";
|
|
3
|
+
import { AppButtonComponent } from "@corp-products/ui-components";
|
|
4
|
+
import { MenuItem } from "primeng/api";
|
|
5
|
+
import { Menu } from "primeng/menu";
|
|
6
|
+
import { AuthService } from "../../services";
|
|
7
|
+
import { finalize } from "rxjs";
|
|
8
|
+
import { UserInfo } from "../../interfaces";
|
|
9
|
+
import { TranslatePipe } from "@ngx-translate/core";
|
|
10
|
+
import { PermissionsActions, UserPermissionsEnum } from "../../enums";
|
|
11
|
+
import { HasPermissionsDirective } from "../../directives";
|
|
12
|
+
@Component({
|
|
13
|
+
selector: "app-header",
|
|
14
|
+
templateUrl: "./app-header.component.html",
|
|
15
|
+
styleUrl: "./app-header.component.scss",
|
|
16
|
+
standalone: true,
|
|
17
|
+
imports: [RouterModule, AppButtonComponent, Menu, TranslatePipe, HasPermissionsDirective],
|
|
18
|
+
encapsulation: ViewEncapsulation.None
|
|
19
|
+
})
|
|
20
|
+
export class AppHeaderComponent {
|
|
21
|
+
@Output() createButtonClicked = new EventEmitter();
|
|
22
|
+
@Output() toggleMenu = new EventEmitter<boolean>();
|
|
23
|
+
@Input() userInfo: UserInfo;
|
|
24
|
+
items: MenuItem[] | undefined = [
|
|
25
|
+
{
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
label: "خــــروج",
|
|
29
|
+
command: () => this.logout()
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
UserPermissionsEnum = UserPermissionsEnum;
|
|
35
|
+
PermissionsActions = PermissionsActions;
|
|
36
|
+
constructor(private authService: AuthService) {}
|
|
37
|
+
|
|
38
|
+
logout(): void {
|
|
39
|
+
this.authService
|
|
40
|
+
.logoutFromSSO()
|
|
41
|
+
.pipe(
|
|
42
|
+
finalize(() => {
|
|
43
|
+
this.authService.clearAuth();
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
.subscribe();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<nav
|
|
2
|
+
[ngClass]="{ 'max-w-0 md:max-w-[500px]': !isOpen, 'max-w-[500px]': isOpen }"
|
|
3
|
+
class="md:flex h-100 bg-primary flex text-white flex-col py-2 text-center gap-y-10 transition-all w-24 hover:md:w-48 duration-500">
|
|
4
|
+
<a [routerLink]="'/'"><i class="font-icon-STC-Logo text-[20px] pt-4 block px-4"></i></a>
|
|
5
|
+
<ul class="flex flex-col flex-grow">
|
|
6
|
+
@for (item of filteredMenu; track $index) {
|
|
7
|
+
<li>
|
|
8
|
+
<a
|
|
9
|
+
[routerLinkActive]="'bg-primary_dark border-secondary'"
|
|
10
|
+
[routerLink]="item.link"
|
|
11
|
+
class="flex flex-col py-3 px-4 border-r-2 border-primary transition-all hover:bg-primary_dark hover:border-secondary">
|
|
12
|
+
<i [class]="item.icon + ' text-[17px] mb-1'"></i>
|
|
13
|
+
<span class="text-[10px] truncate">{{ item.label }}</span>
|
|
14
|
+
</a>
|
|
15
|
+
</li>
|
|
16
|
+
}
|
|
17
|
+
</ul>
|
|
18
|
+
|
|
19
|
+
<a href="#"><i class="font-icon-setting text-[20px] pb-4 block px-4"></i></a>
|
|
20
|
+
</nav>
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {Component, inject, Input, OnInit, ViewEncapsulation} from '@angular/core';
|
|
2
|
+
import { RouterModule } from '@angular/router';
|
|
3
|
+
import { SideMenuItem } from './side-menu';
|
|
4
|
+
import { BASE_SIDE_MENU_ITEMS } from './side-menu-items';
|
|
5
|
+
import {PermissionsService} from "../../services";
|
|
6
|
+
import {PermissionsActions, UserPermissionsEnum} from "../../enums";
|
|
7
|
+
import {NgClass} from "@angular/common";
|
|
8
|
+
@Component({
|
|
9
|
+
selector: "app-side-menu",
|
|
10
|
+
standalone: true,
|
|
11
|
+
imports: [RouterModule, NgClass],
|
|
12
|
+
templateUrl: "./app-side-menu.component.html",
|
|
13
|
+
styleUrl: "./app-side-menu.component.scss",
|
|
14
|
+
encapsulation: ViewEncapsulation.None
|
|
15
|
+
})
|
|
16
|
+
export class AppSideMenuComponent implements OnInit {
|
|
17
|
+
@Input() isOpen!: boolean;
|
|
18
|
+
menuItems: SideMenuItem[] = BASE_SIDE_MENU_ITEMS;
|
|
19
|
+
permissionsService = inject(PermissionsService);
|
|
20
|
+
filteredMenu: SideMenuItem[];
|
|
21
|
+
|
|
22
|
+
ngOnInit(): void {
|
|
23
|
+
this.filteredMenu = this.menuItems.filter((item) => {
|
|
24
|
+
if (!item.permissionKey) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return this.permissionsService.checkKeyHasPermission(item.permissionKey as UserPermissionsEnum, PermissionsActions.VIEW);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RouteNameItem } from "./side-menu";
|
|
2
|
+
|
|
3
|
+
export const ROUTES_NAME: RouteNameItem = {
|
|
4
|
+
board: {
|
|
5
|
+
name: '/board-page',
|
|
6
|
+
},
|
|
7
|
+
users: {
|
|
8
|
+
name: '/users',
|
|
9
|
+
},
|
|
10
|
+
// reports: {
|
|
11
|
+
// name: '/reports',
|
|
12
|
+
// },
|
|
13
|
+
meetings: {
|
|
14
|
+
name: '/meeting-page',
|
|
15
|
+
},
|
|
16
|
+
// tasks: {
|
|
17
|
+
// name: '/tasks',
|
|
18
|
+
// },
|
|
19
|
+
// imported: {
|
|
20
|
+
// name: '/imported',
|
|
21
|
+
// },
|
|
22
|
+
// exported: {
|
|
23
|
+
// name: '/exported',
|
|
24
|
+
// },
|
|
25
|
+
// board: {
|
|
26
|
+
// name: '/board',
|
|
27
|
+
// },
|
|
28
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ROUTES_NAME } from './routes-names';
|
|
2
|
+
import { SideMenuItem } from './side-menu';
|
|
3
|
+
|
|
4
|
+
export const BASE_SIDE_MENU_ITEMS: SideMenuItem[] = [
|
|
5
|
+
{
|
|
6
|
+
label: 'الإجتماعات',
|
|
7
|
+
icon: 'font-icon-menu-board',
|
|
8
|
+
link: ROUTES_NAME['meetings'].name
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
label: 'اللجان و المجالس',
|
|
12
|
+
icon: 'font-icon-people',
|
|
13
|
+
link: ROUTES_NAME['board'].name
|
|
14
|
+
},
|
|
15
|
+
// {
|
|
16
|
+
// label: 'المستخدمين',
|
|
17
|
+
// icon: 'font-icon-profile-2user',
|
|
18
|
+
// link: ROUTES_NAME['users'].name,
|
|
19
|
+
// },
|
|
20
|
+
// {
|
|
21
|
+
// label: 'الإجتماعات',
|
|
22
|
+
// icon: 'font-icon-menu-board',
|
|
23
|
+
// link: ROUTES_NAME['meetings'].name,
|
|
24
|
+
// },
|
|
25
|
+
// {
|
|
26
|
+
// label: 'مهامي',
|
|
27
|
+
// icon: 'font-icon-task-square',
|
|
28
|
+
// link: ROUTES_NAME['tasks'].name,
|
|
29
|
+
// },
|
|
30
|
+
// {
|
|
31
|
+
// label: 'الوارد',
|
|
32
|
+
// icon: 'font-icon-directbox-receive',
|
|
33
|
+
// link: ROUTES_NAME['imported'].name,
|
|
34
|
+
// },
|
|
35
|
+
// {
|
|
36
|
+
// label: 'الصادر',
|
|
37
|
+
// icon: 'font-icon-directbox-send',
|
|
38
|
+
// link: ROUTES_NAME['exported'].name,
|
|
39
|
+
// },
|
|
40
|
+
// {
|
|
41
|
+
// label: 'التقارير',
|
|
42
|
+
// icon: 'font-icon-status-up',
|
|
43
|
+
// link: ROUTES_NAME['reports'].name,
|
|
44
|
+
// },
|
|
45
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@if (loaderService.isLoading$ | async; as data) {
|
|
2
|
+
@if (data.show) {
|
|
3
|
+
<div
|
|
4
|
+
class="fixed inset-0 z-[1111] flex justify-center items-center"
|
|
5
|
+
[ngClass]="{
|
|
6
|
+
'bg-white/75': data?.isSystemLoader,
|
|
7
|
+
'absolute': !data?.isSystemLoader
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<div class="text-center flex flex-col gap-5 items-center">
|
|
11
|
+
<div
|
|
12
|
+
class="animate-spin inline-block w-12 h-12 border-[3px] border-current border-t-transparent text-gray-800 rounded-full">
|
|
13
|
+
</div>
|
|
14
|
+
<span>{{ 'loading' | translate }}</span>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
}
|
|
18
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { GlobalLoaderComponent } from 'libs/core/src/lib/core/components/global-loader/global-loader.component';
|
|
4
|
+
|
|
5
|
+
describe("LoaderComponent", () => {
|
|
6
|
+
let component: GlobalLoaderComponent;
|
|
7
|
+
let fixture: ComponentFixture<GlobalLoaderComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
declarations: [GlobalLoaderComponent]
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
fixture = TestBed.createComponent(GlobalLoaderComponent);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
fixture.detectChanges();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should create", () => {
|
|
22
|
+
expect(component).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AsyncPipe, NgClass } from '@angular/common';
|
|
2
|
+
import { Component, Input } from '@angular/core';
|
|
3
|
+
import {TranslatePipe} from "@ngx-translate/core";
|
|
4
|
+
import {LoaderService} from "../../services";
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: "global-loader",
|
|
8
|
+
templateUrl: "./global-loader.component.html",
|
|
9
|
+
styleUrls: ["./global-loader.component.scss"],
|
|
10
|
+
standalone: true,
|
|
11
|
+
imports: [AsyncPipe, NgClass, TranslatePipe]
|
|
12
|
+
})
|
|
13
|
+
export class GlobalLoaderComponent {
|
|
14
|
+
@Input() color: string;
|
|
15
|
+
constructor(public loaderService: LoaderService) {}
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="flex items-center justify-center flex-col h-full gap-4 w-full">
|
|
2
|
+
<img [src]="imageSrc" alt="no-access" class="h-[300px]"/>
|
|
3
|
+
<strong class="text-secondary font-size-22">{{ 'oops' | translate }}</strong>
|
|
4
|
+
<p class="font-size-22 text-gray-500">{{ 'do_not_have_permission' | translate }}</p>
|
|
5
|
+
|
|
6
|
+
@if (authService.isUserLoggedIn$ | async) {
|
|
7
|
+
<app-button (click)="router.navigate(['/'])" [title]="'home' | translate"/>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
</div>
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Component, inject } from "@angular/core";
|
|
2
|
+
import { Router } from "@angular/router";
|
|
3
|
+
import { AuthService } from "../../services/auth.service";
|
|
4
|
+
import { AsyncPipe } from "@angular/common";
|
|
5
|
+
import { TranslatePipe } from "@ngx-translate/core";
|
|
6
|
+
import { AppButtonComponent } from "@ui-components";
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: "app-no-access",
|
|
10
|
+
templateUrl: "./no-access.component.html",
|
|
11
|
+
styleUrls: ["./no-access.component.scss"],
|
|
12
|
+
imports: [AsyncPipe, TranslatePipe, AppButtonComponent],
|
|
13
|
+
standalone: true
|
|
14
|
+
})
|
|
15
|
+
export class NoAccessComponent {
|
|
16
|
+
imageSrc: string = "assets/images/no-access.svg";
|
|
17
|
+
authService = inject(AuthService);
|
|
18
|
+
public router = inject(Router);
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InjectionToken } from "@angular/core";
|
|
2
|
+
|
|
3
|
+
export interface CoreConfig {
|
|
4
|
+
apiUrl: string;
|
|
5
|
+
production: boolean;
|
|
6
|
+
gatewayUrl: string;
|
|
7
|
+
loginUrl: string;
|
|
8
|
+
logoutEndpoint: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const CORE_CONFIG = new InjectionToken<CoreConfig>("CoreConfig");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './permissions.directive';
|