Commit bd141657 by Nattana Chaiyamat

Calibrate คะแนน

parent 8e618741
<div class="relative">
<div class="flex relative before:absolute before:w-full before:h-full bg-gradient-custom"
style="margin-top: -50px;margin-left: -100px;margin-right: -100px">
<div class="h-[500px] w-full rounded-sm"></div>
</div>
<div class="main-content " style="margin-top:-29rem">
<div class="grid grid-cols-12 gap-x-6">
<div class="col-span-12 xxl:col-span-2">
</div>
<div class="col-span-12 xxl:col-span-8">
<div class="box" style="border-radius:20px">
<div class="box-header">
<div class="grid grid-cols-12 gap-x-6 mb-3 mt-3 relative">
<div class="absolute">
<button type="button" class="ti-btn ti-btn-outline ti-btn-outline-light h-20px m-0 "
style="border-radius:20px" routerLink="/ess/employee-self-service">
<i class="ti ti-chevron-left"></i>
{{'Back' | translate}}
</button>
</div>
<div class="col-span-12">
<h5 class="box-title mb-3 text-center">Calibrate คะแนน</h5>
</div>
</div>
<nav class="sm:flex sm:space-x-2 space-y-2 sm:space-y-0 rtl:space-x-reverse block"
aria-label="Tabs" role="tablist" style="max-width: 250px;">
<button type="button" style="border-radius:20px"
class="py-2 px-3 inline-flex items-center w-full justify-center gap-2 text-sm font-medium text-center border text-gray-500 rounded-sm hover:text-gray-700 active"
id="part-item-1" data-hs-tab="#part-1" aria-controls="part-1" role="tab">
<!-- hs-tab-active:text-white hs-tab-active:bg-primary hs-tab-active:border-primary -->
{{'CDR'}}
</button>
</nav>
</div>
<div class="box-body">
<div id="part-1" class="" role="tabpanel" aria-labelledby="part-item-1">
<div class="w-full min-height-50px mb-10px justify-between items-center">
<div class="flex 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">
<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>
<div class="p-2 pt-0">
<ng-container *ngTemplateOutlet="Datagrid"></ng-container>
</div>
</div>
</div>
</div>
</div>
<div class="col-span-12 xxl:col-span-2">
</div>
</div>
</div>
</div>
<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">
<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-column headerText='action' textAlign='Center'>
<ng-template #headerTemplate let-data>
<span class="font-size-12px font-weight-700 text-primary">{{'Action' | translate}}</span>
</ng-template>
<ng-template #template let-data>
<i class="ti ti-eye cursor-pointer i-gray fs-l px-1" (click)="openEmployeeDialog()"></i>
</ng-template>
</e-column>
</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>
<ng-template #employeeDialog let-modal>
<h3 mat-dialog-title>
<div class="relative">
<div class="absolute" style="top: -43px;">
{{'คะแนน' | translate}}
</div>
<div class="absolute" style="right: 0;top: -43px;">
<button type="button" class="hs-dropdown-toggle ti-modal-clode-btn text-danger" mat-button
[mat-dialog-close]>
<span class="sr-only">Close</span>
<i class="ti ti-circle-x fs-xxl"></i>
</button>
</div>
</div>
</h3>
<div class="w-full flex justify-end mb-1rem">
<div class="absolute flex" style="z-index: 9;">
<div class="px-1">
<button type="button" class="ti-btn ti-btn-soft-info h-45px m-0 shadow-md" (click)="exportPdf()">
<span class="e-btn-icon e-print e-icons e-icon-left"></span>
Print
</button>
</div>
</div>
</div>
<mat-dialog-content>
<div #pdfArea class="row w-full pdf-container" style="justify-content: center;align-content: start;">
<div class="row" style="height: 1123px; width: 794px;justify-content: center;align-content: start">
<div class="row col-12 mb-1">
<div class="col-auto">
<img src="assets/img/logos/logo-myhr-dark.png" alt="Performance illustration" class="hero-img"
style="width: 100px;margin-right: 10px;" />
</div>
<div class="col" style="font-size: 35px;font-weight: 800;align-content: center;">
หลักสูตรฝึกอบรมตามระบบสมรรถนะ (CDR)
</div>
</div>
<div class="row col-12 border">
<div class="row col-12">
<div class="col-2 border text-center"
style="font-weight: 800;height: 32px;align-content: center;">
ประเภทสมรรถนะ
</div>
<div class="col-2 border text-center"
style="font-weight: 800;height: 32px;align-content: center;">
รหัสสมรรถนะ
</div>
<div class="col-6 border text-center"
style="font-weight: 800;height: 32px;align-content: center;">
ชื่อสมรรถนะ
</div>
<div class="col-2 border text-center"
style="font-weight: 800;height: 32px;align-content: center;">
ชื่อหลักสูตร
</div>
</div>
<div class="row col-12">
<div class="col-2 border text-center" style="height: 32px;align-content: center;">
PC
</div>
<div class="col-2 border text-center" style="height: 32px;align-content: center;">
PC-FA-06
</div>
<div class="col-6 border text-center" style="height: 32px;align-content: center;">
ทักษะการสื่อสารและประสานงาน
</div>
<div class="col-2 border text-center" style="height: 32px;align-content: center;">
PC-FA-06-4
</div>
</div>
</div>
<div class="row col-12 border">
<div class="row col-12">
<div class="row col-9 text-center">
<div class="row col-12 border"
style="justify-content: center;font-weight: 800;font-size: 25px;height: 50px;align-content: center">
ชื่อหลักสูตร
</div>
<div class="row col-12 border"
style="justify-content: center;font-weight: 800;height: 50px;align-content: center">
ทักษะการสื่อสารและประสานงาน สำหรับพนักงานระดับ S3-S4
</div>
</div>
<div class="col-3 text-center border">
</div>
</div>
<div class="row col-12">
<div class="col-9 border text-center row p-2">
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
วัตถุประสงค์
</div>
<div class="col-12 row" style="text-align: left" *ngFor="let item of [
'1) เพื่อให้ผู้เรียนสามารถคิดวิเคราะห์ แก้ไขปัญหา และควบคุมดูแลงานได้',
'2) เพื่อให้ผู้เรียนสามารถปฏิบัติงาน ติดตามงาน และปรับปรุงงานอย่างต่อเนื่องได้',
'3) เพื่อให้ผู้เรียนสามารถให้คําแนะนํา สอนงาน และพัฒนาทีมงานได้',
'4) เพื่อให้ผู้เรียนสามารถนําเสนอ และจัดทํารายงาน ทั้งภาษาไทยและภาษาอังกฤษ',
]">
{{item}}
</div>
</div>
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
หัวข้อเรื่อง
</div>
<div class="col-12 row" style="text-align: left" *ngFor="let item of [
'1) การให้คําแนะนํา สอนงาน และพัฒนาทีมงานเกี่ยวกับการประสานงานที่มีประสิทธิภาพได้',
'2) การปฏิบัติงาน ติดตามงาน ปรับปรุงอย่างต่อเนื่องโดยใช้ทักษะการประสานงาน ที่มีประสิทธิภาพ',
'3) การคิดวิเคราะห์ แก้ไขปัญหา ควบคุมดูแล โดยใช้ทักษะการประสานที่มีประสิทธิภาพ',
'4) การนําเสนอ และจัดทํารายงาน ทั้งภาษาไทยและภาษาอังกฤษโดยใช้ทักษะการ ประสานงานที่มีประสิทธิภาพ',
]">
{{item}}
</div>
</div>
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
วิธีการ
</div>
<div class="col-12 row" style="text-align: left" *ngFor="let item of [
'1) บรรยาย (50%)',
'2) Workshop (30%)',
'3) อื่นๆ (20%)',
]">
{{item}}
</div>
</div>
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
คุณสมบัติผู้เข้าร่วม
</div>
<div class="col-12 row" style="text-align: left">
พนักงานระดับ S3-S4
</div>
</div>
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
ผู้สอน/วิทยากร
</div>
<div class="col-12 row" style="text-align: left">
วิทยากร/ผู้เชี่ยวชาญภายในหรือภายนอก
</div>
</div>
<div class="row col-12 pb-3">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
ระยะเวลา
</div>
<div class="col-12 row" style="text-align: left">
1 วัน
</div>
</div>
</div>
<div class="col-3 border text-center p-2 row" style="align-content: start;">
<div class="col-12 row pb-3" style="text-align: left;font-weight: 800;">
ค่านิยามสมรรถนะ
</div>
<div class="col-12 row" style="text-align: left;">
สามารถติดต่อประสานงานกับทางธนาคาร, หน่วยงานราชการ หน่วย งานในองค์กร, การใช้บริการทาง
Internet, และการแก้ไขปัญหาต่างๆ ได้เป็นอย่างดี
</div>
</div>
</div>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button type="button" mat-button [mat-dialog-close]
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>
\ No newline at end of file
.bg-gradient-custom {
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
mask-size: 100% 100%;
mask-repeat: no-repeat;
}
.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;
}
}
@for $i from 1 through 100 {
.m-#{$i}rem {
margin: #{$i}rem;
}
.mt-#{$i}rem {
margin-top: #{$i}rem;
}
.ml-#{$i}rem {
margin-left: #{$i}rem;
}
.mb-#{$i}rem {
margin-bottom: #{$i}rem;
}
.mr-#{$i}rem {
margin-right: #{$i}rem;
}
.p-#{$i}rem {
padding: #{$i}rem;
}
.pt-#{$i}rem {
padding-top: #{$i}rem;
}
.pl-#{$i}rem {
padding-left: #{$i}rem;
}
.pb-#{$i}rem {
padding-bottom: #{$i}rem;
}
.pr-#{$i}rem {
padding-right: #{$i}rem;
}
}
// 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;
}
.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;
}
}
@for $i from 1 through 100 {
.m-#{$i}rem {
margin: #{$i}rem;
}
.mt-#{$i}rem {
margin-top: #{$i}rem;
}
.ml-#{$i}rem {
margin-left: #{$i}rem;
}
.mb-#{$i}rem {
margin-bottom: #{$i}rem;
}
.mr-#{$i}rem {
margin-right: #{$i}rem;
}
.p-#{$i}rem {
padding: #{$i}rem;
}
.pt-#{$i}rem {
padding-top: #{$i}rem;
}
.pl-#{$i}rem {
padding-left: #{$i}rem;
}
.pb-#{$i}rem {
padding-bottom: #{$i}rem;
}
.pr-#{$i}rem {
padding-right: #{$i}rem;
}
}
@media print {
body * {
visibility: hidden;
/* ซ่อนทุก element */
}
#printArea,
#printArea * {
visibility: visible;
/* แสดงเฉพาะ #printArea */
}
#printArea {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}
.pdf-container {
transform: translateZ(0
);
}
\ No newline at end of file
import { ViewportScroller } from '@angular/common';
import { ChangeDetectorRef, Component, ElementRef, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { AggregateService, ColumnMenuService, DetailRowService, EditService, ExcelExportService, FilterService, GridComponent, GroupService, PageService, PdfExportService, ReorderService, SearchService, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { Column, ColumnMenuClickEventArgs, ColumnModel, ExcelExportProperties, FilterSettingsModel, GroupSettingsModel, LoadingIndicatorModel, SelectionSettingsModel } from '@syncfusion/ej2-grids';
import { ToastrService } from 'ngx-toastr';
import { EmployeeModel, MyEmployeeModel } from 'src/app/shared/model/employee.model';
import { JobCodeModel, MyJobCodeModel } from 'src/app/shared/model/job-code.model';
import { MyStatusModel, StatusModel } from 'src/app/shared/model/status.model';
import { EmpStatusService } from 'src/app/shared/services/emp-status.service';
import { EmployeeService } from 'src/app/shared/services/employee.service';
import { EventgrpService } from 'src/app/shared/services/eventgrp.service';
import { FileService } from 'src/app/shared/services/file.service';
import { JobcodeService } from 'src/app/shared/services/jobcode.service';
import { PmsWorkingTimeService } from 'src/app/shared/services/pms-working-time.service';
import Swal from 'sweetalert2';
import { Query } from '@syncfusion/ej2-data';
import { setCulture } from '@syncfusion/ej2-base';
import { AppraisalService } from 'src/app/shared/services/appraisal.service';
import { TokenService } from 'src/app/shared/services/token.service';
export interface BiModel {
name: string,
tools: string[],
degree: string
}
@Component({
selector: 'app-calibrate',
templateUrl: './calibrate.component.html',
providers: [AggregateService, SortService, GroupService, ColumnMenuService, PageService, FilterService, ToolbarService, PdfExportService, ExcelExportService, DetailRowService, ReorderService, EditService, SearchService],
styleUrls: ['./calibrate.component.scss']
})
export class CalibrateComponent {
search = ''
syncfution: {
dataList: any[],
searchSettings: {
fields: string[],
operator: 'contains',
ignoreCase: false
},
columns: ColumnModel[]
} = {
dataList: [],
searchSettings: {
fields: [
'code',
'name',
],
operator: 'contains',
ignoreCase: false
},
columns: [{
field: "code",
headerText: "รหัส",
type: "string",
isPrimaryKey: true,
},
{
field: "name",
headerText: "ชื่อหลักสูตร",
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'
evaluaterId = ''
@ViewChild('pdfArea') pdfArea!: ElementRef<HTMLElement>;
@ViewChild('employeeDialog') employeeDialog: any
employeeDialogRef: any
constructor(private appraisalService: AppraisalService,
private fileService: FileService,
private translateService: TranslateService,
private tokenService: TokenService,
private dialog: MatDialog,
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.evaluaterId = this.decodeJWT(sessionStorage.getItem("accessToken") || '').employeeid
this.getEmpKpi()
}
getEmpKpi() {
this.syncfution.dataList = []
this.setSyncfutionDataList()
// this.appraisalService.getEmpKpi().subscribe({
// next: response => {
// this.subordinate.dataList = response.map(e => new MyAppraisalKpiSettingModel(e))
// this.setSyncfutionDataList()
// this.cdr.markForCheck()
// }, error: error => {
// this.cdr.markForCheck()
// }
// })
}
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.syncfution.dataList = this.dataFilter().map(e => ({
code: e,
name: e
}))
}
dataFilter() {
return [1, 2, 3, 4].filter(x => {
return (x + '').toLowerCase().includes(this.search.toLowerCase())
})
}
decodeJWT(token: string) {
let base64Url = token.split('.')[1];
let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
let jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
title: 'แจ้งเตือน',
text: text,
icon: type,
confirmButtonText: 'ตกลง',
});
}
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)
}
}
async exportPdf() {
const el = this.pdfArea?.nativeElement;
if (!el) return;
// 👉 ลบ shadow ออกจากทุก element ที่มี shadow class
const shadowEls = el.querySelectorAll('.shadow');
shadowEls.forEach(e => e.classList.remove('shadow'));
const mod: any = await import('html2pdf.js');
const html2pdf = mod.default ?? mod;
const cleanupScale = this.expandToFullWidthThenScale(el);
const cleanupUnclip = this.unclipOverflows(el);
const prevBodyOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
const fullW = el.scrollWidth;
const fullH = el.scrollHeight;
const epsilon = 2;
const opt = {
margin: [10, 10, 10, 10],
filename: `test.pdf`,
image: { type: 'jpeg', quality: 0.96 },
html2canvas: {
scale: 2,
useCORS: true,
logging: false,
scrollX: 0,
scrollY: -window.scrollY,
width: 794,
// height: Math.max(0, fullH - epsilon),
height: 1123,
windowWidth: 794,
// windowHeight: Math.max(0, fullH - epsilon),
windowHeight: 1123,
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
pagebreak: { mode: ['css', 'legacy'], avoid: ['.no-break', 'table', 'img'] },
onclone: (doc: any) => {
doc.querySelectorAll('[data-hide-in-pdf]').forEach((el: any) => el.remove());
},
};
try {
await html2pdf().from(el).set(opt).save();
} finally {
cleanupUnclip();
cleanupScale();
document.body.style.overflow = prevBodyOverflow;
// 👉 คืน class shadow กลับ
shadowEls.forEach(e => e.classList.add('shadow'));
}
}
expandToFullWidthThenScale(el: HTMLElement) {
const A4_PX = 794; // ≈ 210mm @ 96dpi
const full = el.scrollWidth; // ความกว้างจริงทั้งหมด (รวมพื้นที่ต้องเลื่อนขวา)
const scale = Math.min(1, A4_PX / full); // สเกลลงให้พอดี A4 (ถ้ากว้างกว่า)
// เก็บค่าเดิมไว้คืนทีหลัง
const prev = {
width: el.style.width,
maxWidth: el.style.maxWidth,
overflowX: el.style.overflowX,
transform: el.style.transform,
transformOrigin: el.style.transformOrigin,
};
// คลี่ความกว้างออกทั้งหมด แล้วสเกลให้พอดี A4
el.style.width = `${full}px`;
el.style.maxWidth = 'none';
el.style.overflowX = 'visible';
el.style.transformOrigin = 'top left';
el.style.transform = `scale(${scale})`;
// คืนค่า
return () => {
el.style.width = prev.width;
el.style.maxWidth = prev.maxWidth;
el.style.overflowX = prev.overflowX;
el.style.transform = prev.transform;
el.style.transformOrigin = prev.transformOrigin;
};
}
private unclipOverflows(el: HTMLElement) {
const patched: Array<{ node: HTMLElement, prev: Partial<CSSStyleDeclaration> }> = [];
const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT);
const patchNode = (node: HTMLElement) => {
const style = window.getComputedStyle(node);
const needOverflowPatch = style.overflowX !== 'visible' || style.overflow !== 'visible';
const isSticky = style.position === 'sticky';
if (needOverflowPatch || isSticky) {
const prev = {
overflow: node.style.overflow,
overflowX: node.style.overflowX,
overflowY: node.style.overflowY,
position: node.style.position,
clipPath: (node.style as any).clipPath,
};
patched.push({ node, prev });
node.style.overflow = 'visible';
node.style.overflowX = 'visible';
node.style.overflowY = 'visible';
if (isSticky) node.style.position = 'static';
(node.style as any).clipPath = 'none';
}
};
// รวม el เองด้วย
patchNode(el as HTMLElement);
while (walker.nextNode()) {
patchNode(walker.currentNode as HTMLElement);
}
// ฟังก์ชันคืนค่าเดิม
return () => {
for (const { node, prev } of patched) {
node.style.overflow = prev.overflow || '';
node.style.overflowX = prev.overflowX || '';
node.style.overflowY = prev.overflowY || '';
node.style.position = prev.position || '';
(node.style as any).clipPath = prev.clipPath || '';
}
};
}
openEmployeeDialog() {
this.employeeDialogRef = this.dialog.open(this.employeeDialog, {
width: '1000px',
})
}
closeEmployeeDialog() {
this.employeeDialogRef.close()
}
}
\ No newline at end of file
...@@ -80,6 +80,7 @@ import { JobManagementComponent } from '../job-management/job-management.compone ...@@ -80,6 +80,7 @@ import { JobManagementComponent } from '../job-management/job-management.compone
import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component'; import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component';
import { TimeAttendanceWizardComponent } from '../time-attendance-wizard/time-attendance-wizard.component'; import { TimeAttendanceWizardComponent } from '../time-attendance-wizard/time-attendance-wizard.component';
import { ReviewHistoryComponent } from '../review-history/review-history.component'; import { ReviewHistoryComponent } from '../review-history/review-history.component';
import { CalibrateComponent } from '../calibrate/calibrate.component';
...@@ -175,6 +176,7 @@ const routes: Routes = [ ...@@ -175,6 +176,7 @@ const routes: Routes = [
{ path: "ess/welfare-reward", title: 'Welfare & Reward', component: WelfareRewardComponent }, { path: "ess/welfare-reward", title: 'Welfare & Reward', component: WelfareRewardComponent },
{ path: "ess/time-attendance-wizard", title: 'TimeAttendance', component: TimeAttendanceWizardComponent }, { path: "ess/time-attendance-wizard", title: 'TimeAttendance', component: TimeAttendanceWizardComponent },
{ path: "ess/review-history", title: 'ดูข้อมูลย้อนหลัง', component: ReviewHistoryComponent }, { path: "ess/review-history", title: 'ดูข้อมูลย้อนหลัง', component: ReviewHistoryComponent },
{ path: "ess/calibrate", title: 'calibrate คะแนน', component: CalibrateComponent },
] ]
} }
]; ];
......
...@@ -236,6 +236,7 @@ import { JobManagementComponent } from '../job-management/job-management.compone ...@@ -236,6 +236,7 @@ import { JobManagementComponent } from '../job-management/job-management.compone
import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component'; import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component';
import { TimeAttendanceWizardComponent } from '../time-attendance-wizard/time-attendance-wizard.component'; import { TimeAttendanceWizardComponent } from '../time-attendance-wizard/time-attendance-wizard.component';
import { ReviewHistoryComponent } from '../review-history/review-history.component'; import { ReviewHistoryComponent } from '../review-history/review-history.component';
import { CalibrateComponent } from '../calibrate/calibrate.component';
export const MY_DATE_FORMATS = { export const MY_DATE_FORMATS = {
parse: { parse: {
...@@ -411,7 +412,8 @@ export class CustomDateAdapter extends NativeDateAdapter { ...@@ -411,7 +412,8 @@ export class CustomDateAdapter extends NativeDateAdapter {
JobManagementComponent, JobManagementComponent,
WelfareRewardComponent, WelfareRewardComponent,
TimeAttendanceWizardComponent, TimeAttendanceWizardComponent,
ReviewHistoryComponent ReviewHistoryComponent,
CalibrateComponent
], imports: [ ], imports: [
TranslateModule, TranslateModule,
CommonModule, CommonModule,
......
...@@ -41,7 +41,7 @@ export class EmployeeSelfServiceComponent { ...@@ -41,7 +41,7 @@ export class EmployeeSelfServiceComponent {
{ text: "ผลงานดีเด่น", link: "/ess/outstanding-performance" }, { text: "ผลงานดีเด่น", link: "/ess/outstanding-performance" },
{ text: "Feedback", link: "/ess/self-evaluation/kpi-sum10" }, { text: "Feedback", link: "/ess/self-evaluation/kpi-sum10" },
{ text: "ดูข้อมูลย้อนหลัง", link: "/ess/review-history" }, { text: "ดูข้อมูลย้อนหลัง", link: "/ess/review-history" },
{ text: "Calibrate คะแนน", description: "หน้ารายงานที่มีการ Calibrate และแสดงค่า K" }, { text: "Calibrate คะแนน", link: "/ess/calibrate" },
{ text: "ข้อสอบ", link: "/ess/self-evaluation/com" }, { text: "ข้อสอบ", link: "/ess/self-evaluation/com" },
{ text: "Download เอกสาร", description: "เป็น pop up แสดงข้อมูลไฟล์ให้ดาวน์โหลด เหมือนของ myHR plus" } { text: "Download เอกสาร", description: "เป็น pop up แสดงข้อมูลไฟล์ให้ดาวน์โหลด เหมือนของ myHR plus" }
]; ];
......
...@@ -116,6 +116,7 @@ export class SidebarComponent { ...@@ -116,6 +116,7 @@ export class SidebarComponent {
this.currentUrl.includes('ess/welfare-reward') || this.currentUrl.includes('ess/welfare-reward') ||
this.currentUrl.includes('ess/time-attendance-wizard') || this.currentUrl.includes('ess/time-attendance-wizard') ||
this.currentUrl.includes('ess/review-history') || this.currentUrl.includes('ess/review-history') ||
this.currentUrl.includes('ess/calibrate') ||
this.currentUrl.includes('ess/job-description') this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) { if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;; const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
...@@ -206,6 +207,7 @@ export class SidebarComponent { ...@@ -206,6 +207,7 @@ export class SidebarComponent {
this.currentUrl.includes('ess/welfare-reward') || this.currentUrl.includes('ess/welfare-reward') ||
this.currentUrl.includes('ess/time-attendance-wizard') || this.currentUrl.includes('ess/time-attendance-wizard') ||
this.currentUrl.includes('ess/review-history') || this.currentUrl.includes('ess/review-history') ||
this.currentUrl.includes('ess/calibrate') ||
this.currentUrl.includes('ess/job-description') this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) { if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;; const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
......
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