Ver código fonte

add core and header and nav bar

IbrahimNour 1 ano atrás
pai
commit
a44f207edb
34 arquivos alterados com 458 adições e 39 exclusões
  1. 7 0
      src/app/core/base/base-form.ts
  2. 30 0
      src/app/core/base/common-base.ts
  3. 8 0
      src/app/core/directives/max-length-directive.directive.spec.ts
  4. 17 0
      src/app/core/directives/max-length-directive.directive.ts
  5. 5 0
      src/app/core/models/nav-items.model.ts
  6. 66 0
      src/app/core/services/api.service.ts
  7. 16 0
      src/app/core/services/authentication/auth.service.spec.ts
  8. 15 0
      src/app/core/services/authentication/auth.service.ts
  9. 29 22
      src/app/modules/components/header/header.component.html
  10. 31 0
      src/app/modules/components/header/header.component.scss
  11. 12 14
      src/app/modules/components/side-nav/side-nav.component.html
  12. 8 1
      src/app/modules/components/side-nav/side-nav.component.scss
  13. 59 2
      src/app/modules/components/side-nav/side-nav.component.ts
  14. BIN
      src/assets/images/Group 82.jpg
  15. 14 0
      src/assets/images/analytics.svg
  16. 3 0
      src/assets/images/attendance.svg
  17. 3 0
      src/assets/images/calendar-days.svg
  18. 7 0
      src/assets/images/chat.svg
  19. 8 0
      src/assets/images/contract.svg
  20. 8 0
      src/assets/images/dashboard.svg
  21. 14 0
      src/assets/images/employee.svg
  22. BIN
      src/assets/images/fox-hub-2.jpg
  23. 5 0
      src/assets/images/human-resource.svg
  24. 12 0
      src/assets/images/logout.svg
  25. 3 0
      src/assets/images/notification.svg
  26. 18 0
      src/assets/images/order.svg
  27. 5 0
      src/assets/images/search-for-staff-svgrepo-com.svg
  28. 12 0
      src/assets/images/setting.svg
  29. 8 0
      src/assets/images/task-manager.svg
  30. 10 0
      src/assets/images/teams.svg
  31. 7 0
      src/assets/images/tracker.svg
  32. 2 0
      src/assets/scss/variables.scss
  33. 4 0
      src/index.html
  34. 12 0
      src/styles.scss

+ 7 - 0
src/app/core/base/base-form.ts

@@ -0,0 +1,7 @@
+import { FormGroup } from '@angular/forms';
+
+export interface BaseForm {
+  form: FormGroup;
+  onSubmit(): void;
+  initForm(): void;
+}

+ 30 - 0
src/app/core/base/common-base.ts

@@ -0,0 +1,30 @@
+import { Directive, OnDestroy } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+import { Subject } from 'rxjs';
+@Directive({
+  selector: '[appComponentBase]',
+})
+export abstract class ComponentBase implements OnDestroy {
+  destroy$ = new Subject<void>();
+  loading: boolean = false;
+  public ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+    this.destroy$.unsubscribe();
+  }
+
+  markControlAsToushed(form: FormGroup): void {
+    Object.values(form.controls).forEach((control) => {
+      control.markAsTouched();
+    });
+  }
+
+  submitValidity(form: FormGroup): boolean {
+    if (!form.valid) {
+      this.markControlAsToushed(form);
+      return false;
+    }
+
+    return true;
+  }
+}

+ 8 - 0
src/app/core/directives/max-length-directive.directive.spec.ts

@@ -0,0 +1,8 @@
+import { MaxLengthDirectiveDirective } from './max-length-directive.directive';
+
+describe('MaxLengthDirectiveDirective', () => {
+  it('should create an instance', () => {
+    const directive = new MaxLengthDirectiveDirective();
+    expect(directive).toBeTruthy();
+  });
+});

+ 17 - 0
src/app/core/directives/max-length-directive.directive.ts

@@ -0,0 +1,17 @@
+import { Directive, HostListener, Input } from '@angular/core';
+
+@Directive({
+  selector: '[appMaxLengthDirective]',
+})
+export class MaxLengthDirectiveDirective {
+  @Input() appMaxLengthDirective!: number;
+
+  constructor() {}
+
+  @HostListener('input', ['$event.target'])
+  onInput(target: HTMLInputElement, event: Event) {
+    if (target.value.length > this.appMaxLengthDirective) {
+      target.value = target.value.slice(0, this.appMaxLengthDirective);
+    }
+  }
+}

