Commit e65d8523 authored by hasan khaddour's avatar hasan khaddour

fix s

parent 1cde9970
This diff is collapsed.
......@@ -41,6 +41,10 @@ export const routes: Routes = [
path: 'projects',
loadChildren: () => import('./projects/projects.module').then(m => m.ProjectsModule)
}
, {
path: 'employees',
loadChildren: () => import('./employees/employees.module').then(m => m.EmployeesModule)
}
]
......
......@@ -4,6 +4,7 @@ import { DataStorageService } from './services/dataStorage/data-storage.service'
import { AuthenticationService } from './services/authentication/authentication.service';
import { ConfigurationService } from './services/configuration/configuration.service';
import { UserService } from './services/authentication/user.service';
import { PdfDownloaderService } from './services/pdfDownloader/pdf-downloader.service';
@NgModule({
......@@ -15,7 +16,8 @@ import { UserService } from './services/authentication/user.service';
DataStorageService,
AuthenticationService,
ConfigurationService,
UserService
UserService,
PdfDownloaderService
]
})
export class CoreModule { }
export interface User {
id: string;
employeeId: number;
userName: string
firstName :string
lastName :string,
......
......@@ -13,7 +13,10 @@ export class UserService {
getCurrentUser():User{
return JSON.parse( this.dataStorage.getItem("userDetails"));
}
getEmployeeId() :number{
return this.getCurrentUser().employeeId;
}
getUserFirstName():string{
return JSON.parse( this.dataStorage.getItem("userDetails")).firstName;
}
......
import { Injectable } from '@angular/core';
import domToImage from 'dom-to-image';
import jsPDF from 'jspdf';
import moment from 'moment';
import html2canvas from 'html2canvas';
@Injectable({
providedIn: 'root'
})
export class PdfDownloaderService {
constructor() { }
public downloadAsPdf(identifier :string){
const element = document.getElementById(`${identifier}`);
const customFont = 'data:font/truetype;base64,https://fonts.googleapis.com/icon?family=Material+Icons';
if (element) {
html2canvas(element, { scale: 3, useCORS: true }).then((canvas) => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p',
unit: 'mm',
format: 'a4',
putOnlyUsedFonts: true}); // Enable Unicode text
pdf.addFileToVFS('CustomFont.ttf', customFont);
pdf.addFont('CustomFont.ttf', 'CustomFont', 'normal');
pdf.setFont('CustomFont');
const imgWidth = 210; // A4 width in mm
const imgHeight = (canvas.height * imgWidth) / canvas.width;
pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight);
pdf.save('download.pdf'); // Save the PDF
});
}
}
}
import { HttpClient } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ConfigurationService } from '../core/services/configuration/configuration.service';
import { Observable } from 'rxjs';
import { Result } from '../core/models/result';
import { Employee } from './models/responses/employee';
import { EmployeeProfileComponent } from './pages/employee-profile/employee-profile.component';
const routes: Routes = [];
const routes: Routes = [
{path:"profile", component:EmployeeProfileComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class EmployeesRoutingModule { }
export class EmployeesRoutingModule {
}
.gradient-custom {
/* fallback for old browsers */
background: #f6d365;
/* Chrome 10-25, Safari 5.1-6 */
background: -webkit-linear-gradient(to right bottom, rgba(246, 211, 101, 1), rgba(253, 160, 133, 1));
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
background: linear-gradient(to right bottom, rgba(246, 211, 101, 1), rgba(253, 160, 133, 1))
}
\ No newline at end of file
<p>employee-profile works!</p>
<section dir="lrt" class="vh-100" style="background-color: #f4f5f7;">
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col col-lg-6 mb-4 mb-lg-0">
<div class="card mb-3" style="border-radius: .5rem;">
<div class="row g-0">
<div class="col-md-4 gradient-custom text-center text-white"
style="border-top-left-radius: .5rem; border-bottom-left-radius: .5rem;">
<img src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava1-bg.webp"
alt="Avatar" class="img-fluid my-5" style="width: 80px;" />
<h5>Marie Horwitz</h5>
<p>Web Designer</p>
<i class="far fa-edit mb-5"></i>
</div>
<div class="col-md-8">
<div class="card-body p-4">
<h6>Information</h6>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-6 mb-3">
<h6>Email</h6>
<p class="text-muted">infoexample.com</p>
</div>
<div class="col-6 mb-3">
<h6>Phone</h6>
<p class="text-muted">123 456 789</p>
</div>
</div>
<h6>Projects</h6>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-6 mb-3">
<h6>Recent</h6>
<p class="text-muted">Lorem ipsum</p>
</div>
<div class="col-6 mb-3">
<h6>Most Viewed</h6>
<p class="text-muted">Dolor sit amet</p>
</div>
</div>
<div class="d-flex justify-content-start">
<a href="#!"><i class="fab fa-facebook-f fa-lg me-3"></i></a>
<a href="#!"><i class="fab fa-twitter fa-lg me-3"></i></a>
<a href="#!"><i class="fab fa-instagram fa-lg"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
\ No newline at end of file
import { TestBed } from '@angular/core/testing';
import { EmployeesService } from './employees.service';
describe('EmployeesService', () => {
let service: EmployeesService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(EmployeesService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Result } from '../../core/models/result';
import { ConfigurationService } from '../../core/services/configuration/configuration.service';
import { Employee } from '../models/responses/employee';
import { UserService } from '../../core/services/authentication/user.service';
import { EmployeeParticipate } from '../models/responses/employeeParticipate';
import { GetEmployeeTrackHistoryRequest } from '../models/requests/getEmployeeTrackHistoryRequest';
import { EmployeeTrack } from '../../tracks/models/responses/employeeTrack';
import { UpdateEmplyeeWorkHours } from '../models/requests/updateEmployeeWorkHoursRequest';
@Injectable({
providedIn: 'root'
})
export class EmployeesService {
constructor() { }
}
constructor(
private http :HttpClient ,
private config : ConfigurationService,
private userService : UserService
) {
}
public getEmployeeById(id : number ):Observable<Result<Employee>>{
return this.http.get<Result<Employee>>(this.config.getServerUrl()+ "/Employees/"+id);
}
public getCurrentEmployee( ):Observable<Result<Employee>>{
let id = this.userService.getEmployeeId();
return this.getEmployeeById(id);
}
public getAvailableEmployees( ):Observable<Result<Employee[]>>{
//this api endpoint take a pagination , i'll use i later
return this.http.get<Result<Employee[]>>(this.config.getServerUrl + "/Employees/Available");
}
public getEmployeeParticipations(id :number ):Observable<Result<EmployeeParticipate[]>>{
return this.http.get<Result<EmployeeParticipate[]>>(`${this.config.getServerUrl}/Employees/EmployeeParticipations/employeeId=${id}`);
}
public getMyParticipation( ):Observable<Result<EmployeeParticipate[]>>{
let id = this.userService.getEmployeeId();
return this.getEmployeeParticipations(id);
}
public getEmployeeTrackHistory( request: GetEmployeeTrackHistoryRequest):Observable<Result<EmployeeTrack[]>>{
let pagination=''
if(request.pageSize && request.pageNumber){
pagination = `&pageSize=${request.pageSize}&pageNumber=${request.pageNumber}`
}
let query =`employeeId=${request.employeeId}&projectId=${request.projectId}${pagination}`
return this
.http
.get<Result<EmployeeTrack[]>>(
`${this.config.getServerUrl}/Employees/TrackHistory/?${query}`
);
}
public getMyTrackHistory(projectId : number):Observable<Result<EmployeeTrack[]>>{
let query =`employeeId=${this.userService.getEmployeeId()}&projectId=${projectId}`
return this
.http
.get<Result<EmployeeTrack[]>>(
`${this.config.getServerUrl}/Employees/TrackHistory/?${query}`
);
}
public postEmployeeWorkHours( request: UpdateEmplyeeWorkHours):Observable<Result<void>>{
return this
.http
.post<Result<void>>(
`${this.config.getServerUrl}/Employees/UpdateWorkHours/`,
request
);
}
}
<p>project-header works!</p>
<div class="row mb-5 d-flex justify-content-start">
<div class="col-3 d-none text-left mb-4">
<h5 class="mb-0 text-uppercase"><small>الجمهورية العربية السورية</small></h5>
<h6>
<small>مركز الدراسات والبحوث العلمية </small>
<br>
<small>المعهد العالي للعلوم التطبيقية والتكنولوجيا</small>
</h6>
</div>
<div class="col-6 col-sm-12 text-center mb-4">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto mb-4" alt="...">
<h3 class="mb-0 text-uppercase">{{projectInfo.name}}</h3>
<p> <small>{{projectInfo.description}}</small></p>
<p class="small text-muted"><small>تاريخ البدء {{projectInfo.startDate | date}} <br>تاريخ الانتهاء المتوقع {{projectInfo.expectedEndDate| date}}</small></p>
</div>
</div>
<hr>
\ No newline at end of file
import { Component } from '@angular/core';
import { Component, Input } from '@angular/core';
import { ProjectInfo } from '../../models/valueObjects/ProjectInfo';
import { ProposalInfo } from '../../models/valueObjects/proposalInfo';
import { Employee } from '../../../employees/models/responses/employee';
@Component({
selector: 'project-header',
......@@ -6,5 +9,7 @@ import { Component } from '@angular/core';
styleUrl: './project-header.component.css'
})
export class ProjectHeaderComponent {
@Input() projectInfo :ProjectInfo
}
import { Aggreement } from "../valueObjects/Aggreement"
import { FinancialFund } from "../valueObjects/FinancialFund"
import { ProjectInfo } from "../valueObjects/ProjectInfo"
import { ProposalInfo } from "../valueObjects/proposalInfo"
export class CreateProjectRequest {
projectInfo :ProjectInfo
proposalInfo :ProposalInfo
projectAggreement :Aggreement
financialFund :FinancialFund
teamLeaderId :number
projectManagerId :number
proposerId :number
executerId :number
}
\ No newline at end of file
......@@ -24,6 +24,6 @@ export class Project
proposerId:number
proposer:Customer
steps :Step[]
participants:EmployeeParticipate[]
employeeParticipates:EmployeeParticipate[]
financialFund:FinancialFund
}
<form [formGroup]="projectForm">
<mat-form-field>
<input type="text" matInput placeholder="Project Manager" [formControl]="projectManagerControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let employee of filteredEmployees | async" [value]="employee.value.personalInfo.firstName">
{{ employee.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
\ No newline at end of file
import { Component } from '@angular/core';
import { EmployeesService } from '../../../employees/services/employees.service';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { debounceTime, distinctUntilChanged, Observable, switchMap } from 'rxjs';
import { Employee } from '../../../employees/models/responses/employee';
import { Result } from '../../../core/models/result';
@Component({
selector: 'project-create',
templateUrl: './project-create.component.html',
styleUrl: './project-create.component.css'
})
export class ProjectCreateComponent {
projectForm: FormGroup;
filteredEmployees: any[] = [];
constructor(private employeeService: EmployeesService) {}
ngOnInit(): void {
this.projectForm = new FormGroup({
projectName: new FormControl(''),
projectManager: new FormControl('')
});
// Watch the projectManager field for changes and fetch matching employees
this.projectForm.get('projectManager').valueChanges.pipe(
debounceTime(300), // Wait for user to stop typing
switchMap(value => this.employeeService.getAvailableEmployees()) // Call the service
).subscribe(employees => this.filteredEmployees = employees.value);
}
selectManager(employee: any): void {
this.projectForm.get('projectManager').setValue(employee.fullName);
this.filteredEmployees = [];
}
}
import { Component, OnInit } from '@angular/core';
import { ProjectService } from '../../services/project.service';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Project } from '../../models/responses/project';
import { PdfDownloaderService } from '../../../core/services/pdfDownloader/pdf-downloader.service';
import { ProjectService } from '../../services/project.service';
@Component({
selector: 'project-details',
......@@ -11,13 +12,14 @@ import { Project } from '../../models/responses/project';
})
export class ProjectDetailsComponent implements OnInit {
project : Project
@ViewChild('dataToExport', { static: false }) public dataToExport: ElementRef;
constructor(
private router : Router,
private route: ActivatedRoute,
private projectService: ProjectService,
private toastr: ToastrService
) {}
private toastr: ToastrService,
private pdfDownloader : PdfDownloaderService
) {}
ngOnInit(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.projectService.getProjectById(id).subscribe({
......@@ -36,5 +38,8 @@ export class ProjectDetailsComponent implements OnInit {
});
}
public downloadAsPdf(): void {
this.pdfDownloader.downloadAsPdf('pdfContent');
}
}
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ProjectService } from '../../services/project.service';
import { ToastrService } from 'ngx-toastr';
import { LoadingService } from '../../../core/services/loading/loading-service.service';
import { Project } from '../../models/responses/project';
import { ProjectService } from '../../services/project.service';
@Component({
selector: 'project-list',
......
import { NgModule } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProjectItemComponent } from './components/project-item/project-item.component';
import { AttachmentComponent } from './components/attachment/attachment.component';
import { ProjectListComponent } from './pages/project-list/project-list.component';
import { ProjectDetailsComponent } from './pages/project-details/project-details.component';
import { ProjectService } from './services/project.service';
import { FormsModule } from '@angular/forms';
import { FormControl, FormsModule } from '@angular/forms';
import { SharedModule } from '../shared/shared.module';
import { ProjectRoutingModule } from './project-routing.module';
import { StepRowItemComponent } from './components/step-row-item/step-row-item.component';
import { ProjectHeaderComponent } from './components/project-header/project-header.component';
import { ProjectCreateComponent } from './pages/project-create/project-create.component';
import { MatCommonModule, MatOption } from '@angular/material/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';
import { ProjectService } from './services/project.service';
......@@ -20,9 +26,10 @@ import { ProjectHeaderComponent } from './components/project-header/project-head
ProjectListComponent,
ProjectDetailsComponent,
StepRowItemComponent,
ProjectHeaderComponent
ProjectHeaderComponent,
ProjectCreateComponent
],
providers:[
providers: [
ProjectService
],
exports:[
......@@ -31,7 +38,14 @@ import { ProjectHeaderComponent } from './components/project-header/project-head
imports: [
CommonModule,
FormsModule,
BrowserAnimationsModule,
MatFormFieldModule,
FormControl,
MatOption,
MatAutocompleteModule,
MatInputModule,
SharedModule
]
],
schemas:[CUSTOM_ELEMENTS_SCHEMA ]
})
export class ProjectsModule { }
......@@ -22,4 +22,4 @@ export class ProjectService {
return this.http.get<Result<Project>>(this.config.getServerUrl()+ "/Projects/"+id);
}
}
\ No newline at end of file
}
import { Pipe, PipeTransform } from '@angular/core';
import { PersonalInfo } from '../../../employees/models/vakueObjects/personalInfo';
@Pipe({
name: 'fullname'
})
export class FullnamePipe implements PipeTransform {
transform(personalInfo : PersonalInfo): string {
if (!personalInfo) {
return '';
}
return `${personalInfo.firstName} ${personalInfo.lastName}`;
}
}
......@@ -8,6 +8,7 @@ import { RouterModule } from '@angular/router';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { NavItemComponent } from './componenets/nav-item/nav-item.component';
import { LoadingSpinnerComponent } from './componenets/loading-spinner/loading-spinner.component';
import { FullnamePipe } from './pipes/fullName/fullname.pipe';
@NgModule({
......@@ -18,7 +19,8 @@ import { LoadingSpinnerComponent } from './componenets/loading-spinner/loading-s
SidebarComponent,
LayoutComponent,
NavItemComponent,
LoadingSpinnerComponent
LoadingSpinnerComponent,
FullnamePipe
],
imports: [
......@@ -30,6 +32,7 @@ import { LoadingSpinnerComponent } from './componenets/loading-spinner/loading-s
FooterComponent,
SidebarComponent,
LayoutComponent,
FullnamePipe,
LoadingSpinnerComponent
],schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
......
......@@ -26,7 +26,7 @@
</li>
<li>
<a class="dropdown-item d-flex align-items-center" href="users-profile.html">
<a class="dropdown-item d-flex align-items-center" [routerLink]="['/employees/profile']">
<i class="fe fe-user"></i>
<span>My Profile</span>
</a>
......@@ -34,27 +34,7 @@
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item d-flex align-items-center" href="users-profile.html">
<i class="fe fe-settings"></i>
<span>Account Settings</span>
</a>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item d-flex align-items-center" href="pages-faq.html">
<i class="fe fe-help-circle"></i>
<span>Need Help?</span>
</a>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<button class="dropdown-item d-flex align-items-center" (click)="logout()">
<i class="fe fe-log-out"></i>
......
......@@ -99,8 +99,8 @@
</li>
<li class="nav-item">
<a class="nav-link collapsed" data-bs-target="#tables-nav" data-bs-toggle="collapse" href="#">
<i class="bi bi-layout-text-window-reverse"></i><span>استعراض ساعات عملي</span><i class="bi bi-chevron-down ms-auto"></i>
<a class="nav-link collapsed" [routerLink]="['/employees/profile']">
<i class="bi bi-layout-text-window-reverse"></i><span>ملفي الشخصي</span><i class="bi bi-chevron-down ms-auto"></i>
</a>
</li>
......
import { EmployeeWork } from "../valueObjects/EmployeeWork"
import { EmployeeWorkInfo } from "../valueObjects/EmployeeWorkInfo"
import { TrackInfo } from "../valueObjects/trackInfo"
export class EmployeeTrack {
emloyeeId :number
trackId :number
rrackInfo :TrackInfo
employeeWorkInfo : EmployeeWorkInfo
employeeWork :EmployeeWork
notes :string
}
\ No newline at end of file
export class EmployeeWork {
assignedWorkingHours: number;
workedHours: number;
contributingRatio: number;
}
export class EmployeeWorkInfo {
assignedWork: string;
performedWork: string;
assignedWorkEnd: Date;
}
export class TrackInfo{
trackDate :Date
isCompleted :boolean
statusDescription :string
}
......@@ -12,7 +12,7 @@
<link href="assets/vendor/boxicons/css/boxicons.min.css" rel="stylesheet">
<link href="assets/vendor/remixicon/remixicon.css" rel="stylesheet">
</head>
<body dir="rtl">
<body dir="rtl" class="mat-typography">
<app-root ></app-root>
<script src="assets/js/jquery.min.js"></script>
......
/**
* Template Name: NiceAdmin
* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/
* Updated: Apr 20 2024 with Bootstrap v5.3.3
* Author: BootstrapMade.com
* License: https://bootstrapmade.com/license/
*/
/* src/styles.css */
#toast-container {
width: 100%;
top: 30px;
......@@ -32,7 +24,7 @@
}
#toast-container .toast-success {
background-color: #4caf50;
background-color: #2196f3;
}
#toast-container .toast-error {
......@@ -887,4 +879,6 @@ h6 {
text-align: center;
font-size: 13px;
color: #012970;
}
\ No newline at end of file
}
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment