Commit d6b931f3 by Nattana Chaiyamat

แก้ไข Individual KPI ตัวเอง

parent 55b00685
......@@ -61,6 +61,7 @@ import { ReportCompetencySummaryComponent } from '../report-component/report-com
import { JobDescriptionEmpComponent } from '../job-description-emp/job-description-emp.component';
import { CompetencyMappingComponent } from '../competency-mapping/competency-mapping.component';
import { Widget1Component } from '../widget1/widget1.component';
import { JobFamilyMappingComponent } from '../job-family-mapping/job-family-mapping.component';
......@@ -137,6 +138,7 @@ const routes: Routes = [
// { path: "admin/job-description-emp", title: 'รายละเอียดของงาน', component: JobDescriptionEmpComponent },
{ path: "ess/job-description-emp", title: 'รายละเอียดของงาน', component: JobDescriptionEmpComponent },
{ path: "ess/competency-mapping", title: 'รายละเอียดของงาน', component: CompetencyMappingComponent },
{ path: "ess/job-family-mapping", title: 'รายละเอียดกลุ่มงานตามวิชาชีพ', component: JobFamilyMappingComponent },
]
}
];
......
......@@ -214,6 +214,7 @@ import { CompetencyMappingComponent } from '../competency-mapping/competency-map
import { JobFamilyComponent } from '../company-components/job-description/job-family/job-family.component';
import { JobGradeComponent } from '../company-components/job-description/job-grade/job-grade.component';
import { JobGradeGroupComponent } from '../company-components/job-description/job-grade-group/job-grade-group.component';
import { JobFamilyMappingComponent } from '../job-family-mapping/job-family-mapping.component';
export const MY_DATE_FORMATS = {
parse: {
......@@ -367,7 +368,8 @@ export class CustomDateAdapter extends NativeDateAdapter {
CompetencyMappingComponent,
JobFamilyComponent,
JobGradeComponent,
JobGradeGroupComponent
JobGradeGroupComponent,
JobFamilyMappingComponent
], imports: [
TranslateModule,
CommonModule,
......
......@@ -30,7 +30,7 @@ export class EmployeeSelfServiceComponent {
generalPages = [
{ text: "JD", link: "/ess/job-description-emp" },
{ text: "Profile พนักงาน", link: "/ess/profile" },
{ text: "Job Family", description: "หน้าใหม่ Job Family" },
{ text: "Job Family", link: "/ess/job-family-mapping" },
{ text: "Time Attendance", description: "หน้าใหม่ แสดงข้อมูล TA" },
{ text: "วินัย และการลงโทษ", description: "หน้าใหม่ คล้าย เมนูความดีความผิดของ myHR plus" },
{ text: "ผลงานดีเด่น", description: "หน้าใหม่" },
......
<div class="row m-2">
<div class="col-12 row w-full">
<div class="col-12">
<div class="row-center col-center" style="padding-bottom: 2rem;">
<div class="text-title text-blue">Job family</div>
</div>
</div>
</div>
<div class="menu-box">
<div class="col-12 row p-2">
<ng-container *ngFor="let item of items; let i = index">
<div class="col-3 box-menu">
<div [style.background-color]="colors[i]" class="row row-center h-full sub-box-menu">
<div class="col-auto p-5">
<img src="./assets/img/brand-logos/new_logo_mySkillX.png" alt="mySkillX"
class="bg-white sub-box-menu" style="width: 9rem;height: 9rem;pointer-events: none" />
</div>
<div class="col-6 row row-space-between h-full" style="padding: 2rem 0 1rem 0;">
<div class="col-12">
<div class="text-card-title text-gray-500">รหัส</div>
</div>
<div class="col-12">
<div class="col-12">
<div class="text-sub-title text-gray-500">ชื่อย่อ</div>
</div>
<div class="col-12">
<div class="text-sub-title text-gray-500">กลุ่ม</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
</div>
</div>
<div class="col-12 row col-center w-full" style="padding-top: 2rem;">
<button class="link-btn" style="height: 80px;">JOB FAMILY MATRIX</button>
</div>
</div>
\ No newline at end of file
/* ===== base variables ===== */
$size-card : 150px; // ( ≥1280px จะขยายเป็น 170 )
$col-hero : 280px; // ความกว้างคอลัมน์ซ้าย
$gap-main : 2.5rem;
$radius-main : 28px;
$radius-sub : 20px;
$dur : .35s;
$easing : cubic-bezier(.4, 0, .2, 1);
$shadow-base : 0 14px 30px -12px rgba(0, 0, 0, .18);
$shadow-deep : 0 20px 42px -16px rgba(0, 0, 0, .24);
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.link-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
transition: transform $dur $easing,
box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
}
.row {
display: flex;
flex-wrap: wrap;
}
.col {
flex: 1;
}
@for $i from 1 through 12 {
$width: (
$i / 12) * 100%;
.col-#{$i} {
flex: 0 0 $width;
max-width: $width;
}
}
.col-start {
display: flex;
justify-content: start;
}
.col-center {
display: flex;
justify-content: center;
}
.row-top {
display: flex;
align-items: start;
align-content: start;
}
.row-center {
display: flex;
align-items: center;
align-content: center;
}
.row-space-between {
display: flex;
align-items: space-between;
align-content: space-between;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.text-blue {
color: #3b82f6;
}
.text-title {
font-weight: 500;
font-size: 3.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-card-title {
font-weight: 800;
font-size: 3.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-sub-title {
font-weight: 300;
font-size: 1rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.box-menu {
padding: 1rem;
}
.sub-box-menu {
border-radius: 15px;
transition: transform $dur $easing,
box-shadow $dur $easing;
// box-shadow: $shadow-deep;
// &:hover {
// transform: translateY(-6px
// );
// box-shadow: $shadow-deep;
// }
}
.menu-box {
box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.2
);
border: 1px solid transparent;
border-radius: 30px;
background-image: linear-gradient(white, white),
linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
background-origin: border-box;
background-clip: content-box,
border-box;
transition: transform $dur $easing,
box-shadow $dur $easing;
// &:hover {
// transform: translateY(-6px);
// box-shadow: $shadow-deep;
// }
// &:hover::before {
// content: '';
// position: absolute;
// inset: -3px;
// border-radius: inherit;
// // background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
// filter: blur(34px);
// opacity: .17;
// z-index: -1;
// transition: opacity $dur;
// opacity: .27;
// border: 1px solid;
// }
.sub-box {
padding: 15px;
}
}
.color-box {
transition: filter 0.2s ease;
&:hover {
filter: brightness(0.9);
}
}
\ No newline at end of file
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-job-family-mapping',
templateUrl: './job-family-mapping.component.html',
styleUrls: ['./job-family-mapping.component.scss']
})
export class JobFamilyMappingComponent {
items = Array.from({ length: 10 }, (_, i) => `PART ${i + 1}`);
colors: string[] = [];
ngOnInit() {
this.colors = this.generateUniqueColors(this.items.length);
}
generateUniqueColors(count: number): string[] {
const usedColors = new Set<string>();
const colors: string[] = [];
while (colors.length < count) {
const hue = Math.floor(Math.random() * 360); // 0–360 องศา
const saturation = 60 + Math.random() * 10; // 60–70%
const lightness = 80 + Math.random() * 10; // 80–90%
const color = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
if (!usedColors.has(color)) {
usedColors.add(color);
colors.push(color);
}
}
return colors;
}
}
......@@ -159,7 +159,7 @@ export class AssessmentTopicsComponent {
}
selectPmstopic(dataSelect?: any) {
const data = this.pmstopic.dataList.find(e => e.pmsTopicId == dataSelect.pmsTopicId)
const data = this.pmstopic.dataList.find(e => e.pmsTopicId == dataSelect?.pmsTopicId)
if (data) {
this.pmstopic.select = new MyPmstopicModel(data)
} else if (this.modalStatus == 'add') {
......
......@@ -10,29 +10,30 @@
</div>
<div class="flex w-full mb-4">
<div class="flex w-1/4 justify-between">
<div class="flex">
<!-- <div class="flex">
<div class="flex items-center">
<input type="checkbox" class="ti-form-checkbox pointer-events-none" id="hs-default-checkbox"
[checked]="selectEmp.size-1 > 0">
<label for="hs-default-checkbox" class="text-sm text-gray-500 mx-2 pointer-events-none">
{{selectEmp.size-1 < 0 ? 0 : selectEmp.size-1}} Selected</label>
{{selectEmp.size-1 < 0 ? 0 : selectEmp.size-1}} {{'Selected' | translate}} </label>
</div>
<div class="mx-1 flex items-center">
<button id='check-boxall' (click)="toggleSelectAll()"
<button (click)="toggleSelectAll()"
class="focus:ring-2 focus:ring-primary rounded-sm flex item-center">
<i class="fs-l transition-all duration-200"
[ngClass]="{'ri-checkbox-multiple-line text-gray-500': !selectEmp.get('selectAll'), 'ri-checkbox-multiple-fill text-primary': selectEmp.get('selectAll')}"></i>
</button>
<label class="text-sm text-gray-500 ml-2 cursor-pointer" for="check-boxall"
(click)="toggleSelectAll()">Select All</label>
</div>
<label (click)="toggleSelectAll()" class="text-sm text-gray-500 ml-2 cursor-pointer">
{{'SelectAll' |translate}}</label>
</div>
</div> -->
</div>
<div class="flex w-3/4 justify-end">
<div class="px-1">
<div class="relative shadow-md">
<input type="text" class="ti-form-input ltr:pl-11 rtl:pr-11 focus:z-10 "
[placeholder]="'SearchByNoOrName' | translate" [(ngModel)]="search">
[placeholder]="'SearchByNoOrName' | translate" [(ngModel)]="search"
(ngModelChange)="setSyncfutionDataList() ">
<div
class="absolute inset-y-0 ltr:left-0 rtl:right-0 flex items-center pointer-events-none z-20 ltr:pl-4 rtl:pr-4">
<i class="ri-search-line text-gray"></i>
......@@ -42,140 +43,9 @@
</div>
</div>
<div class="flex flex-col w-100">
<div class="overflow-auto rounded-t-md">
<div id="card-type-1" role="tabpanel" aria-labelledby="card-type-item-1">
<div class="overflow-auto shadow-md">
<table class="ti-custom-table ti-custom-table-head ti-custom-table-hover">
<thead>
<tr>
<ng-container
*ngFor="let item of ['รหัสพนักงาน','ชื่อพนักงาน','ตำเเหน่งงาน','สถานะ']; let f = first; let l = last; let i = index">
<th scope="col"
class="relative px-10px py-10px bg-soft-secondary text-primary !text-center"
[ngClass]="{'!p-0':f, '!pl-0':i==1}" [colSpan]="f?2:1">
<span class="text-sm">{{ item }}</span>
<div *ngIf="!l" class="absolute top-1/2 transform -translate-y-1/2 right-0">
<svg class="head-table-icon" xmlns="http://www.w3.org/2000/svg" width="50" height="16"
fill="currentColor" viewBox="0 0 16 16">
<path
d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z">
</path>
</svg>
</div>
</th>
</ng-container>
</tr>
</thead>
<tbody *ngIf="subordinate.loading">
<tr>
<td class="text-center" colspan="100%">
<div *ngFor="let item of [1,2,3]" class="ti-spinner w-8 h-8 text-secondary mx-1"
role="status" aria-label="loading">
<span class="sr-only">Loading...</span>
</div>
</td>
</tr>
</tbody>
<tbody *ngIf="!subordinate.loading&&!subordinateFilter().length">
<tr>
<td class="text-center" colspan="100%">
ไม่พบข้อมูล
</td>
</tr>
</tbody>
<tbody *ngIf="!subordinate.loading&&subordinateFilter().length">
<tr *ngFor="let item of subordinateFilter();let i = index">
<td class="text-center !pr-1"
style="font-size: 12px;width: 90px;;min-width: 90px;max-width: 90px;">
<div class="flex-col gap-2">
<input
[disabled]="!(item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending'))"
[class.cursor-not-allowed]="!(item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending'))"
[class.accent-gray-400]="!(item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending'))"
[class.opacity-50]="!(item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending'))"
type="checkbox" class="ti-form-checkbox cursor-pointer"
[checked]="selectEmp.get(item.apsassessy.employeeId)"
(click)="!selectEmp.get(item.apsassessy.employeeId)?selectEmp.set(item.apsassessy.employeeId , true):selectEmp.delete(item.apsassessy.employeeId);checkSelectAll()">
&nbsp;
<ng-container
*ngIf="item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending')">
<img
(click)="!selectEmp.get(item.apsassessy.employeeId)?selectEmp.set(item.apsassessy.employeeId , true):selectEmp.delete(item.apsassessy.employeeId);checkSelectAll()"
class="cursor-pointer avatar shadow-none rounded-full !ring-transparent object-cover h-12 w-12"
[src]="item.apsassessy.picture?getImg(item.apsassessy.picture):'./assets/img/users/defaultperson.jpg'"
(error)="onImageError($event)">
</ng-container>
<ng-container
*ngIf="!(item.apsapproveType.code!='Apsapprove1'&&(item.masfromStatusType.code=='evaluating'||item.masfromStatusType.code=='pending'))">
<img
class="cursor-pointer avatar shadow-none rounded-full !ring-transparent object-cover h-12 w-12"
[src]="item.apsassessy.picture?getImg(item.apsassessy.picture):'./assets/img/users/defaultperson.jpg'"
(error)="onImageError($event)">
</ng-container>
<div class="mb-4">
<ng-container *ngTemplateOutlet="Datagrid"></ng-container>
</div>
</td>
<td class="text-left"
style="font-size: 12px;width: 90px;min-width: 90px;max-width: 90px;padding-left: 10px;">
{{item.apsassessy.employeeId}}
</td>
<td style="font-size: 12px; width: 175px;">
{{item.apsassessy.thFullName}}
</td>
<td style="font-size: 12px;">{{item.apsassessy.position.tdesc}}</td>
<td class="text-center">
<div class="flex justify-center">
<button type="button" class="ti-btn rounded-sm "
[class]="statusButtonClass(item.statusIdp.statusType)"
style="height: 30px; width: auto; font-size: 12px; display: flex; align-items: center; justify-content: center;margin-left:4px;"
(click)="settingIndividualKpi=true">
{{statusCompetencyText(item.statusIdp.statusType)}}
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<nav class="pagination-style-3 my-5" *ngIf="page.length">
<ul class="ti-pagination">
<li>
<a aria-label="anchor" class="page-link" href="javascript:void(0);"
(click)="currentPage = (currentPage-1 || 1)">
<i class="ri-arrow-left-s-line align-middle rtl:rotate-180"></i>
</a>
</li>
<li *ngFor="let item of page;let f = first;let l = last">
<ng-container *ngIf="item==3&&currentPage!=1&&currentPage!=2&&currentPage!=3">
<a aria-label="anchor" class="page-link" href="javascript:void(0);"><i class="ri-more-line"></i>
</a>
</ng-container>
<ng-container *ngIf="(f||l)||(item==currentPage-1||item==currentPage||item==currentPage+1)">
<a class="page-link" href="javascript:void(0);" [class.active]="item==currentPage"
(click)="currentPage=item">{{item}}
</a>
</ng-container>
<ng-container
*ngIf="item==page.length-2&&currentPage!=page.length&&currentPage!=page.length-1&&currentPage!=page.length-2">
<a aria-label="anchor" class="page-link" href="javascript:void(0);"><i class="ri-more-line"></i>
</a>
</ng-container>
</li>
<li>
<a aria-label="anchor" class="page-link" href="javascript:void(0);"
(click)="currentPage = (currentPage > page.length-1 ? currentPage: currentPage+1 )">
<i class="ri-arrow-right-s-line align-middle rtl:rotate-180"></i>
</a>
</li>
</ul>
<ul class="nav-tabs mt-3">
<span>Show {{((currentPage-1) * 10)+1}} to {{subordinateFilter().length<10
?subordinateFilter().length: (currentPage==page.length ? ((currentPage * 10) - ((currentPage * 10)
- subordinateFilter().length) ) :(currentPage * 10) ) }} of {{subordinateFilter().length}}
items</span>
</ul>
</nav>
</div>
</div>
</div>
......@@ -183,7 +53,98 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="settingIndividualKpi">
<app-setting-individual-kpi [evaluationForm]="'sup'"
(sendReturnPath)="getBossList();settingIndividualKpi=false"></app-setting-individual-kpi>
<ng-container *ngIf="settingIndividualKpi&&subordinate.select">
<app-setting-individual-kpi [bossId]="bossId" [employeeId]="subordinate.select.apsassessy.employeeId"
(sendReturnPath)="getEmpKpi();settingIndividualKpi=false"></app-setting-individual-kpi>
</ng-container>
<ng-template #Datagrid>
<ejs-grid #grid id='Grid' [locale]="locale" [dataSource]="syncfution.dataList" [allowFiltering]="true"
[filterSettings]="filterSettings" [selectionSettings]="selectionOptions"
[searchSettings]="syncfution.searchSettings" [groupSettings]="groupSettings" [toolbar]="toolbarOptions"
[editSettings]="editSettings" [loadingIndicator]='loadingIndicator' [query]="query"
[columnMenuItems]="columnMenuItems" [pageSettings]="initialPage" [allowMultiSorting]="true" [allowPaging]="true"
[allowGrouping]="true" [allowSorting]="true" [showColumnMenu]="true" [allowPdfExport]="true"
[allowExcelExport]="true" [allowReordering]="true" width="auto" rowHeight="60" allowEditing="false"
(columnMenuClick)="onColumnMenuClick($event)" (toolbarClick)='toolbarClick($event)'>
<e-columns>
<ng-container *ngFor="let col of syncfution.columns">
<ng-container *ngIf="col.headerText">
<e-column [field]="col.field" [headerText]="col.headerText" [width]="col.width" [format]="col.format"
[isPrimaryKey]="col.isPrimaryKey" [validationRules]="col.validationRules" [visible]="col.visible"
[editType]="false" [allowEditing]="false" [allowFiltering]="true" [allowSorting]="true" [type]="col.type"
textAlign="center">
<ng-template #headerTemplate let-data>
<span class="font-size-12px font-weight-700 text-primary">{{ col.headerText |
translate}}</span>
</ng-template>
<ng-template #template let-data *ngIf="col.headerText=='EmployeeCode'">
<div class="flex-col gap-2">
<!-- <input [disabled]="data.statusCode!='1'" [class.cursor-not-allowed]="data.statusCode!='1'"
[class.accent-gray-400]="data.statusCode!='1'" [class.opacity-50]="data.statusCode!='1'"
type="checkbox" class="ti-form-checkbox cursor-pointer" [checked]="selectEmp.get(data.employeeId)"
(click)="!selectEmp.get(data.employeeId)?selectEmp.set(data.employeeId , true):selectEmp.delete(data.employeeId);checkSelectAll()">
&nbsp; -->
<img class=" avatar shadow-none rounded-full !ring-transparent object-cover h-12 w-12"
[src]="data.picture?getImg(data.picture):'./assets/img/users/defaultperson.jpg'"
(error)="onImageError($event)">
{{data.employeeId}}
</div>
</ng-template>
<ng-template #template let-data *ngIf="col.headerText=='Status'">
<div class="flex justify-center">
<button type="button" class="ti-btn rounded-sm " [class]="statusButtonClass(data.statusEdesc)"
style="height: 30px; width: auto; font-size: 12px; display: flex; align-items: center; justify-content: center;margin-left:4px;"
(click)="selectSubordinate(data);">
{{data.status}}
</button>
</div>
</ng-template>
</e-column>
</ng-container>
</ng-container>
</e-columns>
<e-aggregates>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesSum" [field]="col.field" [type]="'Sum'"
[footerTemplate]="'Sum: ${Sum}'" [groupFooterTemplate]="'Sum: ${Sum}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesCount" [field]="col.field" [type]="'Count'"
[footerTemplate]="'Count: ${Count}'" [groupFooterTemplate]="'Count: ${Count}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesAvg" [field]="col.field" [type]="'Average'"
[footerTemplate]="'Average: ${Average}'" [groupFooterTemplate]="'Average: ${Average}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesMin" [field]="col.field" [type]="'Min'"
[footerTemplate]="'Min: ${Min}'" [groupFooterTemplate]="'Min: ${Min}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesMax" [field]="col.field" [type]="'Max'"
[footerTemplate]="'Max: ${Max}'" [groupFooterTemplate]="'Max: ${Max}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
</e-aggregates>
</ejs-grid>
</ng-template>
\ No newline at end of file
// th{
// position: relative; // เทียบเท่า class "relative"
// padding: 10px; // เทียบเท่า class "px-10px py-10px" (อาจเปลี่ยนตามต้องการ)
// background-color: rgb(96 165 250 / 0.1); // ตัวอย่างแทน "bg-soft-secondary"
// color: #2b2b2b; // ตัวอย่างแทน "text-primary"
// text-align: center !important; // เทียบเท่า "!text-center"
// // หากต้องการดีไซน์อื่น ๆ เพิ่มเติมก็ใส่ในนี้ได้เลย เช่น:
// font-weight: 600;
// border-bottom: 1px solid #eee;
// }
.e-headercell,
.e-detailheadercell {
background-color: rgb(96 165 250 / 0.1) !important;
}
.e-pager .e-currentitem, .e-pager .e-currentitem:hover {
background: rgb(96 165 250) !important;
color: #fff;
opacity: 1 !important;
}
.e-checkbox-wrapper .e-frame.e-check, .e-css.e-checkbox-wrapper .e-frame.e-check {
background-color: rgb(96 165 250) !important;
border-color: transparent;
color: #fff;
}
.e-checkbox-wrapper .e-frame, .e-css.e-checkbox-wrapper .e-frame {
border: 1px solid !important;
border-radius: 2px;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-family: "e-icons";
height: 18px;
line-height: 10px;
padding: 2px 0;
text-align: center;
vertical-align: middle;
width: 1rem !important;
border-color: #64748b !important;
}
.e-grid td.e-selectionbackground {
background-color: #aec2ec !important;
}
import { ChangeDetectorRef, Component, Input } from '@angular/core';
import { ChangeDetectorRef, Component, Input, ViewChild, ViewEncapsulation } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { AggregateService, Column, ColumnMenuClickEventArgs, ColumnMenuService, ColumnModel, DetailRowService, EditService, ExcelExportProperties, ExcelExportService, FilterService, FilterSettingsModel, GridComponent, GroupService, GroupSettingsModel, LoadingIndicatorModel, PageService, PdfExportService, ReorderService, SearchService, SelectionSettingsModel, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { setCulture } from '@syncfusion/ej2-base';
import { Query } from '@syncfusion/ej2-data';
import { MyAppraisalKpiSettingEmpModel } from 'src/app/shared/model/appraisal-kpi-setting-emp.model';
import { AppraisalKpiSettingModel, ApsassessyModel, MyAppraisalKpiSettingModel, MyApsassessyModel } from 'src/app/shared/model/appraisal-kpi-setting.model';
import { AppraisalSubordinateModel, Masfromevaluationassessment } from 'src/app/shared/model/appraisal-subordinate.model';
import { EmployeeModel, MyEmployeeModel } from 'src/app/shared/model/employee.model';
import { AppraisalService } from 'src/app/shared/services/appraisal.service';
import { EmployeeService } from 'src/app/shared/services/employee.service';
import { FileService } from 'src/app/shared/services/file.service';
import { TokenService } from 'src/app/shared/services/token.service';
interface Row { label: string; value: number | string; }
@Component({
selector: 'app-setting-individual-kpi-supervisor',
templateUrl: './setting-individual-kpi-supervisor.component.html',
styleUrls: ['./setting-individual-kpi-supervisor.component.scss']
styleUrls: ['./setting-individual-kpi-supervisor.component.scss'],
providers: [AggregateService, SortService, GroupService, ColumnMenuService, PageService, FilterService, ToolbarService, PdfExportService, ExcelExportService, DetailRowService, ReorderService, EditService, SearchService],
encapsulation: ViewEncapsulation.None
})
export class SettingIndividualKpiSupervisorComponent {
subordinate: { loading: false, select?: AppraisalSubordinateModel, dataList: AppraisalSubordinateModel[] } = { loading: false, select: undefined, dataList: [] }
selectEmp: Map<string, boolean> = new Map<string, boolean>()
currentPage = 1
page = Array.from({ length: 1 }, (_, i) => i + 1)
search = ""
bossId = ''
subordinate: { loading: false, select?: AppraisalKpiSettingModel, dataList: AppraisalKpiSettingModel[] } = { loading: false, select: undefined, dataList: [] }
settingIndividualKpi = false
search = ""
selectEmp: Map<string, boolean> = new Map<string, boolean>()
syncfution: {
dataList: any[],
searchSettings: {
fields: string[],
operator: 'contains',
ignoreCase: false
},
columns: ColumnModel[]
} = {
dataList: [],
searchSettings: {
fields: [
'employeeId',
'fullName',
'position',
'status'],
operator: 'contains',
ignoreCase: false
},
columns: [{
field: "employeeId",
headerText: "EmployeeCode",
type: "string",
isPrimaryKey: true,
},
{
field: "fullName",
headerText: "EmployeeName",
type: "string"
},
{
field: "position",
headerText: "Position",
type: "string"
},
{
field: "status",
headerText: "Status",
type: "string"
}]
}
@ViewChild('grid') public grid?: GridComponent;
filterSettings: FilterSettingsModel = { type: 'Excel' };
selectionOptions: SelectionSettingsModel = { checkboxOnly: true };
groupSettings: GroupSettingsModel = { allowReordering: true, showGroupedColumn: true, showDropArea: false };
toolbarOptions: any[] = ['Print', 'ExcelExport', 'CsvExport'];
editSettings? = { allowEditing: true, mode: 'Batch' };
loadingIndicator: LoadingIndicatorModel = { indicatorType: 'Shimmer' };
query: Query = new Query().addParams('dataCount', '1000');
columnMenuItems: any[] = [
'AutoFit', 'AutoFitAll', 'SortAscending', 'SortDescending',
'Group', 'Ungroup', 'ColumnChooser', 'Filter',
{ text: 'Sum', id: 'aggregate_sum' },
{ text: 'Count', id: 'aggregate_count' },
{ text: 'Average', id: 'aggregate_average' },
{ text: 'Min', id: 'aggregate_min' },
{ text: 'Max', id: 'aggregate_max' }
];
initialPage? = { pageSizes: true, pageSize: 10 };
aggregatesSum: any[] = [];
aggregatesCount: any[] = [];
aggregatesAvg: any[] = [];
aggregatesMin: any[] = [];
aggregatesMax: any[] = [];
locale = 'th-TH'
constructor(private appraisalService: AppraisalService,
private fileService: FileService,
private translateService: TranslateService,
private tokenService: TokenService,
private cdr: ChangeDetectorRef) {
this.locale = this.translateService.getCurrentLang() == 'th' ? 'th-TH' : 'en-US'
this.translateService.onLangChange.subscribe((event) => {
if (event.lang === 'th') {
setCulture('th-TH');
this.locale = 'th-TH'
} else if (event.lang === 'en') {
setCulture('en-US');
this.locale = 'en-US'
}
this.toolbarOptions = [
{ text: this.translateService.instant('Print'), prefixIcon: 'e-print', id: 'Print' },
{ text: this.translateService.instant('ExcelExport'), prefixIcon: 'e-excelexport', id: 'ExcelExport' },
{ text: this.translateService.instant('CSVExport'), prefixIcon: 'e-csvexport', id: 'CsvExport' }
]
this.setSyncfutionDataList()
this.cdr.markForCheck()
});
}
ngOnInit(): void {
this.getBossList()
this.bossId = this.tokenService.getUserData().employeeId
this.getEmpKpi()
}
getBossList() {
this.appraisalService.getBossList().subscribe({
getEmpKpi() {
this.selectEmp.set('selectAll', false)
this.syncfution.dataList = []
this.appraisalService.getEmpKpi().subscribe({
next: response => {
this.subordinate.dataList = JSON.parse(JSON.stringify(response))
this.cdr.detectChanges()
if (this.subordinate.dataList.length) {
this.subordinate.select = JSON.parse(JSON.stringify(this.subordinate.dataList[0]))
this.cdr.detectChanges()
}
this.searchChange()
this.subordinate.dataList = response.map(e => new MyAppraisalKpiSettingModel(e))
this.setSyncfutionDataList()
this.cdr.markForCheck()
}, error: error => {
this.cdr.detectChanges()
}
})
}
searchChange() {
this.currentPage = 1
this.page = Array.from({ length: Math.ceil(this.subordinateFilter().length / 10) }, (_, i) => i + 1);
this.cdr.markForCheck()
}
toggleSelectAll() {
if (this.subordinateFilter().filter(e => e.apsapproveType.code != 'Apsapprove1' && (e.masfromStatusType.code == 'evaluating' || e.masfromStatusType.code == 'pending')).length > 0) {
if (!this.selectEmp.get('selectAll')) {
this.selectEmp.set('selectAll', true)
this.subordinateFilter().filter(e => e.apsapproveType.code != 'Apsapprove1' && (e.masfromStatusType.code == 'evaluating' || e.masfromStatusType.code == 'pending')).forEach(e => {
this.selectEmp.set(e.apsassessy.employeeId, true)
})
} else {
this.selectEmp.set('selectAll', false)
this.selectEmp.clear()
}
}
}
subordinateFilter() {
if (this.subordinate.select) {
return this.subordinate.select.masfromevaluationassessment.filter(x => {
return this.subordinate.dataList.filter(x => {
return x.apsassessy.employeeId.toLowerCase().includes(this.search.toLowerCase()) ||
x.apsassessy.thFullName.toLowerCase().includes(this.search.toLowerCase()) ||
x.apsassessy.position.tdesc.toLowerCase().includes(this.search.toLowerCase()) ||
this.statusCompetencyText(x.apsassessy.position.tdesc).toLowerCase().includes(this.search.toLowerCase()) ||
x.grade.toLowerCase().includes(this.search.toLowerCase()) ||
x.apsapproveType.tdesc.toLowerCase().includes(this.search.toLowerCase())
x.apsassessy.position.edesc.toLowerCase().includes(this.search.toLowerCase()) ||
x.statusApprove.tdesc.toLowerCase().includes(this.search.toLowerCase()) ||
x.statusApprove.edesc.toLowerCase().includes(this.search.toLowerCase())
})
}
return []
setSyncfutionDataList() {
this.syncfution.dataList = this.subordinateFilter().map(e => ({
employeeId: e.apsassessy.employeeId,
fullName: this.translateText(e.apsassessy.thFullName, e.apsassessy.engFullName),
position: this.translateText(e.apsassessy.position.tdesc, e.apsassessy.position.edesc),
status: this.translateText(e.statusApprove.tdesc, e.statusApprove.edesc),
statusCode: e.statusApprove.code,
statusEdesc: e.statusApprove.edesc,
picture: e.apsassessy.picture
}))
this.checkSelectAll()
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
statusCompetencyText = (status: string) => {
if (status === "no access") {
return "ยังไม่ถึงขั้นตอนดำเนินการ"
......@@ -88,15 +178,16 @@ export class SettingIndividualKpiSupervisorComponent {
}
}
statusButtonClass = (status: string) => {
if (status === "no access") {
const text = status.toLowerCase().trim()
if (text === "no access") {
return "ti-btn-soft-mute"
} else if (status === "pending") {
} else if (text === "pending") {
return "ti-btn-soft-secondary"
} else if (status === "evaluating") {
} else if (text === "evaluating") {
return "ti-btn-soft-warning"
} else if (status === "completed") {
} else if (text === "completed") {
return "ti-btn-soft-success"
} else if (status === "rejected") {
} else if (text === "rejected") {
return "ti-btn-soft-danger"
} else {
return ""
......@@ -112,19 +203,102 @@ export class SettingIndividualKpiSupervisorComponent {
imgElement.src = './assets/img/users/defaultperson.jpg';
}
selectSubordinate(data: any) {
this.subordinate.select = new MyAppraisalKpiSettingModel(this.subordinate.dataList.find(e => e.apsassessy.employeeId == data.employeeId))
this.settingIndividualKpi = true
this.cdr.markForCheck()
}
toggleSelectAll() {
if (!this.selectEmp.get('selectAll')) {
this.selectEmp.set('selectAll', true)
this.syncfution.dataList.filter(e => e.statusCode == '1').forEach(e => {
this.selectEmp.set(e.employeeId, true)
})
} else {
this.selectEmp.set('selectAll', false)
this.syncfution.dataList.filter(e => e.statusCode == '1').forEach(e => {
this.selectEmp.set(e.employeeId, false)
})
this.selectEmp.clear()
}
}
checkSelectAll() {
this.selectEmp.set('selectAll', (this.subordinateFilter().filter(e => e.apsapproveType.code != 'Apsapprove1' && (e.masfromStatusType.code == 'evaluating' || e.masfromStatusType.code == 'pending')).length > 0 && this.subordinateFilter().filter(e => e.apsapproveType.code != 'Apsapprove1' && (e.masfromStatusType.code == 'evaluating' || e.masfromStatusType.code == 'pending')).length == this.selectEmp.size - 1))
this.selectEmp.set('selectAll', (this.syncfution.dataList.filter(e => e.statusCode == '1').length > 0 &&
(this.selectEmp.size - 1) == this.syncfution.dataList.filter(e => e.statusCode == '1').length))
}
selectSubordinate(data: Masfromevaluationassessment, competencyTypeId: string, evaluationRoundId?: string, masfromStatusType?: string) {
if (data && evaluationRoundId) {
// this.formEvaluation.evaluateeId = data.apsassessy.employeeId
// this.formEvaluation.competencyTypeId = competencyTypeId
// this.formEvaluation.evaluationRoundId = evaluationRoundId
// this.formEvaluation.masfromStatusType = masfromStatusType || ''
// this.formEvaluation.allCompetencyTypeId = data.typeList
this.cdr.detectChanges()
onColumnMenuClick(args: ColumnMenuClickEventArgs): void {
if (!args.item.id) { return; }
if (args.item.id.startsWith('aggregate_')) {
const colField = (args.column as any)?.field;
if (!colField) { return; }
const selectedAgg = args.item.id.split('_')[1];
if (selectedAgg === 'sum') {
if (this.aggregatesSum.find(a => a.field === colField)) {
this.aggregatesSum = this.aggregatesSum.filter(a => a.field !== colField);
} else {
this.aggregatesSum.push({
field: colField,
type: 'Sum',
footerTemplate: 'Sum: ${Sum}'
});
}
this.cdr.markForCheck()
}
else if (selectedAgg === 'count') {
this.aggregatesCount.push({
field: colField,
type: 'Count',
footerTemplate: 'Count: ${Count}'
});
} else if (selectedAgg === 'average') {
this.aggregatesAvg.push({
field: colField,
type: 'Average',
footerTemplate: 'Avg: ${Average}'
});
}
else if (selectedAgg === 'min') {
this.aggregatesMin.push({
field: colField,
type: 'Min',
footerTemplate: 'Min: ${Min}'
});
}
else if (selectedAgg === 'max') {
this.aggregatesMax.push({
field: colField,
type: 'Max',
footerTemplate: 'Max: ${Max}'
});
}
setTimeout(() => {
this.grid?.refresh();
}, 500);
}
}
toolbarClick(args: any): void {
if (args.item.id === 'Grid_excelexport') {
let exportProperties: ExcelExportProperties = {
columns: this.syncfution.columns.map(col => ({
field: col.field,
headerText: col.headerText
})) as Column[]
};
this.grid?.excelExport(exportProperties);
} else if (args.item.id === 'Grid_csvexport') {
let exportColumns = this.syncfution.columns.map(col => ({
field: col.field || '',
headerText: col.headerText || ''
}));
this.grid?.csvExport({ columns: exportColumns as Column[] });
} else if (args.item.id === 'Grid_print') {
this.cdr.markForCheck()
setTimeout(() => {
this.cdr.markForCheck()
}, 1000)
}
}
}
<ng-container *ngTemplateOutlet="evaluation"></ng-container>
<ng-template #evaluation>
<ng-container *ngTemplateOutlet="newDesige"></ng-container>
<ng-template #newDesige>
<div class=" pt-1.5rem">
<div class="flex flex-col gap-2">
<div class="flex flex-row gap-2">
......@@ -7,19 +7,34 @@
<div class="w-full mb-2">
<div class="font-size-18px font-weight-700 text-primary">
<div class="absolute " style="transform:translateY(-9px)">
<button type="button" *ngIf="evaluationForm=='sup'"
<button type="button" *ngIf="bossId"
class="ti-btn ti-btn-outline ti-btn-outline-light h-20px m-0 shadow-md hover:shadow-xl transition text-blue-500 bg-white"
(click)="returnPath()">
<i class="ti ti-chevron-left"></i>
ย้อนกลับ
</button>
</div>
<span class="whitespace-nowrap relative" [ngStyle]="{'left': evaluationForm=='sup' ? '105px':'0px'}">
แก้ไข Individual KPI {{evaluationForm=='sup'?'โดยหัวหน้า':'ตัวเอง'}}
<span class="whitespace-nowrap relative" [ngStyle]="{'left': bossId ? '105px':'0px'}">
แก้ไข Individual KPI {{bossId?'โดยหัวหน้า':'ตัวเอง'}}
</span>
</div>
</div>
</div>
<div class="flex flex-col w-3/4 gap-2">
<div class="w-full mb-2 flex">
<div class="w-full flex flex-row gap-2">
<ng-container *ngFor="let item of appraisalKpiSettingEmp.list; let i=index ; let f= first">
<div class="font-size-18px font-weight-700">
<span class="cursor-pointer"
[ngClass]="{'text-secondary border-secondary border-b':appraisalKpiSettingEmp.select.pmsEvaluationRoundId==item.pmsEvaluationRoundId,'hover:text-gray-900':!(appraisalKpiSettingEmp.select.pmsEvaluationRoundId==item.pmsEvaluationRoundId)}"
(click)="appraisalKpiSettingEmp.select.pmsEvaluationRoundId !== item.pmsEvaluationRoundId && selectAppraisalKpiSettingEmp(item)">
{{item.pmsEvaluationRoundId}}
</span>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<div class="flex flex-row gap-2">
<div class="flex flex-col gap-2 w-1/4" [class.hidden]="menuClose.get('ข้อมูลรายละเอียด')">
......@@ -29,7 +44,7 @@
<div class="box-body py-2">
<div class="flex flex-col items-center gap-3">
<img
[src]="evaluatee.data.picture?getImg(evaluatee.data.picture):'./assets/img/users/defaultperson.jpg'"
[src]="employee.data.picture?getImg(employee.data.picture):'./assets/img/users/defaultperson.jpg'"
(error)="onImageError($event)" class="h-24 w-24 rounded-full ring-4 ring-primary object-cover"
alt="profile-img" />
</div>
......@@ -38,71 +53,78 @@
</div>
<div class="w-full">
<div class="box shadow-md hover:shadow-xl transition m-0" style="border-radius:20px">
<div class="box-header" [class.border-none]="menuClose.get('ข้อมูลพนักงาน')">
<div class="box-header" [class.border-none]="menuClose.get('EmployeeInformation')">
<div class="flex justify-between">
<h5 class="box-title align-center">ข้อมูลพนักงาน</h5>
<i *ngIf="menuClose.get('ข้อมูลพนักงาน')" title="แสดง"
<h5 class="box-title align-center">{{'EmployeeInformation' | translate}}</h5>
<i *ngIf="menuClose.get('EmployeeInformation')" title="แสดง"
class="bg-white cursor-pointer border ti ti-chevron-down"
style="padding: 1px;border-radius:10px;font-size:27px"
(click)="menuClose.set('ข้อมูลพนักงาน',false)"></i>
<i *ngIf="!menuClose.get('ข้อมูลพนักงาน')" title="ปิด"
(click)="menuClose.set('EmployeeInformation',false)"></i>
<i *ngIf="!menuClose.get('EmployeeInformation')" title="ปิด"
class="bg-white cursor-pointer border ti ti-chevron-up"
style="padding: 1px;border-radius:10px;font-size:27px"
(click)="menuClose.set('ข้อมูลพนักงาน',true)"></i>
(click)="menuClose.set('EmployeeInformation',true)"></i>
</div>
</div>
<div class="box-body py-2" [class.hidden]="menuClose.get('ข้อมูลพนักงาน')">
<div class="box-body py-2" [class.hidden]="menuClose.get('EmployeeInformation')">
<table class="ti-custom-table border-0 ellipsis-text">
<tbody>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">รหัสพนักงาน
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">
{{'EmployeeCode' | translate}}
</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.employeeId}}
{{employee.data.employeeId}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">ชื่อ - สกุล
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">
{{'NameSurname' | translate}}
</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.thFullName}}
{{translateText(employee.data.thFullName,employee.data.engFullName)}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">ตำเเหน่ง</td>
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">{{'Position'
| translate}}</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.position.tdesc}}
{{translateText(employee.data.position.tdesc,employee.data.position.edesc)}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">ฝ่าย</td>
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">{{'Division'
| translate}}</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.bu1.tdesc}}
{{translateText(employee.data.bu1.tdesc,employee.data.bu1.edesc)}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">แผนก</td>
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">
{{'Department' | translate}}</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.bu2.tdesc}}
{{translateText(employee.data.bu2.tdesc,employee.data.bu2.edesc)}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">ระดับ</td>
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">{{'Level' |
translate}}</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.pl.tdesc}}
{{translateText(employee.data.pl.tdesc,employee.data.pl.edesc)}}
</td>
</tr>
<tr class="!border-0">
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">E-mail</td>
<td class="!p-2 align-start" style="font-size: 1rem;font-weight: 500;color:black;">{{'Email' |
translate}}</td>
<td class="!p-2 align-start">:</td>
<td class="font-medium !p-2 align-start !text-warp">
{{evaluatee.data.email}}
{{employee.data.email}}
</td>
</tr>
</tbody>
......@@ -129,16 +151,24 @@
<div class="box shadow-md hover:shadow-xl transition m-0" style="border-radius:20px;">
<div class="box-header" style="border:0">
</div>
<div #scrollContainer class="box-body pt-0" style="overflow-y: auto; height: calc(100vh - 253px)">
<div class="min-height: calc(100vh - 406px);">
<ng-container *ngTemplateOutlet="pmsEvaluation"></ng-container>
<div #scrollContainer class="box-body pt-0" style="overflow-y: auto; height: calc(100vh - 202px)">
<div style="overflow-x: hidden;min-height: calc(100vh - 281px);">
<ng-container *ngTemplateOutlet="kpiTable"></ng-container>
</div>
<div class="box-footer text-end space-x-3 rtl:space-x-reverse"
style="margin: 0.5rem -24px -24px -24px;">
<button class="ti-btn m-0 ti-btn-soft-secondary">
<div class="box-footer text-end space-x-3 rtl:space-x-reverse" style="margin: 0.5rem -24px -24px -24px;"
*ngIf="appraisalKpiSettingEmp.select.currentTopic.length">
<button class="ti-btn m-0 ti-btn-soft-secondary"
*ngIf="appraisalKpiSettingEmp.select.statusAll.code!='1' || lastConfirmBySelf"
(click)="savePmsTopic()">
<i class="ri-save-3-fill"></i>
ยืนยันข้อมูล
</button>
<button class="ti-btn m-0 ti-btn-soft-secondary"
*ngIf="appraisalKpiSettingEmp.select.statusAll.code=='1' && !lastConfirmBySelf"
(click)="approvePmsTopic()">
<i class="ri-save-3-fill"></i>
อนุมัติข้อมูล
</button>
</div>
</div>
</div>
......@@ -147,12 +177,11 @@
</div>
</div>
</div>
</ng-template>
<ng-template #pmsEvaluation>
<ng-template #kpiTable>
<div class="flex flex-col">
<div class="flex flex-col gap-2" style="min-height: calc(100vh - 479px);">
<div class="flex flex-col gap-2">
<div class=" flex flex-col gap-2" [attr.id]="'menu-part-3'">
<button type="button"
class="p-4 w-full bg-gradient-to-r from-primary to-secondary text-white text-left font-semibold"
......@@ -169,25 +198,36 @@
<th scope="col">หน่วยนับ</th>
<th scope="col">กำหนดเสร็จ</th>
<th scope="col">เพิ่มเติม</th>
<th scope="col"
*ngIf="appraisalKpiSettingEmp.select.statusAll.code!='1' || lastConfirmBySelf">
การจัดการ</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of individualKPI.list;let i = index">
<tr class="border-b border-gray-200"
(mouseenter)="tableHover.set(item.groupAssessment1.pmsTopic.indicatorsDetail,true)"
<ng-container *ngFor="let item of appraisalKpiSettingEmp.select.currentTopic;let i = index">
<tr class="border-b border-gray-200" (mouseenter)="tableHover.set(item.pmsTopicId,true)"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background':tableHover.get(item.groupAssessment1.pmsTopic.indicatorsDetail)?'#f1f5f9':'#ffffff'}">
[ngStyle]="{'background':tableHover.get(item.pmsTopicId)?'#f1f5f9':currentTopicColor(item.approveTopicStatus)}">
<td class="py-2" style="vertical-align: top">
{{item.groupAssessment1.pmsTopic.indicatorsDetail}}</td>
&nbsp;
<ng-container
*ngIf="appraisalKpiSettingEmp.select.statusAll.code=='1'&&item.approveTopicStatus!=2 && !lastConfirmBySelf">
<input type="checkbox" class="ti-form-checkbox cursor-pointer"
[checked]="pmsTopicApprove.get(item.pmsTopicId)"
(click)="!pmsTopicApprove.get(item.pmsTopicId)?pmsTopicApprove.set(item.pmsTopicId , true):pmsTopicApprove.delete(item.pmsTopicId);">
&nbsp;
</ng-container>
{{item.pmsTopicId}}
</td>
<td class="py-2 text-center" style="vertical-align: top">{{item.weight}}</td>
<td class="py-2 text-center" style="vertical-align: top">
{{item.groupAssessment1.pmsTopic.performanceGoalsDetail}}
{{item.performanceGoalsDetail}}
</td>
<td class="py-2 text-center" style="vertical-align: top">
{{item.groupAssessment1.pmsTopic.detailUnit}}
{{item.detailUnit}}
</td>
<td class="py-2 text-center" style="vertical-align: top">
{{item.groupAssessment1.pmsTopic.completionDate}}
{{item.completionDate}}
</td>
<td class="py-2 text-center" style="vertical-align: top">
<div class="hs-tooltip ti-main-tooltip [--trigger:hover]">
......@@ -204,7 +244,7 @@
&nbsp;:&nbsp;
</div>
<div class="flex-1">
{{item.groupAssessment1.pmsTopic.tdesc}}
{{item.tdesc}}
</div>
</div>
<div class="text-start flex flex-row">
......@@ -215,7 +255,7 @@
&nbsp;:&nbsp;
</div>
<div class="flex-1">
{{item.groupAssessment1.pmsTopic.indicatorsDetail}}
{{item.indicatorsDetail}}
</div>
</div>
<div class="text-start flex flex-row">
......@@ -226,7 +266,7 @@
&nbsp;:&nbsp;
</div>
<div class="flex-1">
{{item.groupAssessment1.pmsTopic.performanceGoalsDetail}}
{{item.performanceGoalsDetail}}
</div>
</div>
<div class="text-start flex flex-row">
......@@ -237,7 +277,7 @@
&nbsp;:&nbsp;
</div>
<div class="flex-1">
{{item.groupAssessment1.pmsTopic.detailUnit}}
{{item.detailUnit}}
</div>
</div>
<div class="text-start flex flex-row">
......@@ -248,7 +288,7 @@
&nbsp;:&nbsp;
</div>
<div class="flex-1">
{{item.groupAssessment1.pmsTopic.completionDate}}
{{item.completionDate}}
</div>
</div>
<div class="text-start flex flex-row">
......@@ -267,114 +307,27 @@
</div>
</div>
</td>
</tr>
<td class="py-2 text-center" style="vertical-align: top"
*ngIf="appraisalKpiSettingEmp.select.statusAll.code!='1' || lastConfirmBySelf">
<ng-container *ngIf="item.approveTopicStatus==0">
<button type="button" class=" cursor-default" (click)="addPmsTopic(item.pmsTopicId)">
<i class="ti ti-circle-plus text-green-500 cursor-pointer" style="font-size: 20px;"></i>
</button>
</ng-container>
<tr>
<td class="py-2 text-center">
<input type="text" id="input-label" class="ti-form-input">
</td>
<td class="py-2 text-center">
<input type="text" id="input-label" class="ti-form-input">
</td>
<td class="py-2 text-center">
<input type="text" id="input-label" class="ti-form-input">
</td>
<td class="py-2 text-center">
<input type="text" id="input-label" class="ti-form-input">
</td>
<td class="py-2 text-center">
<input type="text" id="input-label" class="ti-form-input">
</td>
<td class="py-2 text-center" style="justify-items:center">
<ng-container *ngIf="true">
<button type="button" class="flex text-start items-center cursor-default">
<i class="ti ti-circle-plus text-green-500 cursor-pointer" style="font-size: 20px;"
(click)="assessmentDialogOpen()"></i>
<ng-container *ngIf="item.approveTopicStatus!=0">
<button type="button" class=" cursor-default" (click)="deletePmsTopic(i)">
<i class="ti ti-trash text-danger cursor-pointer" style="font-size: 20px;"></i>
</button>
</ng-container>
<ng-container *ngIf="false">
<div class="hs-tooltip ti-main-tooltip [--trigger:hover]">
<div class="hs-tooltip-toggle ti-main-tooltip-toggle">
<i class="ti ti-help-circle"></i>
<div class="hs-tooltip-content ti-main-tooltip-content border-secondary" role="tooltip">
<div class="flex flex-col gap-2 font-weight-700">
<div>รายละเอียดข้อมูล</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
ที่มาของนโยบาย
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
ตัวชี้วัด
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
เป้าหมาย
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
หน่วยนับ
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
กำหนดเสร็จ
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
<div class="text-start flex flex-row">
<div style="width: 100px;">
น้ำหนัก
</div>
<div class="px-1">
&nbsp;:&nbsp;
</div>
<div class="flex-1">
</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
</td>
</tr>
<tr>
<td class="py-2 text-center" style="vertical-align: center;justify-items:center" colspan="6">
</ng-container>
<tr
*ngIf="appraisalKpiSettingEmp.select.statusAll.code!='1' || lastConfirmBySelf">
<td class="py-2 text-center" style="vertical-align: center;justify-items:center" colspan="7">
<button type="button" class="flex text-start items-center cursor-default"
(click)="competencyTopicDialogOpen()">
(click)="pmsTopicDialogOpen()">
<i class="ti ti-circle-plus text-green-500 cursor-pointer" style="font-size: 20px;"></i>
</button>
</td>
......@@ -385,97 +338,16 @@
</div>
</div>
</div>
</ng-template>
</ng-template>
<ng-template #assessmentDialog let-modal>
<ng-template #pmsTopicDialog let-modal>
<h3 mat-dialog-title>
รายการประเมิน
</h3>
<mat-dialog-content>
<div class="flex justify-end pb-1rem">
<div class="px-1">
<div class="relative shadow-md">
<input type="text" id="hs-leading-icon" name="hs-leading-icon"
class="ti-form-input ltr:pl-11 rtl:pr-11 focus:z-10 " [placeholder]="'SearchByNoOrName' | translate"
[(ngModel)]="modalData.search">
<div
class="absolute inset-y-0 ltr:left-0 rtl:right-0 flex items-center pointer-events-none z-20 ltr:pl-4 rtl:pr-4">
<i class="ri-search-line text-gray"></i>
</div>
</div>
</div>
</div>
<div class="overflow-auto border">
<table class="ti-custom-table ti-custom-table-head ti-custom-table-hover">
<thead>
<tr>
<ng-container
*ngFor="let item of ['รหัส','ชื่อหัวข้อ','ประเภท','น้ำหนัก','การจัดการ']; let f = first; let l = last; let i = index">
<th scope="col" class=" px-10px py-10px bg-soft-secondary text-primary !text-center">
<span class="font-size-12px font-weight-700">{{ item }}</span>
<div class="absolute top-1/2 transform -translate-y-1/2 right-0">
<i class="ti ti-dots-vertical fs-l"></i>
</div>
</th>
</ng-container>
</tr>
</thead>
<tbody *ngIf="pmstopic.loading">
<tr>
<td class="text-center" colspan="100%">
<div *ngFor="let item of [1,2,3]" class="ti-spinner w-8 h-8 text-secondary mx-1" role="status"
aria-label="loading">
<span class="sr-only">Loading...</span>
</div>
</td>
</tr>
</tbody>
<tbody *ngIf="!pmstopic.loading&&!pmstopicListFilter().length">
<tr>
<td class="text-center" colspan="100%">
ไม่พบข้อมูล
</td>
</tr>
</tbody>
<tbody *ngIf="!pmstopic.loading&&pmstopicListFilter().length">
<tr
*ngFor="let item of pmstopicListFilter() | slice:((modalData.currentPage-1) * 10) : (((modalData.currentPage-1) * 10) + 10);let i = index">
<td class="text-center">
{{item.data.pmsTopicId}}
</td>
<td>{{item.data.tdesc}}</td>
<td>{{item.data.pmsType.tdesc}}</td>
<td>{{item.data.weight}}</td>
<td class="text-center"> <button type="button" class="ti-btn ti-btn-soft-secondary h-20px m-0 shadow-md"
data-hs-overlay="#assessment-table-modal" (click)="selectPmstopic(item.data)">
<i class="ri-add-line"></i>
Select
</button></td>
</tr>
</tbody>
</table>
</div>
<app-pagination [totalItems]="pmstopicListFilter().length" [pageSize]="modalData.pageSize"
(pageChange)="modalData.currentPage = $event"
(pageSizeChange)="modalData.pageSize = $event;modalData.currentPage = 1"></app-pagination>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button type="button" mat-button [mat-dialog-close] (click)="assessmentDialogClose()"
class="hs-dropdown-toggle ti-btn ti-border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:ring-offset-white focus:ring-primary dark:bg-bgdark dark:hover:bg-black/20 dark:border-white/10 dark:text-white/70 dark:hover:text-white dark:focus:ring-offset-white/10">
ย้อนกลับ
</button>
</mat-dialog-actions>
</ng-template>
<ng-template #competencyTopicDialog let-modal>
<h3 mat-dialog-title>
เพิ่ม Individual KPI
{{'เพิ่ม Individual KPI'}}
</h3>
<div class="w-full flex justify-end mb-1rem">
<div class="absolute flex">
<div class="px-1">
<button type="button" class="ti-btn ti-btn-soft-indigo h-45px m-0 shadow-md">
<button type="button" class="ti-btn ti-btn-soft-indigo h-45px m-0 shadow-md" (click)="selectPmsTopic()">
<i class="ti ti-eraser text-base"></i>
Clear
</button>
......@@ -483,72 +355,77 @@
</div>
</div>
<mat-dialog-content>
<label for="detail_th" class="ti-form-label mt-2rem">ชื่อหัวข้อ (ไทย)<span class="text-danger">*</span></label>
<input type="text" id="detail_th" class="ti-form-input h-10" [(ngModel)]="competencyTopic.select.tdesc">
<label for="detail_eng" class="ti-form-label mt-2rem">ชื่อหัวข้อ (อังกฤษ)</label>
<input type="text" id="detail_eng" class="ti-form-input h-10" [(ngModel)]="competencyTopic.select.edesc">
<label for="detail_eng" class="ti-form-label mt-2rem">นิยามสมรรถนะ</label>
<textarea type="text" id="detail_eng" class="ti-form-input" rows="4" cols="50"
[(ngModel)]="competencyTopic.select.competencyDetail"> </textarea>
<label class="ti-form-label mt-2rem">ประเภท Competency<span class="text-danger">*</span></label>
<button type="button" class="ti-btn ti-btn-success h-45px m-0 shadow-md" (click)="pmsTopicTableDialogOpen()">
เพิ่มจากหัวข้อการประเมิน
</button>
<!-- <label class="ti-form-label mt-2rem">ประเภทการประเมินผล<span class="text-danger">*</span></label>
<div class="flex">
<div class="relative flex rounded-md w-1/2">
<input type="text" id="hs-leading-button-add-on-with-icon-and-button"
name="hs-leading-button-add-on-with-icon-and-button"
class="ti-form-input rounded-md ltr:rounded-r-md rtl:rounded-l-md focus:z-10" style="padding-right: 2.5rem;"
readonly [(ngModel)]="competencyTopic.select.competencyType.shortName">
<div class="relative flex rounded-md">
<input type="text" class="ti-form-input rounded-sm ltr:rounded-r-sm rtl:rounded-l-sm focus:z-10" readonly
style="padding-right: 3.5rem;" [(ngModel)]="pmsTopic.select.pmsType.tdesc">
<div class="absolute inset-y-0 ltr:right-0 rtl:left-0 flex items-center z-20 ltr:pr-4 rtl:pl-4 space-x-2">
<button type="button" class="flex items-center text-red-500" (click)="selectCompetencytype()">
<button type="button" class="flex items-center text-red-500" (click)="selectPmsType()">
<i class="ti ti-circle-x cursor-pointer"></i>
</button>
<button type="button" class="flex items-center text-gray-500 dark:text-white/70"
(click)="competencyTypeDialogOpen()">
(click)="pmsTypeTableDialogOpen()">
<i class="ri-search-line cursor-pointer text-gray"></i>
</button>
</div>
</div>
</div>
<label class="ti-form-label mt-2rem">แนบไฟล์ข้อสอบ</label>
<div class="flex rounded-md">
<label class="sr-only">อัปโหลดไฟล์</label>
<input #fileInputMedium id="fileInputMedium" type="file" (change)="onExamSelected($event)" hidden>
<input type="text" [value]="examFileName" readonly onclick="fileInputMedium.click();"
class=" cursor-pointer block w-full border border-gray-200 focus:shadow-sm dark:focus:shadow-white/10 ltr:rounded-l-md rtl:rounded-r-none text-sm focus:z-10 focus:outline-0 focus:border-gray-200 dark:focus:border-white/10 dark:border-white/10 dark:text-white/70 file:border-0 file:bg-gray-100 ltr:file:mr-4 rtl:file:ml-4 file:py-3 file:px-4 dark:file:bg-black/20 dark:file:text-white/70">
<span
class="px-4 inline-flex items-center min-w-fit ltr:rounded-r-md rtl:rounded-l-none border ltr:border-l-0 rtl:border-r-0 border-gray-200 bg-gray-50 text-sm dark:bg-black/20 dark:border-white/10">
<button class="text-sm text-gray-500 dark:text-white/70" onclick="fileInputMedium.click();">Browse</button>
</span>
<div class="flex items-center ml-2">
<button href="javascript:void(0);" class="ti-btn ti-btn-soft-danger h-10px m-0 shadow-md rounded-md"
(click)="fileInputMedium.value = '';examFile=null;examFileName = 'กรุณาเลือกไฟล์'">
<i class="ri-delete-bin-6-line"></i>
Delete
</button>
</div>
</div>
<div class="flex" *ngIf="examFileName==competencyTopic.select.competencyFiles">
<h1 class="cursor-pointer justify-center -mb-px inline-flex items-center gap-2 font-weight-500 font-size-12px
text-center text-secondary border-secondary border-b-2 align-items-end"
(click)="downloadExam(examFileName)">
ดาวน์โหลดไฟล์ข้อสอบ</h1>
</div> -->
<label class="ti-form-label mt-2rem">ชื่อหัวข้อ<span class="text-danger">*</span></label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.tdesc">
<label class="ti-form-label mt-2rem">ชื่อหัวข้อ (อังกฤษ)</label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.edesc">
<label class="ti-form-label mt-2rem">ตัวชี้วัดผลงานหลัก</label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.indicatorsDetail">
<label class="ti-form-label mt-2rem">เป้าหมายผลงาน</label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.performanceGoalsDetail">
<label class="ti-form-label mt-2rem">หน่วยนับ</label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.detailUnit">
<label class="ti-form-label mt-2rem">กำหนดเวลาแล้วเสร็จ</label>
<input type="text" class="ti-form-input" [(ngModel)]="pmsTopic.select.completionDate">
<label class="ti-form-label mt-2rem">น้ำหนัก</label>
<input type="text" class="ti-form-input" appMoneyInput [(ngModel)]="pmsTopic.select.weight">
<label class="ti-form-label mt-2rem">ค่าเป้าหมาย</label>
<div class="grid grid-cols-12 gap-x-2 mt-2rem">
<label class="ti-form-label col-span-3 align-center m-0">A (5 คะแนน)</label>
<input type="text" class="ti-form-input col-span-9" [(ngModel)]="pmsTopic.select.targetAdetail">
</div>
<div class="grid grid-cols-12 gap-x-2 mt-2rem">
<label class="ti-form-label col-span-3 align-center m-0">B (4 คะแนน)</label>
<input type="text" class="ti-form-input col-span-9" [(ngModel)]="pmsTopic.select.targetBdetail">
</div>
<div class="grid grid-cols-12 gap-x-2 mt-2rem">
<label class="ti-form-label col-span-3 align-center m-0">C (3 คะแนน)</label>
<input type="text" class="ti-form-input col-span-9" [(ngModel)]="pmsTopic.select.targetCdetail">
</div>
<div class="grid grid-cols-12 gap-x-2 mt-2rem">
<label class="ti-form-label col-span-3 align-center m-0">D (2 คะแนน)</label>
<input type="text" class="ti-form-input col-span-9" [(ngModel)]="pmsTopic.select.targetDdetail">
</div>
<div class="grid grid-cols-12 gap-x-2 mt-2rem">
<label class="ti-form-label col-span-3 align-center m-0">E (1 คะแนน)</label>
<input type="text" class="ti-form-input col-span-9" [(ngModel)]="pmsTopic.select.targetEdetail">
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button type="button" mat-button [mat-dialog-close]
<button type="button" (click)="pmsTopicDialogClose()"
class="hs-dropdown-toggle ti-btn ti-border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:ring-offset-white focus:ring-primary dark:bg-bgdark dark:hover:bg-black/20 dark:border-white/10 dark:text-white/70 dark:hover:text-white dark:focus:ring-offset-white/10">
ย้อนกลับ
</button>
<button type="button" class="ti-btn ti-btn-success" mat-button (click)="confirmCompetencyTopic()"
[class.ti-btn-disabled]="!competencyTopic.select.competencyType.competencyTypeId||!competencyTopic.select.tdesc||!competencyTopic.select.competencyType.shortName"
[disabled]="!competencyTopic.select.competencyType.competencyTypeId||!competencyTopic.select.tdesc||!competencyTopic.select.competencyType.shortName">
<button type="button" class="ti-btn ti-btn-success" (click)="addPmsTopic();pmsTopicDialogClose()"
[class.ti-btn-disabled]="!pmsTopic.select.tdesc"
[disabled]="!pmsTopic.select.tdesc">
บันทึกข้อมูล
</button>
</mat-dialog-actions>
</ng-template>
</ng-template>
<ng-template #competencyTypeDialog let-modal>
<ng-template #pmsTopicTableDialog let-modal>
<h3 mat-dialog-title>
ประเภทสมรรถนะ
รายการหัวข้อการประเมิน
</h3>
<mat-dialog-content>
<div class="flex justify-end pb-1rem">
......@@ -556,7 +433,7 @@
<div class="relative shadow-md">
<input type="text" id="hs-leading-icon" name="hs-leading-icon"
class="ti-form-input ltr:pl-11 rtl:pr-11 focus:z-10 " [placeholder]="'SearchByNoOrName' | translate"
[(ngModel)]="modalData.search">
[(ngModel)]="syncfutionPmsTopic.search">
<div
class="absolute inset-y-0 ltr:left-0 rtl:right-0 flex items-center pointer-events-none z-20 ltr:pl-4 rtl:pr-4">
<i class="ri-search-line text-gray"></i>
......@@ -564,54 +441,51 @@
</div>
</div>
</div>
<div class="overflow-auto border">
<table class="ti-custom-table ti-custom-table-head ti-custom-table-hover">
<thead>
<tr>
<ng-container *ngFor="let item of ['รหัส','ชื่อประเภท','ชื่อย่อ','การจัดการ']; let f = first; let l = last">
<th scope="col" class="relative px-10px py-10px bg-soft-secondary text-primary !text-center">
<span class="text-sm">{{ item }}</span>
<div class="absolute top-1/2 transform -translate-y-1/2 right-0" *ngIf="!l">
<i class="ti ti-dots-vertical fs-l"></i>
</div>
</th>
</ng-container>
</tr>
</thead>
<tbody *ngIf="!competencytypeListFilter().length">
<tr>
<td class="text-center" colspan="100%">
ไม่พบข้อมูล
</td>
</tr>
</tbody>
<tbody *ngIf="competencytypeListFilter().length">
<tr
*ngFor="let item of competencytypeListFilter()| slice:((modalData.currentPage-1) * 10) : (((modalData.currentPage-1) * 10) + 10);let i = index">
<td>{{item.competencyTypeId}}</td>
<td>{{item.tdesc}}</td>
<td>{{item.shortName}}</td>
<td class="flex justify-center">
<div class="px-1">
<button type="button" class="ti-btn ti-btn-soft-secondary h-20px m-0 shadow-md"
(click)="selectCompetencytype(item)">
<i class="ri-add-line"></i>
Select
<app-datagrid-syncfution [searchSettings]="syncfutionPmsTopic.searchSettings"
[searchText]="syncfutionPmsTopic.search" [dataSource]="syncfutionPmsTopic.list"
[columns]="syncfutionPmsTopic.columns" [checkBoxSetting]="false"
(sendSelectData)="selectPmsTopic($event);addPmsTopic();pmsTopicTableDialogClose();pmsTopicDialogClose()">
</app-datagrid-syncfution>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button type="button" (click)="pmsTopicTableDialogClose()"
class="hs-dropdown-toggle ti-btn ti-border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:ring-offset-white focus:ring-primary dark:bg-bgdark dark:hover:bg-black/20 dark:border-white/10 dark:text-white/70 dark:hover:text-white dark:focus:ring-offset-white/10">
ย้อนกลับ
</button>
</mat-dialog-actions>
</ng-template>
<ng-template #pmsTypeTableDialog let-modal>
<h3 mat-dialog-title>
รายการประเภทการประเมิน
</h3>
<mat-dialog-content>
<div class="flex justify-end pb-1rem">
<div class="px-1">
<div class="relative shadow-md">
<input type="text" id="hs-leading-icon" name="hs-leading-icon"
class="ti-form-input ltr:pl-11 rtl:pr-11 focus:z-10 " [placeholder]="'SearchByNoOrName' | translate"
[(ngModel)]="syncfutionPmsType.search">
<div
class="absolute inset-y-0 ltr:left-0 rtl:right-0 flex items-center pointer-events-none z-20 ltr:pl-4 rtl:pr-4">
<i class="ri-search-line text-gray"></i>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<app-pagination [totalItems]="competencytypeListFilter().length" [pageSize]="modalData.pageSize"
(pageChange)="modalData.currentPage = $event"
(pageSizeChange)="modalData.pageSize = $event;modalData.currentPage = 1"></app-pagination>
</div>
<app-datagrid-syncfution [searchSettings]="syncfutionPmsType.searchSettings"
[searchText]="syncfutionPmsType.search" [dataSource]="syncfutionPmsType.list"
[columns]="syncfutionPmsType.columns" [checkBoxSetting]="false"
(sendSelectData)="selectPmsType($event);pmsTypeTableDialogClose()">
</app-datagrid-syncfution>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button type="button" mat-button [mat-dialog-close]
<button type="button" (click)="pmsTypeTableDialogClose()"
class="hs-dropdown-toggle ti-btn ti-border font-medium bg-white text-gray-700 shadow-sm align-middle hover:bg-gray-50 focus:ring-offset-white focus:ring-primary dark:bg-bgdark dark:hover:bg-black/20 dark:border-white/10 dark:text-white/70 dark:hover:text-white dark:focus:ring-offset-white/10">
ย้อนกลับ
</button>
</mat-dialog-actions>
</ng-template>
</ng-template>
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Router, ActivatedRoute } from '@angular/router';
import { CompetencytopicModel, MyCompetencytopicModel } from 'src/app/shared/model/competencytopic.model';
import { CompetencytypeModel, MyCompetencytypeModel } from 'src/app/shared/model/competencytype.model';
import { EmployeeModel, MyEmployeeModel } from 'src/app/shared/model/employee.model';
import { MyPmsGroupAssessment1Model } from 'src/app/shared/model/pms-group-assessment1.model';
import { MyPmstopicModel, PmstopicModel } from 'src/app/shared/model/pmstopic.model';
import { AppraisalService } from 'src/app/shared/services/appraisal.service';
import { CompetencytopicService } from 'src/app/shared/services/competencytopic.service';
import { CompetencytypeService } from 'src/app/shared/services/competencytype.service';
import { EmployeeService } from 'src/app/shared/services/employee.service';
import { FileService } from 'src/app/shared/services/file.service';
import { PmstopicService } from 'src/app/shared/services/pmstopic.service';
import { TokenService } from 'src/app/shared/services/token.service';
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, SimpleChanges, ViewChild } from "@angular/core";
import { MatDialog, MatDialogRef } from "@angular/material/dialog";
import { Router, ActivatedRoute } from "@angular/router";
import { TranslateService } from "@ngx-translate/core";
import { AppraisalKpiSettingEmpModel, MyAppraisalKpiSettingEmpModel, MyPmsTypeModel, MyTopicModel, PmsTypeModel, TopicModel } from "src/app/shared/model/appraisal-kpi-setting-emp.model";
import { EmployeeModel, MyEmployeeModel } from "src/app/shared/model/employee.model";
import { AppraisalService } from "src/app/shared/services/appraisal.service";
import { EmployeeService } from "src/app/shared/services/employee.service";
import { FileService } from "src/app/shared/services/file.service";
import { PmstypeService } from "src/app/shared/services/pmstype.service";
import { TokenService } from "src/app/shared/services/token.service";
import { ToastrService } from 'ngx-toastr';
import Swal from 'sweetalert2';
export interface ModalData {
search: string,
currentPage: number,
pageSize: number
}
@Component({
selector: 'app-setting-individual-kpi',
templateUrl: './setting-individual-kpi.component.html',
styleUrls: ['./setting-individual-kpi.component.scss']
})
export class SettingIndividualKpiComponent {
@Input() evaluateeId = ""
@Input() evaluationForm: 'self' | 'sup' = "self"
@Input() bossId = ""
@Input() employeeId = this.tokenService.getUserData().employeeId
@Output() sendReturnPath: EventEmitter<any> = new EventEmitter<any>();
@ViewChild("assessmentDialog") assessmentDialog: any;
@ViewChild("competencyTypeDialog") competencyTypeDialog: any;
@ViewChild("competencyTopicDialog") competencyTopicDialog: any;
@ViewChild("fileInputMedium") fileInputMedium: any;
examFile: File | null = null;
examFileName: string = 'กรุณาเลือกไฟล์';
assessmentDialogRef?: MatDialogRef<unknown, any>
competencyTypeDialogRef?: MatDialogRef<unknown, any>
competencyTopicDialogRef?: MatDialogRef<unknown, any>
competencytype: { loading: boolean, select: CompetencytypeModel, dataList: CompetencytypeModel[] } = { loading: false, select: new MyCompetencytypeModel({}), dataList: [] }
competencyTopic: { loading: boolean, select: CompetencytopicModel, dataList: CompetencytopicModel[] } = { loading: false, select: new MyCompetencytopicModel({}), dataList: [] }
evaluatee: { loading: boolean, data: EmployeeModel } = { loading: false, data: new MyEmployeeModel() }
pmstopic: { loading: boolean, select: PmstopicModel, dataList: { check: boolean, data: PmstopicModel }[] } = { loading: false, select: new MyPmstopicModel(), dataList: [] }
individualKPI: { loading: boolean, list: any[] } = { loading: false, list: [] }
menuClose: Map<string, boolean> = new Map<string, boolean>()
tableHover: Map<string, boolean> = new Map<string, boolean>()
modalData: ModalData = {
search: "",
currentPage: 1,
pageSize: 10
}
pmsTopicApprove: Map<string, boolean> = new Map<string, boolean>()
employee: { loading: boolean, data: EmployeeModel } = { loading: false, data: new MyEmployeeModel() }
appraisalKpiSettingEmp: { loading: boolean, select: AppraisalKpiSettingEmpModel, list: AppraisalKpiSettingEmpModel[] } = { loading: false, select: new MyAppraisalKpiSettingEmpModel(), list: [] }
@ViewChild("pmsTopicDialog") pmsTopicDialog: any
pmsTopicDialogRef?: MatDialogRef<unknown, any>
pmsTopic: { loading: boolean, select: TopicModel, list: TopicModel[] } = { loading: false, select: new MyTopicModel(), list: [] }
@ViewChild("pmsTopicTableDialog") pmsTopicTableDialog: any
pmsTopicTableDialogRef?: MatDialogRef<unknown, any>
syncfutionPmsTopic = {
list: [] as any,
columns: [{
field: "pmsTopicId",
headerText: "Code",
type: "string",
isPrimaryKey: true,
},
{
field: "name",
headerText: "TopicName",
type: "string"
},
{
field: "pmsType",
headerText: "PmsTypes",
type: "string"
}],
search: '',
searchSettings: {
fields: ['pmsTopicId', 'name', 'pmsType'],
operator: 'contains',
ignoreCase: false
}
}
pmsType: { loading: boolean, list: PmsTypeModel[] } = { loading: false, list: [] }
@ViewChild("pmsTypeTableDialog") pmsTypeTableDialog: any
pmsTypeTableDialogRef?: MatDialogRef<unknown, any>
syncfutionPmsType = {
list: [] as any,
columns: [{
field: "pmsTypeId",
headerText: "Code",
type: "string",
isPrimaryKey: true,
},
{
field: "name",
headerText: "TopicName",
type: "string"
},
{
field: "shortName",
headerText: "Abbreviations",
type: "string"
}],
search: '',
searchSettings: {
fields: ['pmsTypeId', 'name', 'shortName'],
operator: 'contains',
ignoreCase: false
}
}
lastConfirmBySelf = false
constructor(
private toastr: ToastrService,
private dialog: MatDialog,
private router: Router,
private employeeService: EmployeeService,
......@@ -58,406 +97,271 @@ export class SettingIndividualKpiComponent {
private tokenService: TokenService,
private route: ActivatedRoute,
private fileService: FileService,
private pmstopicService: PmstopicService,
private competencytopicService: CompetencytopicService,
private competencytypeService: CompetencytypeService
) { }
private pmsTypeService: PmstypeService,
private translateService: TranslateService
) {
this.translateService.onLangChange.subscribe((event) => {
this.setSyncfutionPmsTopic()
this.setSyncfutionPmsType()
});
}
ngOnInit(): void {
this.getEvaluatee()
this.getPmstopicList()
this.getCompetencytypeList()
this.getPmsTopicList()
this.getPmsTypeList()
this.getWorkingById()
this.getEmpPmsKpi()
}
returnPath() {
this.sendReturnPath.emit()
}
getCompetencytypeList() {
this.competencytype.loading = true
this.competencytypeService.getList().subscribe({
getWorkingById() {
this.employee.loading = true
this.employeeService.getWorkingById(this.employeeId).subscribe({
next: response => {
this.competencytype.dataList = response.map(x => {
return new MyCompetencytypeModel(x)
})
this.competencytype.loading = false
this.cdr.detectChanges()
this.employee.data = new MyEmployeeModel(response)
this.employee.loading = false
this.cdr.markForCheck()
}, error: error => {
this.competencytype.loading = false
this.cdr.detectChanges()
this.employee.loading = false
this.cdr.markForCheck()
}
})
}
competencytypeListFilter() {
return this.competencytype.dataList.filter(x =>
x.competencyTypeId.toLowerCase().includes(this.modalData.search.toLowerCase()) ||
x.tdesc.toLowerCase().includes(this.modalData.search.toLowerCase()) ||
x.shortName.toLowerCase().includes(this.modalData.search.toLowerCase()))
getImg(text: string) {
return this.fileService.getImg(text)
}
selectCompetencytype(data?: CompetencytypeModel) {
this.competencyTopic.select.competencyType = new MyCompetencytypeModel(data)
this.cdr.markForCheck()
this.competencyTypeDialogClose()
onImageError(event: Event) {
const imgElement = event.target as HTMLImageElement;
imgElement.src = './assets/img/users/defaultperson.jpg';
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
getPmstopicList() {
this.pmstopic.loading = true
this.pmstopicService.getList().subscribe({
getEmpPmsKpi() {
this.appraisalKpiSettingEmp.loading = true
this.appraisalService.getEmpPmsKpi(this.employeeId, this.bossId).subscribe({
next: response => {
this.pmstopic.dataList = response.map(x => ({ check: false, data: new MyPmstopicModel(x) }))
this.pmstopic.loading = false
this.cdr.detectChanges()
this.appraisalKpiSettingEmp.list = response.map(e => new MyAppraisalKpiSettingEmpModel(e))
this.selectAppraisalKpiSettingEmp(this.appraisalKpiSettingEmp.list[0])
if (this.appraisalKpiSettingEmp.select.statusAll.code == '1') {
this.appraisalKpiSettingEmp.select.currentTopic.forEach(e => {
if (e.approveTopicStatus != 2)
this.pmsTopicApprove.set(e.pmsTopicId, true)
})
}
if ((this.bossId && this.appraisalKpiSettingEmp.select.confirmBy == 'boss') ||
(!this.bossId && this.appraisalKpiSettingEmp.select.confirmBy != 'boss')) {
this.lastConfirmBySelf = true
}
this.appraisalKpiSettingEmp.loading = false
this.cdr.markForCheck()
}, error: error => {
this.pmstopic.loading = false
this.cdr.detectChanges()
this.appraisalKpiSettingEmp.loading = false
this.cdr.markForCheck()
}
})
}
pmstopicListFilter() {
return this.pmstopic.dataList.filter(x => {
const data = x.data
const match = data.pmsTopicId.toLowerCase().includes(this.modalData.search.toLowerCase()) ||
data.tdesc.toLowerCase().includes(this.modalData.search.toLowerCase()) ||
data.pmsType.tdesc.toLowerCase().includes(this.modalData.search.toLowerCase()) ||
(data.weight + "").toLowerCase().includes(this.modalData.search.toLowerCase())
return match
selectAppraisalKpiSettingEmp(data?: AppraisalKpiSettingEmpModel) {
this.appraisalKpiSettingEmp.select = new MyAppraisalKpiSettingEmpModel(data)
}
currentTopicFilter() {
return this.appraisalKpiSettingEmp.select.currentTopic.filter(e => e.approveTopicStatus != 0)
}
currentTopicColor(approveTopicStatus: number): string {
switch (approveTopicStatus) {
case 0:
return '#fb923c52';
case 1:
return '#2564eb4f';
case 2:
return '#ffffff';
default:
return '';
}
}
getPmsTopicList() {
this.pmsTopic.loading = true
this.appraisalService.getKpi().subscribe({
next: response => {
this.pmsTopic.list = response.map(x => new MyTopicModel(x))
this.pmsTopic.loading = false
this.cdr.markForCheck()
}, error: error => {
this.pmsTopic.loading = false
this.cdr.markForCheck()
}
})
}
selectPmstopic(data?: PmstopicModel) {
pmsTopicFilter() {
return this.pmsTopic.list.filter(b => !this.currentTopicFilter().some(a => a.pmsTopicId === b.pmsTopicId));
}
selectPmsTopic(data?: any) {
this.pmsTopic.select = new MyTopicModel(this.pmsTopicFilter().find(e => e.pmsTopicId == data?.pmsTopicId))
this.cdr.markForCheck()
}
assessmentDialogOpen() {
this.modalData.search = ''
this.modalData.currentPage = 1
this.modalData.pageSize = 10
this.assessmentDialogRef = this.dialog.open(this.assessmentDialog, {
width: '80vw',
pmsTopicDialogOpen() {
this.selectPmsTopic()
this.pmsTopicDialogRef = this.dialog.open(this.pmsTopicDialog, {
width: '50vw',
disableClose: false,
});
}
assessmentDialogClose() {
this.assessmentDialogRef?.close()
pmsTopicDialogClose() {
this.pmsTopicDialogRef?.close()
}
competencyTypeDialogOpen() {
this.modalData.search = ''
this.modalData.currentPage = 1
this.modalData.pageSize = 10
this.competencyTypeDialogRef = this.dialog.open(this.competencyTypeDialog, {
width: '80vw',
pmsTopicTableDialogOpen() {
this.setSyncfutionPmsTopic()
this.pmsTopicTableDialogRef = this.dialog.open(this.pmsTopicTableDialog, {
width: '50vw',
disableClose: false,
});
}
competencyTypeDialogClose() {
this.competencyTypeDialogRef?.close()
pmsTopicTableDialogClose() {
this.pmsTopicTableDialogRef?.close()
}
setSyncfutionPmsTopic() {
this.syncfutionPmsTopic.list = this.pmsTopicFilter().map(e => ({
pmsTopicId: e.pmsTopicId,
name: this.translateText(e.tdesc, e.edesc),
pmsType: this.translateText(e.pmsType?.tdesc, e.pmsType?.edesc)
}))
}
addPmsTopic(pmsTopicId?: string) {
const currentTopics = this.appraisalKpiSettingEmp.select.currentTopic;
const selectedTopicId = pmsTopicId || this.pmsTopic.select.pmsTopicId;
const index = currentTopics.findIndex(t => t.pmsTopicId === selectedTopicId);
if (index > -1 && selectedTopicId) {
currentTopics[index].approveTopicStatus = 2;
} else {
currentTopics.push({ ...this.pmsTopic.select, approveTopicStatus: 1 });
}
competencyTopicDialogOpen() {
if (this.fileInputMedium) {
this.fileInputMedium.value = ""
this.setSyncfutionPmsTopic()
this.cdr.markForCheck()
}
this.competencyTopicDialogRef = this.dialog.open(this.competencyTopicDialog, {
width: '50vw',
disableClose: false,
});
deletePmsTopic(index: number) {
const currentTopics = this.appraisalKpiSettingEmp.select.currentTopic;
if (currentTopics[index].approveTopicStatus === 1) {
currentTopics.splice(index, 1);
} else if (index > -1) {
currentTopics[index].approveTopicStatus = 0;
}
competencyTopicDialogClose() {
this.competencyTypeDialogRef?.close()
this.cdr.markForCheck()
}
getEvaluatee() {
this.evaluatee.loading = true
this.employeeService.getWorkingById(this.evaluateeId).subscribe({
getPmsTypeList() {
this.pmsType.loading = true
this.pmsTypeService.getList().subscribe({
next: response => {
this.evaluatee.data = new MyEmployeeModel(response)
this.evaluatee.loading = false
this.cdr.detectChanges()
this.pmsType.list = response.map(x => new MyPmsTypeModel(x))
this.setSyncfutionPmsType()
this.pmsType.loading = false
this.cdr.markForCheck()
}, error: error => {
this.evaluatee.loading = false
this.cdr.detectChanges()
this.pmsType.loading = false
this.cdr.markForCheck()
}
})
}
getImg(text: string) {
return this.fileService.getImg(text)
selectPmsType(data?: any) {
this.pmsTopic.select.pmsType = new MyPmsTypeModel(this.pmsType.list.find(e => e.pmsTypeId == data?.pmsTypeId))
this.cdr.markForCheck()
}
onImageError(event: Event) {
const imgElement = event.target as HTMLImageElement;
imgElement.src = './assets/img/users/defaultperson.jpg';
pmsTypeTableDialogOpen() {
this.pmsTypeTableDialogRef = this.dialog.open(this.pmsTypeTableDialog, {
width: '50vw',
disableClose: false,
});
}
returnPath() {
this.sendReturnPath.emit()
pmsTypeTableDialogClose() {
this.pmsTypeTableDialogRef?.close()
}
setSyncfutionPmsType() {
this.syncfutionPmsType.list = this.pmsType.list.map(e => ({
pmsTypeId: e.pmsTypeId,
name: this.translateText(e.tdesc, e.edesc),
shortName: e.shortName
}))
}
onExamSelected(event: any) {
this.examFile = event.target.files.length > 0 ? event.target.files[0] : null;
this.examFileName = this.examFile?.name || "กรุณาเลือกไฟล์"
savePmsTopic() {
Swal.fire({
icon: 'question',
title: 'แจ้งเตือน',
text: 'ยืนยันการบันทึกข้อมูลหรือไม่',
showCancelButton: true,
confirmButtonText: 'บันทึกข้อมูล',
cancelButtonText: 'ย้อนกลับ',
reverseButtons: true,
}).then((result) => {
this.appraisalKpiSettingEmp.select.currentTopic.forEach(obj => {
if (obj.approveTopicStatus != 2) {
obj.createStatusBy = this.bossId ? 'boss' : 'employee';
}
downloadExam(fileName: string) {
this.fileService.downloadFiles(fileName).subscribe({
});
this.appraisalService.postEmpPmsKpi(new MyAppraisalKpiSettingEmpModel(this.appraisalKpiSettingEmp.select)).subscribe({
next: response => {
const url = window.URL.createObjectURL(response);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
this.cdr.detectChanges()
if (response.success) {
this.getEmpPmsKpi()
this.showAlert(response.message, 'success')
} else {
this.showAlert(response.message, 'error')
}
}, error: error => {
this.showAlert(error.message, 'error')
this.cdr.detectChanges()
}
})
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
title: 'แจ้งเตือน',
text: text,
icon: type,
confirmButtonText: 'ตกลง',
});
}
confirmCompetencyTopic() {
approvePmsTopic() {
Swal.fire({
icon: 'question',
title: 'แจ้งเตือน',
text: 'ยืนยันการบันทึกข้อมูลหรือไม่',
text: 'ยืนยันการอนุมัติข้อมูลหรือไม่',
showCancelButton: true,
confirmButtonText: 'บันทึกข้อมูล',
confirmButtonText: 'อนุมัติข้อมูล',
cancelButtonText: 'ย้อนกลับ',
reverseButtons: true,
}).then(result => {
if (result.isConfirmed) {
this.addCompetencyTopic();
}
});
}).then((result) => {
this.appraisalKpiSettingEmp.select.currentTopic.forEach(obj => {
if (this.pmsTopicApprove.get(obj.pmsTopicId)) {
if (obj.approveTopicStatus == 1) {
obj.approveTopicStatus = 2
}
addCompetencyTopic(competencyFiles?: string) {
if (this.examFile) {
this.uploadExam();
return;
if (obj.approveTopicStatus == 0) {
obj.approveTopicStatus = 0
}
const user = this.tokenService.getUser();
const body = new MyCompetencytopicModel({
...this.competencyTopic.select,
companyId: user?.companyid,
competencyFiles: competencyFiles || ((this.examFileName == this.competencyTopic.select.competencyFiles) ? this.competencyTopic.select.competencyFiles : '')
});
this.competencyTopic.loading = true;
this.competencytopicService.post(body).subscribe({
next: response => {
if (response.success) {
this.showAlert(response.message, 'success');
this.getCompetencytypeList();
this.competencyTopicDialogClose()
} else {
this.showAlert(response.message, 'error');
this.competencyTopic.loading = false;
this.cdr.detectChanges();
}
},
error: error => {
this.showAlert(error.message, 'error');
this.competencyTopic.loading = false;
this.cdr.detectChanges();
if (obj.approveTopicStatus != 2) {
obj.approveTopicStatus = 3
}
});
}
uploadExam() {
if (!this.examFile) return;
const formData = new FormData();
formData.append('file', this.examFile);
this.fileService.uploadFiles(formData).subscribe({
})
this.appraisalService.postEmpPmsKpiApprove(new MyAppraisalKpiSettingEmpModel(this.appraisalKpiSettingEmp.select)).subscribe({
next: response => {
if (response.success) {
this.examFile = null;
this.addCompetencyTopic(response.resultObject);
this.getEmpPmsKpi()
this.showAlert(response.message, 'success')
} else {
this.showAlert(response.message, 'error');
this.cdr.detectChanges();
this.showAlert(response.message, 'error')
}
},
error: error => {
this.showAlert(error.message, 'error');
this.cdr.detectChanges();
}, error: error => {
this.showAlert(error.message, 'error')
}
})
});
}
showAlert(text: string, type: 'success' | 'error') {
this.toastr[type](text, 'แจ้งเตือน', {
timeOut: 3000,
positionClass: 'toast-top-right',
});
}
//
//
// modal: DataModal = {
// search: "",
// currentPage: 1,
// page: Array.from({ length: 1 }, (_, i) => i + 1)
// }
//
// @ViewChild("addKpiDialog") addKpiDialog: any;
// addKpiDialogRef: any
// @ViewChild("competencyTypeTableDialog") competencyTypeTableDialog: any;
// competencyTypeTableDialogRef: any
// @ViewChild("fileInputMedium") fileInputMedium: any;
// examFile: File | null = null;
// examFileName: string = 'กรุณาเลือกไฟล์';
// typeModal: table = {
// currentPage: 1,
// page: Array.from({ length: 1 }, (_, i) => i + 1),
// search: "",
// pageSize: 10
// }
// constructor(
// private dialog: MatDialog,
// private router: Router,
// private employeeService: EmployeeService,
// private appraisalService: AppraisalService,
// private cdr: ChangeDetectorRef,
// private tokenService: TokenService,
// private route: ActivatedRoute,
// private fileService: FileService,
// private pmstopicService: PmstopicService,
// private competencytopicService: CompetencytopicService,
// private competencytypeService: CompetencytypeService
// ) {
// }
// getCompetencytypeList() {
// this.competencytype.loading = true
// this.competencytypeService.getList().subscribe({
// next: response => {
// this.competencytype.dataList = response.map(x => {
// return new MyCompetencytypeModel(x)
// })
// this.competencytype.loading = false
// this.cdr.detectChanges()
// }, error: error => {
// this.competencytype.loading = false
// this.cdr.detectChanges()
// }
// })
// }
// selectPmstopic(data?: PmstopicModel) {
// this.cdr.markForCheck()
// }
// searchCompetencytypeChange() {
// this.typeModal.currentPage = 1
// this.typeModal.page = Array.from({ length: Math.ceil(this.competencytypeListFilter().length / 10) }, (_, i) => i + 1);
// }
// openAddKpiDialog() {
// this.addKpiDialogRef = this.dialog.open(this.addKpiDialog, {
// width: '500px',
// disableClose: false,
// });
// }
// openCompetencyTypeTableDialog() {
// this.competencyTypeTableDialogRef = this.dialog.open(this.competencyTypeTableDialog, {
// width: '500px',
// disableClose: false,
// });
// }
// showAlert(text: string, type: 'success' | 'error') {
// Swal.fire({
// title: 'แจ้งเตือน',
// text: text,
// icon: type,
// confirmButtonText: 'ตกลง',
// });
// }
// onExamSelected(event: any) {
// this.examFile = event.target.files.length > 0 ? event.target.files[0] : null;
// this.examFileName = this.examFile?.name || "กรุณาเลือกไฟล์"
// }
// downloadExam(fileName: string) {
// this.fileService.downloadFiles(fileName).subscribe({
// next: response => {
// const url = window.URL.createObjectURL(response);
// const a = document.createElement("a");
// a.href = url;
// a.download = fileName;
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
// window.URL.revokeObjectURL(url);
// this.cdr.detectChanges()
// }, error: error => {
// this.showAlert(error.message, 'error')
// this.cdr.detectChanges()
// }
// })
// }
// confirmAddCompetency_topic() {
// Swal.fire({
// icon: 'question',
// title: 'แจ้งเตือน',
// text: 'ยืนยันการบันทึกข้อมูลหรือไม่',
// showCancelButton: true,
// confirmButtonText: 'บันทึกข้อมูล',
// cancelButtonText: 'ย้อนกลับ',
// reverseButtons: true,
// }).then(result => {
// if (result.isConfirmed) {
// this.addCompetency_topic();
// }
// });
// }
// addCompetency_topic(competencyFiles?: string) {
// if (this.examFile) {
// this.uploadExam();
// return;
// }
// const user = this.tokenService.getUser();
// const body = new MyCompetencytopicModel({
// ...this.competencyTopic.select,
// companyId: user?.companyid,
// competencyFiles: competencyFiles || ((this.examFileName == this.competencyTopic.select.competencyFiles) ? this.competencyTopic.select.competencyFiles : '')
// });
// this.competencyTopic.loading = true;
// this.competencytopicService.post(body).subscribe({
// next: response => {
// if (response.success) {
// this.showAlert(response.message, 'success');
// // this.getCompetencytopicList();
// // this.closeDialog()
// } else {
// this.showAlert(response.message, 'error');
// this.competencyTopic.loading = false;
// this.cdr.detectChanges();
// }
// },
// error: error => {
// this.showAlert(error.message, 'error');
// this.competencyTopic.loading = false;
// this.cdr.detectChanges();
// }
// });
// }
// uploadExam() {
// if (!this.examFile) return;
// const formData = new FormData();
// formData.append('file', this.examFile);
// this.fileService.uploadFiles(formData).subscribe({
// next: response => {
// if (response.success) {
// this.examFile = null;
// this.addCompetency_topic(response.resultObject);
// } else {
// this.showAlert(response.message, 'error');
// this.cdr.detectChanges();
// }
// },
// error: error => {
// this.showAlert(error.message, 'error');
// this.cdr.detectChanges();
// }
// });
// }
// selectCompetencytype(data: CompetencytypeModel) {
// this.competencyTopic.select.competencyType = JSON.parse(JSON.stringify(data));
// }
}
......@@ -99,6 +99,7 @@ export class SidebarComponent {
this.currentUrl.includes('employee-self-service') ||
this.currentUrl.includes('ess/profile') ||
this.currentUrl.includes('ess/competency-mapping') ||
this.currentUrl.includes('ess/job-family-mapping') ||
this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
......@@ -172,6 +173,7 @@ export class SidebarComponent {
this.currentUrl.includes('employee-self-service') ||
this.currentUrl.includes('ess/profile') ||
this.currentUrl.includes('ess/competency-mapping') ||
this.currentUrl.includes('ess/job-family-mapping') ||
this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
......
......@@ -14,12 +14,13 @@ import { NgControl } from '@angular/forms';
})
export class MoneyInputDirective {
@Input() allowEmpty: boolean = false;
constructor(private el: ElementRef<HTMLInputElement>) { }
constructor(private el: ElementRef<HTMLInputElement>) {}
ngAfterViewInit() {
setTimeout(() => {
this.formatValue(this.el.nativeElement.value);
this.onBlur()
this.onBlur();
});
}
......@@ -30,40 +31,66 @@ export class MoneyInputDirective {
@HostListener('blur')
onBlur() {
let val = this.el.nativeElement.value.replace(/,/g, '');
let val = this.el.nativeElement.value.replace(/,/g, '').trim();
if (!val) return;
let num = parseFloat(val);
if (isNaN(num)) return;
let [intPart, decimalPart = ''] = val.split('.');
// จำกัดทศนิยมไม่เกิน 2 หลัก
decimalPart = decimalPart.substring(0, 2);
// เติม .00 ถ้าไม่มีทศนิยม
if (decimalPart === '') decimalPart = '00';
let parts = num.toFixed(2).split('.');
let intPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
this.el.nativeElement.value = `${intPart}.${parts[1]}`;
// ใส่ comma ให้จำนวนเต็ม
intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
this.el.nativeElement.value = `${intPart}.${decimalPart}`;
}
formatValue(value: string) {
let cleaned = value.replace(/[^0-9.]/g, '').replace(/^0+/, '');
const dotIndex = cleaned.indexOf('.');
if (dotIndex == 0) {
cleaned = `0.`;
} else if (dotIndex !== -1) {
const beforeDot = cleaned.slice(0, dotIndex);
let afterDot = cleaned.slice(dotIndex + 1).replace(/\./g, '');
afterDot = afterDot.substring(0, 2); // หลังจุด 2 ตัว
cleaned = `${beforeDot}.${afterDot}`;
}
if (!this.allowEmpty && cleaned == '') {
cleaned = '0'
// เก็บจุดไว้ ถ้ามีอยู่แล้วในข้อความ
const hasDot = value.includes('.');
// ลบอักขระที่ไม่ใช่ตัวเลขหรือจุด
let cleaned = value.replace(/[^0-9.]/g, '');
// จำกัดให้มีจุดทศนิยมได้แค่ 1 จุด
const firstDot = cleaned.indexOf('.');
if (firstDot !== -1) {
cleaned =
cleaned.slice(0, firstDot + 1) +
cleaned.slice(firstDot + 1).replace(/\./g, '');
}
let [intPart, decimalPart] = cleaned.split('.');
intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// กรณีเริ่มด้วยจุด เช่น ".5" → "0.5"
if (cleaned.startsWith('.')) cleaned = '0' + cleaned;
this.el.nativeElement.value = decimalPart !== undefined ? `${intPart}.${decimalPart}` : intPart;
// ลบศูนย์นำหน้า (ถ้าไม่ใช่ "0." และยาวเกิน 1)
if (!cleaned.startsWith('0.') && cleaned.length > 1) {
cleaned = cleaned.replace(/^0+/, '');
}
if (!this.allowEmpty && cleaned === '') cleaned = '0';
let [intPart, decimalPart = ''] = cleaned.split('.');
// จำกัดทศนิยมไม่เกิน 2 หลัก
decimalPart = decimalPart.substring(0, 2);
// ใส่ comma ให้จำนวนเต็ม
intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// รวมกลับ โดยถ้าผู้ใช้เพิ่งพิม "." ให้คงไว้ (เพื่อให้พิมพ์ต่อได้)
this.el.nativeElement.value =
hasDot && !value.endsWith('.') && decimalPart === ''
? `${intPart}.`
: decimalPart !== ''
? `${intPart}.${decimalPart}`
: value.endsWith('.')
? `${intPart}.`
: intPart;
}
}
......
export interface AppraisalKpiSettingEmpModel {
apsassessy: ApsassessyModel
apsyear: string
currentTopic: TopicModel[]
defendTopic: TopicModel[]
jd: string
pmsEvaluationRoundId: string
statusAll: StatusAllModel
confirmBy: string
}
export class MyAppraisalKpiSettingEmpModel implements AppraisalKpiSettingEmpModel {
apsassessy: ApsassessyModel
apsyear: string
currentTopic: TopicModel[]
defendTopic: TopicModel[]
jd: string
pmsEvaluationRoundId: string
statusAll: StatusAllModel
confirmBy: string
constructor(data?: Partial<AppraisalKpiSettingEmpModel>) {
this.apsassessy = new MyApsassessyModel(data?.apsassessy)
this.apsyear = data?.apsyear || ''
this.currentTopic = (data?.currentTopic || []).map(e => new MyTopicModel(e))
this.defendTopic = (data?.defendTopic || []).map(e => new MyTopicModel(e))
this.jd = data?.jd || ''
this.pmsEvaluationRoundId = data?.pmsEvaluationRoundId || ''
this.statusAll = new MyStatusAllModel(data?.statusAll)
this.confirmBy = data?.confirmBy || ''
}
}
export interface StatusAllModel {
code: string
edesc: string
tdesc: string
}
export class MyStatusAllModel implements StatusAllModel {
code: string
edesc: string
tdesc: string
constructor(data?: Partial<StatusAllModel>) {
this.code = data?.code || ''
this.edesc = data?.edesc || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- GradeDetailModel ----------------
export interface GradeDetailModel {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
}
export class MyGradeDetailModel implements GradeDetailModel {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
constructor(data?: Partial<GradeDetailModel>) {
this.companyId = data?.companyId || ''
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.edesc = data?.edesc || ''
this.gradeDetail = data?.gradeDetail || ''
this.gradeId = data?.gradeId || ''
this.gradeMaxScore = data?.gradeMaxScore ?? 0
this.gradeMinScore = data?.gradeMinScore ?? 0
this.tdesc = data?.tdesc || ''
this.weight = data?.weight ?? 0
}
}
// ---------------- GradeModel ----------------
// Note: Grade2 is considered same as Grade -> we use GradeModel
export interface GradeModel {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetailModel[]
}
export class MyGradeModel implements GradeModel {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetailModel[]
constructor(data?: Partial<GradeModel>) {
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.gradeDetail = (data?.gradeDetail || []).map(e => new MyGradeDetailModel(e))
}
}
// ---------------- Bu1Model ----------------
export interface Bu1Model {
bu1id: string
companyId: string
edesc: string
grade: GradeModel
tdesc: string
}
export class MyBu1Model implements Bu1Model {
bu1id: string
companyId: string
edesc: string
grade: GradeModel
tdesc: string
constructor(data?: Partial<Bu1Model>) {
this.bu1id = data?.bu1id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.grade = new MyGradeModel(data?.grade)
this.tdesc = data?.tdesc || ''
}
}
// ---------------- Bu2Model ----------------
export interface Bu2Model {
bu2id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu2Model implements Bu2Model {
bu2id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu2Model>) {
this.bu2id = data?.bu2id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- Bu3Model ----------------
export interface Bu3Model {
bu3id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu3Model implements Bu3Model {
bu3id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu3Model>) {
this.bu3id = data?.bu3id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- Bu4Model ----------------
export interface Bu4Model {
bu4id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu4Model implements Bu4Model {
bu4id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu4Model>) {
this.bu4id = data?.bu4id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- Bu5Model ----------------
export interface Bu5Model {
bu5id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu5Model implements Bu5Model {
bu5id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu5Model>) {
this.bu5id = data?.bu5id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- JobModel ----------------
export interface JobModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
}
export class MyJobModel implements JobModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
constructor(data?: Partial<JobModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.jobCodeId = data?.jobCodeId || ''
this.statusCom = data?.statusCom ?? false
this.statusPms = data?.statusPms ?? false
this.tdesc = data?.tdesc || ''
}
}
// ---------------- PmsTypeModel ----------------
export interface PmsTypeModel {
companyId: string
edesc: string
pmsTypeId: string
shortName: string
tdesc: string
weight: number
}
export class MyPmsTypeModel implements PmsTypeModel {
companyId: string
edesc: string
pmsTypeId: string
shortName: string
tdesc: string
weight: number
constructor(data?: Partial<PmsTypeModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.pmsTypeId = data?.pmsTypeId || ''
this.shortName = data?.shortName || ''
this.tdesc = data?.tdesc || ''
this.weight = data?.weight ?? 0
}
}
export interface TopicModel {
approveTopicStatus: number
companyId: string
completionDate: string
createStatusBy: string
detailUnit: string
edesc: string
indicatorsDetail: string
jl: string
lineNo: number
performanceGoalsDetail: string
pmsTopicId: string
pmsType: PmsTypeModel | null
scoreTopicExpectation: number
scoreTopicExpectationBoss: number
statusApprove: string
targetAdetail: string
targetBdetail: string
targetCdetail: string
targetDdetail: string
targetEdetail: string
tdesc: string
weight: number
}
export class MyTopicModel implements TopicModel {
approveTopicStatus: number
companyId: string
completionDate: string
createStatusBy: string
detailUnit: string
edesc: string
indicatorsDetail: string
jl: string
lineNo: number
performanceGoalsDetail: string
pmsTopicId: string
pmsType: PmsTypeModel | null
scoreTopicExpectation: number
scoreTopicExpectationBoss: number
statusApprove: string
targetAdetail: string
targetBdetail: string
targetCdetail: string
targetDdetail: string
targetEdetail: string
tdesc: string
weight: number
constructor(data?: Partial<TopicModel>) {
this.approveTopicStatus = data?.approveTopicStatus ?? 0
this.companyId = data?.companyId || ''
this.completionDate = data?.completionDate || ''
this.createStatusBy = data?.createStatusBy || ''
this.detailUnit = data?.detailUnit || ''
this.edesc = data?.edesc || ''
this.indicatorsDetail = data?.indicatorsDetail || ''
this.jl = data?.jl || ''
this.lineNo = data?.lineNo ?? 0
this.performanceGoalsDetail = data?.performanceGoalsDetail || ''
this.pmsTopicId = data?.pmsTopicId || ''
this.pmsType = data?.pmsType ? new MyPmsTypeModel(data?.pmsType) : null
this.scoreTopicExpectation = data?.scoreTopicExpectation ?? 0
this.scoreTopicExpectationBoss = data?.scoreTopicExpectationBoss ?? 0
this.statusApprove = data?.statusApprove || ''
this.targetAdetail = data?.targetAdetail || ''
this.targetBdetail = data?.targetBdetail || ''
this.targetCdetail = data?.targetCdetail || ''
this.targetDdetail = data?.targetDdetail || ''
this.targetEdetail = data?.targetEdetail || ''
this.tdesc = data?.tdesc || ''
this.weight = data?.weight ?? 0
}
}
// ---------------- PositionModel ----------------
export interface PositionModel {
companyId: string
consolidate: string
edesc: string
positionId: string
shortName: string
tdesc: string
}
export class MyPositionModel implements PositionModel {
companyId: string
consolidate: string
edesc: string
positionId: string
shortName: string
tdesc: string
constructor(data?: Partial<PositionModel>) {
this.companyId = data?.companyId || ''
this.consolidate = data?.consolidate || ''
this.edesc = data?.edesc || ''
this.positionId = data?.positionId || ''
this.shortName = data?.shortName || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- PrefixModel ----------------
export interface PrefixModel {
edesc: string
prefixId: string
tdesc: string
}
export class MyPrefixModel implements PrefixModel {
edesc: string
prefixId: string
tdesc: string
constructor(data?: Partial<PrefixModel>) {
this.edesc = data?.edesc || ''
this.prefixId = data?.prefixId || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- StatusModel ----------------
export interface StatusModel {
edesc: string
statusCode: string
statusType: string
tdesc: string
}
export class MyStatusModel implements StatusModel {
edesc: string
statusCode: string
statusType: string
tdesc: string
constructor(data?: Partial<StatusModel>) {
this.edesc = data?.edesc || ''
this.statusCode = data?.statusCode || ''
this.statusType = data?.statusType || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- Time0Model ----------------
export interface Time0Model {
edesc: string
hourD: number
stickTm: string
tdesc: string
time0id: string
}
export class MyTime0Model implements Time0Model {
edesc: string
hourD: number
stickTm: string
tdesc: string
time0id: string
constructor(data?: Partial<Time0Model>) {
this.edesc = data?.edesc || ''
this.hourD = data?.hourD ?? 0
this.stickTm = data?.stickTm || ''
this.tdesc = data?.tdesc || ''
this.time0id = data?.time0id || ''
}
}
// ---------------- PlModel ----------------
export interface PlModel {
companyId: string
edesc: string
grade: GradeModel
plId: string
tdesc: string
}
export class MyPlModel implements PlModel {
companyId: string
edesc: string
grade: GradeModel
plId: string
tdesc: string
constructor(data?: Partial<PlModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.grade = new MyGradeModel(data?.grade)
this.plId = data?.plId || ''
this.tdesc = data?.tdesc || ''
}
}
// ---------------- ApsassessyModel (root) ----------------
export interface ApsassessyModel {
bossId: string
bu1: Bu1Model
bu2: Bu2Model
bu3: Bu3Model
bu4: Bu4Model
bu5: Bu5Model
efname: string
elname: string
employeeId: string
endWorkDate: string
engFullName: string
fname: string
job: JobModel
lname: string
picture: string
pl: PlModel
position: PositionModel
prefix: PrefixModel
resignDate: string
startDate: string
status: StatusModel
thFullName: string
time0: Time0Model
}
export class MyApsassessyModel implements ApsassessyModel {
bossId: string
bu1: Bu1Model
bu2: Bu2Model
bu3: Bu3Model
bu4: Bu4Model
bu5: Bu5Model
efname: string
elname: string
employeeId: string
endWorkDate: string
engFullName: string
fname: string
job: JobModel
lname: string
picture: string
pl: PlModel
position: PositionModel
prefix: PrefixModel
resignDate: string
startDate: string
status: StatusModel
thFullName: string
time0: Time0Model
constructor(data?: Partial<ApsassessyModel>) {
this.bossId = data?.bossId || ''
this.bu1 = new MyBu1Model(data?.bu1)
this.bu2 = new MyBu2Model(data?.bu2)
this.bu3 = new MyBu3Model(data?.bu3)
this.bu4 = new MyBu4Model(data?.bu4)
this.bu5 = new MyBu5Model(data?.bu5)
this.efname = data?.efname || ''
this.elname = data?.elname || ''
this.employeeId = data?.employeeId || ''
this.endWorkDate = data?.endWorkDate || ''
this.engFullName = data?.engFullName || ''
this.fname = data?.fname || ''
this.job = new MyJobModel(data?.job)
this.lname = data?.lname || ''
this.picture = data?.picture || ''
this.pl = new MyPlModel(data?.pl)
this.position = new MyPositionModel(data?.position)
this.prefix = new MyPrefixModel(data?.prefix)
this.resignDate = data?.resignDate || ''
this.startDate = data?.startDate || ''
this.status = new MyStatusModel(data?.status)
this.thFullName = data?.thFullName || ''
this.time0 = new MyTime0Model(data?.time0)
}
}
export interface AppraisalKpiSettingModel {
apsassessy: ApsassessyModel
pmsEvaluationRound: PmsEvaluationRoundModel
statusApprove: StatusApproveModel
}
export class MyAppraisalKpiSettingModel implements AppraisalKpiSettingModel {
apsassessy: ApsassessyModel
pmsEvaluationRound: PmsEvaluationRoundModel
statusApprove: StatusApproveModel
constructor(data?: Partial<AppraisalKpiSettingModel>) {
this.apsassessy = new MyApsassessyModel(data?.apsassessy)
this.pmsEvaluationRound = new MyPmsEvaluationRoundModel(data?.pmsEvaluationRound)
this.statusApprove = new MyStatusApproveModel(data?.statusApprove)
}
}
export interface ApsassessyModel {
bossId: string
bu1: Bu1Model
bu2: Bu2Model
bu3: Bu3Model
bu4: Bu4Model
bu5: Bu5Model
efname: string
elname: string
employeeId: string
endWorkDate: string
engFullName: string
fname: string
job: JobModel
lname: string
picture: string
pl: PlModel
position: PositionModel
prefix: PrefixModel
resignDate: string
startDate: string
status: StatusModel
thFullName: string
time0: TimeModel
}
export class MyApsassessyModel implements ApsassessyModel {
bossId: string
bu1: Bu1Model
bu2: Bu2Model
bu3: Bu3Model
bu4: Bu4Model
bu5: Bu5Model
efname: string
elname: string
employeeId: string
endWorkDate: string
engFullName: string
fname: string
job: JobModel
lname: string
picture: string
pl: PlModel
position: PositionModel
prefix: PrefixModel
resignDate: string
startDate: string
status: StatusModel
thFullName: string
time0: TimeModel
constructor(data?: Partial<ApsassessyModel>) {
this.bossId = data?.bossId || ''
this.bu1 = new MyBu1Model(data?.bu1)
this.bu2 = new MyBu2Model(data?.bu2)
this.bu3 = new MyBu3Model(data?.bu3)
this.bu4 = new MyBu4Model(data?.bu4)
this.bu5 = new MyBu5Model(data?.bu5)
this.efname = data?.efname || ''
this.elname = data?.elname || ''
this.employeeId = data?.employeeId || ''
this.endWorkDate = data?.endWorkDate || ''
this.engFullName = data?.engFullName || ''
this.fname = data?.fname || ''
this.job = new MyJobModel(data?.job)
this.lname = data?.lname || ''
this.picture = data?.picture || ''
this.pl = new MyPlModel(data?.pl)
this.position = new MyPositionModel(data?.position)
this.prefix = new MyPrefixModel(data?.prefix)
this.resignDate = data?.resignDate || ''
this.startDate = data?.startDate || ''
this.status = new MyStatusModel(data?.status)
this.thFullName = data?.thFullName || ''
this.time0 = new MyTimeModel(data?.time0)
}
}
// -------------------- BU1 --------------------
export interface Bu1Model {
bu1id: string
companyId: string
edesc: string
grade: GradeModel
tdesc: string
}
export class MyBu1Model implements Bu1Model {
bu1id: string
companyId: string
edesc: string
grade: GradeModel
tdesc: string
constructor(data?: Partial<Bu1Model>) {
this.bu1id = data?.bu1id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.grade = new MyGradeModel(data?.grade)
this.tdesc = data?.tdesc || ''
}
}
// -------------------- GRADE --------------------
export interface GradeModel {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetailModel[]
}
export class MyGradeModel implements GradeModel {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetailModel[]
constructor(data?: Partial<GradeModel>) {
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.gradeDetail = (data?.gradeDetail || []).map(e => new MyGradeDetailModel(e))
}
}
export interface GradeDetailModel {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
}
export class MyGradeDetailModel implements GradeDetailModel {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
constructor(data?: Partial<GradeDetailModel>) {
this.companyId = data?.companyId || ''
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.edesc = data?.edesc || ''
this.gradeDetail = data?.gradeDetail || ''
this.gradeId = data?.gradeId || ''
this.gradeMaxScore = data?.gradeMaxScore ?? 0
this.gradeMinScore = data?.gradeMinScore ?? 0
this.tdesc = data?.tdesc || ''
this.weight = data?.weight ?? 0
}
}
// -------------------- BU2-BU5 --------------------
export interface Bu2Model {
bu2id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu2Model implements Bu2Model {
bu2id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu2Model>) {
this.bu2id = data?.bu2id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
export interface Bu3Model {
bu3id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu3Model implements Bu3Model {
bu3id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu3Model>) {
this.bu3id = data?.bu3id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
export interface Bu4Model {
bu4id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu4Model implements Bu4Model {
bu4id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu4Model>) {
this.bu4id = data?.bu4id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
export interface Bu5Model {
bu5id: string
companyId: string
edesc: string
parent: string
tdesc: string
}
export class MyBu5Model implements Bu5Model {
bu5id: string
companyId: string
edesc: string
parent: string
tdesc: string
constructor(data?: Partial<Bu5Model>) {
this.bu5id = data?.bu5id || ''
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.parent = data?.parent || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- JOB --------------------
export interface JobModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
}
export class MyJobModel implements JobModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
constructor(data?: Partial<JobModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.jobCodeId = data?.jobCodeId || ''
this.statusCom = data?.statusCom ?? false
this.statusPms = data?.statusPms ?? false
this.tdesc = data?.tdesc || ''
}
}
// -------------------- PL + Grade2 --------------------
export interface PlModel {
companyId: string
edesc: string
grade: Grade2Model
plId: string
tdesc: string
}
export class MyPlModel implements PlModel {
companyId: string
edesc: string
grade: Grade2Model
plId: string
tdesc: string
constructor(data?: Partial<PlModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.grade = new MyGrade2Model(data?.grade)
this.plId = data?.plId || ''
this.tdesc = data?.tdesc || ''
}
}
export interface Grade2Model {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetail2Model[]
}
export class MyGrade2Model implements Grade2Model {
countAverage: string
countEmployee: string
countScore: string
gradeDetail: GradeDetail2Model[]
constructor(data?: Partial<Grade2Model>) {
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.gradeDetail = (data?.gradeDetail || []).map(e => new MyGradeDetail2Model(e))
}
}
export interface GradeDetail2Model {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
}
export class MyGradeDetail2Model implements GradeDetail2Model {
companyId: string
countAverage: string
countEmployee: string
countScore: string
edesc: string
gradeDetail: string
gradeId: string
gradeMaxScore: number
gradeMinScore: number
tdesc: string
weight: number
constructor(data?: Partial<GradeDetail2Model>) {
this.companyId = data?.companyId || ''
this.countAverage = data?.countAverage || ''
this.countEmployee = data?.countEmployee || ''
this.countScore = data?.countScore || ''
this.edesc = data?.edesc || ''
this.gradeDetail = data?.gradeDetail || ''
this.gradeId = data?.gradeId || ''
this.gradeMaxScore = data?.gradeMaxScore ?? 0
this.gradeMinScore = data?.gradeMinScore ?? 0
this.tdesc = data?.tdesc || ''
this.weight = data?.weight ?? 0
}
}
// -------------------- POSITION --------------------
export interface PositionModel {
companyId: string
consolidate: string
edesc: string
positionId: string
shortName: string
tdesc: string
}
export class MyPositionModel implements PositionModel {
companyId: string
consolidate: string
edesc: string
positionId: string
shortName: string
tdesc: string
constructor(data?: Partial<PositionModel>) {
this.companyId = data?.companyId || ''
this.consolidate = data?.consolidate || ''
this.edesc = data?.edesc || ''
this.positionId = data?.positionId || ''
this.shortName = data?.shortName || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- PREFIX --------------------
export interface PrefixModel {
edesc: string
prefixId: string
tdesc: string
}
export class MyPrefixModel implements PrefixModel {
edesc: string
prefixId: string
tdesc: string
constructor(data?: Partial<PrefixModel>) {
this.edesc = data?.edesc || ''
this.prefixId = data?.prefixId || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- STATUS --------------------
export interface StatusModel {
edesc: string
statusCode: string
statusType: string
tdesc: string
}
export class MyStatusModel implements StatusModel {
edesc: string
statusCode: string
statusType: string
tdesc: string
constructor(data?: Partial<StatusModel>) {
this.edesc = data?.edesc || ''
this.statusCode = data?.statusCode || ''
this.statusType = data?.statusType || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- TIME0 --------------------
export interface TimeModel {
edesc: string
hourD: number
stickTm: string
tdesc: string
time0id: string
}
export class MyTimeModel implements TimeModel {
edesc: string
hourD: number
stickTm: string
tdesc: string
time0id: string
constructor(data?: Partial<TimeModel>) {
this.edesc = data?.edesc || ''
this.hourD = data?.hourD ?? 0
this.stickTm = data?.stickTm || ''
this.tdesc = data?.tdesc || ''
this.time0id = data?.time0id || ''
}
}
// -------------------- PmsEvaluationRound --------------------
export interface PmsEvaluationRoundModel {
active: number
apsPeriodEnd: string
apsPeriodStart: string
apsyear: string
checkForm: number
companyId: string
edesc: string
jd: JdModel[]
jdId: string
pmsEvaluationRoundId: string
statusCode: StatusCodeModel
statusFrom: StatusFromModel
tdesc: string
}
export class MyPmsEvaluationRoundModel implements PmsEvaluationRoundModel {
active: number
apsPeriodEnd: string
apsPeriodStart: string
apsyear: string
checkForm: number
companyId: string
edesc: string
jd: JdModel[]
jdId: string
pmsEvaluationRoundId: string
statusCode: StatusCodeModel
statusFrom: StatusFromModel
tdesc: string
constructor(data?: Partial<PmsEvaluationRoundModel>) {
this.active = data?.active ?? 0
this.apsPeriodEnd = data?.apsPeriodEnd || ''
this.apsPeriodStart = data?.apsPeriodStart || ''
this.apsyear = data?.apsyear || ''
this.checkForm = data?.checkForm ?? 0
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.jd = (data?.jd || []).map(e => new MyJdModel(e))
this.jdId = data?.jdId || ''
this.pmsEvaluationRoundId = data?.pmsEvaluationRoundId || ''
this.statusCode = new MyStatusCodeModel(data?.statusCode)
this.statusFrom = new MyStatusFromModel(data?.statusFrom)
this.tdesc = data?.tdesc || ''
}
}
// -------------------- JD --------------------
export interface JdModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
}
export class MyJdModel implements JdModel {
companyId: string
edesc: string
jobCodeId: string
statusCom: boolean
statusPms: boolean
tdesc: string
constructor(data?: Partial<JdModel>) {
this.companyId = data?.companyId || ''
this.edesc = data?.edesc || ''
this.jobCodeId = data?.jobCodeId || ''
this.statusCom = data?.statusCom ?? false
this.statusPms = data?.statusPms ?? false
this.tdesc = data?.tdesc || ''
}
}
// -------------------- StatusCode --------------------
export interface StatusCodeModel {
code: string
edesc: string
tdesc: string
}
export class MyStatusCodeModel implements StatusCodeModel {
code: string
edesc: string
tdesc: string
constructor(data?: Partial<StatusCodeModel>) {
this.code = data?.code || ''
this.edesc = data?.edesc || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- StatusFrom --------------------
export interface StatusFromModel {
code: string
edesc: string
tdesc: string
}
export class MyStatusFromModel implements StatusFromModel {
code: string
edesc: string
tdesc: string
constructor(data?: Partial<StatusFromModel>) {
this.code = data?.code || ''
this.edesc = data?.edesc || ''
this.tdesc = data?.tdesc || ''
}
}
// -------------------- StatusApprove --------------------
export interface StatusApproveModel {
code: string
edesc: string
tdesc: string
}
export class MyStatusApproveModel implements StatusApproveModel {
code: string
edesc: string
tdesc: string
constructor(data?: Partial<StatusApproveModel>) {
this.code = data?.code || ''
this.edesc = data?.edesc || ''
this.tdesc = data?.tdesc || ''
}
}
......@@ -8,6 +8,8 @@ import { AppraisalSubordinateModel } from '../model/appraisal-subordinate.model'
import { AppraisalPmsFormModel } from '../model/appraisal-pms-form.model';
import { AppraisalPmsModel } from '../model/appraisal-pms.model';
import { CompetencyModel } from '../model/competency.model';
import { AppraisalKpiSettingModel } from '../model/appraisal-kpi-setting.model';
import { AppraisalKpiSettingEmpModel, TopicModel } from '../model/appraisal-kpi-setting-emp.model';
@Injectable({
providedIn: 'root'
})
......@@ -51,4 +53,22 @@ export class AppraisalService {
}): Observable<AlertModel> {
return this.http.post<AlertModel>(this.urlApi + "/approveAll", body)
}
getEmpKpi(): Observable<AppraisalKpiSettingModel[]> {
return this.http.get<AppraisalKpiSettingModel[]>(this.urlApi + "/boss/emp-kpi")
}
getKpi(): Observable<TopicModel[]> {
return this.http.get<TopicModel[]>(environment.baseUrl + `/pmstopic/kpi`)
}
getEmpPmsKpi(apsassessy: string, bossid?: string): Observable<AppraisalKpiSettingEmpModel[]> {
return this.http.get<AppraisalKpiSettingEmpModel[]>(this.urlApi + `/pms-all-kpi?apsassessy=${apsassessy}` + (bossid ? `&bossid=${bossid}` : ''))
}
postEmpPmsKpi(body: AppraisalKpiSettingEmpModel): Observable<AlertModel> {
return this.http.post<AlertModel>(this.urlApi + `/pms-all-kpi`, body)
}
postEmpPmsKpiApprove(body: AppraisalKpiSettingEmpModel): Observable<AlertModel> {
return this.http.post<AlertModel>(this.urlApi + `/pms-all-kpi/approve`, body)
}
}
\ No newline at end of file
......@@ -122,26 +122,26 @@ export class NavService implements OnDestroy {
// { id: 'm12', path: 'ess/supervisor-evaluation', title: 'ประเมินโดยหัวหน้า', type: 'link', show: true },
// ],
},
// {
// title: 'แก้ไข Individual KPI ตนเอง',
// type: 'link',
// selected: false,
// active: false,
// path: 'ess/self-setting-individual-kpi',
// id: 'm3',
// show: true,
// icon: ''
// },
// {
// title: 'แก้ไข Individual KPI โดยหัวหน้า',
// type: 'link',
// selected: false,
// active: false,
// path: 'ess/supervisor-setting-individual-kpi',
// id: 'm4',
// show: true,
// icon: ''
// },
{
title: 'แก้ไข Individual KPI ตนเอง',
type: 'link',
selected: false,
active: false,
path: 'ess/self-setting-individual-kpi',
id: 'm3',
show: true,
icon: ''
},
{
title: 'แก้ไข Individual KPI โดยหัวหน้า',
type: 'link',
selected: false,
active: false,
path: 'ess/supervisor-setting-individual-kpi',
id: 'm4',
show: true,
icon: ''
},
];
}
......
......@@ -34,6 +34,8 @@
"PmsManage": "การจัดการการประเมินผล",
"EvaluationPeriod": "รอบการประเมิน"
},
"TimeAttendance_head": "ทะเบียนเครื่องมือ",
"TimeAttendanceManage": "เครื่องมือประเมิน",
"PerformanceManagementSystem": "ประเมินการจัดการประสิทธิภาพ",
"SearchByNoOrName": "ค้นหาตามรหัสหรือชื่อ",
"Import": "นำเข้า",
......
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