@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.
Files changed (70) hide show
  1. package/README.md +3 -0
  2. package/eslint.config.js +48 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +20 -0
  5. package/project.json +29 -0
  6. package/src/index.ts +9 -0
  7. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.html +7 -0
  8. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.scss +8 -0
  9. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.component.ts +125 -0
  10. package/src/lib/core/components/app-breadcrumb/app-breadcrumb.interface.ts +15 -0
  11. package/src/lib/core/components/app-header/app-header.component.html +26 -0
  12. package/src/lib/core/components/app-header/app-header.component.scss +0 -0
  13. package/src/lib/core/components/app-header/app-header.component.ts +48 -0
  14. package/src/lib/core/components/app-side-menu/app-side-menu.component.html +20 -0
  15. package/src/lib/core/components/app-side-menu/app-side-menu.component.scss +0 -0
  16. package/src/lib/core/components/app-side-menu/app-side-menu.component.ts +30 -0
  17. package/src/lib/core/components/app-side-menu/routes-names.ts +28 -0
  18. package/src/lib/core/components/app-side-menu/side-menu-items.ts +45 -0
  19. package/src/lib/core/components/app-side-menu/side-menu.ts +12 -0
  20. package/src/lib/core/components/global-loader/global-loader.component.html +18 -0
  21. package/src/lib/core/components/global-loader/global-loader.component.scss +0 -0
  22. package/src/lib/core/components/global-loader/global-loader.component.spec.ts +24 -0
  23. package/src/lib/core/components/global-loader/global-loader.component.ts +16 -0
  24. package/src/lib/core/components/index.ts +4 -0
  25. package/src/lib/core/components/no-access/no-access.component.html +10 -0
  26. package/src/lib/core/components/no-access/no-access.component.scss +0 -0
  27. package/src/lib/core/components/no-access/no-access.component.ts +19 -0
  28. package/src/lib/core/core-config.ts +11 -0
  29. package/src/lib/core/directives/index.ts +1 -0
  30. package/src/lib/core/directives/permissions.directive.ts +114 -0
  31. package/src/lib/core/enums/index.ts +1 -0
  32. package/src/lib/core/enums/user-permissions.enum.ts +36 -0
  33. package/src/lib/core/guards/auth.guard.ts +17 -0
  34. package/src/lib/core/guards/index.ts +2 -0
  35. package/src/lib/core/guards/permissions.guard.ts +17 -0
  36. package/src/lib/core/handlers/http-context-handler.ts +9 -0
  37. package/src/lib/core/handlers/storage-handler.ts +37 -0
  38. package/src/lib/core/handlers/stortage.ts +7 -0
  39. package/src/lib/core/interceptors/app-http-config.ts +8 -0
  40. package/src/lib/core/interceptors/base-http.interceptor.ts +44 -0
  41. package/src/lib/core/interceptors/global-http-error.interceptor.ts +21 -0
  42. package/src/lib/core/interceptors/index.ts +3 -0
  43. package/src/lib/core/interfaces/index.ts +3 -0
  44. package/src/lib/core/interfaces/jwt-token-decoded.interface.ts +7 -0
  45. package/src/lib/core/interfaces/list-response.interface.ts +5 -0
  46. package/src/lib/core/interfaces/router-data.interface.ts +20 -0
  47. package/src/lib/core/interfaces/user-info.interface.ts +4 -0
  48. package/src/lib/core/interfaces/user-profile-data.ts +19 -0
  49. package/src/lib/core/services/auth.service.ts +63 -0
  50. package/src/lib/core/services/base-http-service/base-http-response.ts +14 -0
  51. package/src/lib/core/services/base-http-service/base-http-types.ts +42 -0
  52. package/src/lib/core/services/base-http-service/base-http-utils.ts +48 -0
  53. package/src/lib/core/services/base-http-service/base-http.service.ts +106 -0
  54. package/src/lib/core/services/base-http-service/http-context-handler.ts +9 -0
  55. package/src/lib/core/services/base-http-service/index.ts +4 -0
  56. package/src/lib/core/services/base-pagination-service/base-pagination.service.ts +59 -0
  57. package/src/lib/core/services/base-pagination-service/index.ts +3 -0
  58. package/src/lib/core/services/base-pagination-service/list-options.interface.ts +8 -0
  59. package/src/lib/core/services/base-pagination-service/pagination.ts +6 -0
  60. package/src/lib/core/services/enviroment.base.service.ts +16 -0
  61. package/src/lib/core/services/error-handler.service.ts +74 -0
  62. package/src/lib/core/services/index.ts +8 -0
  63. package/src/lib/core/services/jwt-decoder.service.ts +23 -0
  64. package/src/lib/core/services/loader.service.ts +32 -0
  65. package/src/lib/core/services/lov.service.ts +22 -0
  66. package/src/lib/core/services/permissions.service.ts +66 -0
  67. package/src/lib/core/services/toaster.service.ts +65 -0
  68. package/tsconfig.json +26 -0
  69. package/tsconfig.lib.json +11 -0
  70. package/tsconfig.lib.prod.json +9 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # core
2
+
3
+ This library was generated with [Nx](https://nx.dev).
@@ -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
+ ];
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/libs/core",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
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,7 @@
1
+ @if(isShownBreadcrumb) {
2
+ <nav class="flex gap-x-3 text-[12px] w-full items-center mb-5">
3
+ <div class="card flex justify-center">
4
+ <p-breadcrumb [model]="items"></p-breadcrumb>
5
+ </div>
6
+ </nav>
7
+ }
@@ -0,0 +1,8 @@
1
+ .p-breadcrumb-list {
2
+ li:first-child a {
3
+ @apply text-secondary;
4
+ }
5
+ .p-breadcrumb-separator {
6
+ transform: scale(0.7);
7
+ }
8
+ }
@@ -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>
@@ -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>
@@ -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,12 @@
1
+ export interface SideMenuItem {
2
+ label: string;
3
+ icon: string;
4
+ link: string;
5
+ permissionKey?: string;
6
+ }
7
+
8
+ export interface RouteNameItem {
9
+ [key: string]: {
10
+ name: string;
11
+ };
12
+ }
@@ -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
+ }
@@ -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,4 @@
1
+ export * from './app-breadcrumb/app-breadcrumb.component';
2
+ export * from './app-header/app-header.component';
3
+ export * from './app-side-menu/app-side-menu.component';
4
+ export * from './global-loader/global-loader.component';
@@ -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>
@@ -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';