+ 5 - 0
src/app/core/models/nav-items.model.ts

@@ -0,0 +1,5 @@
+export interface NAV_ITEMS {
+  name: string;
+  img: string;
+  index: number;
+}

+ 66 - 0
src/app/core/services/api.service.ts

@@ -0,0 +1,66 @@
+import { Observable } from 'rxjs';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { environment } from 'src/environments/environment.development';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class ApiService {
+  constructor(private readonly http: HttpClient, private readonly translate: TranslateService) {}
+
+  getUploadHeaders() {
+    const token: string | null = localStorage.getItem('token') || null;
+    const language: string = localStorage.getItem('lang')
+      ? String(localStorage.getItem('lang'))
+      : this.translate.defaultLang;
+
+    if (token) {
+      return {
+        headers: new HttpHeaders().set('lang', language).set('Authorization', 'Bearer ' + token),
+      };
+    } else {
+      return {
+        headers: new HttpHeaders().set('lang', language).set('AppId', '107'),
+      };
+    }
+  }
+
+  public get<TResponse>(type: string, params?: any): Observable<TResponse> {
+    return this.http.get<TResponse>(environment.baseURL + type, {
+      params: { ...params },
+      ...this.getUploadHeaders(),
+    });
+  }
+
+  public post<TInput, TResponse>(type: string, data: TInput): Observable<TResponse> {
+    // if (type === 'File/Upload') {
+    //   return this.http.post<TResponse>(environment.BASE_FILE_URL + type, data, {
+    //     ...this.getUploadHeaders(),
+    //   });
+    // }
+    return this.http.post<TResponse>(environment.baseURL + type, data ?? {}, {
+      ...this.getUploadHeaders(),
+    });
+  }
+
+  public put<TInput, TResponse>(type: string, data: TInput, params?: any): Observable<TResponse> {
+    return this.http.put<TResponse>(environment.baseURL + type, data, {
+      params: { ...params },
+      ...this.getUploadHeaders(),
+    });
+  }
+
+  public delete<TInput, TResponse>(type: string, id?: TInput): Observable<TResponse> {
+    if (id) {
+      return this.http.delete<TResponse>(environment.baseURL + type + id, {
+        ...this.getUploadHeaders(),
+      });
+    } else {
+      return this.http.delete<TResponse>(environment.baseURL + type, {
+        ...this.getUploadHeaders(),
+      });
+    }
+  }
+}

