Commit 3f5b4d85 by Nattana Chaiyamat

เพิ่มหน้าจอ สำหรับ Module one X ทั้งหมด 11หน้า

parent 528b4d5d
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Career Path Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture3.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Career Path </h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-3" style="margin-top: 4rem">
<div class="col-11 menu-box h-full">
<div class="sub-box">
<div class="col-12 row-top col-start mb-5">
<div class="text-title text-blue">General</div>
</div>
<div class="row col-12">
<div class="col-12 p-2">
<table>
<ng-container *ngFor="let item of generalPages; let i = index">
<tbody class="table-link" [attr.data-disable-hover]="false" style="line-height: 2.25rem;">
<!-- <td class="text-menu-list row-top w-auto">
<a [routerLink]="null">
{{i + 1}}&nbsp;
</a>
</td> -->
<td class="text-menu-list">
<a class="li-text-menu-general table-link-link bullet" [routerLink]="null">
{{ item.text }}
</a>
</td>
</tbody>
</ng-container>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row col-6 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Career Path
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'เส้นทางสายอาชีพของพนักงาน เพื่อให้พนักงานองค์กรได้เห็นโอกาสความก้าวหน้าในชีวิตการทำงาน',
'สามารถปรับระดับตามเส้นทางสายอาชีพของพนักงานได้',
'สามารถติดตามการโยกย้ายของพนักงานได้',
'เชื่อมโยงกับโครงสร้างเงินเดือน',
'เชื่อมโยงกับผลประเมิน Performance & Potential',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Career Path
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu2 bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 16rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 1rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu-general {
font-weight: 400;
font-size: 1.5rem;
line-height: normal;
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.li-text-menu2 {
font-weight: 400;
font-size: 1rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-career-path',
templateUrl: './career-path.component.html',
styleUrls: ['./career-path.component.scss']
})
export class CareerPathComponent {
generalPages = [
{ text: "สรุปข้อมูลตำแหน่งงานว่าง", description: "" },
{ text: "สรุป JD ตำแหน่งงานว่าง", description: "" },
{ text: "สรุปข้อมูลพนักงานที่มีความประสงค์ขอโยกย้าย", description: "" },
{ text: "สรุปความเห็นผู้บังคับบัญชา หรือผู้ที่ต้องการรับโอนย้าย", description: "" },
];
menuList = [
[{ text: "ผังโครงสร้างองค์กร", description: "" },
{ text: "Job Family", description: "" },
{ text: "Job Grade", description: "" }],
[{ text: "Job Grade", description: "" },
{ text: "ข้อมูลตำแหน่งงาน", description: "" },
{ text: "เกณฑ์กติกา(Criteria) ในการ Promotion", description: "" }],
[{ text: "อายุตัว อายุงานในระดับ", description: "" },
{ text: "Job Classification", description: "" },
{ text: "ข้อมูลประวัติการ Promotion", description: "" }],
[{ text: "ข้อมูลการโยกย้าย", description: "" },
{ text: "อำนาจในการปรับ", description: "" }]
]
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Compensation Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture7.jpg" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Compensation</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
ระบบบริหารค่าตอบแทน
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'เปรียบเทียบในลักษณะ Basic Salary และ Remuneration',
'เทียบกับตลาดแรงงาน และถูกต้องตามกฎหมายแรงงาน',
'ผลสำรวจค่าตอบแทนเป็น Factor ที่สำคัญ',
'ผลประเมินค่างานทำให้งานน่าเชื่อถือ',
'โครงสร้างค่าตอบแทน มีอายุการใช้งาน 3-5 ปี',
'เชื่อมโยงกับ Career Path เสมอ',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Compensation
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 12rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-compensation',
templateUrl: './compensation.component.html',
styleUrls: ['./compensation.component.scss']
})
export class CompensationComponent {
menuList = [
[{ text: "โครงสร้างเงินเดือนขององค์กร", description: "" },
{ text: "Job Grade", description: "" },
{ text: "ผลประเมินค่างาน", description: "" },],
[{ text: "Starting Salary", description: "" },
{ text: "Placement พนักงานเทียบกับตลาด", description: "" },
{ text: "Job Description", description: "" },],
[{ text: "Job Classification", description: "" },
{ text: "Criteria", description: "" },
{ text: "การปรับเงินในกรณีต่างๆ เลื่อนตำแหน่ง", description: "" },],
[{ text: "Package Remuneration แต่ละตำแหน่งงาน", description: "" },
{ text: "เงินช่วยเหลือ", description: "" },
{ text: "เงินประจำตำแหน่ง", description: "" },],
[{ text: "ผลสำรวจค่าตอบแทนปัจจุบัน", description: "" },
{ text: "เปรียบเทียบเงินเดือนกับผลสำรวจ", description: "" },],
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
......@@ -67,6 +67,17 @@ import { DocumentUploadManagerComponent } from '../company-components/account-se
import { DisciplinaryActionComponent } from '../disciplinary-action/disciplinary-action.component';
import { OutstandingPerformanceComponent } from '../outstanding-performance/outstanding-performance.component';
import { PostEvaluationTrackingComponent } from '../post-evaluation-tracking/post-evaluation-tracking.component';
import { OnboardingProgramComponent } from '../onboarding-program/onboarding-program.component';
import { TalentSuccessorComponent } from '../talent-successor/talent-successor.component';
import { CareerPathComponent } from '../career-path/career-path.component';
import { TrainingrRoadmapComponent } from '../training-roadmap/training-roadmap.component';
import { LeadershipDevelopmentComponent } from '../leadership-development/leadership-development.component';
import { WorkforceComponent } from '../workforce/workforce.component';
import { CompensationComponent } from '../compensation/compensation.component';
import { SalarySurveyComponent } from '../salary-survey/salary-survey.component';
import { RecruitmentSelectionComponent } from '../recruitment-selection/recruitment-selection.component';
import { JobManagementComponent } from '../job-management/job-management.component';
import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component';
......@@ -148,7 +159,18 @@ const routes: Routes = [
{ path: "ess/job-family-matrix", title: 'รายละเอียดกลุ่มงานตามวิชาชีพ', component: JobFamilyMatrixComponent },
{ path: "ess/disciplinary-action", title: 'วินัยและการลงโทษ', component: DisciplinaryActionComponent },
{ path: "ess/outstanding-performance", title: 'ผลงานดีเด่น', component: OutstandingPerformanceComponent },
{ path: "ess/post-evaluation-tracking", title: 'ผลงานดีเด่น', component: PostEvaluationTrackingComponent },
{ path: "ess/post-evaluation-tracking", title: 'ติดตามหลังการประเมิน', component: PostEvaluationTrackingComponent },
{ path: "ess/onboarding-program", title: 'Onboarding Program', component: OnboardingProgramComponent },
{ path: "ess/talent-successor", title: 'Talent & Successor', component: TalentSuccessorComponent },
{ path: "ess/career-Path", title: 'Career Path', component: CareerPathComponent },
{ path: "ess/training-roadmap", title: 'Training Roadmap', component: TrainingrRoadmapComponent },
{ path: "ess/leadership-development", title: 'Leadership', component: LeadershipDevelopmentComponent },
{ path: "ess/workforce", title: 'Workforce', component: WorkforceComponent },
{ path: "ess/compensation", title: 'Compensation', component: CompensationComponent },
{ path: "ess/salary-survey", title: 'Salary Survey', component: SalarySurveyComponent },
{ path: "ess/recruitment-selection", title: 'Recruitment & Selection', component: RecruitmentSelectionComponent },
{ path: "ess/job-management", title: 'Job Management', component: JobManagementComponent },
{ path: "ess/welfare-reward", title: 'Welfare & Reward', component: WelfareRewardComponent },
]
}
];
......
......@@ -221,6 +221,19 @@ import { DisciplinaryActionComponent } from '../disciplinary-action/disciplinary
import { OutstandingPerformanceComponent } from '../outstanding-performance/outstanding-performance.component';
import { PostEvaluationTrackingComponent } from '../post-evaluation-tracking/post-evaluation-tracking.component';
import { EvaluationConfirmationComponent } from '../post-evaluation-tracking/evaluation-confirmation/evaluation-confirmation.component';
import { GapTrackingComponent } from '../post-evaluation-tracking/gap-tracking/gap-tracking.component';
import { GapTrackingSupervisorComponent } from '../post-evaluation-tracking/gap-tracking-supervisor/gap-tracking-supervisor.component';
import { OnboardingProgramComponent } from '../onboarding-program/onboarding-program.component';
import { TalentSuccessorComponent } from '../talent-successor/talent-successor.component';
import { CareerPathComponent } from '../career-path/career-path.component';
import { TrainingrRoadmapComponent } from '../training-roadmap/training-roadmap.component';
import { LeadershipDevelopmentComponent } from '../leadership-development/leadership-development.component';
import { WorkforceComponent } from '../workforce/workforce.component';
import { CompensationComponent } from '../compensation/compensation.component';
import { SalarySurveyComponent } from '../salary-survey/salary-survey.component';
import { RecruitmentSelectionComponent } from '../recruitment-selection/recruitment-selection.component';
import { JobManagementComponent } from '../job-management/job-management.component';
import { WelfareRewardComponent } from '../welfare-reward/welfare-reward.component';
export const MY_DATE_FORMATS = {
parse: {
......@@ -381,7 +394,20 @@ export class CustomDateAdapter extends NativeDateAdapter {
DisciplinaryActionComponent,
OutstandingPerformanceComponent,
PostEvaluationTrackingComponent,
EvaluationConfirmationComponent
EvaluationConfirmationComponent,
GapTrackingComponent,
GapTrackingSupervisorComponent,
OnboardingProgramComponent,
TalentSuccessorComponent,
CareerPathComponent,
TrainingrRoadmapComponent,
LeadershipDevelopmentComponent,
WorkforceComponent,
CompensationComponent,
SalarySurveyComponent,
RecruitmentSelectionComponent,
JobManagementComponent,
WelfareRewardComponent,
], imports: [
TranslateModule,
CommonModule,
......
......@@ -92,13 +92,13 @@
</div>
<div class="row">
<div class="col-3 ">
<div class="row row-top h-full">
<div class="row row-top ">
<div class="col-12 row-center col-center">
<img src="./assets/img/brand-logos/new_logo_mySkillX.png" alt="mySkillX" class="w-auto"
style="height: 8rem;" />
</div>
<div class="col-12 row-top col-center mt-5 h-full">
<div class="row col-11 row-center col-center dashboard-box" style="height: calc(100% - 9.25rem);">
<div class="row col-11 row-center col-center dashboard-box" style="height: 64vh;">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/HRD/1.png" alt="Performance illustration" class="hero-img" />
......@@ -137,12 +137,12 @@
<tbody class="table-link" [attr.data-disable-hover]="!documentManager.dataList.length"
[attr.data-hs-overlay]="documentManager.dataList.length ? '#download-file-manage-modal' : null"
style="line-height: 2.25rem;">
<td class="text-menu row-top w-auto">
<td class="text-menu-list row-top w-auto">
<a [routerLink]="item.link?item.link:null">
{{i + 1}}&nbsp;
</a>
</td>
<td class="text-menu">
<td class="text-menu-list">
<a class="table-link-link" [routerLink]="item.link?item.link:null">
{{ item.text }}
</a>
......@@ -150,13 +150,13 @@
</tbody>
</ng-container>
<ng-container *ngIf="item.text != 'Download เอกสาร'">
<tbody class="table-link" [attr.data-disable-hover]="!item.link" style="line-height: 2.25rem;">
<td class="text-menu row-top w-auto">
<tbody class="table-link" [attr.data-disable-hover]="false" style="line-height: 2.25rem;">
<td class="text-menu-list row-top w-auto">
<a [routerLink]="item.link?item.link:null">
{{i + 1}}&nbsp;
</a>
</td>
<td class="text-menu">
<td class="text-menu-list">
<a class="table-link-link" [routerLink]="item.link?item.link:null">
{{ item.text }}
</a>
......@@ -181,7 +181,7 @@
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 p-2 ">
<a [routerLink]="item.link?item.link:null" class="row sub-box-menu p-2 row-center"
[attr.data-disable-hover]="!item.link" style="min-height: 65px;">
[attr.data-disable-hover]="false" style="min-height: 65px;">
<div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
......@@ -208,7 +208,7 @@
<ng-container *ngFor="let item of items; let l = index">
<div class=" col-4 p-2 ">
<a [routerLink]=" item.link?item.link:null" class="row sub-box-menu p-2 row-center"
[attr.data-disable-hover]="!item.link" style="min-height: 65px;">
[attr.data-disable-hover]="false" style="min-height: 65px;">
<div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
......
......@@ -349,10 +349,18 @@ transition: transform $dur $easing,
transition: color .25s;
}
.text-menu-list {
font-weight: 400;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a {
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
......
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Job Management Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture11.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Job Management</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Job Management
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'คำอธิบายวัตถุประสงค์หลักของหน่วยงาน',
'คำอธิบายหน้าที่หลักของแต่ละตำแหน่งงาน',
'คำอธิบายคุณสมบัติหลักของตำแหน่งงาน',
'คำอธิบายหน้าที่ที่นำมาอ้างอิง',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Job Management
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 12rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.5rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-job-management',
templateUrl: './job-management.component.html',
styleUrls: ['./job-management.component.scss']
})
export class JobManagementComponent {
menuList = [
[{ text: "Function Description", description: "" },
{ text: "Job Description", description: "" },
{ text: "Benchmark Job Description", description: "" },],
[{ text: "Job Specification ของแต่ละตำแหน่งงาน", description: "" },],
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">
Leadership Development Wizard
</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture5.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Leadership Development</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Leadership Development
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'การพัฒนาผู้บริหาร หัวหน้างานทุกระดับ ให้มีทักษะทั้งการบังคับบัญชา ดูแลคน และการบริหารจัดการ ควบคู่กันไป',
'หลักสูตรเชื่อมโยงกับ Career Path',
'เกณฑ์กติกาที่เหมาะสมก่อนเข้าเรียน และหลังเรียน',
'หลักสูตรที่ Update ทันสมัยตรงตาม Business Plan หรือกลยุทธ์',
'ผู้จับหลักสูตรมาแล้วเป็นผู้นำที่พร้อมจะเป็นอนาคตให้กับองค์กร',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Leadership Development
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 25rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-leadership-development',
templateUrl: './leadership-development.component.html',
styleUrls: ['./leadership-development.component.scss']
})
export class LeadershipDevelopmentComponent {
menuList = [
[{ text: "Managerial Competency", description: "" },
{ text: "หลักสูตรมาตรฐาน", description: "" },
{ text: "รายชื่อผู้ที่มีเข้าแต่ละหลักสูตรมาตรฐาน", description: "" }],
[{ text: "เกณฑ์ และปัจจัยของผู้มีสิทธิ์เข้าหลักสูตร", description: "" },
{ text: "Course Outline", description: "" },
{ text: "ข้อมูล E-learning", description: "" }],
[{ text: "ข้อมูลประวัติฝึกอบรม", description: "" },
{ text: "Project Assignment", description: "" },
{ text: "การติดตามผล", description: "" }],
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
......@@ -31,8 +31,7 @@
<ng-container *ngFor="let sliceNum of [[0,3],[3,6]];let i=index">
<div class="flex flex-row" style="gap:1.2rem">
<ng-container *ngFor="let m of hrdModules | slice:sliceNum[0] : sliceNum[1] ;let f = first">
<div class="flex-1 module-card" [routerLink]=" f ? '/ess/employee-self-service' :null"
(click)="!f ? goToWebsite('https://www.myhr.co.th/') : null">
<div class="flex-1 module-card" (click)="goTo(m.link)">
<img [src]="m.icon" [alt]="m.title + ' icon'" class="module-img" />
<p class="module-label">
<span class="font-semibold text-sky-600">One X</span><br />
......@@ -57,7 +56,7 @@
<ng-container *ngFor="let sliceNum of [[0,3],[3,6]];let i=index">
<div class="flex flex-row" style="gap:1.2rem">
<ng-container *ngFor="let m of hrmModules | slice:sliceNum[0] : sliceNum[1] ;let f = first">
<div class="flex-1 module-card" (click)="goToWebsite('https://www.myhr.co.th/')">
<div class="flex-1 module-card" (click)="goTo(m.link)">
<img [src]="m.icon" [alt]="m.title + ' icon'" class="module-img" />
<p class="module-label">
<span class="font-semibold text-sky-600">One X</span><br />
......@@ -74,7 +73,7 @@
</section>
<!-- eco-system pill --------------------------------------------------->
<!-- <div (click)="goToWebsite('https://www.myhr.co.th/')"
<!-- <div (click)="goTo('https://www.myhr.co.th/')"
class="eco-pill bp-glow mx-auto mt-5 text-center !p-0 cursor-pointer">
<span class="flex flex-row" style="font-size: 30px;align-items: center;justify-content: center;">
Eco system <span class="font-semibold">myHR</span>
......@@ -115,8 +114,7 @@
<div class="bp-grid">
<ng-container *ngFor="let m of hrdModules let f = first">
<div class="module-card" [routerLink]=" f ? '/ess/employee-self-service' :null"
(click)="!f ? goToWebsite('https://www.myhr.co.th/') : null">
<div class="module-card" (click)="goTo(m.link)">
<img [src]="m.icon" [alt]="m.title + ' icon'" class="module-img" />
<p class="module-label">
<span class="font-semibold text-sky-600">One X</span><br />
......@@ -137,7 +135,7 @@
<div class="bp-grid">
<ng-container *ngFor="let m of hrmModules">
<div class="module-card" (click)="goToWebsite('https://www.myhr.co.th/')">
<div class="module-card" (click)="goTo(m.link)">
<img [src]="m.icon" [alt]="m.title + ' icon'" class="module-img" />
<p class="module-label">
<span class="font-semibold text-sky-600">One X</span><br />
......@@ -152,7 +150,7 @@
</section>
<!-- eco-system pill --------------------------------------------------->
<!-- <div (click)="goToWebsite('https://www.myhr.co.th/')"
<!-- <div (click)="goTo('https://www.myhr.co.th/')"
class="eco-pill bp-glow mx-auto mt-5 text-center !p-0 cursor-pointer">
<span class="flex flex-row" style="font-size: 30px;align-items: center;justify-content: center;">
Eco system <span class="font-semibold">myHR</span>
......
import { Component, EventEmitter, Input, Output } from '@angular/core';
type ModuleCard = { icon: string; title: string };
import { Router } from '@angular/router';
type ModuleCard = { link: string, icon: string; title: string };
@Component({
selector: 'app-my-skill-x-module',
templateUrl: './my-skill-x-module.component.html',
......@@ -7,24 +8,30 @@ type ModuleCard = { icon: string; title: string };
})
export class MySkillXModuleComponent {
hrdModules: ModuleCard[] = [
{ icon: 'assets/img/logos/HRD/1.png', title: 'Performance & Competency' },
{ icon: 'assets/img/logos/HRD/2.png', title: 'On-Boarding Program' },
{ icon: 'assets/img/logos/HRD/3.png', title: 'Talent & Successor' },
{ icon: 'assets/img/logos/HRD/4.png', title: 'Career Path' },
{ icon: 'assets/img/logos/HRD/5.png', title: 'Training Roadmap' },
{ icon: 'assets/img/logos/HRD/6.png', title: 'Leadership' }
{ link: '/ess/employee-self-service', icon: 'assets/img/logos/HRD/1.png', title: 'Performance & Competency' },
{ link: '/ess/onboarding-program', icon: 'assets/img/logos/HRD/2.png', title: 'On-Boarding Program' },
{ link: '/ess/talent-successor', icon: 'assets/img/logos/HRD/3.png', title: 'Talent & Successor' },
{ link: '/ess/career-Path', icon: 'assets/img/logos/HRD/4.png', title: 'Career Path' },
{ link: '/ess/training-roadmap', icon: 'assets/img/logos/HRD/5.png', title: 'Training Roadmap' },
{ link: '/ess/leadership-development', icon: 'assets/img/logos/HRD/6.png', title: 'Leadership' }
];
hrmModules: ModuleCard[] = [
{ icon: 'assets/img/logos/HDM/1.png', title: 'Workforce' },
{ icon: 'assets/img/logos/HDM/2.png', title: 'Compensation' },
{ icon: 'assets/img/logos/HDM/3.png', title: 'Salary Survey' },
{ icon: 'assets/img/logos/HDM/4.png', title: 'Recruit & Selection' },
{ icon: 'assets/img/logos/HDM/5.png', title: 'JD & Org Chart' },
{ icon: 'assets/img/logos/HDM/6.png', title: 'Welfare & Reward' }
{ link: '/ess/workforce', icon: 'assets/img/logos/HDM/1.png', title: 'Workforce' },
{ link: '/ess/compensation', icon: 'assets/img/logos/HDM/2.png', title: 'Compensation' },
{ link: '/ess/salary-survey', icon: 'assets/img/logos/HDM/3.png', title: 'Salary Survey' },
{ link: '/ess/recruitment-selection', icon: 'assets/img/logos/HDM/4.png', title: 'Recruit & Selection' },
{ link: '/ess/job-management', icon: 'assets/img/logos/HDM/5.png', title: 'Job Management' },
{ link: '/ess/welfare-reward', icon: 'assets/img/logos/HDM/6.png', title: 'Welfare & Reward' }
];
constructor(private router: Router) { }
goToWebsite(web: string) {
window.open(web, '_blank');
goTo(link?: string) {
if (link) {
this.router.navigate([link]);
} else {
window.open('https://www.myhr.co.th/', '_blank');
}
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Onboarding Program Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture1.png" alt="Performance illustration" class="hero-img"
style="margin-right: 26px;" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Onboarding Program</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Onboarding Program
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of ['ระบบพี่เลี้ยงที่ช่วยรักษาคนในช่วงเข้ามาใหม่ให้อยู่กับองค์กร',
'การเข้าใจ Culture และการปรับตัวของพนักงานใหม่',
'แผนการเรียนงานสำหรับพนักงานใหม่',
'การประเมินเพื่อปรับตัว และสามารถทำงานได้',
'คู่มือสำหรับพี่เลี้ยง']; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Onboarding Program
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 25rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.5rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-onboarding-program',
templateUrl: './onboarding-program.component.html',
styleUrls: ['./onboarding-program.component.scss']
})
export class OnboardingProgramComponent {
menuList = [
[{ text: "ข้อมูลพนักงานใหม่", description: "" },
{ text: "ข้อมูลพี่เลี้ยง", description: "" },
{ text: "ข้อมูลปฐมนิเทศ", description: "" }],
[{ text: "แผนการเรียนงาน", description: "" },
{ text: "ผลประเมินพนักงานใหม่", description: "" },
{ text: "ประวัติการเรียนงาน", description: "" }],
[{ text: "คู่มือสำหรับพี่เลี้ยง", description: "" }]
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
......@@ -297,9 +297,19 @@
<div class="flex !items-center"
*ngFor="let competencyCourse of data.competencyCourse"
style="min-height: 100px;flex-direction:column">
<i class="ti ti-book fs-xxl !fw-b"></i><br>
<div class="row">
<div class="col-6"><i class="ti ti-book fs-xxl !fw-b"></i></div>
<div class="col-6" style="align-content: center;">
{{competencyCourse.competencyCourseId }}
</div>
<div class="col-12">
<img [src]="'./assets/img/logos/myLearn_Gray.png'">
</div>
<!-- <div class="col-12">
<img [src]="'./assets/img/logos/logo_myLearn.jpg'">
</div> -->
</div>
</div>
</ng-container>
</td>
<td class="align-start text-center !white-space-normal"
......
......@@ -67,3 +67,22 @@ table, .card, .section-block {
// break-after: auto !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;
}
}
......@@ -79,7 +79,11 @@
</ng-template>
<ng-template #template let-data>
<i class="ti ti-eye cursor-pointer i-gray fs-l px-1" (click)="page=2"></i>
<div class="relative" style="display: inline-block;">
<i class="ti ti-file cursor-pointer i-gray fs-l px-1" (click)="openEmployeeDialog()"></i>
<span class="absolute"
style="top: 0; right: 5px; width: 8px; height: 8px; background-color: red; border-radius: 50%; border: 1px solid white; "></span>
</div>
</ng-template>
</e-column>
</e-columns>
......
......@@ -86,6 +86,9 @@ export class EvaluationConfirmationComponent {
@ViewChild('employeeDialog') employeeDialog: any
employeeDialogRef: any
evaluaterId = ''
@ViewChild('pdfArea') pdfArea!: ElementRef<HTMLElement>;
constructor(private appraisalService: AppraisalService,
private fileService: FileService,
private translateService: TranslateService,
......@@ -248,7 +251,6 @@ export class EvaluationConfirmationComponent {
}
}
@ViewChild('pdfArea') pdfArea!: ElementRef<HTMLElement>;
async exportPdf() {
const el = this.pdfArea?.nativeElement;
......
<div class="flex item-center w-full font-size-18px font-weight-700 text-primary px-5 mt-1 mb-4"
style="height: 50px;align-items: center;border-width: 1px;background: #eff6fe; border-radius:20px">
{{'SubordinateList' | translate}}
</div>
<div class="flex w-full mb-4">
<div class="flex w-1/4 justify-between">
<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' | translate}} </label>
</div>
<div class="mx-1 flex items-center">
<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 (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">
<button type="button" class="ti-btn ti-btn-soft-secondary h-45px m-0 shadow-md"
[class.ti-btn-disabled]="selectEmp.size-1 <= 0" [disabled]="selectEmp.size-1 <= 0"
(click)="approveAll()">
{{'ApproveAll' | translate}}
</button>
</div>
<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" (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>
</div>
</div>
</div>
</div>
</div>
<div class="flex flex-col w-100">
<div class="mb-4">
<ng-container *ngTemplateOutlet="Datagrid"></ng-container>
</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">
<input 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>
</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-edit cursor-pointer i-gray fs-l px-1" (click)="openGapDialog()"></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 #gapDialog let-modal>
<h3 mat-dialog-title>
<!-- {{'รายงานสรุปผลการประเมิน' | translate}} -->
<div class="relative">
<div class="absolute" style="top: -43px;">
{{'ดำเนินการปิด Gap' | 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>
<mat-dialog-content>
<app-gap-tracking></app-gap-tracking>
</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>
<button type="button" (click)="save()" class="ti-btn ti-btn-success">
{{'SaveData' | translate}}
</button>
</mat-dialog-actions>
</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;
}
.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-3-9 {
flex: 0 0 32.5%;
max-width: 32.5%;
}
@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
);
}
.mx-0 {
margin-left: 0 !important;
margin-right: 0 !important;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, ElementRef, 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';
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 { Query } from '@syncfusion/ej2-data';
import { setCulture } from '@syncfusion/ej2-base';
@Component({
selector: 'app-gap-tracking-supervisor',
templateUrl: './gap-tracking-supervisor.component.html',
providers: [AggregateService, SortService, GroupService, ColumnMenuService, PageService, FilterService, ToolbarService, PdfExportService, ExcelExportService, DetailRowService, ReorderService, EditService, SearchService],
styleUrls: ['./gap-tracking-supervisor.component.scss']
})
export class GapTrackingSupervisorComponent {
search = ''
syncfution: {
dataList: any[],
searchSettings: {
fields: string[],
operator: 'contains',
ignoreCase: false
},
columns: ColumnModel[]
} = {
dataList: [],
searchSettings: {
fields: ['employeeId', 'name', 'status', 'gap'],
operator: 'contains',
ignoreCase: false
},
columns: [{
field: "employeeId",
headerText: "EmployeeCode",
type: "string",
isPrimaryKey: true,
},
{
field: "name",
headerText: "EmployeeName",
type: "string"
},
{
field: "status",
headerText: "AppraiserStatus",
type: "string"
},
{
field: "gap",
headerText: "Gap 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'
@ViewChild('gapDialog') gapDialog: any
gapDialogRef: any
evaluaterId = ''
@ViewChild("fileInputMedium") fileInputMedium: any;
selectedFile: File | null = null;
selectedFileName: string = 'PleaseSelectFile';
subordinate: { loading: boolean, select: EmployeeModel, list: EmployeeModel[] } = { loading: false, select: new MyEmployeeModel(), list: [] }
selectEmp: Map<string, boolean> = new Map<string, boolean>()
constructor(private appraisalService: AppraisalService,
private fileService: FileService,
private translateService: TranslateService,
private tokenService: TokenService,
private dialog: MatDialog,
private employeeService: EmployeeService,
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.getEmpList()
}
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);
}
openGapDialog() {
this.gapDialogRef = this.dialog.open(this.gapDialog, {
width: '1000px',
})
}
closeGapDialog() {
this.gapDialogRef.close()
}
getEmpList() {
this.subordinate.loading = true
this.selectEmp.clear()
this.employeeService.getList().subscribe({
next: response => {
this.subordinate.list = response.map((x: any) => new MyEmployeeModel(x))
this.subordinate.loading = false
this.setSyncfutionDataList()
this.cdr.detectChanges()
}, error: error => {
this.subordinate.loading = false
this.cdr.detectChanges()
}
})
}
toggleSelectAll() {
if (!this.selectEmp.get('selectAll')) {
this.selectEmp.set('selectAll', true)
this.subordinateFilter().forEach(e => {
this.selectEmp.set(e.employeeId, true)
})
} else {
this.selectEmp.set('selectAll', false)
this.selectEmp.clear()
}
}
checkSelectAll() {
this.selectEmp.set('selectAll', (this.subordinateFilter().length == this.selectEmp.size - 1))
}
setSyncfutionDataList() {
this.syncfution.dataList = this.subordinateFilter().map(e => ({
employeeId: e.employeeId,
name: this.translateText(e.thFullName, e.engFullName),
position: this.translateText(e.position.tdesc, e.position.edesc),
status: '1',
gap: '2',
picture: e.picture
}))
}
subordinateFilter() {
return this.subordinate.list.filter(x => {
return x.employeeId.toLowerCase().includes(this.search.toLowerCase()) ||
x.thFullName.toLowerCase().includes(this.search.toLowerCase()) ||
x.position.tdesc.toLowerCase().includes(this.search.toLowerCase()) ||
x.position.edesc.toLowerCase().includes(this.search.toLowerCase())
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
dataFilter() {
return [1, 2, 3, 4].filter(x => {
return (x + '').toLowerCase().includes(this.search.toLowerCase())
})
}
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)
}
}
save() {
Swal.fire({
icon: 'question',
title: 'แจ้งเตือน',
text: 'ยืนยันการบันทึกข้อมูลหรือไม่',
showCancelButton: true,
confirmButtonText: 'บันทึกข้อมูล',
cancelButtonText: 'ย้อนกลับ',
reverseButtons: true,
}).then((result) => {
if (result.isConfirmed) {
this.showAlert('บันทึกข้อมูลสำเร็จ', 'success')
this.closeGapDialog();
}
});
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
title: 'แจ้งเตือน',
text: text,
icon: type,
confirmButtonText: 'ตกลง',
});
}
getImg(text: string) {
return this.fileService.getImg(text)
}
onImageError(event: Event) {
const imgElement = event.target as HTMLImageElement;
imgElement.src = './assets/img/users/defaultperson.jpg';
}
approveAll() {
if (this.subordinate.select) {
Swal.fire({
iconHtml: `
<div class="flex items-center justify-center rounded-full !h-80px !w-80px" style="background-color: #E8F8EE;">
<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="39" height="39" fill="#D2D2D2"/>
<g id="Component">
<g id="alert cart">
<g id="mdi:file-export">
<circle cx="22.5" cy="19.5" r="33.5" fill="#E8F8EE"/>
<path d="M9.75 3.25C8.88805 3.25 8.0614 3.59241 7.4519 4.2019C6.84241 4.8114 6.5 5.63805 6.5 6.5V32.5C6.5 33.362 6.84241 34.1886 7.4519 34.7981C8.0614 35.4076 8.88805 35.75 9.75 35.75H29.25C30.112 35.75 30.9386 35.4076 31.5481 34.7981C32.1576 34.1886 32.5 33.362 32.5 32.5V13L22.75 3.25M21.125 5.6875L30.0625 14.625H21.125M14.5113 19.8575H26V31.3463L22.555 27.9013L17.9563 32.5L13.3575 27.9013L17.9563 23.3188"
fill="#1DBE5A"/>
</g>
</g>
</g>
</svg>
</div>
`,
title: 'อนุมัติ',
text: 'คุณต้องการยืนยันการอนุมัตินี้ใช่หรือไม่',
showCancelButton: true,
confirmButtonText: 'ยืนยันการอนุมัติ',
cancelButtonText: 'ย้อนกลับ',
customClass: {
title: '!swal2-title-mt-20px',
actions: '!swal2-actions-mt-20px',
icon: '!swal2-icon-no-border',
confirmButton: '!swal2-button-bg-green',
cancelButton: '!swal2-button-bg-gray',
},
}).then((result) => {
if (result.isConfirmed) {
this.getEmpList()
Swal.fire({
title: 'บันทึกสำเร็จ!',
text: 'การอนุมัติของคุณถูกบันทึกแล้ว',
icon: 'success',
customClass: {
confirmButton: '!swal2-button-bg-green',
}
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
title: 'ยกเลิก!',
text: 'การอนุมัติถูกยกเลิก',
icon: 'error',
customClass: {
confirmButton: '!swal2-button-bg-danger',
}
})
}
})
}
}
}
<ng-container *ngIf="page==1">
<div class="flex item-center w-full font-size-18px font-weight-700 text-primary px-5 mt-1 mb-4"
style="height: 50px;align-items: center;border-width: 1px;background: #eff6fe; border-radius:20px">
{{'Gap Detail' | translate}}
</div>
<div class="flex w-full mb-4">
<div class="flex w-1/4 justify-between">
</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"
(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>
</div>
</div>
</div>
</div>
</div>
<div class="flex flex-col w-100">
<div class="mb-4">
<ng-container *ngTemplateOutlet="Datagrid"></ng-container>
</div>
</div>
</ng-container>
<ng-container *ngIf="page==2">
<app-pms-form-employee [evaluationForm]="'sup'" [currentTap]="'EvaluationInfo'" (sendReturnPath)="page=1"
[evaluaterId]="evaluaterId" [evaluateeId]="evaluaterId"></app-pms-form-employee>
</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">
<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=='Assessment Tools'">
<div class="flex justify-center">
<button type="button" class="h-12 w-12 p-0 shadow border text-white bg-black/50 pointer-events-none"
style="border-radius:10px">
J
</button>
<button type="button" class="h-12 w-12 p-0 shadow border text-white bg-warning pointer-events-none"
style="border-radius:10px">
C
</button>
<button type="button" class="h-12 w-12 p-0 shadow border text-white bg-black/50 pointer-events-none"
style="border-radius:10px">
T
</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-edit cursor-pointer i-gray fs-l px-1" (click)="openGapDialog()"></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 #gapDialog let-modal>
<h3 mat-dialog-title>
<!-- {{'รายงานสรุปผลการประเมิน' | translate}} -->
<div class="relative">
<div class="absolute" style="top: -43px;">
{{'ดำเนินการปิด Gap' | 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-indigo h-45px m-0 shadow-md">
<i class="ti ti-eraser text-base"></i>
Clear
</button>
</div>
</div>
</div>
<mat-dialog-content>
<div class="row">
<div class="p-2 col-3">
<label class="ti-form-label mt-1rem">รหัสสมรรถนะ</label>
<input type="text" class="ti-form-input" style="background-color: rgb(241 245 249);" readonly>
</div>
<div class="p-2 col-9">
<label class="ti-form-label mt-1rem">หัวข้อสมรรถนะ</label>
<input type="text" class="ti-form-input" style="background-color: rgb(241 245 249);" readonly>
</div>
</div>
<div class="row">
<div class="p-2 col-12">
<label class="ti-form-label mt-1rem">ตัวชี้วันที่ติด Gap</label>
<textarea type="text" rows="3" class="ti-form-input col-12" style="background-color: rgb(241 245 249);"
readonly></textarea>
</div>
</div>
<div class="row mt-1rem">
<div class="p-2 col-12 border row" style="border-radius: 20px;">
<div class="col-12">
<nav class="flex rtl:space-x-reverse space-x-2 row" style="justify-content: space-between;">
<a style="border-radius: 20px;height: 35px;"
class="col-3-9 border justify-center rounded-4px hs-tab-active:!bg-primary hs-tab-active:border-primary hs-tab-active:!text-white -mb-px py-2 px-3 inline-flex items-center gap-2 font-size-16px font-weight-500 text-center text-gray-600 hover:text-gray-900 active"
href="javascript:void(0);" id="gap-tacking-item-1" data-hs-tab="#gap-tacking-1"
aria-controls="gap-tacking-1">
{{'OJT/Job Assignment' }}
</a>
<a style="border-radius: 20px;height: 35px;"
class="col-3-9 border justify-center rounded-4px hs-tab-active:!bg-primary hs-tab-active:border-primary hs-tab-active:!text-white -mb-px py-2 px-3 inline-flex items-center gap-2 font-size-16px font-weight-500 text-center text-gray-600 hover:text-gray-900"
href="javascript:void(0);" id="gap-tacking-item-2" data-hs-tab="#gap-tacking-2"
aria-controls="gap-tacking-2">
{{'Coach/Mentor' }}
</a>
<a style="border-radius: 20px;height: 35px;"
class="col-3-9 border justify-center rounded-4px hs-tab-active:!bg-primary hs-tab-active:border-primary hs-tab-active:!text-white -mb-px py-2 px-3 inline-flex items-center gap-2 font-size-16px font-weight-500 text-center text-gray-600 hover:text-gray-900"
href="javascript:void(0);" id="gap-tacking-item-3" data-hs-tab="#gap-tacking-3"
aria-controls="gap-tacking-3">
{{'Training' }}
</a>
</nav>
</div>
<div class="col-12 border mt-2 mb-2">
</div>
<div class="col-12">
<div id="gap-tacking-1" role="tabpanel" aria-labelledby="gap-tacking-item-1">
<ng-container *ngTemplateOutlet="ojt"></ng-container>
</div>
<div id="gap-tacking-2" class="hidden" role="tabpanel" aria-labelledby="gap-tacking-item-2">
<ng-container *ngTemplateOutlet="ojt"></ng-container>
</div>
<div id="gap-tacking-3" class="hidden" role="tabpanel" aria-labelledby="gap-tacking-item-3">
<ng-container *ngTemplateOutlet="ojt"></ng-container>
</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>
<button type="button" (click)="save()" class="ti-btn ti-btn-success">
{{'SaveData' | translate}}
</button>
</mat-dialog-actions>
</ng-template>
<ng-template #ojt>
<div class="row border p-2" style="border-radius: 20px;">
<div class="col-6 row" style="justify-content: center;">
<div class="col-11">
<input type="text" class="ti-form-input" style="height: 38px;">
</div>
</div>
<div class="col-5 row" style="justify-content: center;">
<div class="flex rounded-md">
<input #fileInputMedium id="fileInputMedium" accept=".pdf,.ppt,.pptx,.xls,.xlsx,.doc,.docx" type="file"
(change)="onFileSelected($event)" hidden>
<input type="text" [value]="selectedFileName | translate" 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>
<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 = '';selectedFile=null;selectedFileName = 'PleaseSelectFile';">
<i class="ri-delete-bin-6-line"></i>
{{'Delete' | translate}}
</button>
</div>
</div>
<div class="col-1 row relative" style="justify-content: center;align-content: center;">
<div class="absolute border" style="height: 145%;top: -9px;left: 0;"></div>
<i class="ti ti-circle-x text-danger" style="font-size: 30px;"></i>
</div>
</div>
<div class="row p-2" style="justify-content: center;">
<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: 30px;"></i>
</button>
</div>
</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;
}
.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-3-9 {
flex: 0 0 32.5%;
max-width: 32.5%;
}
@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
);
}
.mx-0 {
margin-left: 0 !important;
margin-right: 0 !important;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, ElementRef, 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';
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 { Query } from '@syncfusion/ej2-data';
import { setCulture } from '@syncfusion/ej2-base';
@Component({
selector: 'app-gap-tracking',
templateUrl: './gap-tracking.component.html',
providers: [AggregateService, SortService, GroupService, ColumnMenuService, PageService, FilterService, ToolbarService, PdfExportService, ExcelExportService, DetailRowService, ReorderService, EditService, SearchService],
styleUrls: ['./gap-tracking.component.scss']
})
export class GapTrackingComponent {
page = 1
search = ''
syncfution: {
dataList: any[],
searchSettings: {
fields: string[],
operator: 'contains',
ignoreCase: false
},
columns: ColumnModel[]
} = {
dataList: [],
searchSettings: {
fields: [
'code',
'name',
'round',
'year',
'end',
'tools',
'status'],
operator: 'contains',
ignoreCase: false
},
columns: [{
field: "code",
headerText: "Code",
type: "string",
isPrimaryKey: true,
},
{
field: "name",
headerText: "Competency Name",
type: "string"
},
{
field: "round",
headerText: "Evaluation Period",
type: "string"
},
{
field: "year",
headerText: "Year",
type: "string"
},
{
field: "end",
headerText: "End Date DEV.",
type: "string"
},
{
field: "tools",
headerText: "Assessment Tools",
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'
@ViewChild('gapDialog') gapDialog: any
gapDialogRef: any
evaluaterId = ''
@ViewChild("fileInputMedium") fileInputMedium: any;
selectedFile: File | null = null;
selectedFileName: string = 'PleaseSelectFile';
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()
}
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);
}
openGapDialog() {
this.gapDialogRef = this.dialog.open(this.gapDialog, {
width: '1000px',
})
}
closeGapDialog() {
this.gapDialogRef.close()
}
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,
round: e,
year: e,
end: e,
tools: e,
status: e
}))
}
dataFilter() {
return [1, 2, 3, 4].filter(x => {
return (x + '').toLowerCase().includes(this.search.toLowerCase())
})
}
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)
}
}
save() {
Swal.fire({
icon: 'question',
title: 'แจ้งเตือน',
text: 'ยืนยันการบันทึกข้อมูลหรือไม่',
showCancelButton: true,
confirmButtonText: 'บันทึกข้อมูล',
cancelButtonText: 'ย้อนกลับ',
reverseButtons: true,
}).then((result) => {
if (result.isConfirmed) {
this.showAlert('บันทึกข้อมูลสำเร็จ', 'success')
this.closeGapDialog();
}
});
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
title: 'แจ้งเตือน',
text: text,
icon: type,
confirmButtonText: 'ตกลง',
});
}
onFileSelected(event: any) {
this.selectedFile = event.target.files.length > 0 ? event.target.files[0] : null;
this.selectedFileName = this.selectedFile?.name || "PleaseSelectFile"
this.uploadFile()
}
uploadFile() {
if (!this.selectedFile) return;
const formData = new FormData();
formData.append('file', this.selectedFile);
// this.fileService.uploadFiles(formData).subscribe({
// next: response => {
// if (response.success) {
// this.selectedFile = null;
// // this.documentManager.select.documentFiles = response.resultObject
// } else {
// this.showAlert(response.message, 'error');
// this.cdr.detectChanges();
// }
// },
// error: error => {
// this.showAlert(error.message, 'error');
// this.cdr.detectChanges();
// }
// });
}
}
......@@ -19,12 +19,12 @@
<a style="border-radius: 20px;width: 210px;height: 35px;"
class="border justify-center rounded-4px hs-tab-active:!bg-primary hs-tab-active:border-primary hs-tab-active:!text-white -mb-px py-2 px-3 inline-flex items-center gap-2 font-size-16px font-weight-500 text-center text-gray-600 hover:text-gray-900"
href="javascript:void(0);" id="card-type-item-2" data-hs-tab="#card-type-2" aria-controls="card-type-2">
{{'การติดตามผล Gap' | translate}}
{{'Follow Gap' | translate}}
</a>
<a style="border-radius: 20px;width: 210px;height: 35px;"
class="border justify-center rounded-4px hs-tab-active:!bg-primary hs-tab-active:border-primary hs-tab-active:!text-white -mb-px py-2 px-3 inline-flex items-center gap-2 font-size-16px font-weight-500 text-center text-gray-600 hover:text-gray-900"
href="javascript:void(0);" id="card-type-item-3" data-hs-tab="#card-type-3" aria-controls="card-type-3">
{{'ติดตามผล Gap โดยหัวหน้า' | translate}}
{{'Supervisor Follow Gap' | translate}}
</a>
</nav>
<div class="pt-20px">
......@@ -32,10 +32,10 @@
<app-evaluation-confirmation></app-evaluation-confirmation>
</div>
<div id="card-type-2" class="hidden" role="tabpanel" aria-labelledby="card-type-item-2">
<app-evaluation-confirmation></app-evaluation-confirmation>
<app-gap-tracking></app-gap-tracking>
</div>
<div id="card-type-3" class="hidden" role="tabpanel" aria-labelledby="card-type-item-3">
<app-evaluation-confirmation></app-evaluation-confirmation>
<app-gap-tracking-supervisor></app-gap-tracking-supervisor>
</div>
</div>
</div>
......
......@@ -20,11 +20,4 @@ import { setCulture } from '@syncfusion/ej2-base';
styleUrls: ['./post-evaluation-tracking.component.scss']
})
export class PostEvaluationTrackingComponent {
@Output() sendPathTitle: EventEmitter<string[]> = new EventEmitter<string[]>();
currentTab = 1
onSendPathTitle(pathTitle: string) {
this.sendPathTitle.emit(['menu.Organization', 'menu.Company', 'BusinessUnit', pathTitle])
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Recruitment & Selection Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture10.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Recruitment & Selection</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
ระบบสรรหา และคัดเลือก
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'การสรรหาให้ทันเวลา และความต้องการ',
'เทคนิคการคัดเลือกคน',
'เครื่องมือในการคัดเลือกคน',
'การเห็น Candidate เข้ามาทำงานตามใบ Request',
'การลดเวลาสรรหา โดยเฉพาะตำแหน่งสำคัญ',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Recruitment & Selection
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 17rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-recruitment-selection',
templateUrl: './recruitment-selection.component.html',
styleUrls: ['./recruitment-selection.component.scss']
})
export class RecruitmentSelectionComponent {
menuList = [
[{ text: "ตำแหน่งงานว่าง", description: "" },
{ text: "JD ของตำแหน่งงานว่าง", description: "" },
{ text: "Job Specification ของตำแหน่งงานว่าง", description: "" },],
[{ text: "การเปรียบเทียบคู่ Candidate (Persona)", description: "" },
{ text: "ข้อสอบสรรหา", description: "" },
{ text: "ฐานข้อมูลของผู้สมัคร", description: "" },],
[{ text: "ตรวจสอบประวัติของ Candidate", description: "" },
{ text: "การแจ้งผล", description: "" },
{ text: "ส่งต่อฐานข้อมูล", description: "" },],
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Salary Survey Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture9.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Salary Survey</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
การสำรวจค่าตอบแทน
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'ช่วยให้รู้กลยุทธ์การจ่ายค่าจ้างและให้สวัสดิการของตลาด แล้วนำมาปรับใช้ในองค์กรได้',
'ช่วยให้รู้สภาวะของคู่แข่งขันในกลุ่มธุรกิจเดียวกัน',
'ช่วยกำหนดจุดยืนของการจ่ายค่าตอบแทนให้กับพนักงานได้ว่าควรจะจ่ายอย่างไร จ่ายเท่าไหร่ จึงจะจูงใจให้ มีคนสมัครงาน และสามารถรักษาพนักงานไม่ให้หลุดไปอยู่กับคู่แข่งได้',
'มีข้อมูลที่ช่วยให้ผู้บริหารสามารถกำหนดงบประมาณในเรื่องต่าง ๆ ได้ชัดเจนมากขึ้นในด้านต้นทุนการ ผลิต การพัฒนาพนักงาน และการให้ commission แก่พนักงานขายเพื่อจูงใจในการขายสินค้า เป็นต้น',
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Salary Survey
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 12rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-salary-survey',
templateUrl: './salary-survey.component.html',
styleUrls: ['./salary-survey.component.scss']
})
export class SalarySurveyComponent {
menuList = [
[{ text: "ผลสำรวจโครงสร้างเงินเดือนมาตรฐาน", description: "" },
{ text: "ผลสำรวจสวัสดิการมาตรฐาน", description: "" },
{ text: "ผลสำรวจ Starting", description: "" },],
[{ text: "ผลสำรวจแต่ละตำแหน่งงาน", description: "" },
{ text: "เปรียบเทียบผลสำรวจกับองค์กร", description: "" },
{ text: "ผลสำรวจ Benchmark", description: "" },],
[{ text: "ผลสำรวจเงินช่วยเหลือ", description: "" },
{ text: "ผลสำรวจตามธุรกิจ", description: "" },
{ text: "ผลวิเคราะห์เทียบกับตำแหน่งงานขององค์กร", description: "" },]
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Talent & Successor Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture2.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Talent & Successor</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-3" style="margin-top: 4rem">
<div class="col-11 menu-box h-full">
<div class="sub-box">
<div class="col-12 row-top col-start mb-5">
<div class="text-title text-blue">General</div>
</div>
<div class="row col-12">
<div class="col-12 p-2">
<table>
<ng-container *ngFor="let item of generalPages; let i = index">
<tbody class="table-link" [attr.data-disable-hover]="false" style="line-height: 2.25rem;">
<!-- <td class="text-menu-list row-top w-auto">
<a [routerLink]="null">
{{i + 1}}&nbsp;
</a>
</td> -->
<td class="text-menu-list">
<a class="li-text-menu-general table-link-link bullet" [routerLink]="null">
{{ item.text }}
</a>
</td>
</tbody>
</ng-container>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row col-6 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box" style="height: calc(50% - 10px);">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
ประเมินผลงาน (Performance)
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of performancePages; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5" style="height: calc(50% - 10px);">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
ประเมินศักยภาพ (Potential)
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of competencyPages; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 13rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 1rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu-general {
font-weight: 400;
font-size: 1.5rem;
line-height: normal;
}
.li-text-menu {
font-weight: 400;
font-size: 1rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-talent-successor',
templateUrl: './talent-successor.component.html',
styleUrls: ['./talent-successor.component.scss']
})
export class TalentSuccessorComponent {
generalPages = [
{ text: "เลือก Target Position", description: "" },
{ text: "เกณฑ์และปัจจัยในการประเมิน Talent", description: "" },
{ text: "สรุปรายชื่อคนที่เข้าเกณฑ์เป็น Talent", description: "" },
{ text: "สรุปรายชื่อคนที่เข้าเกณฑ์เป็น Successor", description: "" },
{ text: "ผลประเมินศักยภาพ (9 box grid)", description: "" },
{ text: "หลักสูตรภาคบังคับ", description: "" },
{ text: "หลักสูตรพัฒนา", description: "" },
{ text: "Individual Development Plan (IDP)", description: "" },
{ text: "ผลประเมินประจำปี", description: "" },
{ text: "แบบฟอร์มการประเมิน", description: "" },
{ text: "เชื่อมโยงกับระบบ Promotion", description: "" },
];
performancePages = [
[{ text: "ข้อมูลผลงานย้อนหลัง", description: "" },
{ text: "ข้อมูลผลงานที่โดดเด่น", description: "" },
{ text: "ข้อมูลผลประเมิน Accountability", description: "" }],
[{ text: "ข้อมูลประสบการณ์ภายนอก", description: "" },
{ text: "อื่นๆ", description: "" }]
];
competencyPages = [
[{ text: "ข้อมูลผลประเมินตำแหน่งงานในอนาคต", description: "" },
{ text: "ข้อมูลผลประเมินความเป็นผู้นำ", description: "" },
{ text: "ข้อมูลผลประเมินพฤติกรรมและทัศนคติ", description: "" }],
[{ text: "ข้อมูลผลประเมิน ภาษาอังกฤษ / จีน / ญี่ปุ่น", description: "" },
{ text: "ข้อมูลผลประเมิน Competency", description: "" }]
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Training Roadmap Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture4.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Training Roadmap</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box" >
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Training Roadmap
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'ภาพรวม Master Blueprint ในการพัฒนาคน',
'ภาพรวมหลักสูตรในแต่ละ Job Grade',
'ภาพรวมหลักสูตรในแต่ละช่วงอายุ',
'พนักงานที่ยังไม่เข้าหลักสูตร และพนักงานที่เข้าหลักสูตรแล้ว',
'ผลประเมินผู้ที่ผ่านหลักสูตร',
'เชื่อมโยงกับ Career Path และการ Promotion',
'หลักสูตรที่ชัดเจนในแต่ละ Level และอายุงาน',
'คู่มือสำหรับพี่เลี้ยง'
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5" >
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Training Roadmap
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 25rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-training-roadmap',
templateUrl: './training-roadmap.component.html',
styleUrls: ['./training-roadmap.component.scss']
})
export class TrainingrRoadmapComponent {
menuList = [
[{ text: "ยุทธศาสตร์ในการพัฒนาพนักงาน", description: "" },
{ text: "Master Blueprint สำหรับการพัฒนาพนักงาน", description: "" },
{ text: "ประเภทหลักสูตร", description: "" }],
[{ text: "เงื่อนไขในการเข้าอบรมในแต่ละหลักสูตร", description: "" },
{ text: "Course outline", description: "" },
{ text: "ข้อมูล E-learning", description: "" }],
[{ text: "ข้อมูลวิทยากร", description: "" },
{ text: "เอกสารแนบอื่น ๆ", description: "" },
{ text: "ข้อมูลงบประมาณ", description: "" }],
[{ text: "ข้อมูลประวัติฝึกอบรม", description: "" }],
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Welfare & Reward Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture12.png" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Welfare & Reward</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
Welfare & Reward
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'ความรวดเร็วในการเบิกจ่าย',
'สามารถหาข้อมูลถูกต้องได้ทุกที่ทุกเวลา',
'การปรับปรุงให้ทันกับตลาดแรงงาน และ Gen ที่เปลี่ยนแปลง',
'รูปแบบสวัสดิการ',
]; let i = index;let l = last">
<li [class.table-link]="!l" [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
<ng-container *ngIf="l">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'สวัสดิการพื้นฐาน เช่น ค่ารักษาพยาบาล ประกันสังคม ประกันอุบัติเหตุ กองทุนสำรองเลี้ยงชีพ',
'สวัสดิการยืดหยุ่น เช่น ค่าสมาชิก ค่ามือถือ ค่าทางด่วน',
'สวัสดิการผู้บริหาร เช่น รถประจำตำแหน่ง PERK',
]; let i = index;let l = last">
<li [class.table-link]="!l" [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</ng-container>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Welfare & Reward
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 12rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.5rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-welfare-reward',
templateUrl: './welfare-reward.component.html',
styleUrls: ['./welfare-reward.component.scss']
})
export class WelfareRewardComponent {
menuList = [
[{ text: "สวัสดิการตามกฎหมาย", description: "" },
{ text: "สวัสดิการพื้นฐานที่บริษัทจัดเพิ่มเติม", description: "" },
{ text: "Flexible Benefits", description: "" },],
[{ text: "สวัสดิการผู้บริหาร", description: "" },
{ text: "ข้อมูลการเบิกจ่าย", description: "" },
{ text: "เกณฑ์กติกาการเบิกจ่าย", description: "" },]
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
<div class="relative m-2">
<div class="row absolute w-full">
<div class="col-12">
<div class="row-center col-center relative row">
<div class="col-12" style="justify-content: start;display: flex;">
<a class="back-button absolute col-12" routerLink="/ess/my-skill-x-module">
</a>
</div>
<div class="text-title-head text-blue">Workforce Wizard</div>
</div>
</div>
</div>
<div class="row ">
<div class="row col-3" style="margin-top: 4rem">
<div class="row col-11 row-center col-center dashboard-box">
<div class="row col-12 sub-box h-full">
<div class="col-12 row-center col-center m-2">
<img src="assets/img/logos/wizard/Picture6.jpg" alt="Performance illustration" class="hero-img" />
</div>
<div class="col-12 row-center col-center m-2">
<h4 class="hero-label hero-title text-blue">One X</h4>
</div>
<div class="col-12 row-center col-center m-2">
<h3 class="hero-title text-blue">Workforce อัตรากำลัง</h3>
</div>
<div class="col-12 row-center col-center m-2">
<button routerLink="/dashboard/projects" class="hero-btn" style="height: 80px;">Dashboard</button>
</div>
<!-- <div class="col-12 row-center col-start m-2">
<a routerLink="/ess/my-skill-x-module" class="dashboard-link">
<i class="ti ti-arrow-left"></i> กลับสู่เมนูหลัก
</a>
</div> -->
</div>
</div>
</div>
<div class="row col-9 row-top" style="margin-top: 4rem;">
<div class="row col-12 row-top col-center menu-box">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
ระบบบริหารอัตรากำลัง
</div>
</div>
<div class="row col-12">
<ul class="col-12" style="padding-left: 2rem;">
<ng-container *ngFor="let items of [
'การบริหาร Workforce ให้เหมาะสมกับองค์กร',
'วิเคราะห์อัตรากำลังภาพรวมขององค์กรเทียบกับ Actual',
'แผนอัตรากำลัง 3-5 ปี เทียบกับ Business Plan หรือคู่แข่งในธุรกิจเดียวกัน'
]; let i = index">
<li class="table-link " [attr.data-disable-hover]="false">
<div class="li-text-menu table-link-link">
{{items}}
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
<div class="row col-12 row-top col-center menu-box mt-5">
<div class="sub-box col-12">
<div class="col-12 mb-5">
<div class="text-title text-blue">
One X Workforce อัตรากำลัง
</div>
</div>
<div class="row col-12">
<ng-container *ngFor="let items of menuList; let i = index">
<ng-container *ngFor="let item of items; let l = index">
<div class="col-4 row py-2">
<div class="col-11">
<a [routerLink]="null" class="row sub-box-menu p-3 row-center " [attr.data-disable-hover]="false"
style="min-height: 65px;">
<!-- <div class="col-2">
<span
class="flex align-items-center justify-content-center rounded-circle text-white bg-primary text-left text-no">
{{(i*3)+l+1}}
</span>
</div> -->
<div class="col text-menu li-text-menu bullet">
{{item.text}}
</div>
</a>
</div>
</div>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
</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);
/* ===== wrapper ===== */
.wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 1.25rem;
margin-top: 5rem;
}
/* ===== GRID ชั้นแรก ===== */
.page-grid {
display: grid;
gap: $gap-main;
grid-template-columns: $col-hero 1fr;
@media (max-width: 1023px) {
/* < lg : 1 คอลัมน์ */
grid-template-columns: 1fr;
}
}
/* ===== HERO CARD (คงเดิม – ตัด flex ออก) ===== */
.hero-card {
width: 100%;
max-width: 280px; // ไม่เกินจอ desktop
box-sizing: border-box;
padding-inline: clamp(1rem, 4vw, 1.75rem);
padding-block: 2rem 3rem;
position: relative;
border-radius: $radius-main;
padding: 2rem 1.75rem 3rem;
background: #fff;
box-shadow: $shadow-base;
text-align: center;
display: grid;
place-items: center;
gap: 1.3rem;
transition: transform $dur $easing, box-shadow $dur $easing;
&:hover {
transform: translateY(-6px);
box-shadow: $shadow-deep;
}
/* glow */
&.bp-glow::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;
}
&:hover.bp-glow::before {
opacity: .27;
}
}
@media (max-width: 639px) {
.hero-img {
max-width: 68%;
}
}
.hero-img {
width: 12rem;
}
.hero-label {
font: 600 .95rem/1.2 'Inter', sans-serif;
color: #3b82f6;
}
.hero-title {
font: 700 1.5rem/1.3 'Inter', sans-serif;
color: #1e3a8a;
}
.hero-btn {
@apply bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full px-10 py-2 transition;
}
.back-link {
position: absolute;
left: 1.75rem;
bottom: 1rem;
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
/* ===== GRID ชั้นสอง – เมนูย่อย 2 แถวพอดี ===== */
.sub-grid {
display: grid;
grid-auto-flow: column;
/* เติมแนวนอนก่อน */
grid-template-rows: repeat(2, $size-card);
grid-auto-columns: $size-card;
gap: 1.5rem;
place-content: start;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
/* ≥1280px ขยายการ์ด */
@media (min-width: 1280px) {
grid-template-rows: repeat(2, 170px);
grid-auto-columns: 170px;
}
/* มือถือ: 2 คอลัมน์เสมอ */
@media (max-width: 639px) {
grid-template-rows: repeat(2, 46vw);
grid-auto-columns: 46vw;
}
}
/* ===== SUB-CARD + เอฟเฟกต์ ===== */
.sub-card {
// aspect-ratio: 1 / 1;
position: relative;
border-radius: $radius-sub;
background: rgba(255, 255, 255, .5);
/* glass */
backdrop-filter: blur(9px) saturate(170%);
box-shadow: 0 8px 18px -10px rgba(71, 85, 105, .25);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 1rem;
cursor: pointer;
user-select: none;
transition: transform .45s $easing, box-shadow .45s $easing, background .45s;
/* gradient rim */
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
// background: linear-gradient(160deg,#60a5fa,#818cf8 50%,#a855f7);
background: linear-gradient(135deg, #4f46e5, #ec4899 60%, #f59e0b);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: .45;
transition: opacity .4s;
}
&:hover {
background: #ffffff;
transform: translateY(-7px) rotateX(5deg) rotateY(-3deg);
box-shadow: $shadow-deep;
&::before {
opacity: .9;
}
}
}
.sub-title {
font-weight: 600;
font-size: .9rem;
line-height: 1.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
.sub-title2 {
font-weight: 600;
font-size: 1.5rem;
line-height: 2.25rem;
color: #2563eb;
pointer-events: none;
word-break: break-word;
}
// div {
// border: black 1px solid;
// min-height: 20px;
// }
.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;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.dashboard-link {
font-size: .9rem;
color: #64748b;
transition: color .25s;
&:hover {
color: #3b82f6;
}
}
.dashboard-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: 2rem;
}
}
.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: 2rem;
}
}
.text-blue {
color: #3b82f6;
}
.text-title-head {
font-weight: 600;
font-size: 2.5rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-title {
font-weight: 600;
font-size: 2rem;
line-height: 2.25rem;
pointer-events: none;
word-break: break-word;
}
.text-menu {
font-weight: 500;
font-size: 1rem;
transition: color .25s;
}
.text-menu-list {
font-weight: 300;
line-height: 58px;
font-size: 2rem;
transition: color 0.25s;
}
.table-link {
transition: color 0.25s;
&[data-disable-hover="false"]:hover .text-menu a,
&[data-disable-hover="false"]:hover .li-text-menu,
&[data-disable-hover="false"]:hover .text-menu-list a {
color: #3b82f6;
cursor: pointer;
&.table-link-link {
text-decoration: underline;
}
}
&[data-disable-hover="true"] {
opacity: 0.5;
cursor: default;
}
}
.sub-box-menu {
border: #3b82f6 1px solid;
border-radius: 15px;
min-height: 50px;
&:hover {
cursor: pointer;
background: rgba(59, 130, 246, 0.2);
}
&[data-disable-hover="true"] {
--tw-bg-opacity: 1;
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
&:hover {
background-color: rgb(226 232 240 / var(--tw-bg-opacity));
cursor: default;
}
}
}
.text-no {
font-weight: 600;
font-size: 1.5rem;
}
.back-button {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 30px solid rgb(37 99 235);
cursor: pointer;
display: inline-block;
transition: transform 0.2s;
}
.back-button:hover {
border-right: 30px solid rgb(29 78 216);
}
ul {
list-style-type: disc;
/* disc, circle, square, none */
}
.li-text-menu {
font-weight: 400;
font-size: 1.25rem;
line-height: normal;
}
.bullet {
display: list-item;
list-style-type: disc;
margin-left: 20px;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { DocumentManagerModel, MyDocumentManagerModel } from 'src/app/shared/model/document-manager.model';
import { DocumentManagerService } from 'src/app/shared/services/documents.service';
import { FileService } from 'src/app/shared/services/file.service';
import Swal from 'sweetalert2';
interface SubModule {
title: SafeHtml;
route?: string;
file?: string;
}
@Component({
selector: 'app-workforce',
templateUrl: './workforce.component.html',
styleUrls: ['./workforce.component.scss']
})
export class WorkforceComponent {
menuList = [
[{ text: "แผนยุทธศาสตร์องค์กร", description: "" },
{ text: "ผังโครงสร้างองค์กร", description: "" },
{ text: "อัตรากำลังปัจจุบันขององค์กร", description: "" },],
[{ text: "มาตรฐานอัตรากำลังของหน่วยงานกลาง/ ราชการ", description: "" },
{ text: "วิเคราะห์ FTE ของแต่ละตำแหน่งงาน", description: "" },
{ text: "วิเคราะห์อัตรากำลังทดแทน", description: "" },],
[{ text: "วิเคราะห์อัตรากำลังใหม่", description: "" },
{ text: "วิเคราะห์อัตรากำลังตามยุทธศาสตร์", description: "" },
{ text: "วิเคราะห์คนขาด-เกิน", description: "" },],
[{ text: "รายงานอัตรากำลัง", description: "" }]
];
documentManager: { loading: boolean, dataList: DocumentManagerModel[] } = { loading: false, dataList: [] }
constructor(private sanitizer: DomSanitizer,
private fileService: FileService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService,
private documentManagerService: DocumentManagerService
) {
}
ngOnInit(): void {
this.getDocumentManagerList()
}
getDocumentManagerList() {
this.documentManager.loading = true
this.documentManagerService.getList().subscribe({
next: response => {
this.documentManager.dataList = response.map(x => {
return new MyDocumentManagerModel(x)
})
this.documentManager.loading = false
this.cdr.detectChanges();
}, error: error => {
this.documentManager.loading = false
console.error('Error fetching employee types:', error);
this.cdr.detectChanges()
}
})
}
openReport(file: string) {
const url = 'assets/reports/' + file;
window.open(url, '_blank');
}
downloadFile(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()
}
})
}
showAlert(text: string, type: 'success' | 'error') {
Swal.fire({
icon: type,
title: 'แจ้งเตือน',
text: text,
showCancelButton: false,
confirmButtonText: 'ยืนยัน',
})
}
translateText(th?: string, en?: string) {
return this.translateService.getCurrentLang() == 'th' ? (th || '') : (en || '')
}
}
......@@ -103,6 +103,17 @@ export class SidebarComponent {
this.currentUrl.includes('ess/job-family-matrix') ||
this.currentUrl.includes('ess/disciplinary-action') ||
this.currentUrl.includes('ess/outstanding-performance') ||
this.currentUrl.includes('ess/onboarding-program') ||
this.currentUrl.includes('ess/talent-successor') ||
this.currentUrl.includes('ess/career-Path') ||
this.currentUrl.includes('ess/training-roadmap') ||
this.currentUrl.includes('ess/leadership-development') ||
this.currentUrl.includes('ess/workforce') ||
this.currentUrl.includes('ess/compensation') ||
this.currentUrl.includes('ess/salary-survey') ||
this.currentUrl.includes('ess/recruitment-selection') ||
this.currentUrl.includes('ess/job-management') ||
this.currentUrl.includes('ess/welfare-reward') ||
this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
......@@ -180,6 +191,17 @@ export class SidebarComponent {
this.currentUrl.includes('ess/job-family-matrix') ||
this.currentUrl.includes('ess/disciplinary-action') ||
this.currentUrl.includes('ess/outstanding-performance') ||
this.currentUrl.includes('ess/onboarding-program') ||
this.currentUrl.includes('ess/talent-successor') ||
this.currentUrl.includes('ess/career-Path') ||
this.currentUrl.includes('ess/training-roadmap') ||
this.currentUrl.includes('ess/leadership-development') ||
this.currentUrl.includes('ess/workforce') ||
this.currentUrl.includes('ess/compensation') ||
this.currentUrl.includes('ess/salary-survey') ||
this.currentUrl.includes('ess/recruitment-selection') ||
this.currentUrl.includes('ess/job-management') ||
this.currentUrl.includes('ess/welfare-reward') ||
this.currentUrl.includes('ess/job-description')
if (this.showSideMenu) {
const html: any = this.elementRef.nativeElement.ownerDocument.documentElement;;
......
......@@ -408,5 +408,12 @@
"Post Eval. & Follow-up": "Post Eval. & Follow-up",
"Result Evaluate": "Result Evaluate",
"Detail Evaluate": "Detail Evaluate",
"Evaluation Period": "Evaluation Period"
"Evaluation Period": "Evaluation Period",
"Follow Gap": "Follow Gap",
"Gap Detail": "Gap Detail",
"Competency Name": "Competency Name",
"End Date DEV.": "End Date DEV.",
"Assessment Tools": "Assessment Tools",
"Supervisor Follow Gap": "Supervisor Follow Gap",
"Gap Status": "Gap Status"
}
\ No newline at end of file
......@@ -409,6 +409,12 @@
"Post Eval. & Follow-up": "ติดตามหลังการประเมิน",
"Result Evaluate": "ผลการประเมิน",
"Detail Evaluate": "รายละเอียดการประเมิน",
"Evaluation Period": "รอบการประเมิน"
"Evaluation Period": "รอบการประเมิน",
"Follow Gap": "การติดตามผล Gap",
"Gap Detail": "รายละเอียดหัวข้อที่ติด Gap",
"Competency Name": "หัวข้อ",
"End Date DEV.": "กำหนดเสร็จ",
"Assessment Tools": "เครื่องมือพัฒนา",
"Supervisor Follow Gap": "ติดตาม Gap โดยหัวหน้า",
"Gap Status": "สถานะ Gap"
}
\ No newline at end of file
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