Commit 27f02f55 by Ooh-Ao

portal

parent 8b2e4a55
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MockDataService {
constructor() { }
getKpiData(kpiName: string) {
const kpiData: { [key: string]: { value: number, trend: 'up' | 'down' | 'flat', percentage: number } } = {
'New Hires': { value: 12, trend: 'up', percentage: 15 },
'Turnover Rate': { value: 4, trend: 'down', percentage: 10 },
'Employee Satisfaction': { value: 4.5, trend: 'up', percentage: 5 },
'Projects Completed': { value: 25, trend: 'up', percentage: 20 }
};
return kpiData[kpiName] || { value: 0, trend: 'flat', percentage: 0 };
}
getChartData() {
return {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
series: [
{
name: 'Hiring',
data: [5, 8, 12, 10, 15, 18]
},
{
name: 'Resignations',
data: [2, 3, 5, 4, 6, 5]
}
]
};
}
getWelcomeMessage(userName: string = 'Admin') {
return `Welcome back, ${userName}!`;
}
getQuickLinks() {
return [
{ name: 'Company Portal', url: '#' },
{ name: 'HR Handbook', url: '#' },
{ name: 'Submit IT Ticket', url: '#' },
{ name: 'View Paystubs', url: '#' }
];
}
}
<app-page-header [title]="'พนักงาน'" [activeTitle]="'จัดการบริษัท'" [title1]="'พนักงาน'"></app-page-header>
<div class="grid grid-cols-12 gap-x-6">
<div class="xl:col-span-12 col-span-12">
<div class="box mt-6">
<div class="box-header justify-between">
<div class="box-title">
{{ 'All List' | translate}} <span
class="badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle">{{itemsList.length}}</span>
</div>
<div class="flex flex-wrap gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2" (click)="new()"
data-hs-overlay="#modal-detail"><i class="ri-add-line font-semibold align-middle"></i>{{ 'เพิ่ม' |
translate}}
</a>
<div>
<input [(ngModel)]='searchTerm' class="form-control form-control" type="text"
placeholder="{{ 'ค้นหาข้อมูลพนักงาน' | translate}}" aria-label=".form-control-sm example">
</div>
<!-- <a href="javascript:void(0);" class="ti-btn ti-btn-primary-full !py-1 !px-2" aria-expanded="false">
เรียงตาม<i class="ri-arrow-down-s-line align-middle ms-1 inline-block"></i>
</a>
<ul class="hs-dropdown-menu ti-dropdown-menu hidden" role="menu">
<li><a class="ti-dropdown-item" href="javascript:void(0);">Posted Date</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Status</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Department</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Job Type</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Newest</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Oldest</a></li>
</ul> -->
</div>
</div>
</div>
</div>
@for(item of filterList;track filterList){
<div class="xxl:col-span-4 xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-12 col-span-12">
<div class="box team-member-card">
<div class="teammember-cover-image mt-1">
<span class="avatar avatar-xl avatar-rounded">
<img [src]="item.member.getPicture()" alt="">
</span>
</div>
<div class="box-body !p-0">
<div
class="flex flex-wrap align-item-center sm:mt-0 mt-[3rem] justify-between border-b border-dashed dark:border-defaultborder/10 p-4">
<div class="team-member-details flex-grow">
<p class="mb-0 font-semibold text-[1rem] text-truncate">
<a href="javascript:void(0);">{{item.member.firstName+ ' ' +item.member.lastName}}</a>
</p>
<p class="mb-0 text-[0.75rem] text-[#8c9097] dark:text-white/50 text-truncate">
{{item.member.email}}</p>
</div>
</div>
<div class="team-member-stats sm:flex items-center justify-evenly">
<div class="text-center p-4 w-full">
<p class="font-semibold mb-0">รหัสพนักงาน</p>
<span class="text-[#8c9097] dark:text-white/50 text-[0.75rem]">{{item.employeeId}}</span>
</div>
<div class="text-center p-4 w-full">
<p class="font-semibold mb-0">แผนก</p>
<span class="text-[#8c9097] dark:text-white/50 text-[0.75rem]">{{item.position.thName}}</span>
</div>
<div class="text-center p-4 w-full">
<p class="font-semibold mb-0">ตำแหน่ง</p>
<span class="text-[#8c9097] dark:text-white/50 text-[0.75rem]">{{item.department.thName}}</span>
</div>
</div>
</div>
<div class="box-footer border-block-start-dashed dark:border-defaultborder/10 text-center">
<div class="btn-list">
<div class="btn-list">
<button type="button" aria-label="button" data-hs-overlay="#modal-detail" (click)="view(item)"
class="ti-btn ti-btn-sm ti-btn-primary me-[0.375rem]"><i class="ri-edit-line"></i></button>
<button routerLink="/company/timestamp-log/{{item.company_employeeId}}" routerLinkActive="active"
type="button" aria-label="button" class="ti-btn ti-btn-sm ti-btn-secondary !me-[0.375rem]">
<i class="ri-time-line"></i>
</button>
<button data-hs-overlay="#modal-face" (click)="view(item)" type="button" aria-label="button"
class="ti-btn ti-btn-sm ti-btn-warning me-[0.375rem]">
<i class="ri-account-circle-line"></i>
</button>
<button (click)="delete(item)" type="button" aria-label="button"
class="ti-btn ti-btn-sm ti-btn-danger me-0"><i class="ri-delete-bin-line"></i></button>
</div>
</div>
</div>
</div>
</div>
}
</div>
<!-- <nav aria-label="Page navigation" class="mb-4">
<ul class="ti-pagination !justify-end py-[0.375rem] px-3 text-[1rem] flex flex-row">
<li class="page-item disabled"><a class="page-link py-[0.375rem] px-3" href="javascript:void(0);">Previous</a></li>
<li class="page-item"><a class="page-link py-[0.375rem] px-3" href="javascript:void(0);">1</a></li>
<li class="page-item"><a class="page-link py-[0.375rem] px-3" href="javascript:void(0);">2</a></li>
<li class="page-item"><a class="page-link py-[0.375rem] px-3" href="javascript:void(0);">Next</a></li>
</ul>
</nav> -->
<!-- Start:: New Deal -->
<div id="modal-detail" class="hs-overlay hidden ti-modal">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">ข้อมูลพนักงาน
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-detail">
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<div class="mb-0 text-center">
<span class="avatar avatar-xxl avatar-rounded">
<img [src]="selectModel.getPicture()" alt="" id="profile-img">
<span class="badge rounded-full bg-primary avatar-badge">
<input ng2FileSelect [uploader]="uploaderProfile" type="file" name="photo"
class="absolute w-full h-full opacity-[0]" id="profile-change">
<i class="fe fe-camera text-[.625rem]"></i>
</span>
</span>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ชื่อ</label>
<input type="text" class="form-control" id="deal-name" placeholder="ชื่อ"
[(ngModel)]="selectModel.member.firstName">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-lead-score" class="form-label">นามสกุล</label>
<input type="text" class="form-control" id="deal-lead-score" placeholder="นามสกุล"
[(ngModel)]="selectModel.member.lastName">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="company-name" class="form-label">อีเมล</label>
<input type="text" class="form-control" id="company-name" placeholder="อีเมล"
[(ngModel)]="selectModel.member.email">
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">วันเริ่มงาน</label>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" id="endDate" placeholder="Choose date with time" mwlFlatpickr
[(ngModel)]="selectModel.startDate" [enableTime]="true" [convertModelValue]="true"
[dateFormat]="'Y-m-d H:i'">
</div>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-lead-score" class="form-label">รหัสพนักงาน</label>
<input type="text" class="form-control" id="deal-lead-score" placeholder="นามสกุล"
[(ngModel)]="selectModel.employeeId">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">หัวหน้างาน</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModel.bossId">
@for(item of itemsList;track itemsList){
<ng-option [value]="item.member.memberId">{{item.member.firstName+"
"+item.member.lastName}}</ng-option>
}
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">แผนก</label>
<!-- <ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModel.department">
@for(item of departmentList;track departmentList){
<ng-option [value]="item">{{item.thName}}</ng-option>
}
</ng-select> -->
<ng-select #select [items]="departmentList" [searchable]="false" [(ngModel)]="selectModel.department"
bindLabel="thName">
<ng-template ng-header-tmp>
<input style="width: 100%; line-height: 24px" type="text"
(input)="select.filter($any($event.target).value)" />
</ng-template>
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ตำแหน่ง</label>
<!-- <ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModel.position.positionId">
@for(item of positionList;track positionList){
<ng-option [value]="item">{{item.thName}}</ng-option>
}
</ng-select> -->
<ng-select #select2 [items]="positionList" [searchable]="false" [(ngModel)]="selectModel.position"
bindLabel="thName">
<ng-template ng-header-tmp>
<input style="width: 100%; line-height: 24px" type="text"
(input)="select2.filter($any($event.target).value)" />
</ng-template>
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ประเภท</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModel.empType">
<ng-option [value]="0">รายวัน</ng-option>
<ng-option [value]="1">รายเดือน</ng-option>
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">เงินเดือน</label>
<input type="text" class="form-control" id="company-name" placeholder="เงินเดือน"
[(ngModel)]="selectModel.salary">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">สิทธิ์การใช้งาน</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModel.companyRoleType">
<ng-option [value]="0">พนักงาน</ng-option>
<ng-option [value]="1">ผู้ดูแลบริษัท</ng-option>
</ng-select>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button #closeModal type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-detail">
ยกเลิก
</button>
<button type="button" class="ti-btn bg-primary text-white !font-medium" (click)="save()">บันทึก</button>
</div>
</div>
</div>
</div>
<div id="modal-face" class="hs-overlay hidden ti-modal">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">ข้อมูลพนักงาน
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-face">
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body">
<div
class="flex flex-wrap align-item-center sm:mt-0 mt-[3rem] justify-between border-b border-dashed dark:border-defaultborder/10 p-4">
<div class="team-member-details flex-grow">
<video #video width="640" height="480" style="width: 100%; object-fit: cover;" autoplay
(loadedmetadata)="detectFace()">
</video>
<!-- Canvas element -->
<canvas #canvas width="640" height="480" hidden
style="position: absolute; width: 100%; height: 100%; object-fit: cover;">
</canvas>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button #closeModalFace type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-face">
ยกเลิก
</button>
<button *ngIf="checkMatch" type="button" class="ti-btn bg-primary text-white !font-medium"
(click)="captureImage()">บันทึกใบหน้า</button>
</div>
</div>
</div>
</div>
<!-- <div id="modal-detail-edit" class="hs-overlay hidden ti-modal" *ngIf="selectModelEdit">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">ข้อมูลพนักงาน
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-detail">
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<div class="mb-0 text-center">
<span class="avatar avatar-xxl avatar-rounded">
<img [src]="'./assets/images/faces/1.jpg'" alt="" id="profile-img">
<span class="badge rounded-full bg-primary avatar-badge">
<input type="file" name="photo" class="absolute w-full h-full opacity-[0]" id="profile-change">
<i class="fe fe-camera text-[.625rem]"></i>
</span>
</span>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ชื่อ</label>
<input type="text" class="form-control" id="deal-name" placeholder="ชื่อ"
[(ngModel)]="selectModelEdit.member.firstName">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-lead-score" class="form-label">นามสกุล</label>
<input type="text" class="form-control" id="deal-lead-score" placeholder="นามสกุล"
[(ngModel)]="selectModelEdit.member.lastName">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="company-name" class="form-label">อีเมล</label>
<input type="text" class="form-control" id="company-name" placeholder="อีเมล"
[(ngModel)]="selectModelEdit.member.email">
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">วันเริ่มงาน</label>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" id="endDate" placeholder="Choose date with time" mwlFlatpickr [(ngModel)]="selectModelEdit.startDate"
[enableTime]="true" [convertModelValue]="true" [dateFormat]="'Y-m-d H:i'">
</div>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">แผนก</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModelEdit.department.departmentId">
@for(item of departmentList;track departmentList){
<ng-option [value]="item.departmentId">{{item.thName}}</ng-option>
}
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ตำแหน่ง</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModelEdit.position.positionId">
@for(item of positionList;track positionList){
<ng-option [value]="item.positionId">{{item.thName}}</ng-option>
}
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">ประเภท</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder=""
[(ngModel)]="selectModelEdit.empType">
<ng-option [value]="0">รายวัน</ng-option>
<ng-option [value]="1">รายเดือน</ng-option>
</ng-select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-name" class="form-label">เงินเดือน</label>
<input type="text" class="form-control" id="company-name" placeholder="เงินเดือน" [(ngModel)]="selectModelEdit.salary">
</div>
</div>
</div>
<div class="ti-modal-footer">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-detail-edit">
ยกเลิก
</button>
<button type="button" class="ti-btn bg-primary text-white !font-medium" (click)="updateEmp()">บันทึก</button>
</div>
</div>
</div>
</div> -->
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core';
import { MaterialModuleModule } from "../../../material-module/material-module.module";
import { RouterModule } from "@angular/router";
import { NgSelectModule } from "@ng-select/ng-select";
import { SharedModule } from "../../../shared/shared.module";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { FlatpickrDefaults, FlatpickrModule } from "angularx-flatpickr";
import { MatDatepickerModule } from "@angular/material/datepicker";
import { EmployeeModel } from "../../models/employee.model";
import { EmployeeService } from "../../services/employee.service";
import { TokenService } from "../../../shared/services/token.service";
import swal from 'sweetalert';
import { DepartmentService } from "../../services/department.service";
import { PositionService } from "../../services/position.service";
import { DepartmentModel } from "../../models/department.model";
import { PositionModel } from "../../models/position.model";
import { FormsModule } from "@angular/forms";
import { FileUploadModule } from 'ng2-file-upload';
import { FileItem, FileUploader, ParsedResponseHeaders } from "ng2-file-upload";
import { environment } from "../../../../environments/environment";
import { HttpClient } from "@angular/common/http";
import * as faceapi from 'face-api.js';
@Component({
selector: 'app-company-emp',
standalone: true,
imports: [SharedModule, NgSelectModule, RouterModule, MaterialModuleModule, TranslateModule, FlatpickrModule, MatDatepickerModule, FormsModule, CommonModule, FileUploadModule],
providers: [FlatpickrDefaults],
templateUrl: './company-emp.component.html',
styleUrl: './company-emp.component.css',
})
export class CompanyEmpComponent {
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('closeModalFace') public childModalFace?: ElementRef;
@ViewChild('modalDetail') public modalDetail?: ElementRef;
allSelected = false;
someSelected = false;
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
itemsList: EmployeeModel[] = []
filterList: EmployeeModel[] = []
selectModel: EmployeeModel = new EmployeeModel()
selectedItems = new Map<string, boolean>();
departmentList: DepartmentModel[] = []
positionList: PositionModel[] = []
pageIndex = 0;
get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(val: string) {
this.pageIndex = 0;
this.allSelected = false
this._searchTerm = val;
if (val != '') {
this.filterList = this.filter(val);
} else {
this.updatePagedItems()
}
}
companyId = ""
_searchTerm = "";
isEdit = false;
constructor(private http: HttpClient,private employeeService: EmployeeService, public translate: TranslateService, private departmentService: DepartmentService, private positionService: PositionService, private tokenService: TokenService) {
this.uploadConfig()
this.companyId = this.tokenService.getSelectCompany().companyId;
}
@ViewChild('video') video: ElementRef;
@ViewChild('canvas') canvas: ElementRef;
capturedImage: string | null = null;
uploadStatus: string = '';
checkMatch = false;
memberId = ""
isFaceDetected = false; // Flag to determine if a face is detected
initFace() {
// โหลดโมเดล Face Detection ของ face-api.js
faceapi.nets.tinyFaceDetector.loadFromUri('/assets/models/tiny_face_detector');
faceapi.nets.faceLandmark68Net.loadFromUri('/assets/models/face_landmark_68');
faceapi.nets.faceRecognitionNet.loadFromUri('/assets/models/face_recognition');
faceapi.nets.faceExpressionNet.loadFromUri('/assets/models/face_expression');
// เปิดกล้อง
this.startVideo();
}
startVideo(): void {
console.log("test")
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
this.video.nativeElement.srcObject = stream;
})
.catch(err => {
console.error('Error accessing camera: ', err);
});
}
// ตรวจจับใบหน้าและวาดกรอบครอบใบหน้า
async detectFace() {
const videoEl = this.video.nativeElement;
const displaySize = { width: videoEl.width, height: videoEl.height };
faceapi.matchDimensions(this.canvas.nativeElement, displaySize);
setInterval(async () => {
const detections = await faceapi.detectAllFaces(videoEl, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceExpressions();
const resizedDetections = faceapi.resizeResults(detections, displaySize);
// Clear the canvas
this.canvas.nativeElement.getContext('2d').clearRect(0, 0, displaySize.width, displaySize.height);
// Draw detections
faceapi.draw.drawDetections(this.canvas.nativeElement, resizedDetections);
faceapi.draw.drawFaceExpressions(this.canvas.nativeElement, resizedDetections);
faceapi.draw.drawFaceLandmarks(this.canvas.nativeElement, resizedDetections);
// Check if at least one face is detected
this.isFaceDetected = detections.length > 0;
this.checkMatch = this.isFaceDetected;
}, 500);
}
// @ViewChild('video') videoElement!: ElementRef;
// @ViewChild('canvas') canvasElement!: ElementRef;
// capturedImage: string | null = null;
// uploadStatus: string = '';
// constructor(private http: HttpClient) {}
// ngOnInit(): void {
// this.startCamera();
// }
// // เริ่มการทำงานของกล้อง
// startCamera(): void {
// navigator.mediaDevices.getUserMedia({ video: true })
// .then(stream => {
// this.videoElement.nativeElement.srcObject = stream;
// })
// .catch(err => {
// console.error('Error accessing camera: ', err);
// });
// }
// // แคปเจอร์ภาพจากกล้อง
captureImage(): void {
const video = this.video.nativeElement;
const canvas = this.canvas.nativeElement;
const context = canvas.getContext('2d');
// วาดภาพจาก video ลงใน canvas
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// แปลงภาพใน canvas เป็น base64
this.capturedImage = canvas.toDataURL('image/jpeg');
this.uploadImage()
}
// // อัปโหลดภาพที่แคปเจอร์แล้ว
uploadImage(): void {
if (this.capturedImage) {
// แปลง Base64 เป็นไฟล์
const file = this.dataURLtoFile(this.capturedImage, 'face.jpg');
const formData = new FormData();
formData.append('file', file);
// ส่งคำร้อง HTTP POST ไปยัง backend
this.http.post(environment.baseUrl + `/face/members/${this.selectModel.member.memberId}/add-face`, formData).subscribe({
next: (response: any) => {
this.uploadStatus = 'Face uploaded successfully!';
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
},
error: (error) => {
this.uploadStatus = 'Error uploading face!';
console.error('Error:', error);
}
});
}
}
// // แปลง Base64 เป็นไฟล์ (Blob)
dataURLtoFile(dataurl: string, filename: string): File {
const arr: any = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
uploadConfig() {
this.uploaderProfile = new FileUploader({
url: environment.baseUrl + "/api/upload-image",
isHTML5: true,
authToken: this.tokenService.getToken()!,
});
this.uploaderProfile.onAfterAddingFile = (fileItem: FileItem) => {
fileItem.withCredentials = false;
this.uploadErrorMsg = "";
while (this.uploaderProfile!.queue.length > 1) {
this.uploaderProfile!.queue[0].remove();
}
if (fileItem.file.size > 5000000) {
this.uploadErrorMsg = "maximum file size 5mb.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
if (fileItem.file.type!.indexOf("image") === -1) {
this.uploadErrorMsg = "please upload image only.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
fileItem.upload();
};
this.uploaderProfile.onCompleteItem = (
item: FileItem,
response: string,
status: number,
headers: ParsedResponseHeaders
) => {
if (item.isSuccess) {
const res = JSON.parse(response);
console.log("res", res);
this.selectModel.member.picture = res.filename
swal(res.message, "บันทึกสำเร็จ", "success");
} else {
this.uploadErrorMsg = "cannot upload file.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
}
};
}
ngOnInit(): void {
this.initFace()
this.getDepartment()
this.getPosition()
this.employeeService.getLists(this.companyId).subscribe(result => {
this.itemsList = result
this.updatePagedItems()
})
}
getDepartment() {
this.departmentService.getLists(this.companyId).subscribe(result => {
this.departmentList = result
})
}
getPosition() {
this.positionService.getLists(this.companyId).subscribe(result => {
this.positionList = result
})
}
filter(v: string) {
this.pageIndex = 0;
return this.itemsList?.filter(
(x) =>
x.company_employeeId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.employeeId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getFullname()?.toLowerCase().indexOf(v.toLowerCase()) !== -1
||
x.getType()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getRole()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.position.getName().toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.department.getName().toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
delete(item: EmployeeModel) {
swal({
title: "Are you sure?",
text: "You won't be able to revert this!",
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes,Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.employeeService.delete(this.companyId,item).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit()
})
}
});
}
new() {
this.isEdit = false
this.selectModel = new EmployeeModel()
}
view(item: EmployeeModel) {
console.log(item)
this.isEdit = true;
this.selectModel = item
}
save() {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
if (!this.isEdit) {
this.selectModel.companyRoleType = 0
this.employeeService.save(this.selectModel, this.companyId).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit()
this.childModal?.nativeElement.click()
})
} else {
this.employeeService.update(this.selectModel, this.companyId).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit()
this.childModal?.nativeElement.click()
})
}
// this.selectModel.member.role = 0
}
});
}
updateEmp() {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
this.employeeService.save(this.selectModel, this.companyId).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit()
this.childModal?.nativeElement.click()
})
}
});
}
// filterEmp(empId: string) {
// this.selectModel.supervisor = this.itemsList.filter(e => e.employeeId == empId)[0]
// }
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
// this.filterList = this.itemsList.slice(startIndex, endIndex);
this.filterList = this.itemsList
}
}
:host {
display: block;
}
.map-container{
width: 100% !important
}
<app-page-header [title]="'ข้อมูลบริษัท'" [activeTitle]="'จัดการบริษัท'" [title1]="'ข้อมูลบริษัท'"></app-page-header>
<!-- Start::row-1 -->
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-8 col-span-12">
<div class="box custom-box">
<div class="box-header justify-between flex">
<div class="box-title">
ข้อมูลบริษัท
</div>
<div>
</div>
</div>
<div class="box-body">
<div class="sm:p-4 p-0">
<h6 class="font-semibold mb-4 text-[1rem]">
รูปภาพ :
</h6>
<div class="mb-6 sm:flex items-center">
<div class="mb-0 me-[3rem]">
<span class="avatar avatar-xxl avatar-rounded">
<img [src]="companyModel.getPicture()" alt="" id="profile-img">
<a aria-label="anchor" href="javascript:void(0);" class="badge rounded-full bg-primary avatar-badge">
<input ng2FileSelect [uploader]="uploaderProfile" type="file" name="photo"
class="absolute w-full h-full opacity-0" id="profile-image">
<i class="fe fe-camera !text-[0.65rem]"></i>
</a>
</span>
</div>
<!-- <div class="inline-flex">
<button type="button" class="ti-btn bg-primary text-white !rounded-e-none !font-medium">แก้ไข</button>
<button type="button" class="ti-btn ti-btn-light !font-medium !rounded-s-none">ลบ</button>
</div> -->
</div>
<div class="sm:grid grid-cols-12 gap-6 mb-6">
<div class="xl:col-span-6 col-span-12">
<label for="first-name" class="form-label">ชื่อบริษัท</label>
<input type="text" class="form-control w-full !rounded-md" id="first-name"
[(ngModel)]="companyModel.companyName" placeholder="ชื่อบริษัท">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="first-name" class="form-label">รหัสบริษัท</label>
<input type="text" class="form-control w-full !rounded-md" id="first-name" placeholder="CODE"
[(ngModel)]="companyModel.companyCode">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="bio" class="form-label">เกี่ยวกับบริษัท :</label>
<textarea class="form-control w-full !rounded-md dark:!text-defaulttextcolor/70" id="bio" rows="5"
[(ngModel)]="companyModel.companyInfo"></textarea>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="bio" class="form-label">ที่อยู่ :</label>
<textarea class="form-control w-full !rounded-md dark:!text-defaulttextcolor/70" id="bio" rows="5"
[(ngModel)]="companyModel.address"></textarea>
</div>
</div>
<h6 class="font-semibold mb-4 text-[1rem]">
ข้อมูลเจ้าของบริษัท :
</h6>
<div class="sm:grid grid-cols-12 gap-6 mb-6">
<div class="xl:col-span-6 col-span-12">
<label for="email-address" class="form-label">ชื่อ-สกุล :</label>
<input type="text" class="form-control w-full !rounded-md" id="email-address"
[(ngModel)]="companyModel.ownerName" placeholder="ชื่อ-สกุล">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="Contact-Details" class="form-label">เบอร์ติดต่อ :</label>
<input type="text" class="form-control w-full !rounded-md" id="Contact-Details"
[(ngModel)]="companyModel.contact" placeholder="การติดต่อ">
</div>
</div>
</div>
</div>
<div class="box-footer">
<div class="ltr:float-right rtl:float-left">
<button type="button" class="ti-btn bg-primary text-white m-1" (click)="save()">
บันทึกการเปลี่ยนแปลง
</button>
<button type="button" class="ti-btn ti-btn-ghost-secondary ti-btn-wave" (click)="reset()">ยกเลิก</button>
</div>
</div>
</div>
</div>
<div class="xl:col-span-4 col-span-12">
<div class="box custom-box">
<div class="box-header justify-between">
<div class="box-title">แผนที่บริษัท</div>
</div>
<div class="box-body">
<div class="grid grid-cols-12 sm:gap-6">
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-6 col-span-6">
<label for="input-label" class="form-label">{{"ละติจูด" | translate}}</label>
<input type="text" class="form-control" id="input" [(ngModel)]="companyModel.latitude"
(ngModelChange)="changeMarker()">
</div>
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-6 col-span-6">
<label for="input-label" class="form-label">{{"ลองจิจูด" | translate}}</label>
<input type="text" class="form-control" id="input" [(ngModel)]="companyModel.longitude"
(ngModelChange)="changeMarker()">
</div>
<div class="xl:col-span-12 col-span-12">
<div class="xl:col-span-12 col-span-12">
<input id="searchBox" #searchBox type="text" placeholder="ค้นหาสถานที่..."
class="form-control w-full p-2 border rounded mb-3">
</div>
<google-map class="xl:col-span-12 col-span-12" height="400px"
[center]="{ lat: companyModel.latitude, lng: companyModel.longitude }" [zoom]="12"
(mapClick)="addMarker($event,mapMarker)">
<map-marker #mapMarker="mapMarker"
[position]="{ lat: companyModel.latitude, lng: companyModel.longitude }" [options]="markerOptions" />
</google-map>
</div>
</div>
</div>
</div>
</div>
</div>
<!--End::row-1 -->
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
import { SharedModule } from "../../../shared/shared.module";
import { GoogleMapsModule, MapInfoWindow, MapMarker } from '@angular/google-maps';
import { NgSelectModule } from "@ng-select/ng-select";
import { ActivatedRoute, Router } from "@angular/router";
import { TokenService } from "../../../shared/services/token.service";
import { CompanyService } from "../../services/company.service";
import { CompanyModel } from "../../models/company.model";
import { FormsModule } from "@angular/forms";
import { FileUploadModule } from 'ng2-file-upload';
import { FileItem, FileUploader, ParsedResponseHeaders } from "ng2-file-upload";
import { TranslateModule } from '@ngx-translate/core';
import swal from 'sweetalert';
import { environment } from "../../../../environments/environment";
@Component({
selector: 'app-company-info',
standalone: true,
imports: [SharedModule, NgSelectModule, CommonModule, FormsModule, FileUploadModule, TranslateModule, NgSelectModule, GoogleMapsModule, MapMarker],
templateUrl: './company-info.component.html',
styleUrl: './company-info.component.css',
encapsulation: ViewEncapsulation.None
})
export class CompanyInfoComponent {
@ViewChild(MapInfoWindow) infoWindow: MapInfoWindow | undefined;
center: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
zoom = 4;
markerOptions: google.maps.MarkerOptions = { draggable: false };
markerPositions: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
companyId = ""
companyModel: CompanyModel = new CompanyModel()
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
@ViewChild('searchBox', { static: false }) searchBoxElement!: ElementRef;
constructor(private router: Router, private route: ActivatedRoute, private comService: CompanyService, private tokenService: TokenService) {
this.companyId = this.tokenService.getSelectCompany().companyId;
this.getCompanyInfo()
this.uploadConfig()
}
async ngAfterViewInit() {
// await this.loadGoogleMapsAPI();
this.initAutocomplete();
}
initAutocomplete() {
if (!this.searchBoxElement || !this.searchBoxElement.nativeElement) {
console.error("❌ SearchBox element not found!");
return;
}
const input = this.searchBoxElement.nativeElement;
const searchBox = new google.maps.places.SearchBox(input);
searchBox.addListener('places_changed', () => {
const places = searchBox.getPlaces();
if (!places || places.length === 0) return;
const place = places[0];
if (!place.geometry || !place.geometry.location) return;
this.companyModel.latitude = place.geometry.location.lat()
this.companyModel.longitude = place.geometry.location.lng()
// this.center = {
// lat: place.geometry.location.lat(),
// lng: place.geometry.location.lng(),
// };
// this.markerPositions = this.center;
// console.log(`📍 New Location: ${this.center.lat}, ${this.center.lng}`);
});
}
uploadConfig() {
this.uploaderProfile = new FileUploader({
url: environment.baseUrl + "/api/upload-image",
isHTML5: true,
authToken: this.tokenService.getToken()!,
});
this.uploaderProfile.onAfterAddingFile = (fileItem: FileItem) => {
fileItem.withCredentials = false;
this.uploadErrorMsg = "";
while (this.uploaderProfile!.queue.length > 1) {
this.uploaderProfile!.queue[0].remove();
}
if (fileItem.file.size > 5000000) {
this.uploadErrorMsg = "maximum file size 5mb.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
if (fileItem.file.type!.indexOf("image") === -1) {
this.uploadErrorMsg = "please upload image only.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
fileItem.upload();
};
this.uploaderProfile.onCompleteItem = (
item: FileItem,
response: string,
status: number,
headers: ParsedResponseHeaders
) => {
if (item.isSuccess) {
const res = JSON.parse(response);
console.log("res", res);
this.companyModel.picture = res.filename
swal(res.message, "บันทึกสำเร็จ", "success");
} else {
this.uploadErrorMsg = "cannot upload file.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
}
};
}
changeMarker() {
this.markerPositions = { lat: this.companyModel.latitude, lng: this.companyModel.longitude }
}
addMarker(event: google.maps.MapMouseEvent, marker: MapMarker) {
console.log(event)
console.log(marker)
this.companyModel.latitude = event.latLng!.toJSON().lat
this.companyModel.longitude = event.latLng!.toJSON().lng
this.markerPositions = { lat: event.latLng!.toJSON().lat, lng: event.latLng!.toJSON().lng }
if (this.infoWindow && marker.title) {
this.infoWindow.open(marker);
}
}
getCompanyInfo() {
this.comService.getById(this.companyId).subscribe(result => {
;
this.companyModel = result
this.tokenService.saveSelectCompany(result);
console.log("companyModel", this.companyModel)
})
}
reset() {
this.getCompanyInfo()
}
save() {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
this.comService.update(this.companyModel).subscribe(result => {
swal("Update Success!!", "บันทึกข้อมูลสมาชิก", "success");
this.getCompanyInfo()
})
});
}
}
:host {
display: block;
}
.map-container{
width: 100% !important
}
<app-page-header [title]="'สถานที่การลงเวลา'" [activeTitle]="'จัดการการลงเวลา'"
[title1]="'สถานที่การลงเวลา'"></app-page-header>
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
{{ 'All List' | translate}} <span
class="badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle">{{itemsList.length}}</span>
</div>
<div class="flex flex-wrap gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2" (click)="new()"
data-hs-overlay="#modal-detail"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<!-- <a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-success-full me-2" *ngIf="someSelected"
(click)="adjustSelect('A')"><i class="ri-user-follow-line font-semibold align-middle"></i>{{ 'Active' |
translate}}
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-secondary-full me-2"
*ngIf="someSelected" (click)="adjustSelect('U')"><i
class="ri-user-unfollow-line font-semibold align-middle"></i>{{ 'Unactive' |
translate}}
</a> -->
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-danger-full me-2" *ngIf="someSelected"
(click)="deleteSelect()"><i class="ri-delete-bin-line font-semibold align-middle"></i>{{ 'Delete' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาสถานที่"
aria-label=".form-control-sm example" [(ngModel)]='searchTerm'>
</div>
<!-- <div>
<input class="form-control form-control" type="text" placeholder="กรองตามบริษัท"
aria-label=".form-control-sm example" [(ngModel)]='searchTerm'>
</div> -->
<!-- <a href="javascript:void(0);" class="ti-btn ti-btn-primary-full !py-1 !px-2" aria-expanded="false">
เรียงตาม<i class="ri-arrow-down-s-line align-middle ms-1 inline-block"></i>
</a>
<ul class="hs-dropdown-menu ti-dropdown-menu hidden" role="menu">
<li><a class="ti-dropdown-item" href="javascript:void(0);">Newest</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Oldest</a></li>
</ul> -->
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr class="border-b border-defaultborder">
<th scope="col" class="!text-start">
<input class="form-check-input check-all" type="checkbox" id="all-products"
(change)="toggleAll($event)" [checked]="allSelected" aria-label="...">
</th>
<th scope="col" class="text-start">ชื่อสถานที่</th>
<th scope="col" class="text-start">ละติจูด</th>
<th scope="col" class="text-start">ลองจิจูด</th>
<th scope="col" class="text-start">{{"ประเภท" | translate}}</th>
<th scope="col" class="text-start">วันที่เริ่มต้น</th>
<th scope="col" class="text-start">วันที่สิ้นสุด</th>
<th scope="col" class="text-start">{{"Update Date" | translate}}</th>
<th scope="col" class="text-start"></th>
</tr>
</thead>
<tbody>
@for(item of filterList;track filterList){
<tr class="border border-defaultborder dark:border-defaultborder/10">
<td class="product-checkbox"><input class="form-check-input" type="checkbox"
[checked]="selectedItems.get(item.locationId)" (change)="onCheckboxChange(item.locationId)"
aria-label="..." value="">
</td>
<td>
<div class="flex">
<div class="ms-2">
<p class="font-semibold mb-0 flex items-center text-primary"><a (click)="view(item)"
routerLinkActive="active">
{{item.locationName}}</a></p>
<p class="text-[0.75rem] text-muted mb-0">{{item.locationId}}</p>
</div>
</div>
</td>
<td> {{item.latitude}}</td>
<td> {{item.longitude}}</td>
<td> <span
class="badge bg-{{ item.locationType == 0 ? 'primary' : 'warning'}} {{ item.locationType == 0 ? 'text-white' : ''}}">{{item.getStatus()}}</span>
</td>
<td><span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{item.startDate ?
(item.startDate | date
: 'medium') : '-'}}</span></td>
<td><span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{item.endDate ?
(item.endDate | date
: 'medium') : '-'}}</span></td>
<td><span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{item.updatedAt ?
(item.updatedAt | date
: 'medium') : '-'}}</span>
<td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="view(item)" data-hs-overlay="#modal-detail"
class="ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info"><i
class="ri-pencil-line"></i></a>
<a aria-label="anchor" href="javascript:void(0);" (click)="delete(item)"
class="ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-danger/10 text-danger hover:bg-danger hover:text-white hover:border-danger"><i
class="ri-delete-bin-line"></i></a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<div class="box custom-box">
<div class="box-header justify-between">
<div class="box-title">แผนที่บริษัท</div>
</div>
<div class="box-body">
<div class="grid grid-cols-12 sm:gap-6">
<div class="xl:col-span-12 col-span-12">
<google-map class="xl:col-span-12 col-span-12" height="400px" [center]="center" [zoom]="zoom">
<map-circle *ngFor="let item of itemsList" [center]="{ lat: item.latitude, lng: item.longitude }"
[radius]="item.radius*1000" />
<map-marker *ngFor="let item of itemsList" #mapMarker="mapMarker"
[position]="{ lat: item.latitude, lng: item.longitude }" [options]="markerOptions" />
</google-map>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Start:: Create Contact -->
<div id="modal-detail" class="hs-overlay hidden ti-modal [--overlay-backdrop:static]">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">{{ 'Create' |
translate }} {{ 'Location' | translate }}
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-detail" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<label for="deal-title" class="form-label">{{'ชื่อสถานที่' | translate}}</label>
<!-- <input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.locationName"> -->
<input [(ngModel)]="selectModel.locationName" id="searchBox" #searchBox type="text"
placeholder="ค้นหาสถานที่..." class="form-control w-full p-2 border rounded mb-3">
<div class="text-danger" *ngIf="!selectModel.locationName">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">{{'ประเภท' | translate}}</label>
<ng-select name="choices-multiple-remove-button2" id="choices-multiple-remove-button2" placeholder=""
[(ngModel)]="selectModel.locationType">
<ng-option [value]="0">{{'PERMANENT' | translate}}</ng-option>
<ng-option [value]="1" selected>{{'TEMPORARY' | translate}}</ng-option>
</ng-select>
</div>
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-12 col-span-12">
<label for="input-date" class="form-label">{{ "Plan Start" | translate}}</label>
<input type="date" class="form-control" id="input-date" [(ngModel)]="selectModel.startDate">
</div>
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-12 col-span-12">
<label for="input-date" class="form-label">{{ "Plan Start" | translate}}</label>
<input type="date" class="form-control" id="input-date" [(ngModel)]="selectModel.endDate">
</div>
</div>
<div class="grid grid-cols-12 sm:gap-6">
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-6 col-span-6">
<label for="input-label" class="form-label">{{"ละติจูด" | translate}}</label>
<input type="text" class="form-control" id="input" [(ngModel)]="selectModel.latitude"
(ngModelChange)="changeMarker()">
</div>
<div class="xl:col-span-6 lg:col-span-6 md:col-span-6 sm:col-span-6 col-span-6">
<label for="input-label" class="form-label">{{"ลองจิจูด" | translate}}</label>
<input type="text" class="form-control" id="input" [(ngModel)]="selectModel.longitude"
(ngModelChange)="changeMarker()">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="deal-title" class="form-label">{{'รัศมี (KM)' | translate}}</label>
<!-- <input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.locationName"> -->
<input [(ngModel)]="selectModel.radius" type="number" placeholder="รัศมี"
class="form-control w-full p-2 border rounded mb-3">
</div>
<div class="xl:col-span-12 col-span-12">
<div class="mb-4">
</div>
<google-map class="xl:col-span-12 col-span-12" height="400px"
[center]="{ lat: selectModel.latitude, lng: selectModel.longitude }" [zoom]="12"
(mapClick)="addMarker($event,mapMarker)">
<map-circle [center]="{ lat: selectModel.latitude, lng: selectModel.longitude }"
[radius]="selectModel.radius*1000" />
<map-marker #mapMarker="mapMarker" [position]="{ lat: selectModel.latitude, lng: selectModel.longitude }"
[options]="markerOptions" />
</google-map>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-detail">
{{'Cancel' | translate}}
</button>
<button type="button" (click)="save()" class="ti-btn bg-primary text-white !font-medium">{{'Save' |
translate}}</button>
</div>
</div>
</div>
</div>
<!-- End:: Create Contact -->
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
import { SharedModule } from "../../../shared/shared.module";
import { NgSelectModule } from "@ng-select/ng-select";
import { CompanyLocationModel } from "../../models/company-location.model";
import { CompanyLocationService } from "../../services/company-location.service";
import { TokenService } from "../../../shared/services/token.service";
import swal from 'sweetalert';
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { FormsModule } from "@angular/forms";
import { RouterModule } from "@angular/router";
import { GoogleMap, GoogleMapsModule, MapInfoWindow, MapMarker } from "@angular/google-maps";
@Component({
selector: 'app-company-location',
standalone: true,
imports: [CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
GoogleMapsModule, MapMarker
],
templateUrl: './company-location.component.html',
styleUrl: './company-location.component.css',
encapsulation: ViewEncapsulation.None
})
export class CompanyLocationComponent {
@ViewChild('searchBox', { static: false }) searchBoxElement!: ElementRef;
@ViewChild(GoogleMap) map!: GoogleMap;
@ViewChild(MapInfoWindow) infoWindow: MapInfoWindow | undefined;
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('modalDetail') public modalDetail?: ElementRef;
action = "new";
allSelected = false;
someSelected = false;
itemsList: CompanyLocationModel[] = []
filterList: CompanyLocationModel[] = []
selectModel: CompanyLocationModel = new CompanyLocationModel()
empList: CompanyLocationModel[] = []
selectedItems = new Map<string, boolean>();
descName = 'engName'
pageIndex = 0;
get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(val: string) {
this.pageIndex = 0;
this.allSelected = false
this._searchTerm = val;
if (val != '') {
this.filterList = this.filter(val);
} else {
this.updatePagedItems()
}
}
center: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
zoom = 5;
radius = 50;
markerOptions: google.maps.MarkerOptions = { draggable: false };
markerPositions: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
autocompleteInput: string = '';
_searchTerm = "";
companyId = ""
adressType: string = 'geocode';
@ViewChild('addresstext') addresstext: any;
constructor(private comLocationService: CompanyLocationService, public translate: TranslateService, private tokenService: TokenService) {
this.companyId = this.tokenService.getSelectCompany().companyId;;
this.getLocation()
}
ngOnInit(): void {}
async ngAfterViewInit() {
// await this.loadGoogleMapsAPI();
this.initAutocomplete();
}
initAutocomplete() {
if (!this.searchBoxElement || !this.searchBoxElement.nativeElement) {
console.error("❌ SearchBox element not found!");
return;
}
const input = this.searchBoxElement.nativeElement;
const searchBox = new google.maps.places.SearchBox(input);
searchBox.addListener('places_changed', () => {
const places = searchBox.getPlaces();
if (!places || places.length === 0) return;
const place = places[0];
if (!place.geometry || !place.geometry.location) return;
this.selectModel.latitude = place.geometry.location.lat()
this.selectModel.longitude = place.geometry.location.lng()
this.selectModel.locationName = place.name!
// this.center = {
// lat: place.geometry.location.lat(),
// lng: place.geometry.location.lng(),
// };
// this.markerPositions = this.center;
// console.log(`📍 New Location: ${this.center.lat}, ${this.center.lng}`);
});
}
getLocation() {
this.comLocationService.getLists(this.companyId).subscribe(result => {
this.itemsList = result
this.updatePagedItems()
})
}
filter(v: string) {
return this.itemsList?.filter(
(x) =>
x.locationId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.locationName?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getStatus().toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
delete(item: CompanyLocationModel) {
swal({
title: "Are you sure?",
text: "You won't be able to revert this!",
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes,Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.comLocationService.delete(this.companyId, item).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getLocation()
})
}
});
}
new() {
this.action = 'add'
this.selectModel = new CompanyLocationModel()
this.selectModel.latitude = 0.00
this.selectModel.longitude = 0.00
}
view(item: CompanyLocationModel) {
this.action = 'edit'
this.selectModel = new CompanyLocationModel(item)
this.markerPositions = { lat: this.selectModel.latitude, lng: this.selectModel.longitude }
console.log(this.selectModel)
}
save() {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
if (this.action == 'add') {
this.comLocationService.save(this.companyId, this.selectModel).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getLocation()
this.childModal?.nativeElement.click()
})
} else if (this.action == 'edit') {
this.comLocationService.update(this.companyId, this.selectModel).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getLocation()
this.childModal?.nativeElement.click()
})
}
}
});
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList = this.itemsList.slice(startIndex, endIndex);
}
toggleAll(event: any) {
this.allSelected = event.target.checked;
this.selectedItems.clear();
this.itemsList.forEach(item => {
this.selectedItems.set(item.locationId, this.allSelected);
});
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.locationId));
}
onCheckboxChange(locationId: string) {
const isSelected = this.selectedItems.get(locationId) || false;
this.selectedItems.set(locationId, !isSelected);
this.allSelected = this.itemsList.every(item => this.selectedItems.get(item.locationId));
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.locationId));
}
deleteSelect() {
let employeeInfo = '';
this.selectedItems.forEach((isSelected, locationId) => {
if (isSelected) {
const item = this.itemsList.find(item => item.locationId === locationId);
if (item) {
employeeInfo += `${this.translate.instant('ชื่อตำแหน่ง')}: ${item.locationName}\n`;
}
}
});
swal({
title: "Are you sure?",
text: employeeInfo,
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes, Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.selectedItems.forEach((isSelected, locationId) => {
if (isSelected) {
const item = this.itemsList.find(item => item.locationId === locationId);
if (item) {
this.comLocationService.delete(this.companyId, item).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getLocation();
});
}
}
});
}
});
}
changeMarker() {
this.markerPositions = { lat: this.selectModel.latitude, lng: this.selectModel.longitude }
}
addMarker(event: google.maps.MapMouseEvent, marker: MapMarker) {
console.log(event)
console.log(marker)
this.selectModel.latitude = event.latLng!.toJSON().lat
this.selectModel.longitude = event.latLng!.toJSON().lng
this.markerPositions = { lat: event.latLng!.toJSON().lat, lng: event.latLng!.toJSON().lng }
if (this.infoWindow && marker.title) {
this.infoWindow.open(marker);
}
}
}
import { Component } from '@angular/core';
@Component({
selector: 'app-company-management',
template: `
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">Company Management</h4>
</div>
<div class="card-body">
<p>ยินดีต้อนรับสู่ระบบจัดการบริษัท</p>
<p>ระบบจัดการข้อมูลบริษัทและพนักงาน</p>
</div>
</div>
</div>
</div>
</div>
`,
styles: [`
.card {
margin: 20px 0;
}
.card-header {
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
`]
})
export class CompanyManagementComponent {
constructor() { }
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { CompanyManagementComponent } from './company-management.component';
export const routes: Routes = [
{
path: '',
component: CompanyManagementComponent
}
];
@NgModule({
declarations: [
CompanyManagementComponent
],
imports: [
CommonModule,
RouterModule.forChild(routes)
],
exports: [
CompanyManagementComponent
]
})
export class CompanyManagementModule {
static routes = routes;
}
<video #video width="640" height="480"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover;" autoplay
(loadedmetadata)="detectFace()">
</video>
<!-- Canvas element -->
<canvas #canvas width="640" height="480"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover;">
</canvas>
<button *ngIf="checkMatch" style="position: absolute; left: 0;bottom: 0;margin-bottom: 3rem;width: 100%;" type="button"
class="ti-btn bg-primary text-white !font-medium" (click)="captureImage()">ตรวจสอบใบหน้า</button>
import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import * as faceapi from 'face-api.js';
import swal from 'sweetalert';
import { environment } from '../../../../environments/environment';
import { CommonModule } from '@angular/common';
import { TokenService } from '../../../shared/services/token.service';
import { SharedModule } from '../../../shared/shared.module';
import { TimestampModel } from '../../models/timestamp.model';
import { EmployeeModel } from '../../models/employee.model';
import { TimestampService } from '../../services/timestamp.service';
import { EmployeeService } from '../../services/employee.service';
import { CompanyLocationModel } from '../../models/company-location.model';
import { CompanyLocationService } from '../../services/company-location.service';
@Component({
selector: 'app-enroll-face',
standalone: true,
imports: [CommonModule, SharedModule],
templateUrl: './enroll-face.component.html',
styleUrl: './enroll-face.component.scss'
})
export class EnrollFaceComponent {
@ViewChild('video') video: ElementRef;
@ViewChild('canvas') canvas: ElementRef;
locationList: CompanyLocationModel[] = []
locationArea: CompanyLocationModel[] = []
capturedImage: string | null = null;
uploadStatus: string = '';
checkMatch = false;
companyId = ""
memberId = ""
timestampModel: TimestampModel = new TimestampModel()
employeeModel: EmployeeModel = new EmployeeModel()
location: { latitude: number; longitude: number } = { latitude: 0, longitude: 0 };
errorMessage: string | null = null;
constructor(private http: HttpClient, private timestampService: TimestampService, private tokenService: TokenService, private employeeService: EmployeeService, private comLocationService: CompanyLocationService) {
this.companyId = this.tokenService.getSelectCompany().companyId;
// this.employeeService.getMemberById(this.tokenService.getSelectCompany().companyId, this.memberId).subscribe(result => {
// this.employeeModel = result
// })
}
ngOnInit() {
this.getCurrentLocation()
// โหลดโมเดล Face Detection ของ face-api.js
faceapi.nets.tinyFaceDetector.loadFromUri('/assets/models/tiny_face_detector');
faceapi.nets.faceLandmark68Net.loadFromUri('/assets/models/face_landmark_68');
faceapi.nets.faceRecognitionNet.loadFromUri('/assets/models/face_recognition');
faceapi.nets.faceExpressionNet.loadFromUri('/assets/models/face_expression');
// เปิดกล้อง
this.startVideo();
this.getCompanyLocation()
}
getCompanyLocation() {
this.comLocationService.getLists(this.companyId).subscribe(result => {
this.locationList = result
this.getCurrentLocation()
})
}
startVideo(): void {
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
this.video.nativeElement.srcObject = stream;
})
.catch(err => {
console.error('Error accessing camera: ', err);
});
}
// ตรวจจับใบหน้าและวาดกรอบครอบใบหน้า
async detectFace() {
const videoEl = this.video.nativeElement;
const displaySize = { width: videoEl.width, height: videoEl.height };
faceapi.matchDimensions(this.canvas.nativeElement, displaySize);
setInterval(async () => {
const detections = await faceapi.detectAllFaces(videoEl, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceExpressions()
const resizedDetections = faceapi.resizeResults(detections, displaySize);
// ล้าง canvas ก่อนวาดใหม่
this.canvas.nativeElement.getContext('2d').clearRect(0, 0, displaySize.width, displaySize.height);
// วาดกรอบครอบใบหน้าลงบน canvas
faceapi.draw.drawDetections(this.canvas.nativeElement, resizedDetections);
faceapi.draw.drawFaceExpressions(this.canvas.nativeElement, resizedDetections);
faceapi.draw.drawFaceLandmarks(this.canvas.nativeElement, resizedDetections);
if (detections.length > 0) {
this.checkMatch = true
} else {
this.checkMatch = false
}
}, 100); // ตรวจจับทุกๆ 100ms
}
// // แคปเจอร์ภาพจากกล้อง
captureImage(): void {
const video = this.video.nativeElement;
const canvas = this.canvas.nativeElement;
const context = canvas.getContext('2d');
// วาดภาพจาก video ลงใน canvas
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// แปลงภาพใน canvas เป็น base64
this.capturedImage = canvas.toDataURL('image/jpeg');
// console.log(this.capturedImage)
// this.uploadImage() checkface
this.uploadImage()
}
// // อัปโหลดภาพที่แคปเจอร์แล้ว
uploadImage(): void {
if (this.capturedImage) {
const file = this.dataURLtoFile(this.capturedImage, 'face.jpg');
const formData = new FormData();
formData.append('file', file);
this.http.post(environment.baseUrl + `/face/members/check-face-emp`, formData).subscribe({
next: (response: any) => {
this.employeeService.getMemberById(this.tokenService.getSelectCompany().companyId, response.message).subscribe(result => {
this.employeeModel = result
this.timestampModel = new TimestampModel({
"company_employeeId": this.employeeModel.company_employeeId,
"timestampType": 1,
"latitude": this.location?.latitude,
"longitude": this.location?.longitude
})
this.uploadPhototimestamp();
})
},
error: (error) => {
swal("Face Check Failed", this.uploadStatus, "error");
}
});
}
}
uploadPhototimestamp() {
const base64ImagePayload = {
base64_image: this.capturedImage // Ensure `capturedImage` includes the full base64 string (e.g., "data:image/png;base64,...")
};
const file = this.dataURLtoFile(this.capturedImage!, 'face.jpg');
const formData = new FormData();
formData.append('file', file);
this.http.post(environment.baseUrl + `/api/upload-image/`, formData).subscribe({
next: (response: any) => {
console.log('Upload successful:', response);
this.timestampModel.photoTimestamp = response.filename
this.saveTimestamp()
},
error: (error) => {
console.error('Error uploading image:', error);
alert('Error: ' + error.error.detail || 'Upload failed.');
}
});
}
saveTimestamp() {
if (this.locationArea.length > 0) {
this.timestampModel.timestampType = 1
this.timestampModel.locationName = this.locationArea[0].locationName
} else {
this.timestampModel.timestampType = 0
}
this.timestampService.save(this.companyId, this.timestampModel).subscribe(result => {
swal(result.timestampType == 0 ? "Warning Location !!" : "Complete !!", "ลงเวลาการทำงาน " + this.employeeModel.getFullname() + " เวลา "+ new Date(result.timestamp).toLocaleTimeString("th", { hour: '2-digit', minute: '2-digit' }), result.timestampType == 0 ? "info" : "success");
})
}
// // แปลง Base64 เป็นไฟล์ (Blob)
dataURLtoFile(dataurl: string, filename: string): File {
const arr: any = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
getCurrentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
this.location = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
};
this.searchCompanyLocation()
this.errorMessage = null; // Clear any previous errors
console.log('Location fetched successfully:', this.location);
},
(error) => {
switch (error.code) {
case error.PERMISSION_DENIED:
this.errorMessage = 'Permission denied by the user.';
break;
case error.POSITION_UNAVAILABLE:
this.errorMessage = 'Location information is unavailable.';
break;
case error.TIMEOUT:
this.errorMessage = 'The request to get user location timed out.';
break;
default:
this.errorMessage = 'An unknown error occurred.';
}
console.error('Error fetching location:', this.errorMessage);
}
);
} else {
this.errorMessage = 'Geolocation is not supported by this browser.';
}
}
private toRad(value: number): number {
return (value * Math.PI) / 180;
}
getDistance(lat1: string, lon1: string, lat2: string, lon2: string): number {
const R = 6371; // รัศมีของโลกในหน่วยกิโลเมตร
const dLat = this.toRad(parseFloat(lat2) - parseFloat(lat1));
const dLon = this.toRad(parseFloat(lon2) - parseFloat(lon1));
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(this.toRad(parseFloat(lat1))) *
Math.cos(this.toRad(parseFloat(lat2))) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
}
searchCompanyLocation() {
this.locationArea = this.getFilteredLocations();
console.log("locationArea", this.locationArea)
}
getFilteredLocations(): CompanyLocationModel[] {
return this.locationList.filter(location => {
const distance = this.getDistance(
this.location.latitude.toString(),
this.location.longitude.toString(),
location.latitude.toString(),
location.longitude.toString()
);
return distance <= location.radius;
});
}
}
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { HomeInstallerComponent } from './home-installer.component';
describe('HomeInstallerComponent', () => {
let component: HomeInstallerComponent;
let fixture: ComponentFixture<HomeInstallerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HomeInstallerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeInstallerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { SharedModule } from '../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { ActivatedRoute, Router } from '@angular/router';
import { CompanyService } from '../../services/company.service';
import { TokenService } from '../../../shared/services/token.service';
@Component({
selector: 'app-home-installer',
templateUrl: './home-installer.component.html',
standalone: true,
imports: [CommonModule, SharedModule, TranslateModule],
styleUrls: ['./home-installer.component.css']
})
export class HomeInstallerComponent implements OnInit {
companyId = ""
constructor(private router: Router, private route: ActivatedRoute, private comService: CompanyService, private tokenService: TokenService) {
this.companyId = this.route.snapshot.paramMap.get('companyId')!;
this.comService.getById(this.companyId).subscribe(result => {
console.log("SAVEEE")
this.tokenService.saveSelectCompany(result);
// this.router.navigate(["/company"]);
})
}
ngOnInit() {
}
}
:host {
display: block;
}
.map-container{
width: 100% !important
}
<app-page-header [title]="'ข้อมูลการลงเวลา'" [activeTitle]="'จัดการข้อมูลการลงเวลา'"
[title1]="'ข้อมูลการลงเวลา'"></app-page-header>
<!-- Start::row-1 -->
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-12 col-span-12">
<div class="flex flex-wrap gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2"
routerLink="/company/timestamp-face"><i class="ri-add-line font-semibold align-middle"></i>{{ 'ลงเวลาพนักงาน' |
translate}}
</a>
<!-- <a href="javascript:void(0);" class="ti-btn ti-btn-primary-full !py-1 !px-2" aria-expanded="false">
เรียงตาม<i class="ri-arrow-down-s-line align-middle ms-1 inline-block"></i>
</a>
<ul class="hs-dropdown-menu ti-dropdown-menu hidden" role="menu">
<li><a class="ti-dropdown-item" href="javascript:void(0);">Posted Date</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Status</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Department</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Job Type</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Newest</a></li>
<li><a class="ti-dropdown-item" href="javascript:void(0);">Oldest</a></li>
</ul> -->
</div>
</div>
<div class="xxl:col-span-12 xl:col-span-8 col-span-12">
<div class="grid grid-cols-12 gap-6 items-center mb-6">
<div class="lg:col-span-12 col-span-12">
<div class="inline-flex !w-full companies-search-input">
<input type="text" class="form-control !rounded-e-none border-e-0"
aria-label="Text input with segmented dropdown button" placeholder="ค้นหาจากข้อมูลพนักงาน"
[(ngModel)]="searchTerm">
<input class="form-control" id="daterange" [(ngModel)]="dateRange" placeholder="ช่วงวันที่" mwlFlatpickr
[altInput]="true" [convertModelValue]="true" mode="range" />
<ng-select class="form-control !rounded-none" name="choices-multiple-remove-button1"
id="choices-multiple-remove-button1" placeholder="พนักงาน" [(ngModel)]="empSelect">
<ng-option value="">ทั้งหมด</ng-option>
@for(item of empList;track empList){
<ng-option [value]="item.company_employeeId">{{item.getFullname()}}</ng-option>
}
</ng-select>
<ng-select class="form-control !rounded-none" name="choices-multiple-remove-button1"
id="choices-multiple-remove-button1" placeholder="สถานะ" [(ngModel)]="statusSelect">
<ng-option value="">ทั้งหมด</ng-option>
<ng-option value="WARNING">Warning</ng-option>
<ng-option value="CHECK_IN">Complete</ng-option>
<ng-option value="DISAPPROVE">Absent</ng-option>
</ng-select>
<button (click)="getTimestamp()" type="button" aria-label="button"
class="ti-btn ti-btn-primary-full !mb-0 !rounded-s-none"><i class="ri-search-line"></i></button>
</div>
</div>
</div>
</div>
<div class="xxl:col-span-8 xl:col-span-8 col-span-12">
<div class="xxl:col-span-8 xl:col-span-12 lg:col-span-12 md:col-span-12 sm:col-span-12 col-span-12">
<ul class="list-none mb-0 notification-container">
@for(item of filterList;track filterList){
<li>
<div
class="box border-s-[0.25rem] {{item.timestampType==0?'border-warning':''}} {{item.timestampType==1||item.timestampType==2?'border-success':''}} {{item.timestampType==3?'border-danger':''}}"
(click)="view(item)">
<div class="box-body !p-4">
<a href="javascript:void(0);">
<div class="flex items-start mt-0 flex-wrap">
<span class="avatar avatar-xl avatar-rounded">
<img [src]="item.getPicture()" alt="">
</span>
<div class="flex-grow">
<div class="sm:flex items-center">
<div class="sm:mt-0 mt-2 mx-2">
<p class="font-semibold mb-0 flex items-center text-primary">{{item.employee.member.firstName+ "
"+item.employee.member.lastName}}</p>
<p class="mb-0 text-secondary dark:text-white/50">{{item.getLocation()}}
</p>
<p class="mb-0 text-[#8c9097] dark:text-white/50"><span
class="badge {{item.timestampType==0?'bg-warning':''}} {{item.timestampType==1||item.timestampType==2?'bg-success':''}} {{item.timestampType==3?'bg-danger':''}} text-white">{{item.getStatus()}}</span>
</p>
</div>
<div class="ms-auto">
<h5
class="ltr:float-right rtl:float-left badge bg-light font-semibold text-primary dark:text-white whitespace-nowrap">
{{item.timestamp | date: "HH:mm"}} {{item.timestamp | date: "medium"}}
</h5>
</div>
</div>
</div>
</div>
</a>
</div>
</div>
</li>
}
</ul>
</div>
<ul class="ti-pagination mb-4 justify-end">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]" (click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous'
| translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</ul>
</div>
<div class="xxl:col-span-4 xl:col-span-4 col-span-12" *ngIf="selectModel">
<div class="box custom-box">
<div class="box-header justify-between">
<div class="box-title">
รายละเอียดการลงเวลา
</div>
<div>
</div>
</div>
<div class="box-body !p-0">
<!-- <div class="box-body p-6">
<div class="text-center">
<span class="avatar avatar-xl avatar-rounded">
<img src="./assets/images/faces/2.jpg" alt="">
</span>
<div class="mt-2">
<p class="mb-0 font-semibold">Samantha May</p>
<p class="text-[0.75rem] opacity-[0.7] mb-1 text-[#8c9097] dark:text-white/50">
09:00</p>
<p class="mb-0 text-[#8c9097] dark:text-white/50"><span
class="badge bg-danger text-white">ลงเวลาผิดสถานที่</span></p>
</div>
</div>
</div>
<div class="box-footer text-center">
<div class="btn-list">
<button type="button" class="ti-btn btn-sm !py-1 !px-2 !text-[0.75rem] me-2 ti-btn-light">อนุมัติ</button>
<button type="button"
class="ti-btn btn-sm !py-1 !px-2 !text-[0.75rem] text-white bg-primary">ไม่อนุมัติ</button>
</div>
</div> -->
<div class="box product-card">
<div class="box-body">
<div class="grid grid-cols-12 gap-6">
<div class="xxl:col-span-6 xl:col-span-6 col-span-12">
<!-- <span class="avatar avatar-xl avatar-rounded">
<img [src]="item.getPicture()" alt="">
</span> -->
<img src="{{selectModel.getPicture()}}" class="card-img mb-3 avatar-rounded" alt="...">
</div>
<div class="xxl:col-span-6 xl:col-span-6 col-span-12">
<div class="flex-grow">
<div class="sm:flex items-center">
<div class="sm:mt-0 mt-2 mx-2">
<p class="font-semibold mb-0 flex items-center text-primary">
{{selectModel.employee.member.firstName+ "
"+selectModel.employee.member.lastName}}</p>
<p class="mb-0 text-secondary dark:text-white/50">{{selectModel.latitude}} ,
{{selectModel.longitude}}
</p>
<p class="mb-0 text-[#8c9097] dark:text-white/50"><span
class="badge {{selectModel.timestampType==0?'bg-warning':''}} {{selectModel.timestampType==1||selectModel.timestampType==2?'bg-success':''}} {{selectModel.timestampType==3?'bg-danger':''}} text-white">{{selectModel.getStatus()}}</span>
</p>
</div>
<div class="ms-auto">
<h5
class="ltr:float-right rtl:float-left badge bg-light font-semibold text-primary dark:text-white whitespace-nowrap">
{{selectModel.timestamp | date: "HH:mm"}} {{selectModel.timestamp | date: "medium"}}
</h5>
</div>
</div>
</div>
<input type="text" class="form-control !rounded-e-none border-e-0 mt-5"
aria-label="Text input with segmented dropdown button" placeholder="ชื่อสถานที่"
[(ngModel)]="selectModel.locationName" [disabled]="selectModel.timestampType!=0">
<a *ngIf="selectModel.timestampType==0" (click)="approveTime(selectModel)"
class="ti-btn ti-btn-primary m-1 !me-2 !font-medium"><i
class="ri-check-line me-1 align-middle"></i>อนุมัติ</a>
<a *ngIf="selectModel.timestampType==0" (click)="disapproveTime(selectModel)"
class="ti-btn ti-btn-success m-1 !font-medium"><i
class="ri-close-line me-1 align-middle"></i>ไม่อนุมัติ</a>
</div>
<div class="xxl:col-span-12 xl:col-span-12 col-span-12">
<google-map height="400px" [center]="{ lat: selectModel.latitude, lng: selectModel.longitude }"
[zoom]="12">
<map-marker #mapMarker="mapMarker"
[position]="{ lat: selectModel.latitude, lng: selectModel.longitude }"
[options]="markerOptions" />
</google-map>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!--End::row-1 -->
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { TimestampLogComponent } from './timestamp-log.component';
describe('TimestampLogComponent', () => {
let component: TimestampLogComponent;
let fixture: ComponentFixture<TimestampLogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TimestampLogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimestampLogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
import { SharedModule } from '../../../shared/shared.module';
import { SimplebarAngularModule } from 'simplebar-angular';
import { MaterialModuleModule } from '../../../material-module/material-module.module';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { FlatpickrDefaults, FlatpickrModule } from 'angularx-flatpickr';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { TimestampService } from '../../services/timestamp.service';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { TokenService } from '../../../shared/services/token.service';
import { TimestampModel } from '../../models/timestamp.model';
import swal from 'sweetalert';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import moment from 'moment';
import { EmployeeModel } from '../../models/employee.model';
import { EmployeeService } from '../../services/employee.service';
import { GoogleMapsModule, MapMarker } from '@angular/google-maps';
@Component({
selector: 'app-timestamp-log',
templateUrl: './timestamp-log.component.html',
standalone: true,
imports: [CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
GoogleMapsModule, MapMarker,
RouterModule,
FlatpickrModule],
styleUrls: ['./timestamp-log.component.css'],
providers: [FlatpickrDefaults],
encapsulation: ViewEncapsulation.None
})
export class TimestampLogComponent {
public dateRange: { from: Date, to: Date } = { from: new Date(new Date().getFullYear(), new Date().getMonth(), 1), to: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0) };
public options: any = {
"autoApply": true,
ranges: this.translate.currentLang == 'th' ? {
'วันนี้': [moment(), moment()],
'7 วันล่าสุด': [moment().subtract(6, 'days'), moment()],
'30 วันล่าสุด': [moment().subtract(29, 'days'), moment()],
'เดือนนี้': [moment().startOf('month'), moment().endOf('month')]
} : {
'Today': [moment(), moment()],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')]
},
"locale": {
"format": "DD/MM/YYYY",
"separator": " - ",
"applyLabel": "ตกลง",
"cancelLabel": "ยกเลิก",
"fromLabel": "จาก",
"toLabel": "ถึง",
"daysOfWeek": this.translate.instant("NameDayShort"),
"monthNames": this.translate.instant("NameMonth"),
"customRangeLabel": this.translate.instant("Range date"),
"firstDay": 1,
direction: 'daterange-center shadow'
},
"alwaysShowCalendars": true,
"startDate": this.dateRange.from,
"endDate": this.dateRange.to,
};
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('modalDetail') public modalDetail?: ElementRef;
action = "new";
allSelected = false;
someSelected = false;
itemsList: TimestampModel[] = []
filterList: TimestampModel[] = []
selectModel?: TimestampModel
empList: EmployeeModel[] = []
empSelect = ""
statusSelect = ""
descName = 'engName'
pageIndex = 0;
center: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
zoom = 5;
markerOptions: google.maps.MarkerOptions = { draggable: false };
markerPositions: google.maps.LatLngLiteral = { lat: 13.7563, lng: 100.5018 };
get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(val: string) {
this.pageIndex = 0;
this.allSelected = false
this._searchTerm = val;
if (val != '') {
this.filterList = this.filter(val);
} else {
this.updatePagedItems()
}
}
_searchTerm = "";
companyId = ""
constructor(private timeService: TimestampService, public translate: TranslateService, private tokenService: TokenService, private employeeService: EmployeeService, private route: ActivatedRoute) {
this.empSelect = this.route.snapshot.paramMap.get('company_employeeId') || "";
this.companyId = this.tokenService.getSelectCompany().companyId;;
this.getTimestamp()
this.getEmp()
}
ngOnInit(): void {
}
getTimestamp() {
this.pageIndex = 0;
console.log(this.dateRange)
this.timeService.getListsSearch(this.tokenService.getSelectCompany().companyId, this.empSelect, new Date(this.dateRange.from).toISOString(), new Date(this.dateRange.to).toISOString(), this.statusSelect).subscribe(result => {
this.itemsList = result.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
this.updatePagedItems()
})
}
getEmp() {
this.employeeService.getLists(this.companyId).subscribe(result => {
this.empList = result
})
}
searchTime() {
console.log(this.dateRange)
}
filter(v: string) {
return this.itemsList?.filter(
(x) =>
x.company_employeeId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.timestampId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.timestamp?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.locationName?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getStatus().toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.timestamp.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.employee.getFullname().toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.employee.position.getName().toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.employee.department.getName().toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.employee.employeeId.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
delete(item: TimestampModel) {
swal({
title: "Are you sure?",
text: "You won't be able to revert this!",
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes,Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.timeService.delete(this.companyId, item).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getTimestamp()
})
}
});
}
new() {
this.action = 'add'
this.selectModel = new TimestampModel()
this.selectModel.latitude = 0.00
this.selectModel.longitude = 0.00
}
view(item: TimestampModel) {
this.action = 'edit'
this.selectModel = new TimestampModel(item, this.translate)
console.log(this.selectModel)
}
approveTime(item: TimestampModel) {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
item.timestampType = 1;
this.timeService.update(this.companyId, item).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.selectModel = undefined
this.getTimestamp()
this.childModal?.nativeElement.click()
})
}
});
}
disapproveTime(item: TimestampModel) {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
item.timestampType = 3;
this.timeService.update(this.companyId, item).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.selectModel = undefined
this.getTimestamp()
this.childModal?.nativeElement.click()
})
}
});
}
save() {
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
if (this.action == 'add') {
this.timeService.save(this.companyId, this.selectModel!).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getTimestamp()
this.childModal?.nativeElement.click()
})
} else if (this.action == 'edit') {
this.timeService.update(this.companyId, this.selectModel!).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getTimestamp()
this.childModal?.nativeElement.click()
})
}
}
});
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList = this.itemsList.slice(startIndex, endIndex);
}
}
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { WarningTimetampComponent } from './warning-timetamp.component';
describe('WarningTimetampComponent', () => {
let component: WarningTimetampComponent;
let fixture: ComponentFixture<WarningTimetampComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ WarningTimetampComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WarningTimetampComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-warning-timetamp',
templateUrl: './warning-timetamp.component.html',
standalone: true,
styleUrls: ['./warning-timetamp.component.css']
})
export class WarningTimetampComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
...@@ -285,18 +285,7 @@ export class HomeComponent implements OnInit { ...@@ -285,18 +285,7 @@ export class HomeComponent implements OnInit {
category: 'applications', category: 'applications',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true } permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
}, },
// การบริการ
{
id: 'dashboard',
name: 'dashboard',
displayName: 'Dashboard',
description: 'แดชบอร์ดหลัก',
icon: './assets/images/icons/dashboard.png',
path: '/portal-manage/dashboard',
isVisible: true,
category: 'services',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{ {
id: 'meetingBooking', id: 'meetingBooking',
name: 'meeting-booking', name: 'meeting-booking',
...@@ -320,66 +309,13 @@ export class HomeComponent implements OnInit { ...@@ -320,66 +309,13 @@ export class HomeComponent implements OnInit {
category: 'system', category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true } permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
}, },
{
id: 'roleManagement',
name: 'role-management',
displayName: 'Role Management',
description: 'ระบบจัดการบทบาท',
icon: './assets/images/icons/users.png',
path: '/portal-manage/role-management',
isVisible: true,
category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'menuPermissionManagement',
name: 'menu-permission-management',
displayName: 'Menu Permission',
description: 'ระบบจัดการสิทธิ์เมนู',
icon: './assets/images/icons/menu.png',
path: '/portal-manage/menu-permission-management',
isVisible: true,
category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'userManagement',
name: 'user-role-management',
displayName: 'User & Role Management',
description: 'ระบบจัดการผู้ใช้และบทบาท',
icon: './assets/images/icons/users.png',
path: '/portal-manage/user-role-management',
isVisible: true,
category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'companyManagement',
name: 'company-management',
displayName: 'Company Management',
description: 'ระบบจัดการบริษัท',
icon: './assets/images/icons/building.png',
path: '/portal-manage/company-management',
isVisible: true,
category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{
id: 'widgetManagement',
name: 'widget-management',
displayName: 'Widget Management',
description: 'ระบบจัดการวิดเจ็ต',
icon: './assets/images/icons/widget.png',
path: '/portal-manage/widget-management',
isVisible: true,
category: 'system',
permissions: { view: true, create: true, edit: true, delete: true, export: true, import: true }
},
{ {
id: 'dashboardManagement', id: 'dashboardManagement',
name: 'dashboard-management', name: 'dashboard-management',
displayName: 'Dashboard Management', displayName: 'Dashboard Management',
description: 'ระบบจัดการแดชบอร์ด', description: 'ระบบจัดการแดชบอร์ด และคลังวิดเจ็ต',
icon: './assets/images/icons/dashboard.png', icon: './assets/images/icons/dashboard.png',
path: '/portal-manage/dashboard-management', path: '/portal-manage/dashboard-management',
isVisible: true, isVisible: true,
......
...@@ -95,13 +95,6 @@ export const portalManageRoutes: Routes = [ ...@@ -95,13 +95,6 @@ export const portalManageRoutes: Routes = [
canActivate: [moduleAccessGuard] canActivate: [moduleAccessGuard]
}, },
// Company Management
{
path: 'company-management',
canActivate: [moduleAccessGuard],
loadChildren: () => import('./company-management/company-management.module').then(m => m.CompanyManagementModule)
},
// Widget Management // Widget Management
{ {
path: 'widget-management', path: 'widget-management',
......
/* Add custom styles for your widget stock component here */
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { IWidget } from '../../shared/models/dashboard.model';
import { DashboardService } from '../../shared/services/dashboard.service';
@Component({
selector: 'app-widget-stock',
standalone: true,
imports: [CommonModule],
templateUrl: './widget-stock.component.html',
styleUrls: ['./widget-stock.component.scss']
})
export class WidgetStockComponent implements OnInit {
public availableWidgets$!: Observable<IWidget[]>;
constructor(private dashboardService: DashboardService) { }
ngOnInit(): void {
this.availableWidgets$ = this.dashboardService.getAvailableWidgets();
}
}
...@@ -451,6 +451,15 @@ ...@@ -451,6 +451,15 @@
</a> </a>
</div> </div>
<div class="">
<a routerLink="/portal-manage/widget-stock"
class="p-4 items-center related-app block text-center rounded-sm hover:bg-gray-50 dark:hover:bg-black/20">
<img src="./assets/images/icons/widget.png" alt="Widget Stock"
class="leading-[1.75] text-2xl !h-[1.75rem] align-middle flex justify-center mx-auto">
<div class="text-[0.75rem] text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50 mt-1">Widget Stock</div>
</a>
</div>
</div> </div>
</div> </div>
......
...@@ -14,7 +14,6 @@ import { formsRoutingModule } from '../../components/forms/forms.routes'; ...@@ -14,7 +14,6 @@ import { formsRoutingModule } from '../../components/forms/forms.routes';
import { mapsRoutingModule } from '../../components/maps/maps.routes'; import { mapsRoutingModule } from '../../components/maps/maps.routes';
import { iconsRoutingModule } from '../../components/icons/icons.routes'; import { iconsRoutingModule } from '../../components/icons/icons.routes';
import { tablesRoutingModule } from '../../components/tables/tables.routes'; import { tablesRoutingModule } from '../../components/tables/tables.routes';
import { CompanyManagementModule } from '../../portal-manage/company-management/company-management.module';
import { MyskillXModule } from '../../portal-manage/myskill-x/myskill-x.module'; import { MyskillXModule } from '../../portal-manage/myskill-x/myskill-x.module';
import { MylearnModule } from '../../portal-manage/mylearn/mylearn.module'; import { MylearnModule } from '../../portal-manage/mylearn/mylearn.module';
import { MyjobModule } from '../../portal-manage/myjob/myjob.module'; import { MyjobModule } from '../../portal-manage/myjob/myjob.module';
...@@ -43,7 +42,6 @@ export const content: Routes = [ ...@@ -43,7 +42,6 @@ export const content: Routes = [
...mapsRoutingModule.routes, ...mapsRoutingModule.routes,
...iconsRoutingModule.routes, ...iconsRoutingModule.routes,
...tablesRoutingModule.routes, ...tablesRoutingModule.routes,
...CompanyManagementModule.routes,
...MyskillXModule.routes, ...MyskillXModule.routes,
...MylearnModule.routes, ...MylearnModule.routes,
...MyjobModule.routes, ...MyjobModule.routes,
......
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