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') {
......
// 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;
}
......@@ -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;
}
}
......
......@@ -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