+ 16 - 0
src/app/core/services/authentication/auth.service.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AuthService } from './auth.service';
+
+describe('AuthService', () => {
+  let service: AuthService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(AuthService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 15 - 0
src/app/core/services/authentication/auth.service.ts

@@ -0,0 +1,15 @@
+import { Injectable } from '@angular/core';
+import { VerifySSP } from '@core/models/authentication.model';
+import { ApiService } from '../api.service';
+import { Observable } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class AuthService {
+  constructor(private readonly apiService: ApiService) {}
+
+  verifySSP(data: VerifySSP): Observable<any> {
+    return this.apiService.post<VerifySSP, any>('', data);
+  }
+}

+ 29 - 22
src/app/modules/components/header/header.component.html

@@ -1,34 +1,41 @@
-<header>
-  <h1>Good morning, Mohamed!</h1>
-  <div>
-    <mat-chip-listbox aria-label="Fish selection">
-      <mat-chip-option>Check in </mat-chip-option>
-      <mat-chip-option>Check out</mat-chip-option>
-    </mat-chip-listbox>
-  </div>
+<header fxLayout="row wrap" fxLayoutAlign="space-between center">
+  <h1 fxFlex.lt-md="100" fxFlex.gt-sm="50">Good morning, Mohamed!</h1>
 
-  <div class="search">
-    <mat-form-field class="example-full-width" appearance="outline">
+  <div
+    class="gap-20"
+    fxLayout="row wrap"
+    fxLayoutAlign="end center"
+    fxFlex.lt-md="100"
+    fxFlex.gt-sm="50"
+  >
+    <div class="search" fxHide.lt-sm>
       <input type="search" matInput placeholder="Search..." />
-    </mat-form-field>
-  </div>
-  <div>
-    <button
-      mat-icon-button
+    </div>
+
+    <div
+      fxLayout="row"
+      fxLayoutAlign="start center"
+      class="gap-10"
+      fxHide.lt-sm
+    >
+      <img class="ptr" src="/assets/images/calendar-days.svg" alt="" title="" />
+      <img class="ptr" src="/assets/images/notification.svg" alt="" title="" />
+      <img class="ptr" src="/assets/images/chat.svg" alt="" title="" />
+    </div>
+
+    <img
+      class="profile"
+      src="../../../../assets/images/fox-hub-2.jpg"
+      alt=""
+      title=""
       [matMenuTriggerFor]="menu"
       aria-label="Example icon-button with a menu"
-    >
-      <mat-icon>more_vert</mat-icon>
-    </button>
+    />
     <mat-menu #menu="matMenu">
       <button mat-menu-item>
         <mat-icon>dialpad</mat-icon>
         <span>Redial</span>
       </button>
-      <button mat-menu-item disabled>
-        <mat-icon>voicemail</mat-icon>
-        <span>Check voice mail</span>
-      </button>
       <button mat-menu-item>
         <mat-icon>notifications_off</mat-icon>
         <span>Disable alerts</span>

+ 31 - 0
src/app/modules/components/header/header.component.scss

@@ -0,0 +1,31 @@
+@mixin main-header() {
+  app-header {
+    header {
+      width: 100%;
+      background-color: $main-color;
+      border-radius: 20px;
+      padding: 10px 20px;
+      h1 {
+        color: $white-color;
+        font-size: 20px;
+      }
+
+      .search {
+        width: 50%;
+        input {
+          width: 100%;
+          background-color: $white-color;
+          border: none;
+          padding: 10px;
+          border-radius: 20px;
+        }
+      }
+
+      .profile {
+        object-fit: cover;
+        border-radius: 50%;
+        cursor: pointer;
+      }
+    }
+  }
+}

+ 12 - 14
src/app/modules/components/side-nav/side-nav.component.html

@@ -1,24 +1,22 @@
 <nav fxLayout="column" fxLayoutAlign="start center">
   <img src="../../../../assets/images/logo.svg" alt="" title="" />
   <ul fxLayout="column" fxLayoutAlign="start center" class="gap-10">
-    <li>
-      <a routerLink="['']">Dashboard</a>
+    <li *ngFor="let item of navItems">
+      <img class="nav-img" [src]="item.img" alt="" [title]="item.name" />
     </li>
-    <li><a [routerLink]="['']">Employees</a></li>
-    <li><a routerLink="['']">Teams</a></li>
-    <li><a routerLink="['']">Contracts</a></li>
-    <li><a routerLink="['']">Analytics</a></li>
-    <li><a routerLink="['']">Task Management </a></li>
-    <li><a routerLink="['']">Tracker</a></li>
-    <li><a routerLink="['']">Search for employees</a></li>
-    <li><a routerLink="['']"> Human recourse</a></li>
-    <li><a routerLink="['']">Attendance</a></li>
-    <li><a routerLink="['']">Orders</a></li>
   </ul>
   <div class="absolute bottom">
     <ul>
-      <li><a routerLink="['']">Setting</a></li>
-      <li><a routerLink="['']">Close</a></li>
+      <li class="mb-1">
+        <a routerLink="['']">
+          <img src="../../../../assets/images/setting.svg" />
+        </a>
+      </li>
+      <li>
+        <a routerLink="['']"
+          ><img src="../../../../assets/images/logout.svg"
+        /></a>
+      </li>
     </ul>
   </div>
 </nav>

+ 8 - 1
src/app/modules/components/side-nav/side-nav.component.scss

@@ -10,7 +10,7 @@
       ul {
         list-style: none;
         color: $white-color;
-        padding: 40px 0;
+        padding: 40px 40px;
         li {
           a {
             color: $white-color;
@@ -22,6 +22,13 @@
           }
         }
       }
+
+      .nav-img {
+        width: 30px;
+        height: 30px;
+        object-fit: cover;
+        cursor: pointer;
+      }
     }
   }
 }

+ 59 - 2
src/app/modules/components/side-nav/side-nav.component.ts

@@ -1,10 +1,67 @@
 import { Component } from '@angular/core';
+import { NAV_ITEMS } from '@core/models/nav-items.model';
 
 @Component({
   selector: 'app-side-nav',
   templateUrl: './side-nav.component.html',
-  styleUrls: ['./side-nav.component.scss']
+  styleUrls: ['./side-nav.component.scss'],
 })
 export class SideNavComponent {
-
+  navItems: NAV_ITEMS[] = [
+    {
+      index: 1,
+      name: 'Dashboard',
+      img: '../../../../assets/images/dashboard.svg',
+    },
+    {
+      index: 2,
+      name: 'Employees',
+      img: '../../../../assets/dashboard.svg',
+    },
+    {
+      index: 3,
+      name: 'Teams',
+      img: '../../../../assets/images/teams.svg',
+    },
+    {
+      index: 4,
+      name: 'Contracts',
+      img: '../../../../assets/images/dashboard.svg',
+    },
+    {
+      index: 5,
+      name: 'Analytics',
+      img: '../../../../assets/images/analytics.svg',
+    },
+    {
+      index: 6,
+      name: 'Task Management',
+      img: '../../../../assets/images/task-manager.svg',
+    },
+    {
+      index: 7,
+      name: 'Tracker',
+      img: '../../../../assets/images/tracker.svg',
+    },
+    {
+      index: 8,
+      name: 'Search for employees',
+      img: '../../../../assets/images/search-for-staff-svgrepo-com.svg',
+    },
+    {
+      index: 9,
+      name: 'Human recourse',
+      img: '../../../../assets/images/human-resource.svg',
+    },
+    {
+      index: 10,
+      name: 'Attendance',
+      img: '../../../../assets/images/attendance.svg',
+    },
+    {
+      index: 11,
+      name: 'Orders',
+      img: '../../../../assets/images/order.svg',
+    },
+  ];
 }

BIN
src/assets/images/Group 82.jpg


Diferenças do arquivo suprimidas por serem muito extensas
+ 14 - 0
src/assets/images/analytics.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 3 - 0
src/assets/images/attendance.svg


+ 3 - 0
src/assets/images/calendar-days.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="17.5" height="20" viewBox="0 0 17.5 20">
+  <path id="calendar-days" d="M5.938.938a.938.938,0,1,0-1.875,0V2.5H2.5A2.5,2.5,0,0,0,0,5V17.5A2.5,2.5,0,0,0,2.5,20H15a2.5,2.5,0,0,0,2.5-2.5V5A2.5,2.5,0,0,0,15,2.5H13.438V.938a.938.938,0,0,0-1.875,0V2.5H5.938ZM1.875,7.5H5V9.688H1.875Zm0,4.062H5v2.5H1.875Zm5,0h3.75v2.5H6.875Zm5.625,0h3.125v2.5H12.5Zm3.125-1.875H12.5V7.5h3.125Zm0,6.25V17.5a.627.627,0,0,1-.625.625H12.5V15.938Zm-5,0v2.188H6.875V15.938ZM5,15.938v2.188H2.5a.627.627,0,0,1-.625-.625V15.938Zm5.625-6.25H6.875V7.5h3.75Z" fill="#fff"/>
+</svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 7 - 0
src/assets/images/chat.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 8 - 0
src/assets/images/contract.svg


+ 8 - 0
src/assets/images/dashboard.svg

@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+  <g id="Group_81" data-name="Group 81" transform="translate(-50 -102)">
+    <rect id="Rectangle_42" data-name="Rectangle 42" width="20" height="20" transform="translate(50 102)" fill="rgba(255,255,255,0)"/>
+    <g id="_4006459f3a94c6d79ad60b887305347c" data-name="4006459f3a94c6d79ad60b887305347c" transform="translate(40 92)">
+      <path id="Path_1165" data-name="Path 1165" d="M10,21.667V30h8.333V21.667Zm13.333,1.665h5v5h-5Zm-1.665-1.665V30H30V21.667Zm1.665-10h5v5h-5ZM21.667,10v8.333H30V10Zm-10,1.667h5v5h-5ZM10,10v8.333h8.333V10Z" transform="translate(0 0)" fill="#fff"/>
+    </g>
+  </g>
+</svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 14 - 0
src/assets/images/employee.svg


BIN
src/assets/images/fox-hub-2.jpg


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
src/assets/images/human-resource.svg


+ 12 - 0
src/assets/images/logout.svg

@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+  <g id="Group_331" data-name="Group 331" transform="translate(-50 -663)">
+    <g id="Rectangle_45" data-name="Rectangle 45" transform="translate(50 663)" fill="#fff" stroke="#707070" stroke-width="1" opacity="0">
+      <rect width="20" height="20" stroke="none"/>
+      <rect x="0.5" y="0.5" width="19" height="19" fill="none"/>
+    </g>
+    <g id="cd75a5103a07e98935e36e2acdba9a42" transform="translate(51 664)">
+      <path id="Path_1132" data-name="Path 1132" d="M22.4,36.7v1.231a7.891,7.891,0,1,1-6.765,0V36.7a9.019,9.019,0,1,0,6.765,0Z" transform="translate(-10 -36.372)" fill="#fff"/>
+      <path id="Path_1133" data-name="Path 1133" d="M438.7,18.9h2.254v6.763H438.7Z" transform="translate(-430.811 -18.9)" fill="#fff"/>
+    </g>
+  </g>
+</svg>

