Commit e65d8523 authored by hasan khaddour's avatar hasan khaddour

fix s

parent 1cde9970
......@@ -23,11 +23,15 @@
"@ngx-translate/http-loader": "^8.0.0",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.3",
"dom-to-image": "^2.6.0",
"feather-icons": "^4.29.2",
"html2canvas": "^1.4.1",
"i": "^0.3.7",
"jquery": "^3.7.1",
"jquery-sparkline": "^2.4.0",
"jspdf": "^2.5.1",
"material": "^0.9.15",
"moment": "^2.30.1",
"ngx-pagination": "^6.0.3",
"ngx-toastr": "^19.0.0",
"rxjs": "~7.8.0",
......@@ -39,6 +43,7 @@
"@angular/cli": "^18.0.6",
"@angular/compiler-cli": "^18.0.0",
"@angular/localize": "^18.1.0",
"@types/dom-to-image": "^2.6.7",
"@types/feather-icons": "^4.29.4",
"@types/jasmine": "~5.1.0",
"@types/jquery": "^3.5.30",
......@@ -2299,7 +2304,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz",
"integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
......@@ -4315,6 +4319,12 @@
"@types/node": "*"
}
},
"node_modules/@types/dom-to-image": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.7.tgz",
"integrity": "sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==",
"dev": true
},
"node_modules/@types/eslint": {
"version": "8.56.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
......@@ -4446,6 +4456,12 @@
"integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==",
"dev": true
},
"node_modules/@types/raf": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
"optional": true
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
......@@ -4958,6 +4974,17 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"bin": {
"atob": "bin/atob.js"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/autoprefixer": {
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
......@@ -5081,6 +5108,14 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
......@@ -5284,6 +5319,17 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
"bin": {
"btoa": "bin/btoa.js"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
......@@ -5459,6 +5505,31 @@
}
]
},
"node_modules/canvg": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
"optional": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
"core-js": "^3.8.3",
"raf": "^3.4.1",
"regenerator-runtime": "^0.13.7",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^6.0.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/canvg/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"optional": true
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
......@@ -6131,6 +6202,14 @@
"node": ">= 8"
}
},
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/css-loader": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
......@@ -6418,6 +6497,11 @@
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/dom-to-image": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz",
"integrity": "sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA=="
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
......@@ -6445,6 +6529,12 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz",
"integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==",
"optional": true
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
......@@ -7036,6 +7126,11 @@
"core-js": "^3.1.3"
}
},
"node_modules/fflate": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
......@@ -7640,6 +7735,18 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
......@@ -8530,6 +8637,23 @@
"node >= 0.2.0"
]
},
"node_modules/jspdf": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz",
"integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==",
"dependencies": {
"@babel/runtime": "^7.14.0",
"atob": "^2.1.2",
"btoa": "^1.2.1",
"fflate": "^0.4.8"
},
"optionalDependencies": {
"canvg": "^3.0.6",
"core-js": "^3.6.0",
"dompurify": "^2.2.0",
"html2canvas": "^1.0.0-rc.5"
}
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
......@@ -9724,6 +9848,14 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/mrmime": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
......@@ -10783,7 +10915,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"dev": true
"devOptional": true
},
"node_modules/picocolors": {
"version": "1.0.1",
......@@ -11099,6 +11231,15 @@
}
]
},
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"optional": true,
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
......@@ -11197,8 +11338,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/regenerator-transform": {
"version": "0.15.2",
......@@ -11440,6 +11580,15 @@
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true
},
"node_modules/rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
"optional": true,
"engines": {
"node": ">= 0.8.15"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
......@@ -12272,6 +12421,15 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/stackblur-canvas": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
"optional": true,
"engines": {
"node": ">=0.1.14"
}
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
......@@ -12436,6 +12594,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svg-pathdata": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
"optional": true,
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
......@@ -12623,6 +12790,14 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
......@@ -13012,6 +13187,14 @@
"node": ">= 0.4.0"
}
},
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
......
......@@ -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 = [];
}
}
<div class="container-fluid">
<div class="container-fluid" *ngIf="project">
<div class="row justify-content-center">
<div class="col-12 ">
<div class="row align-items-center mb-4">
......@@ -6,25 +6,17 @@
<h2 class="h5 page-title"><small class="text-muted text-uppercase">بطاقة معلومات مشروع</small></h2>
</div>
<div class="col-auto">
<button type="button" class="btn btn-secondary m-1">Print</button>
<button type="button" (click)="downloadAsPdf()" class="btn btn-secondary m-1">Print</button>
</div>
</div>
<div class="card shadow">
<div class="card shadow" id="pdfContent">
<div class="card-body p-5">
<div class="row mb-5">
<div class="col-10 offset-lg-4 col-lg-4 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">{{project.projectInfo.name}}</h3>
<p> <small>{{project.projectInfo.description}}</small></p>
<p class="small text-muted"><small>تاريخ البدء {{project.projectInfo.startDate | date}} <br>تاريخ الانتهاء المتوقع {{project.projectInfo.expectedEndDate| date}}</small></p>
</div>
</div>
<project-header [projectInfo]="project.projectInfo"></project-header>
<div class="row mb-5">
<div class="col-md-4">
<p class="mb-4">
<strong>ذاتية المشروع</strong>
<br /> رئيس فريق العمل: <small>{{project.projectInfo.code}}</small>
<br /> رمز المشروع: <small>{{project.projectInfo.code}}</small>
<br /> كتاب الطرح : <small>{{project.proposalInfo.proposingBookNumber}} / {{project.proposalInfo.proposingBookDate | date}}</small>
<br />كتاب الموافقة : <small>{{project.projectAggreement.aggreementNumber}} / {{project.projectAggreement.aggreementDate |date}}</small>
<br />التمويل : <small> {{project.financialFund.financialStatus}} / {{project.financialFund.source}} </small>
......@@ -36,8 +28,8 @@
<p class="mb-4">
<strong>المعلومات التنفيذية</strong>
<br /> الجهة المنفذة: <small>{{project.executer.name}}</small>
<br /> رئيس فريق العمل: <small>{{project.teamLeader.personalInfo.firstName}} {{project.teamLeader.personalInfo.lastName}}</small>
<br /> مدير المشروع : <small>{{project.projectManager.personalInfo.firstName}} {{project.projectManager.personalInfo.lastName}}</small>
<br /> رئيس فريق العمل: <small>{{project.teamLeader.personalInfo | fullname}}</small>
<br /> مدير المشروع : <small>{{project.projectManager.personalInfo | fullname}}</small>
<br />ينفذ لصالح : <small> {{project.proposer.customerName}} </small>
</p>
......@@ -51,7 +43,7 @@
</p>
</div>
</div> <!-- /.row -->
<br>
<hr>
<p>مراحل المشروع</p>
<table class="table table-borderless table-striped">
<thead>
......@@ -80,61 +72,36 @@
</tr>
</tbody>
</table>
<hr>
<p>المشاركين في المشروع</p>
<table class="table table-borderless table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">اسم المشارك</th>
<th scope="col" class="text-left">الرقم الذاتي</th>
<th scope="col" class="text-left">صفة العمل</th>
<th scope="col" class="text-left">الهيئة</th>
<th scope="col" class="text-left">نسبة التفرغ</th>
<th scope="col" class="text-left">المساهمة</th>
<th scope="col" clsss="text-center">الرقم الذاتي</th>
<th scope="col" class="text-center">اسم المشارك</th>
<th scope="col" class="text-center">صفة العمل</th>
<th scope="col" class="text-center">الهيئة</th>
<th scope="col" class="text-center">نسبة التفرغ</th>
<th scope="col" class="text-center">المساهمة</th>
</tr>
</thead>
<tbody>
<!-- <tr *ngFor="let participant of project.e " >
<tr *ngFor="let participant of project.employeeParticipates" >
<th scope="row"></th>
<td> {{step.stepInfo.stepName}}
<td class="text-center"> {{participant.employee.hiastId}}
</td>
<td class="text-left">{{step.stepInfo.description}}</td>
<td class="text-left">{{step.currentCompletionRatio}}</td>
<td class="text-left">{{step.weight}}</td>
<td class="text-center">{{participant.employee.personalInfo | fullname }}</td> <td class="text-center">{{participant.employee.workInfo.workJob}}</td>
<td class="text-center">{{participant.employee.workInfo.workType}}</td>
<td class="text-center">{{participant.partialTimeRatio}}</td>
<td class="text-left">{{step.stepInfo.startDate | date}}</td>
<td class="text-left">{{step.stepInfo.duration}}</td>
</tr> -->
<td class="text-center">{{participant.role}}</td>
</tr>
</tbody>
</table>
<hr>
<div class="row mt-5">
<button class="btn btn-primary">لاطلاع على خطة الانفاق</button>
<button class="btn btn-primary">الاطلاع على المرفقات </button>
<button class="btn btn-primary">الاطلاع على عمليات المتابعة</button>
</div>
<div class="row mt-5 d-flex align-items-around">
<button class=" btn btn-primary">إضافة مرفقات</button>
<button class=" btn btn-primary">إضافة دراسة جدوى </button>
<button class=" btn btn-primary">إضافة تصنيف مشروع</button>
</div>
<div class="row mt-5">
<button class="btn btn-primary">إضافة مرحلة</button>
<button class="btn btn-primary">إضافة مشارك</button>
</div>
<div class="row mt-5">
<button class="btn btn-primary">إضافة عملية متابعة</button>
</div>
<div class="row mt-5">
<div class="col-2 text-center">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto my-4" alt="...">
......@@ -182,6 +149,71 @@
</div> <!-- /.card-body -->
</div> <!-- /.card -->
</div> <!-- /.col-12 -->
</div> <!-- .row -->
<div class="row justify-content-center">
<div class="col-12 ">
<div class="row align-items-center mb-4">
<div class="col">
<h2 class="h5 page-title"><small class="text-muted text-uppercase">إدارة المشروع</small></h2>
</div>
<div class="col-auto">
</div>
</div>
<div class="card shadow" id="pdfContent">
<div class="card-body p-5">
<div class="row ">
<div class="col-2 text-center">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto my-4" alt="...">
</div>
<div class="col-8">
</div>
<div class="col-2 text-center">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto my-4" alt="...">
</div>
</div> <!-- /.row -->
<hr>
<div class="row mt-5">
<button class="btn btn-primary">لاطلاع على خطة الانفاق</button>
<button class="btn btn-primary">الاطلاع على المرفقات </button>
<button class="btn btn-primary">الاطلاع على عمليات المتابعة</button>
</div>
<div class="row mt-5 d-flex align-items-around">
<button class=" btn btn-primary">إضافة مرفقات</button>
<button class=" btn btn-primary">إضافة دراسة جدوى </button>
<button class=" btn btn-primary">إضافة تصنيف مشروع</button>
</div>
<div class="row mt-5">
<button class="btn btn-primary">إضافة مرحلة</button>
<button class="btn btn-primary">إضافة مشارك</button>
</div>
<div class="row mt-5">
<button class="btn btn-primary">إضافة عملية متابعة</button>
</div>
<hr>
<div class="row mt-5">
<div class="col-2 text-center">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto my-4" alt="...">
</div>
<div class="col-8">
</div>
<div class="col-2 text-center">
<img src="/assets/images/users/4.jpg" class="navbar-brand-img brand-sm mx-auto my-4" alt="...">
</div>
</div> <!-- /.row -->
</div> <!-- /.card-body -->
</div> <!-- /.card -->
</div> <!-- /.col-12 -->
</div>
</div> <!-- .container-fluid -->
\ No newline at end of file
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