+ 3 - 0
src/assets/images/notification.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="17.498" height="20" viewBox="0 0 17.498 20">
+  <path id="bell" d="M8.771,0a1.249,1.249,0,0,0-1.25,1.25v.7a5.939,5.939,0,0,0-5,5.863v1.3A7.819,7.819,0,0,1,.811,14l-.582.73a.937.937,0,0,0,.73,1.523H16.584a.937.937,0,0,0,.73-1.523L16.732,14a7.838,7.838,0,0,1-1.711-4.883v-1.3a5.939,5.939,0,0,0-5-5.863v-.7A1.249,1.249,0,0,0,8.771,0Zm0,3.75h.312a4.064,4.064,0,0,1,4.062,4.062v1.3A9.7,9.7,0,0,0,14.7,14.375H2.846A9.7,9.7,0,0,0,4.4,9.117v-1.3A4.064,4.064,0,0,1,8.459,3.75Zm2.5,13.75h-5a2.5,2.5,0,1,0,5,0Z" transform="translate(-0.022)" fill="#fff"/>
+</svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 18 - 0
src/assets/images/order.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
src/assets/images/search-for-staff-svgrepo-com.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 12 - 0
src/assets/images/setting.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 8 - 0
src/assets/images/task-manager.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 10 - 0
src/assets/images/teams.svg


+ 7 - 0
src/assets/images/tracker.svg

@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+  <g id="dashboard-svgrepo-com" transform="translate(0.048 0.048)">
+    <path id="Path_4264" data-name="Path 4264" d="M20.619,12.32l-3.27,3.215a1.66,1.66,0,1,0,.78.786L21.4,13.106Z" transform="translate(-6.693 -5.511)" fill="#fff"/>
+    <path id="Path_4265" data-name="Path 4265" d="M10.626,4.25A9.125,9.125,0,0,0,3.654,19.274l.166.194H17.433l.166-.194A9.125,9.125,0,0,0,10.626,4.25ZM16.9,18.361H4.351A7.985,7.985,0,0,1,2.63,14.022H4.539V12.916H2.63a7.974,7.974,0,0,1,1.909-4.77L5.9,9.5l.78-.78L5.331,7.36a7.974,7.974,0,0,1,4.742-1.981V7.316H11.18V5.384a8.007,8.007,0,0,1,7.415,7.531H16.669v1.107h1.953A7.985,7.985,0,0,1,16.9,18.361Z" transform="translate(-0.674 -1.906)" fill="#fff"/>
+    <rect id="Rectangle_1569" data-name="Rectangle 1569" width="20" height="20" transform="translate(-0.048 -0.048)" fill="rgba(0,0,0,0)"/>
+  </g>
+</svg>

+ 2 - 0
src/assets/scss/variables.scss

@@ -75,6 +75,7 @@ $warning-color: #e732321c;
 @import "../../app/authentication/auth-sign-up/auth-sign-up.component.scss";
 @import "../../app/shared/components/warning/warning.component.scss";
 @import "../../app/modules/components/side-nav/side-nav.component.scss";
+@import "../../app//modules/components/header/header.component.scss";
 @import "./spineer.scss";
 
 @mixin component-theme() {
@@ -84,6 +85,7 @@ $warning-color: #e732321c;
   @include signUp-theme();
   @include warning-theme();
   @include sid-nav-theme();
+  @include main-header();
 }
 
 @include component-theme();

+ 4 - 0
src/index.html

@@ -6,6 +6,10 @@
     <base href="/" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
     <link rel="icon" type="image/x-icon" href="favicon.ico" />
+    <link
+      rel="stylesheet"
+      href="https://fonts.googleapis.com/icon?family=Material+Icons"
+    />
   </head>
   <body>
     <app-root></app-root>

+ 12 - 0
src/styles.scss

@@ -66,6 +66,10 @@ body {
   gap: 10px;
 }
 
+.gap-20 {
+  gap: 20px;
+}
+
 .gap-5 {
   gap: 5px;
 }
@@ -129,3 +133,11 @@ input.mat-input-element {
 .bottom {
   bottom: 2rem;
 }
+
+.mat-drawer-content {
+  padding: 0 20px;
+}
+
+.mb-1 {
+  margin-bottom: 1rem;
+}