Commit 73e72e5e by sawit

ปรับ module myskill-x

parent a76efb87
<!-- <div class="modal-header">
<h5 class="modal-title" id="editLabel">ข้อความแจ้งเตือน</h5>
<button type="button" class="close" (click)="activeModal.dismiss('dismiss')" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<ng-container *ngIf="innerHTML!=undefined then InnerHTML else Message"></ng-container>
<ng-template #Message>
<div class="modal-body">
{{message}}
</div>
</ng-template>
<ng-template #InnerHTML>
<div class="modal-body" [innerHTML]="innerHTML">
</div>
</ng-template> -->
<div class="modal-header">
<h5 class="modal-title" id="editLabel">ข้อความแจ้งเตือน</h5>
<button type="button" class="close" (click)="close()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<ng-container *ngIf="data.innerHTML != undefined then InnerHTML else Message"></ng-container>
<ng-template #Message>
<div class="modal-body">
{{data.message}}
</div>
</ng-template>
<ng-template #InnerHTML>
<div class="modal-body" [innerHTML]="data.innerHTML">
</div>
</ng-template>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AlertModalComponent } from './alert-modal.component';
describe('AlertModalComponent', () => {
let component: AlertModalComponent;
let fixture: ComponentFixture<AlertModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AlertModalComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AlertModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-alert-modal',
templateUrl: './alert-modal.component.html',
styleUrls: ['./alert-modal.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
MatDialogModule
]
})
export class AlertModalComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<AlertModalComponent>,
@Inject(MAT_DIALOG_DATA) public data: { message: string; innerHTML?: string }
) { }
ngOnInit(): void {
}
close(): void {
this.dialogRef.close();
}
}
\ No newline at end of file
<div class="modal-header">
<h5 class="modal-title" id="editLabel">ข้อความแจ้งเตือน</h5>
<button type="button" class="close" (click)="activeModal.dismiss('dismiss')" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" (click)="activeModal.close('close')">
ยืนยัน
</button>
<button type="button" class="btn btn-danger" (click)="activeModal.dismiss('dismiss')">
ยกเลิก
</button>
</div>
\ No newline at end of file
import { Component, Input, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-confirm-modal',
templateUrl: './confirm-modal.component.html',
styleUrls: ['./confirm-modal.component.scss']
})
export class ConfirmModalComponent implements OnInit {
@Input() message: string = "";
constructor(public activeModal: NgbActiveModal) { }
ngOnInit(): void {
}
}
<app-page-header [title]="'Datasource Table'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'Datasource Table'"></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-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>
</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">{{'รหัสบริษัท' | translate}}</th>
<th scope="col" class="text-start">{{ 'Company Name' | translate}}</th>
<th scope="col" class="text-start">{{ 'Database Type' | translate}}</th>
<th scope="col" class="text-start">{{ 'Port Number' | translate}}</th>
<th scope="col" class="text-start">{{ 'Database Name' | translate}}</th>
<th scope="col" class="text-start">{{ 'Server Name' | translate}}</th>
<th scope="col" class="text-start">{{ 'Status' | translate}}</th>
<!-- <th scope="col" class="text-start">{{ 'User Name' | translate}}</th> -->
<!-- <th scope="col" class="text-start">{{ 'Password' | translate}}</th> -->
<th scope="col" class="text-start">{{ 'Action' | translate}}</th>
</tr>
</thead>
<tbody>
@if (filterList.length > 0) {
@for (item of filterList; track item.companyId) {
<tr class="border border-defaultborder dark:border-defaultborder/10">
<td class="product-checkbox">
<input class="form-check-input" type="checkbox" [checked]="selectedItems.get(item.companyId) || false"
(change)="onCheckboxChange(item.companyId)" aria-label="...">
</td>
<td>
<span class="block mb-1">
{{item.companyId}}
</span>
</td>
<td>
<div>
<span class="block mb-1">
{{item.companyName}}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{item.dbType}}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{item.portNumber}}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{item.databaseName}}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{item.serverName}}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{item.status}}
</span>
</div>
</td>
<!-- <td>
<div>
<span class="block mb-1">
{{item.userName}}
</span>
</div>
</td> -->
<!-- <td class="text-center" style="white-space: normal !important;word-break: break-word;">
<span class="cursor-pointer" (click)="selectData(item)">
{{tableShowPassword.get(item.companyId)?item.password : '*'.repeat(item.password?.length || 0) }}</span>&nbsp;
<i class="cursor-pointer fa fa-eye" *ngIf="tableShowPassword.get(item.companyId)" (click)="tableShowPassword.set(item.companyId,false)"></i>
<i class="cursor-pointer fa fa-eye-slash" *ngIf="!tableShowPassword.get(item.companyId)" (click)="tableShowPassword.set(item.companyId,true)"></i>
</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>
}
} @else {
<tr>
<td [attr.colspan]="6" class="text-center py-4">
<ng-container *ngIf="itemsList.length === 0 && !searchTerm">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
<ng-container *ngIf="itemsList.length > 0 && filterList.length === 0 && searchTerm">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</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] cursor-pointer"
(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] cursor-pointer"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</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">
<!-- {{ (modalStatus == 'add' ? ('Create' | translate) : ('Edit' | translate)) + ' ' + ('Datasource Table Information' | translate) }} -->
{{('Datasource Table Information' | 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 my-2" *ngIf="action == 'edit'">
<label for="companyId" class="form-label">{{'รหัสบริษัท' | translate}}</label>
<input type="text" class="form-control !bg-input-readonly" id="companyId" placeholder="" [(ngModel)]="selectModel.companyId"
[disabled]="action === 'edit'" >
<!-- <div class="text-danger" *ngIf="!selectModel.companyId && action === 'add'">
{{'Please fill in information' | translate}}
</div> -->
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Company Name' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.companyName">
<div class="text-danger" *ngIf="!selectModel.companyName">
{{'Please fill in information' | translate}}
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Database Type' | translate}}</label>
<!-- <input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.dbType"> -->
<select class="form-control" [(ngModel)]="selectModel.dbType">
<option style="color: red" [class.d-none]="selectModel.dbType==''" [value]="''">
{{selectModel.dbType!=''?'ยกเลิก':''}}</option>
<option value="MSSQL">MSSQL</option>
<option value="PGSQL">PGSQL</option>
</select>
<div class="text-danger" *ngIf="!selectModel.dbType">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Port Number' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.portNumber">
<div class="text-danger" *ngIf="!selectModel.portNumber">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Database Name' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder="" [(ngModel)]="selectModel.databaseName">
<div class="text-danger" *ngIf="!selectModel.databaseName">
{{'Please fill in information' | translate}}
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Server Name' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.serverName">
<div class="text-danger" *ngIf="!selectModel.serverName">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Status' | translate}}</label>
<!-- <input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.status"> -->
<select class="form-control" [(ngModel)]="selectModel.status">
<option style="color: red" [class.d-none]="selectModel.status==''" [value]="''">
{{selectModel.status!=''?'ยกเลิก':''}}</option>
<option value="online">online</option>
<option value="offline">offline</option>
</select>
<div class="text-danger" *ngIf="!selectModel.status">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'User Name' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="selectModel.userName">
<div class="text-danger" *ngIf="!selectModel.userName">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12 my-2" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{ 'Password' | translate}}</label><br>
<input type="text" class="form-control" style="width: 95%" id="deal-title" placeholder=""
[(ngModel)]="selectModel.password" [class.secure-input]="!showPassword">
<i class="fa fa-eye cursor-pointer" *ngIf="showPassword" (click)="showPassword=false"></i>
<i class="fa fa-eye-slash cursor-pointer" *ngIf="!showPassword" (click)="showPassword=true"></i>
<div class="text-danger" *ngIf="!selectModel.password">
{{'Please fill in information' | translate}}
</div>
</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"
[class.ti-btn-disabled]="!selectModel.companyName||!selectModel.dbType||!selectModel.portNumber||!selectModel.databaseName||!selectModel.serverName||!selectModel.status||!selectModel.userName||!selectModel.password"
[disabled]="!selectModel.companyName||!selectModel.dbType||!selectModel.portNumber||!selectModel.databaseName||!selectModel.serverName||!selectModel.status||!selectModel.userName||!selectModel.password">
{{'Save' | translate}}</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
.secure-input {
-webkit-text-security: disc;
text-security: disc;
}
\ No newline at end of file
import { Component, ElementRef, ViewChild } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Router, RouterModule } from '@angular/router';
import { SharedModule } from '../../../../shared/shared.module';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import swal from 'sweetalert';
import { MatPaginator } from '@angular/material/paginator';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { CommonModule } from '@angular/common';
import { FileUploadModule } from 'ng2-file-upload';
import { FileItem, FileUploader, ParsedResponseHeaders } from "ng2-file-upload";
import { environment } from '../../../../../environments/environment';
import { TokenService } from '../../../../shared/services/token.service'
import { QuillModule } from 'ngx-quill';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { DatasourseTableService } from '../../../services/datasourse-table.service';
import { NgbPagination } from '@ng-bootstrap/ng-bootstrap';
import { DatasourceTableModel, MyDatasourceTableModel } from '../../../models/datasource-table.model';
@Component({
selector: 'app-datasource-table',
templateUrl: './datasource-table.component.html',
styleUrls: ['./datasource-table.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
]
})
export class DatasourceTableComponent {
quillConfig = {
toolbar: [
['link'], // เพิ่มปุ่มลิงก์
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }], // custom button values
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'align': [] }],
['clean'], // remove formatting button
]
};
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('modalDetail') public modalDetail?: ElementRef;
@ViewChild("CareerClusterModel") CareerClusterModel: any;
@ViewChild('profileChangeInput') profileChangeInputRef!: ElementRef;
dialogRef: any
currentContentTab: number = 1;
currentExcerptTab: number = 1;
action = "new";
allSelected = false;
someSelected = false;
itemsList: DatasourceTableModel[] = [];
filterList: DatasourceTableModel[] = [];
selectModel: DatasourceTableModel = new MyDatasourceTableModel();
selectedItems = new Map<string, boolean>();
// empList: DatasourceTableModel[] = [];
// descName = 'engName';
pageIndex = 0;
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
modalStatus: "add" | "edit" = "add"
showPassword = false
tableShowPassword: Map<string, boolean> = new Map<string, boolean>()
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 = "";
constructor(
private datasourceTable: DatasourseTableService,
public translate: TranslateService,
private tokenService: TokenService,
private router: Router,
private dialog: MatDialog,
) {
}
getData() {
this.datasourceTable.getList().subscribe({
next: (response: DatasourceTableModel[]) => {
this.itemsList = response.map((x: any) => new MyDatasourceTableModel(x));
console.log('ข้อมูล (itemsList)', this.itemsList);
this.updatePagedItems();
},
error: (error) => {
console.error('error cant get position', error);
swal("ข้อผิดพลาด", "ไม่สามารถดึงข้อมูลได้", "error");
}
});
}
ngOnInit(): void {
this.getData();
}
filter(v: string) {
return this.itemsList?.filter(
(x) =>
x.companyId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.companyName?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.databaseName?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.dbType?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.password?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.portNumber?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.serverName?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.status?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.userName?.toLowerCase().indexOf(v.toLowerCase()) !== -1
// x.getStatus().toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
delete(item: DatasourceTableModel) {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณจะไม่สามารถกู้คืนข้อมูลนี้ได้!",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ใช่, ลบเลย!"],
})
.then((willDelete: any) => {
if (willDelete) {
const newData = new MyDatasourceTableModel(item)
this.datasourceTable.delete(newData).subscribe(result => {
swal("ลบสำเร็จ!!", "ลบข้อมูลสำเร็จ", "success");
this.ngOnInit();
}, error => {
console.error("เกิดข้อผิดพลาดในการลบ:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถลบข้อมูลได้", "error");
});
}
});
}
new() {
this.action = 'add';
this.selectModel = new MyDatasourceTableModel();
// this.selectModel.status = 1;
this.selectModel.companyId = "";
this.selectModel.companyName = "";
this.selectModel.databaseName = "";
this.selectModel.dbType = "";
this.selectModel.password = "";
this.selectModel.portNumber = "";
this.selectModel.serverName = "";
this.selectModel.status = "";
this.selectModel.userName = "";
}
view(item: DatasourceTableModel) {
this.action = 'edit';
this.selectModel = new MyDatasourceTableModel(item);
console.log(this.selectModel);
}
save() {
console.log('Before Save, selectModel is:', this.selectModel);
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
})
.then((willSave: any) => {
if (willSave) {
this.datasourceTable.post(this.selectModel).subscribe(result => {
console.log(result);
swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสมาชิก", "success");
this.ngOnInit();
this.childModal?.nativeElement.click();
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
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.companyId, this.allSelected);
});
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.companyId));
}
onCheckboxChange(companyId: string) {
const isSelected = this.selectedItems.get(companyId) || false;
this.selectedItems.set(companyId, !isSelected);
this.allSelected = this.itemsList.every(item => this.selectedItems.get(item.companyId));
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.companyId));
}
deleteSelect() {
let employeeInfo = '';
this.selectedItems.forEach((isSelected, companyId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.companyId === companyId);
if (user) {
employeeInfo += `${this.translate.instant('companyName')}: ${user.companyName}\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, companyId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.companyId === companyId);
if (user) {
const newData = new MyDatasourceTableModel(user)
this.datasourceTable.delete(newData).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit();
});
}
}
});
}
});
}
selectData(data: DatasourceTableModel) {
this.selectModel = new MyDatasourceTableModel(data)
this.scrollTop()
}
scrollTop() {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}
<app-page-header [title]="'รายงาน Excel'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'รายงาน Excel'"></app-page-header>
<div class="box">
<div class="relative w-full max-w-4xl mx-auto my-8 font-sans">
<button (click)="toggleDropdown()"
class="w-full bg-white bg-gradient-to-r from-blue-600 to-blue-800 text-white font-semibold py-4 px-6 rounded-xl shadow-lg hover:from-blue-700 hover:to-blue-900 focus:outline-none focus:ring-4 focus:ring-blue-300 transition-all duration-300 ease-in-out flex items-center justify-between">
<span class="text-xl sm:text-2xl text-primary">
{{ selectedTemplate ? selectedTemplate.tdesc + ' (' + selectedTemplate.fileName + ')' : 'รายงาน Excel' }}
</span>
<svg class="w-6 h-6 transform transition-transform duration-300 text-primary" [class.rotate-180]="isDropdownOpen"
fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div *ngIf="isDropdownOpen"
class="absolute z-10 w-full mt-2 bg-white border border-gray-200 rounded-md shadow-lg overflow-hidden max-h-60 overflow-y-auto">
<ul class="py-1">
<li *ngFor="let template of excelReportTemplates" (click)="selectTemplate(template)"
class="px-4 py-2 hover:bg-gray-100 cursor-pointer text-gray-800 text-lg">
{{ template.tdesc }} ({{ template.fileName }}) </li>
<li *ngIf="excelReportTemplates.length === 0 && !loadingTemplates" class="px-4 py-2 text-gray-500 text-lg">
ไม่พบรายการ Template
</li>
<li *ngIf="loadingTemplates" class="px-4 py-2 text-gray-500 text-lg">
กำลังโหลด...
</li>
</ul>
</div>
</div>
<div *ngIf="selectedTemplate && excelReport"
class="p-6 bg-gray-100 rounded-b-xl shadow-inner transition-all duration-300 ease-in-out"
[class.max-h-0]="!selectedTemplate" [class.max-h-screen]="selectedTemplate">
<div class="pt-6 border-t border-gray-200 bg-white p-3 rounded-xl">
<h2 class="text-2xl font-bold text-gray-800 mb-4">รายงาน Excel: {{ selectedTemplate.tdesc }}</h2>
<p class="mb-4 text-gray-700">ชื่อไฟล์: {{ selectedTemplate.fileName }}</p>
<!-- <p class="mb-4 text-gray-700">รหัส Template: {{ selectedTemplate.templateId }}</p> -->
<div *ngIf="variableSheet.length > 0" class="grid grid-cols-1 md:grid-cols-2 gap-4 items-end mb-4 mt-4">
<ng-container *ngFor="let sheetVar of variableSheet">
<div [ngSwitch]="sheetVar.type">
<label [for]="sheetVar.key" class="block text-gray-700 text-sm font-medium mb-1">{{ sheetVar.label
}}</label>
<ng-container *ngSwitchCase="'text'">
<input type="text" [id]="sheetVar.key" [(ngModel)]="sheetVar.value" [placeholder]="sheetVar.tname"
class="mt-1 block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
</ng-container>
<ng-container *ngSwitchCase="'calendar'">
<input type="date" [id]="sheetVar.key" [(ngModel)]="sheetVar.displayValue"
(ngModelChange)="formatNgbDate(sheetVar.key, $event)"
class="mt-1 block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
</ng-container>
<ng-container *ngSwitchCase="'list'">
<select [id]="sheetVar.key" [(ngModel)]="sheetVar.value"
class="mt-1 block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
<option *ngFor="let opt of sheetVar.option" [value]="opt.value">{{ opt.text }}</option>
</select>
</ng-container>
<ng-container *ngSwitchCase="'radio'">
<div class="flex flex-wrap gap-4 mt-1">
<label *ngFor="let opt of sheetVar.option" class="inline-flex items-center">
<input type="radio" [name]="sheetVar.key" [value]="opt.value" [(ngModel)]="sheetVar.value"
class="form-radio text-blue-600" />
<span class="ml-2 text-gray-700">{{ opt.text }}</span>
</label>
</div>
</ng-container>
<ng-container *ngSwitchCase="'help'">
<div class="relative">
<input type="text" [id]="sheetVar.key" [(ngModel)]="sheetVar.value.tdesc"
class="mt-1 block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
<div class="absolute inset-y-0 right-0 pr-3 flex items-center">
<button type="button" (click)="selectData(sheetVar.key)"
class="p-1 rounded-full bg-blue-500 text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
<svg class="h-5 w-5 text-black" fill="none" stroke="currentColor" viewBox="0 0 24 24"
(click)="openModal(sheetVar, detailModal); $event.stopPropagation();"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
</div>
</div>
</ng-container>
</div>
</ng-container>
</div>
<div class="flex justify-end gap-3 mt-6">
<!-- <button (click)="dowloadExcelReport()"
class="bg-primary py-3 px-6 bg-green-600 text-white font-semibold rounded-lg shadow-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105">
Download Excel
</button> -->
<button (click)="printExcelReport()"
class="bg-primary py-3 px-6 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105">
Print Excel
</button>
</div>
</div>
</div>
</div>
<ng-template #detailModal>
<h2 mat-dialog-title clas>
<div class="text-primary">รายชื่อพนักงาน</div>
</h2>
<mat-dialog-content>
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
รายการทั้งหมด
</div>
<div class="flex flex-wrap gap-2">
<div>
<input class="form-control form-control" type="text" placeholder="Search..."
aria-label=".form-control-sm example" [(ngModel)]="searchModal" (ngModelChange)="updateModalPagedItems()">
</div>
</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 bg-primary">
<th scope="col" *ngFor="let head of modalDetail.text.tableHead" class="text-start text-white">{{ head }}
</th>
</tr>
</thead>
<tbody>
@if (modalFilterList.length > 0) {
@for (item of modalFilterList; track item.id) {
<tr class="border border-defaultborder dark:border-defaultborder/10 cursor-pointer hover:bg-gray-100"
(click)="selectData(item)">
<td>{{ item.id }}</td>
<td>{{ item.tdesc }}</td>
<td>{{ item.edesc }}</td>
</tr>
}
} @else {
<tr>
<td [attr.colspan]="modalDetail.text.tableHead.length" class="text-center py-4 text-gray-500">
ไม่พบข้อมูล
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<!-- <input type="text" [(ngModel)]="searchModal" placeholder="Search..."
(ngModelChange)="updateModalPagedItems()"
class="mt-1 block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm mb-4" /> -->
<!-- <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 bg-primary">
<th scope="col" *ngFor="let head of modalDetail.text.tableHead" class="text-start text-white">{{ head }}
</th>
</tr>
</thead>
<tbody>
@if (modalFilterList.length > 0) {
@for (item of modalFilterList; track item.id) {
<tr class="border border-defaultborder dark:border-defaultborder/10 cursor-pointer hover:bg-gray-100"
(click)="selectData(item)">
<td>{{ item.id }}</td>
<td>{{ item.tdesc }}</td>
<td>{{ item.edesc }}</td>
</tr>
}
} @else {
<tr>
<td [attr.colspan]="modalDetail.text.tableHead.length" class="text-center py-4 text-gray-500">
ไม่พบข้อมูล
</td>
</tr>
}
</tbody>
</table>
</div>
</div> -->
<div class="box-footer">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{ (modalPageIndex * modalPageSize) + 1 }} {{'to' | translate}} {{
Math.min((modalPageIndex + 1) * modalPageSize, totalModalItemsCount) }} {{'of' | translate}} {{
totalModalItemsCount }} {{'entries' | translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="flex items-center flex-wrap overflow-auto mt-3" *ngIf="totalModalItemsCount > 0">
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li class="page-item" [class.disabled]="modalPageIndex === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="onModalPageChange(modalPageIndex - 1)">{{'Previous' | translate}}</a>
</li>
@for (pageNumber of totalPagesArrayInModal; track pageNumber) {
<li class="page-item" [class.active]="modalPageIndex === (pageNumber - 1)">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="onModalPageChange(pageNumber - 1)">
{{ pageNumber }}
</a>
</li>
}
<li class="page-item" [class.disabled]="modalPageIndex === (totalPagesArrayInModal.length - 1)">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="onModalPageChange(modalPageIndex + 1)">
{{'Next' | translate}}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions style="justify-content: end; padding: 16px 20px;">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle" mat-button
(click)="closeDialog()">Close</button>
</mat-dialog-actions>
</ng-template>
\ No newline at end of file
/* 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 { ExcelReportComponent } from './excel-report.component';
describe('ExcelReportComponent', () => {
let component: ExcelReportComponent;
let fixture: ComponentFixture<ExcelReportComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ExcelReportComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ExcelReportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { ExcelReportService } from '../../../services/excel-report.service';
import { NgbDate } from '@ng-bootstrap/ng-bootstrap';
import FileSaver from 'file-saver';
import { TemplateFileMiniModel } from '../../../models/template-file-mini.model';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
export interface ValueDetailItem {
id: string;
tdesc: string;
edesc: string;
}
export interface ModalDetail {
text: { cardHead: string, search: string[], tableHead: string[] }
}
@Component({
selector: 'app-excel-report',
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
],
templateUrl: './excel-report.component.html',
styleUrls: ['./excel-report.component.css']
})
export class ExcelReportComponent implements OnInit {
isDropdownOpen: boolean = false;
excelReportStartDate: string = '2025-07-09';
employeeId: string = '';
// ลบ pageIndex และ _searchTerm ถ้าไม่ได้ใช้สำหรับส่วนอื่นนอก Modal
// pageIndex = 0;
// _searchTerm = "";
dialogRef: any
// ตัวแปรสำหรับ Pagination ของ Modal
modalPageIndex = 0; // เริ่มที่ index 0 เหมือนต้นฉบับ
modalPageSize = 10; // 10 รายการต่อหน้า
Math = Math; // เพื่อให้ template เข้าถึง Math ได้
readonly maxPageButtons = 15;
changeDate = new Date();
select: any = {}
excelReport?: any
variableSheet: any = []
loading = false
loadingExcel = false
valueDetail: ValueDetailItem[] = [] // ข้อมูลทั้งหมดที่ได้จาก API สำหรับ Modal
modalFilterList: ValueDetailItem[] = []; // <--- เพิ่มตัวแปรนี้: ข้อมูลที่ถูกแบ่งหน้าแล้วสำหรับแสดงในตาราง Modal
modalDetail: ModalDetail = {
text: {
cardHead: '',
search: [],
tableHead: []
}
}
keySelect = ""
searchModal = "" // ใช้เป็น searchTerm สำหรับ Modal
// ลบ page และ pageSize ถ้าไม่ได้ใช้สำหรับส่วนอื่นนอก Modal
// page = 1
// pageSize = 10
// ** Updated properties for dropdown **
excelReportTemplates: TemplateFileMiniModel[] = [];
loadingTemplates: boolean = false;
selectedTemplate: TemplateFileMiniModel | null = null;
constructor(private route: ActivatedRoute,
private excelReportService: ExcelReportService,
private cdr: ChangeDetectorRef,
private dialog: MatDialog,
public translate: TranslateService, // ต้อง inject TranslateService เพื่อให้ translate pipe ทำงาน
) { }
ngOnInit(): void {
// Existing route parameter handling (if needed for direct links)
this.route.paramMap.subscribe(params => {
const rawId = params.get('id');
if (rawId) {
const [id, queryString] = rawId.split('?');
const searchParams = new URLSearchParams(queryString); // Changed to searchParams for clarity
const template = searchParams.get('template');
const name = searchParams.get('name');
// If you want to pre-select a template based on URL params:
if (template && name) {
// This assumes the list of templates is already loaded or will be loaded
// You might need to adjust this to wait for excelReportTemplates to be populated
this.excelReportService.getTemplateFileLists().subscribe(
templates => {
this.excelReportTemplates = templates;
const preselected = templates.find(t => t.templateId === template && t.fileName === name);
if (preselected) {
this.selectedTemplate = preselected;
this.getDataExcelReport(preselected.templateId, preselected.fileName);
}
}
);
}
}
});
// Load templates for the dropdown when the component initializes
this.loadExcelReportTemplates();
}
openModal(data: any, modal: any) {
this.searchModal = ''; // รีเซ็ต search
this.modalPageIndex = 0; // รีเซ็ต index หน้าเมื่อเปิด Modal
this.valueDetail = data.valueDetail.map((x: any) => ({ id: x.id || '', tdesc: x.tdesc || '', edesc: x.edesc || '' })) as ValueDetailItem[];
this.keySelect = data.key;
this.modalDetail = {
text: {
cardHead: "Table " + data.table,
search: ["Table " + data.table],
tableHead: ['ID', 'detailTH', 'detailENG']
}
}
this.updateModalPagedItems(); // <--- เรียก updateModalPagedItems() หลังจากโหลดข้อมูล Modal ครั้งแรก
this.dialogRef = this.dialog.open(modal,{
width: '1100px',
height: '650px'
});
}
closeDialog() {
console.log('closeDialog() called.');
if (this.dialogRef) {
this.dialogRef.close();
this.dialogRef = undefined;
console.log('Modal closed and dialogRef cleared.');
} else {
console.log('closeDialog() called, but dialogRef is undefined.');
}
}
// ลบ updatePagedItems() ออกไป ถ้าไม่ได้ใช้สำหรับส่วนอื่นนอก Modal
// updatePagedItems() {
// const startIndex = this.pageIndex * 10;
// const endIndex = startIndex + 10;
// this.filterList = this.itemsList.slice(startIndex, endIndex);
// }
loadExcelReportTemplates(): void {
this.loadingTemplates = true;
this.excelReportService.getTemplateFileLists().subscribe({
next: (data) => {
this.excelReportTemplates = data;
this.loadingTemplates = false;
this.cdr.detectChanges();
// ลบการเรียก updatePagedItems() ออกจากตรงนี้ด้วย ถ้าไม่ได้ใช้กับส่วนหลัก
// this.updatePagedItems();
},
error: (err) => {
console.error('Failed to load Excel report templates for dropdown', err);
this.loadingTemplates = false;
this.excelReportTemplates = [];
this.cdr.detectChanges();
}
});
}
toggleDropdown(): void {
this.isDropdownOpen = !this.isDropdownOpen;
}
selectTemplate(template: TemplateFileMiniModel): void {
this.selectedTemplate = template;
this.isDropdownOpen = false;
this.cdr.detectChanges();
this.getDataExcelReport(this.selectedTemplate.templateId, this.selectedTemplate.fileName);
}
getDataExcelReport(templateId: string, fileName: string) {
this.loading = true
this.excelReport = undefined
this.variableSheet = []
this.valueDetail = []
this.excelReportService.getTemplateFile(templateId, fileName).subscribe((res: any) => {
console.log('response', res);
this.excelReport = res;
if (this.excelReport && this.excelReport.param && this.excelReport.param.variableSheet) {
Object.entries(this.excelReport.param.variableSheet).forEach(([key, value]) => {
const data = value as any
if (data.type == 'text') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key
})
} else if (data.type == 'list') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'radio') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'help') {
this.variableSheet.push({ ...data, value: data.valueDefault ? { id: data.valueDefault, tdesc: data.valueDefault, edesc: data.valueDefault } : { id: "", tdesc: "", edesc: "" }, key: key })
} else if (data.type == 'calendar') {
let initialValueForInput: string = ''; // ค่าสำหรับ input type="date" (YYYY-MM-DD)
let initialValueForModel: string = ''; // ค่าสำหรับ sheetVar.value (DD-MM-YYYY, ตามที่คุณต้องการส่งกลับไป)
let initialNgbDate: NgbDate | null = null;
if (data.valueDefault) {
// ถ้ามี valueDefault ให้ใช้ค่าจาก API
initialValueForModel = data.valueDefault;
const [d, m, y] = data.valueDefault.split('-').map(Number);
initialNgbDate = new NgbDate(y, m, d);
initialValueForInput = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
} else {
// ถ้าไม่มี valueDefault ให้กำหนดเป็นวันที่ปัจจุบัน
const today = new Date();
const day = String(today.getDate()).padStart(2, '0');
const month = String(today.getMonth() + 1).padStart(2, '0');
const year = today.getFullYear();
initialValueForModel = `${day}-${month}-${year}`;
initialValueForInput = `${year}-${month}-${day}`;
initialNgbDate = new NgbDate(year, today.getMonth() + 1, today.getDate());
}
this.variableSheet.push({
...data,
value: initialValueForModel,
key: key,
displayValue: initialValueForInput
});
this.select[key] = initialNgbDate;
}
})
}
this.loading = false
this.cdr.detectChanges()
}, error => {
console.error("Error fetching detailed excel report:", error);
this.loading = false
// Handle error, maybe show a message to the user
this.cdr.detectChanges()
})
}
formatNgbDate(key: string, date?: NgbDate) {
if (date) {
const day = String(date.day).padStart(2, '0');
const month = String(date.month).padStart(2, '0');
const year = date.year;
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = `${day}-${month}-${year}`
} else {
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = ''
}
}
valueDetailFilter(): ValueDetailItem[] {
return this.valueDetail.filter((item: ValueDetailItem) =>
item.id.toLowerCase().includes(this.searchModal.toLowerCase()) ||
item.tdesc.toLowerCase().includes(this.searchModal.toLowerCase()) ||
item.edesc.toLowerCase().includes(this.searchModal.toLowerCase())
)
}
// >>>>>> เมธอดสำหรับแบ่งหน้าใน Modal (คล้าย updatePagedItems ของต้นฉบับ)
updateModalPagedItems() {
const filteredData = this.valueDetailFilter(); // ใช้ข้อมูลที่กรองแล้ว
const startIndex = this.modalPageIndex * this.modalPageSize;
const endIndex = startIndex + this.modalPageSize;
this.modalFilterList = filteredData.slice(startIndex, endIndex);
}
// >>>>>> Getter สำหรับจำนวนรายการทั้งหมดหลังจาก filter (คล้าย itemsList.length / filterList.length)
get totalModalItemsCount(): number {
return this.valueDetailFilter().length;
}
// >>>>>> Getter สำหรับ Array ของเลขหน้า (คล้าย Math.ceil(total / pageSize) ของต้นฉบับ)
get totalPagesArrayInModal(): number[] {
const totalPages = Math.ceil(this.totalModalItemsCount / this.modalPageSize);
const pageButtons: number[] = [];
// กำหนดขอบเขตของปุ่มที่จะแสดง
let startPage: number, endPage: number;
if (totalPages <= this.maxPageButtons) {
// ถ้าจำนวนหน้าน้อยกว่าหรือเท่ากับจำนวนปุ่มสูงสุดที่กำหนดไว้ ให้แสดงทั้งหมด
startPage = 1;
endPage = totalPages;
} else {
// ถ้าจำนวนหน้ามากกว่าจำนวนปุ่มสูงสุด
const halfButtons = Math.floor(this.maxPageButtons / 2);
if (this.modalPageIndex < halfButtons) {
// อยู่ใกล้จุดเริ่มต้น
startPage = 1;
endPage = this.maxPageButtons;
} else if (this.modalPageIndex >= totalPages - halfButtons) {
// อยู่ใกล้จุดสิ้นสุด
startPage = totalPages - this.maxPageButtons + 1;
endPage = totalPages;
} else {
// อยู่กลางๆ
startPage = this.modalPageIndex - halfButtons + 1;
endPage = this.modalPageIndex + halfButtons;
}
}
// สร้าง Array ของเลขหน้าตามขอบเขตที่คำนวณได้
for (let i = startPage; i <= endPage; i++) {
pageButtons.push(i);
}
return pageButtons;
}
// ... (เมธอดอื่นๆ ที่เหลือเหมือนเดิม)
selectData(data: any) {
const item = this.variableSheet.find((i: any) => i.key === this.keySelect);
if (item) item.value = data;
this.closeDialog();
}
dowloadExcelReport() {
this.loadingExcel = true
if (!this.excelReport || !this.excelReport.param || !this.excelReport.param.excelFile) {
console.error("No excel report selected or missing excelFile parameter.");
this.loadingExcel = false;
return;
}
const fileName = this.excelReport.param.excelFile
this.excelReportService.downloadTemplateFile(fileName).subscribe((res: any) => {
const blob = new Blob([res], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, fileName);
this.loadingExcel = false
this.cdr.detectChanges()
}, (err) => {
console.error("Error downloading excel report:", err);
this.loadingExcel = false
this.cdr.detectChanges()
})
}
printExcelReport(): void {
if (!this.selectedTemplate || !this.excelReport || !this.excelReport.param) {
alert('Please select an Excel report template first.');
return;
}
this.loadingExcel = true; // Use loadingExcel for print as well
const fileName = this.excelReport.param.excelFile;
const paramObj: { [key: string]: string } = {}; // Object to hold parameters
this.variableSheet.forEach((item: any) => {
if (item.type === 'help') {
paramObj["__" + item.key] = item.value.id || '';
} else if (item.type === 'calendar' || item.type === 'list' || item.type === 'radio' || item.type === 'text') {
paramObj["__" + item.key] = item.value || '';
}
});
// Convert paramObj to string based on backend expectation, typically JSON.stringify
// Or if backend expects a pipe-separated string, adjust accordingly.
const paramString = JSON.stringify(paramObj); // Common format
// const paramString = Object.entries(paramObj).map(([key, value]) => `${key}=${value}`).join('|'); // If backend expects old format
this.excelReportService.printExcelReport({ fileName: fileName, paramObj: paramString }).subscribe(
(res: any) => {
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); // Correct MIME type for .xlsx
FileSaver.saveAs(blob, fileName);
this.loadingExcel = false;
this.cdr.detectChanges();
},
(err) => {
console.error('Error printing Excel report:', err);
this.loadingExcel = false;
this.cdr.detectChanges();
}
);
}
// >>>>>> เมธอดสำหรับเปลี่ยนหน้าใน Modal (คล้าย onCheckboxChange)
onModalPageChange(newPageIndex: number): void {
this.modalPageIndex = newPageIndex;
this.updateModalPagedItems(); // เรียก updateModalPagedItems() เมื่อเปลี่ยนหน้า
}
}
<app-page-header [title]="'Analytics'" [activeTitle]="'Dashboards'" [title1]="'Analytics'"></app-page-header>
<div class="grid grid-cols-12 gap-x-6">
<div class="xl:col-span-7 col-span-12">
<div class="grid grid-cols-12 gap-x-6">
<div class="xl:col-span-4 lg:col-span-4 md:col-span-4 sm:col-span-6 col-span-12">
<div class="box">
<div class="box-body">
<div class="flex flex-wrap items-center justify-between">
<div>
<h6 class="font-semibold mb-3 text-[1rem]">Total Users</h6>
<span class="text-[1.5625rem] font-semibold">9,789</span>
<span class="block text-success text-[0.75rem]">+0.892 <i class="ti ti-trending-up ms-1"></i></span>
</div>
<div id="analytics-users">
<apx-chart [series]="chartOptions1.series" [chart]="chartOptions1.chart"
[xaxis]="chartOptions1.xaxis" [dataLabels]="chartOptions1.dataLabels"
[colors]="chartOptions1.colors" [fill]="chartOptions1.fill"
[markers]="chartOptions1.markers" [stroke]="chartOptions1.stroke"></apx-chart>
</div>
</div>
</div>
</div>
</div>
<div class="xl:col-span-4 lg:col-span-4 md:col-span-4 sm:col-span-6 col-span-12">
<div class="box">
<div class="box-body">
<div class="flex items-center justify-between">
<div>
<h6 class="font-semibold mb-3 text-[1rem]">Live Visitors</h6>
<span class="text-[1.5625rem] font-semibold">12,240</span>
<span class="block text-danger text-[0.75rem]">+0.59<i class="ti ti-trending-down ms-1 inline-flex"></i></span>
</div>
<div>
<span class="avatar avatar-md bg-secondary text-white">
<i class="ri-user-3-line"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="xl:col-span-4 lg:col-span-4 md:col-span-4 sm:col-span-6 col-span-12">
<div class="box overflow-hidden">
<div class="box-body mb-3">
<div class="flex items-center justify-between">
<div>
<h6 class="font-semibold text-primary mb-4 text-[1rem]">Bounce Rate</h6>
<span class="text-[1.5625rem] flex items-center">77.3% <span class=" text-[0.75rem] text-warning opacity-[0.7] ms-2">+0.59<i class="ti ti-arrow-big-up-line ms-1 inline-flex"></i></span></span>
</div>
</div>
</div>
<div id="analytics-bouncerate" class="mt-1 w-full">
<apx-chart [series]="chartOptions2.series" [chart]="chartOptions2.chart"
[xaxis]="chartOptions2.xaxis" [dataLabels]="chartOptions2.dataLabels"
[colors]="chartOptions2.colors" [fill]="chartOptions2.fill"
[markers]="chartOptions2.markers" [stroke]="chartOptions2.stroke"></apx-chart>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
Audience Report
</div>
<div>
<button type="button" class="ti-btn ti-btn-primary ti-btn-wave !font-medium"><i class="ri-share-forward-line me-1 align-middle inline-block"></i>Export</button>
</div>
</div>
<div class="box-body">
<div id="audienceReport">
<apx-chart [series]="chartOptions.series" [chart]="chartOptions.chart"
[yaxis]="chartOptions.yaxis" [xaxis]="chartOptions.xaxis"
[labels]="chartOptions.labels" [stroke]="chartOptions.stroke"
[plotOptions]="chartOptions.plotOptions" [markers]="chartOptions.markers"
[fill]="chartOptions.fill" [tooltip]="chartOptions.tooltip" [legend]="chartOptions.legend" [colors]="chartOptions.colors"></apx-chart>
</div>
</div>
</div>
</div>
<div class="xxl:col-span-6 xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
Top Countries Sessions vs Bounce Rate
</div>
<div class="hs-dropdown ti-dropdown">
<a href="javascript:void(0);" class="px-2 font-normal text-[0.75rem] text-[#8c9097] dark:text-white/50"
aria-expanded="false">
View All<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 !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Today</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">This Week</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Last Week</a></li>
</ul>
</div>
</div>
<div class="box-body">
<div id="country-sessions">
<apx-chart [series]="chartOptions3.series" [chart]="chartOptions3.chart"
[yaxis]="chartOptions3.yaxis" [xaxis]="chartOptions3.xaxis"
[labels]="chartOptions3.labels" [stroke]="chartOptions3.stroke"
[plotOptions]="chartOptions3.plotOptions" [markers]="chartOptions3.markers"
[fill]="chartOptions3.fill" [tooltip]="chartOptions3.tooltip" [legend]="chartOptions3.legend" [colors]="chartOptions3.colors"></apx-chart>
</div>
</div>
</div>
</div>
<div class="xxl:col-span-6 xl:col-span-12 col-span-12">
<div class="box overflow-hidden">
<div class="box-header justify-between">
<div class="box-title">
Traffic Sources
</div>
<div class="hs-dropdown ti-dropdown">
<a href="javascript:void(0);" class="px-2 font-normal text-[0.75rem] text-[#8c9097] dark:text-white/50"
aria-expanded="false">
View All<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 !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Today</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">This Week</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Last Week</a></li>
</ul>
</div>
</div>
<div class="box-body !p-0">
<div class="table-responsive">
<table class="table table-hover whitespace-nowrap min-w-full">
<thead>
<tr>
<th scope="col" class="text-start">Browser</th>
<th scope="col" class="text-start">Sessions</th>
<th scope="col" class="text-start">Traffic</th>
</tr>
</thead>
<tbody>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td>
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-google-fill text-[1.125rem] text-primary"></i>
</span>
<div class="font-semibold">Google</div>
</div>
</td>
<td>
<span><i class="ri-arrow-up-s-fill me-1 text-success align-middle text-[1.125rem]"></i>23,379</span>
</td>
<td>
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-[78%]" aria-valuenow="78" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td>
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-safari-line text-[1.125rem] text-secondary"></i>
</span>
<div class="font-semibold">Safari</div>
</div>
</td>
<td>
<span><i class="ri-arrow-up-s-fill me-1 text-success align-middle text-[1.125rem]"></i>78,973</span>
</td>
<td>
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-[32%]" aria-valuenow="32" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td>
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-opera-fill text-[1.125rem] text-success"></i>
</span>
<div class="font-semibold">Opera</div>
</div>
</td>
<td>
<span><i class="ri-arrow-down-s-fill me-1 text-danger align-middle text-[1.125rem]"></i>12,457</span>
</td>
<td>
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-[21%]" aria-valuenow="21" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td>
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-edge-fill text-[1.125rem] text-info"></i>
</span>
<div class="font-semibold">Edge</div>
</div>
</td>
<td>
<span><i class="ri-arrow-up-s-fill me-1 text-success align-middle text-[1.125rem]"></i>8,570</span>
</td>
<td>
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-1/4" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td>
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-firefox-fill text-[1.125rem] text-warning"></i>
</span>
<div class="font-semibold">Firefox</div>
</div>
</td>
<td>
<span><i class="ri-arrow-down-s-fill me-1 text-danger align-middle text-[1.125rem]"></i>6,135</span>
</td>
<td>
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-[35%]" aria-valuenow="35" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<tr class="border-t border-inherit border-solid hover:bg-gray-100 dark:hover:bg-light dark:border-defaultborder/10">
<td class="border-bottom-0">
<div class="flex items-center">
<span class="avatar avatar-rounded avatar-sm p-2 bg-light me-2">
<i class="ri-ubuntu-fill text-[1.125rem] text-danger"></i>
</span>
<div class="font-semibold">Ubuntu</div>
</div>
</td>
<td class="border-bottom-0">
<span><i class="ri-arrow-up-s-fill me-1 text-success align-middle text-[1.125rem]"></i>4,789</span>
</td>
<td class="border-bottom-0">
<div class="progress progress-xs">
<div class="progress-bar bg-primary w-[12%]" aria-valuenow="12" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="xl:col-span-5 col-span-12">
<div class="grid grid-cols-12 gap-x-6">
<div class="xxl:col-span-5 cxl:ol-span-12 col-span-12">
<div class="box custom-card upgrade-card text-white">
<div class="box-body text-white">
<span class="avatar avatar-xxl !border-0">
<img src="./assets/images/media/media-84.png" alt="">
</span>
<div class="upgrade-card-content">
<span class="opacity-[0.7] font-normal mb-1 !text-white">Plan is expiring !</span>
<span class="text-[0.9375rem] font-semibold block mb-[3rem] upgrade-text !text-white">Upgrade to premium</span>
<button type="button" class="ti-btn !py-1 !px-2 bg-light text-defaulttextcolor !text-[0.75rem] font-medium ti-btn-wave">Upgrade Now</button>
</div>
</div>
</div>
<div class="box">
<div class="box-body !p-1">
<div class="flex items-center flex-wrap">
<div id="analytics-followers">
<apx-chart [series]="optionsCircle2.series" [chart]="optionsCircle2.chart"
[xaxis]="optionsCircle2.xaxis" [colors]="optionsCircle2.colors"
[labels]="optionsCircle2.labels" [legend]="optionsCircle2.legend"
[stroke]="optionsCircle2.stroke" [tooltip]="optionsCircle2.tooltip"
[plotOptions]="optionsCircle2.plotOptions"
[dataLabels]="optionsCircle2.dataLabels"></apx-chart>
</div>
<div class="ms-1">
<p class="mb-1 text-[#8c9097] dark:text-white/50">Impressions</p>
<h5 class="font-semibold mb-0 text-[1.25rem]">9,903</h5>
</div>
</div>
</div>
</div>
<div class="box">
<div class="box-body !p-1">
<div class="flex items-center flex-wrap">
<div id="analytics-views">
<apx-chart [series]="optionsCircle3.series" [chart]="optionsCircle3.chart"
[xaxis]="optionsCircle3.xaxis" [colors]="optionsCircle3.colors"
[labels]="optionsCircle3.labels" [legend]="optionsCircle3.legend"
[stroke]="optionsCircle3.stroke" [tooltip]="optionsCircle3.tooltip"
[plotOptions]="optionsCircle3.plotOptions"
[dataLabels]="optionsCircle3.dataLabels"></apx-chart>
</div>
<div class="ms-1">
<p class="mb-1 text-[#8c9097] dark:text-white/50">Clicks</p>
<h5 class="font-semibold mb-0 text-[1.25rem]">16,789</h5>
</div>
</div>
</div>
</div>
</div>
<div class="xxl:col-span-7 xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
Sessions By Device
</div>
<div>
<button type="button" class="ti-btn ti-btn-primary 1 !text-[0.85rem] !m-0 !font-medium">View All</button>
</div>
</div>
<div class="box-body !my-2 !py-6 !px-2">
<div id="sessions">
<apx-chart [series]="optionsCircle1.series" [chart]="optionsCircle1.chart"
[xaxis]="optionsCircle1.xaxis"
[labels]="optionsCircle1.labels" [legend]="optionsCircle1.legend"
[stroke]="optionsCircle1.stroke" [tooltip]="optionsCircle1.tooltip"
[plotOptions]="optionsCircle1.plotOptions"
[dataLabels]="optionsCircle1.dataLabels"[colors]="optionsCircle1.colors"></apx-chart>
</div>
</div>
<div class="box-footer !p-0">
<div class="grid grid-cols-12 justify-center">
<div class="col-span-3 pe-0 text-center">
<div class="sm:p-4 p-2 ">
<span class="text-[#8c9097] dark:text-white/50 text-[0.6875rem]">Mobile</span>
<span class="block text-[1rem] font-semibold">68.3%</span>
</div>
</div>
<div class="col-span-3 px-0 text-center">
<div class="sm:p-4 p-2">
<span class="text-[#8c9097] dark:text-white/50 text-[0.6875rem]">Tablet</span>
<span class="block text-[1rem] font-semibold">17.68%</span>
</div>
</div>
<div class="col-span-3 px-0 text-center">
<div class="sm:p-4 p-2 ">
<span class="text-[#8c9097] dark:text-white/50 text-[0.6875rem]">Desktop</span>
<span class="block text-[1rem] font-semibold">10.5%</span>
</div>
</div>
<div class="col-span-3 px-0 text-center">
<div class="sm:p-4 p-2">
<span class="text-[#8c9097] dark:text-white/50 text-[0.6875rem]">Others</span>
<span class="block text-[1rem] font-semibold">5.16%</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">Sessions Duration By New Users</div>
<div class="hs-dropdown ti-dropdown">
<a href="javascript:void(0);" class="px-2 font-normal text-[0.75rem] text-[#8c9097] dark:text-white/50"
aria-expanded="false">
View All<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 !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Today</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">This Week</a></li>
<li><a class="ti-dropdown-item !py-2 !px-[0.9375rem] !text-[0.8125rem] !font-medium block"
href="javascript:void(0);">Last Week</a></li>
</ul>
</div>
</div>
<div class="box-body">
<div id="session-users">
<apx-chart [series]="chartOptions4.series" [chart]="chartOptions4.chart"
[yaxis]="chartOptions4.yaxis" [xaxis]="chartOptions4.xaxis"
[labels]="chartOptions4.labels" [stroke]="chartOptions4.stroke"
[plotOptions]="chartOptions4.plotOptions" [markers]="chartOptions4.markers"
[fill]="chartOptions4.fill" [tooltip]="chartOptions4.tooltip" [colors]="chartOptions4.colors" [legend]="chartOptions4.legend"></apx-chart>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
/* 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 { HomeCommonComponent } from './home-common.component';
describe('HomeCommonComponent', () => {
let component: HomeCommonComponent;
let fixture: ComponentFixture<HomeCommonComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HomeCommonComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeCommonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ChangeDetectorRef, Component, ElementRef, Renderer2 } from '@angular/core';
import { RouterModule } from '@angular/router';
import ApexCharts from 'apexcharts'
import { SharedModule } from '../../../shared/shared.module';
import { NgApexchartsModule } from 'ng-apexcharts';
@Component({
selector: 'app-home-common',
imports: [RouterModule,SharedModule,NgApexchartsModule],
standalone: true,
templateUrl: './home-common.component.html',
styleUrls: ['./home-common.component.css']
})
export class HomeCommonComponent {
chartOptions1:any;
chartOptions2:any;
chartOptions3:any
chartOptions:any;
optionsCircle1:any;
chartOptions4:any;
optionsCircle2:any;
optionsCircle3:any
constructor(private el: ElementRef, private renderer: Renderer2,private cdr: ChangeDetectorRef){
this.chartOptions1={
chart: {
type: 'line',
height: 40,
width: 120,
sparkline: {
enabled: true
},
dropShadow: {
enabled: true,
top: 0,
left: 0,
blur: 3,
color: '#000',
opacity: 0.1
},
},
grid: {
show: false,
xaxis: {
lines: {
show: false
}
},
yaxis: {
lines: {
show: false
}
},
},
stroke: {
show: true,
curve: 'straight',
lineCap: 'butt',
width: 1.5,
dashArray: 0,
},
fill: {
gradient: {
enabled: false
}
},
series: [{
name: 'Value',
data: [0, 21, 54, 38, 56, 24, 65]
}],
yaxis: {
min: 0,
show: false
},
xaxis: {
show: false,
axisTicks: {
show: false,
axisBorder: {
show: false
},
},
axisBorder: {
show: false
}
},
colors: ['#23b7e5'],
}
this.chartOptions2={
chart: {
type: 'line',
height: 45,
sparkline: {
enabled: true
},
dropShadow: {
enabled: true,
top: 0,
left: 0,
blur: 1,
color: '#fff',
opacity: 0.05
}
},
stroke: {
show: true,
curve: 'smooth',
lineCap: 'butt',
width: 2,
dashArray: 0,
},
fill: {
gradient: {
enabled: false
}
},
series: [{
name: 'Value',
data: [54, 38, 56, 35, 65, 43, 53, 45, 62, 80, 35, 48]
}],
yaxis: {
min: 0,
show: false,
axisBorder: {
show: false
},
},
xaxis: {
axisBorder: {
show: false
},
},
colors: ["rgba(132, 90, 223, 0.1)"],
tooltip: {
enabled: false,
}
}
this.optionsCircle1 = {
series: [1754, 1234, 878, 270],
labels: ["Mobile", "Tablet", "Desktop", "Others"],
chart: {
height: 250,
type: 'donut',
},
dataLabels: {
enabled: false,
},
legend: {
show: false,
},
stroke: {
show: true,
curve: 'smooth',
lineCap: 'round',
colors: ["#fff"],
width: 0,
dashArray: 0,
},
plotOptions: {
pie: {
expandOnClick: false,
donut: {
size: '80%',
background: 'transparent',
labels: {
show: true,
name: {
show: true,
fontSize: '20px',
color: '#495057',
offsetY: -4
},
value: {
show: true,
fontSize: '18px',
offsetY: 8,
formatter: function (val: string) {
return val + "%";
}
},
total: {
show: true,
showAlways: true,
label: 'Total',
fontSize: '22px',
fontWeight: 600,
color: '#495057',
}
}
},
},
},
colors: ["rgba(132, 90, 223, 1)", "rgba(35, 183, 229, 1)", "rgba(38, 191, 148, 1)", "rgba(245, 184, 73, 1)",],
};
this.chartOptions = {
series: [
{
name: 'Views',
type: 'column',
data: [23, 11, 22, 27, 13, 22, 37, 21, 44, 22, 45, 35],
},
{
name: 'Followers',
type: 'line',
data: [44, 55, 41, 67, 22, 43, 21, 41, 56, 27, 43, 27],
},
],
chart: {
toolbar: {
show: false,
},
type: 'line',
height: 250,
},
grid: {
borderColor: '#f1f1f1',
strokeDashArray: 3,
},
labels: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
],
dataLabels: {
enabled: false,
},
stroke: {
width: [1, 1.1],
curve: ['straight', 'smooth'],
},
legend: {
show: true,
position: 'top',
},
xaxis: {
axisBorder: {
color: '#e9e9e9',
},
},
plotOptions: {
bar: {
columnWidth: '20%',
borderRadius: 2,
},
},
colors: ["rgba(132, 90, 223, 1)", '#23b7e5'],
};
this.chartOptions3 = {
series: [
{
name: 'Session',
data: [24, 23, 20, 25, 27, 26, 24, 23, 23, 25, 23, 23],
type: 'line',
},
{
name: 'Bounce Rate',
data: [20, 23, 26, 22, 20, 26, 28, 26, 22, 27, 25, 26],
type: 'bar',
},
],
chart: {
height: 328,
zoom: {
enabled: false
},
},
dataLabels: {
enabled: false,
},
grid: {
borderColor: '#f1f1f1',
strokeDashArray: 3
},
legend: {
show: true,
position: 'top',
},
plotOptions: {
bar: {
borderRadius: 5,
columnWidth: "40%",
dataLabels: {
position: 'top', // top, center, bottom
},
}
},
colors: ["rgb(132, 90, 223)", "#ededed"],
stroke: {
curve: ['smooth', 'stepline'],
width: [2, 0],
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
axisBorder: {
color: '#e9e9e9',
},
}
};
this.chartOptions4 = {
series: [
{
name: 'New Users',
data: [32, 15, 63, 51, 36, 62, 99, 42, 78, 76, 32, 120],
},
{
name: 'Sessions',
data: [56, 58, 38, 50, 64, 45, 55, 32, 15, 63, 51, 136],
},
{
name: 'Avg Session Duration',
data: [48, 29, 50, 69, 20, 59, 52, 12, 48, 28, 17, 98],
},
],
chart: {
height: 425,
type: 'line',
toolbar: {
show: false,
},
background: 'none',
fill: '#fff',
},
grid: {
borderColor: '#f2f6f7',
},
colors: ['rgb(132, 90, 223)', '#23b7e5', '#f5b849'],
background: 'transparent',
dataLabels: {
enabled: false,
},
stroke: {
curve: 'straight',
width: 3,
},
legend: {
show: true,
position: 'top',
},
xaxis: {
categories: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
],
show: false,
axisBorder: {
show: false,
color: 'rgba(119, 119, 142, 0.05)',
offsetX: 0,
offsetY: 0,
},
axisTicks: {
show: false,
borderType: 'solid',
color: 'rgba(119, 119, 142, 0.05)',
width: 6,
offsetX: 0,
offsetY: 0,
},
labels: {
rotate: -90,
},
},
yaxis: {
show: false,
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
tooltip: {
x: {
format: 'dd/MM/yy HH:mm',
},
},
};
this.optionsCircle2={
chart: {
height: 120,
width: 100,
type: "radialBar",
},
series: [48],
colors: ["#23b7e5"],
plotOptions: {
radialBar: {
hollow: {
margin: 0,
size: "50%",
background: "#fff"
},
dataLabels: {
name: {
offsetY: -10,
color: "#4b9bfa",
fontSize: "10px",
show: false
},
value: {
offsetY: 5,
color: "#4b9bfa",
fontSize: "12px",
show: true,
fontWeight: 800
}
}
}
},
stroke: {
lineCap: "round"
},
labels: ["Followers"]
}
this.optionsCircle3={
chart: {
height: 120,
width: 100,
type: "radialBar",
},
series: [65],
colors: ["#ffc107"],
plotOptions: {
radialBar: {
hollow: {
margin: 0,
size: "50%",
background: "#fff"
},
dataLabels: {
name: {
offsetY: -10,
color: "#4b9bfa",
fontSize: "10px",
show: false
},
value: {
offsetY: 5,
color: "#4b9bfa",
fontSize: "12px",
show: true,
fontWeight: 800
},
}
}
},
stroke: {
lineCap: "round"
},
labels: ["Views"]
}
}
// ngAfterViewInit(): void {
// this.setChartWidth();
// this.cdr.detectChanges();
// }
// private setChartWidth(): void {
// const chartContainer = this.el.nativeElement.querySelector('.chart-container');
// const chartContainer1 = this.el.nativeElement.querySelector('.chart-container1');
// const chartContainer2 = this.el.nativeElement.querySelector('.chart-container2');
// const chartContainer3 = this.el.nativeElement.querySelector('.chart-container3');
// const chart = new ApexCharts(chartContainer1, this.chartOptions);
// const chart1 = new ApexCharts(chartContainer, this.optionsCircle1);
// const chart2 = new ApexCharts(chartContainer2, this.chartOptions3);
// const chart3 = new ApexCharts(chartContainer3, this.chartOptions4);
// chart.render();
// chart2.render();
// chart1.render();
// chart3.render();
// }
}
<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}}
</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)="openEditExcelDialog()"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาบริษัท"
aria-label=".form-control-sm example" [(ngModel)]="search" (ngModelChange)="onSearchChange()">
</div>
</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 text-white" style="background-color: #49b6f5;">
<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">รายละเอียด</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">การจัดการ</th>
</tr>
</thead>
<tbody>
<tr class="border border-defaultborder dark:border-defaultborder/10"
*ngFor="let data of paginatedCourses; let i = index">
<td>
<div>
<span class="block mb-1">
{{pageIndex * pageSize + i + 1}}
</span>
</div>
</td>
<td>
<div class="flex items-center">
<span class="p-3 me-1" style="width: 200px;">
<img src="{{data.getImage()}}" (click)="openEmployeeDialog(data.getImage())" id="profile-img"
class="border-radius-1 cursor-pointer" style="width: 180px; height: 120px; object-fit: cover;">
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thName }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thDesc }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0 cursor-pointer active: hover:text-danger" (click)="openLink(data.link1)"
style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0 cursor-pointer active hover:text-success" (click)="downloadFile(data.logId)"
style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<ng-container *ngIf="data.status === 0">
<span class="badge bg-amber-300 text-white">
รออนุมัติ </span>
</ng-container>
<ng-container *ngIf="data.status === 1">
<span class="badge bg-success text-white">
เปิดใช้งาน </span>
</ng-container>
<ng-container *ngIf="data.status === 2">
<span class="badge bg-gray-500 text-white">
ไม่อนุมัติ </span>
</ng-container>
</div>
</td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="openEditExcelDialog(data)"
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)="deleteFile(data)"
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>
<tr *ngIf="paginatedCourses.length === 0">
<td colspan="9" class="text-center py-4">ไม่พบข้อมูล</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto">
<div class="mb-2 sm:mb-0">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="goToPage(0)">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0 flex">
<li class="page-item" [class.disabled]="pageIndex === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex - 1)">
{{ 'Previous' | translate }}
</a>
</li>
<ng-container *ngFor="let p of pages">
<li class="page-item" [class.active]="p === pageIndex">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" [class.active]="p === pageIndex"
(click)="goToPage(p)">
{{ p + 1 }} </a>
</li>
</ng-container>
<li class="page-item" [class.disabled]="pageIndex === totalPages - 1 || totalPages === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex + 1)">
{{ 'Next' | translate }}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #editTemplateModal let-modal>
<div class="modal-headtitle ti-modal-header flex justify-between items-center p-4" style="background-color: #fefbfb;">
<h3 class="modal-title text-sm font-semibold text-defaulttextcolor" id="edittemplateLabel">
{{ checkEdit ? ('แก้ไขไฟล์หลักสูตร' | translate) : ('เพิ่มไฟล์หลักสูตร' | translate) }}
</h3>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeDialog()" #closeModal> <span class="sr-only">{{ 'Close' | translate }}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="w-full flex justify-end">
<div class="absolute flex">
<div class="px-1">
</div>
</div>
</div>
<mat-dialog-content style="padding: 0px; padding-left: 20px; padding-right: 20px;">
<div class="box p-9 top-4">
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-6 col-span-12">
<label for="nameth" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (ไทย)' | translate }}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameth" [(ngModel)]="modelCourse.thName" name="thName">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="nameeng" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (อังกฤษ)' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameeng" [(ngModel)]="modelCourse.engName" name="engName">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="thDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (ไทย)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="thDesc" [(ngModel)]="modelCourse.thDesc" rows="3" name="thDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="engDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (อังกฤษ)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="engDesc" [(ngModel)]="modelCourse.engDesc" rows="3" name="engDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="objective" class="block text-primary mb-2 font-bold font-14">{{ 'วัตถุประสงค์' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="objective" [(ngModel)]="modelCourse.objective" rows="2" name="objective"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="courseOutline" class="block text-primary mb-2 font-bold font-14">{{ 'ตัวอย่างบทเรียน' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseOutline" [(ngModel)]="modelCourse.courseOutline" rows="2" name="courseOutline"></textarea>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="courseType" class="block text-primary mb-2 font-bold font-14">{{ 'ประเภทหลักสูตร' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseType" [(ngModel)]="modelCourse.courseType" name="courseType">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="courseLevel" class="block text-primary mb-2 font-bold font-14">{{ 'ระดับผู้เรียน' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseLevel" [(ngModel)]="modelCourse.courseLevel" name="courseLevel">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="courseTime" class="block text-primary mb-2 font-bold font-14">{{ 'ระยะเวลาในการเรียนรู้' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseTime" [(ngModel)]="modelCourse.courseTime" name="courseTime">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="courseTrainer" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อผู้สอน' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseTrainer" [(ngModel)]="modelCourse.courseTrainer" name="courseTrainer">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="courseTrainerDetail" class="block text-primary mb-2 font-bold font-14">{{ 'ประวัติผู้สอน' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="courseTrainerDetail" [(ngModel)]="modelCourse.courseTrainerDetail" rows="3" name="courseTrainerDetail"></textarea>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="linkExample" class="block text-primary mb-2 font-bold font-14">{{ 'Link ตัวอย่าง' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="linkExample" [(ngModel)]="modelCourse.link1" name="link1">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="fileUpload" class="block text-primary mb-2 font-bold font-14">{{ 'ไฟล์อัพโหลด' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="fileUpload" (change)="onSelectFile($event)">
<button *ngIf="modelCourse.courseObj && checkEdit" (click)="downloadFile(modelCourse.logId)"
class="ti-btn ti-btn-primary flex-shrink-0 px-4 py-2 rounded-md transition-all duration-200">
<i class="fas fa-download mr-1"></i> {{ 'ดาวน์โหลด' | translate }}
</button>
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.xlsx *.xlsm
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="imageUpload" class="block text-primary mb-2 font-bold font-14">{{ 'รูปภาพตัวอย่าง' | translate
}}</label>
<div class="flex items-center gap-2"> <input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="imageUpload" (change)="onUploadImage($event)">
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.jpeg, *.jpg, *.png
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="tags" class="block text-primary mb-2 font-bold font-14">{{ 'แท็ก' | translate }}</label>
<tag-input [theme]="'bootstrap'" [(ngModel)]='modelCourse.tags' placeholder="{{ '+แท็ก' | translate }}"
[onlyFromAutocomplete]="true" secondaryPlaceholder="{{ 'กดปุ่ม Enter เพื่อเพิ่มแท็กใหม่' | translate }}"
[addOnBlur]="true" [clearOnBlur]="true" class="w-full" name="tags">
<tag-input-dropdown [autocompleteItems]="listTag" [showDropdownIfEmpty]="true">
</tag-input-dropdown>
</tag-input>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'กลุ่ม' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="group" [(ngModel)]="modelCourse.group.groupId" (change)="selectGroup()" name="group">
<option *ngFor="let item of listGroup" [ngValue]="item.groupId">{{item.thName}}</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="remark" class="block text-primary mb-2 font-bold font-14">{{ 'หมายเหตุ' | translate }}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="remark" [(ngModel)]="modelCourse.remark" rows="4" name="remark"></textarea>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions style="justify-content: end;">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle" (click)="closeDialog()">
{{ 'Cancel' | translate }}
</button>
<button type="submit" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!modelCourse.thName||!modelCourse.engName"
[disabled]="!modelCourse.thName||!modelCourse.engName" (click)="onSumit()">
{{ 'Save' | translate }}
</button>
</mat-dialog-actions>
</ng-template>
\ No newline at end of file
::ng-deep ng2-dropdown-menu {
z-index: 9999 !important;
.ng2-dropdown-menu {
z-index: 9999 !important;
ng2-menu-item {
z-index: 9999 !important;
}
}
}
\ No newline at end of file
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { OpenImageComponent } from '../open-image/open-image.component';
import { UploadService } from '../../../services/upload.service';
import { TagService } from '../../../services/tag.service';
import { TagModel } from '../../../models/tag.mmodel';
import { GroupModel } from '../../../models/group.mmodel';
import { GroupService } from '../../../services/group.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { CourseService } from '../../../services/course.service';
import { CourseModel } from '../../../models/course.model'; // Corrected model import
import saveAs from 'file-saver';
import { TagInputModule } from 'ngx-chips';
import swal from 'sweetalert';
@Component({
selector: 'app-list-course',
templateUrl: './list-course.component.html',
styleUrls: ['./list-course.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
TagInputModule,
],
})
export class ListCourseComponent implements OnInit {
@ViewChild("editTemplateModal") editTemplateModalRef!: TemplateRef<any>;
dialogRef: any;
page = 1; // This 'page' property is not directly used for slicing anymore, but can remain if other parts of your code rely on it.
pageSize = 10;
pageIndex: number = 0;
maxPagesToShow: number = 3;
originalListCourse: CourseModel[] = []; // Stores the complete, unfiltered list from the backend
listCourse: CourseModel[] = []; // Stores the list after applying search filter
paginatedCourses: CourseModel[] = []; // Stores the subset of courses for the current page
modelCourse: CourseModel = new CourseModel({});
checkEdit: boolean = false;
search: string = '';
listTag: TagModel[] = [];
listGroup: GroupModel[] = [];
constructor(
private modalService: NgbModal,
private courseService: CourseService,
private uploadService: UploadService,
private tagService: TagService,
private groupService: GroupService,
private dialog: MatDialog,
) {
}
async onUploadImage(event: any) {
try {
if (event.target.files.length > 0) {
let fileData = event.target.files[0];
const formData = new FormData();
formData.append('file', fileData);
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
if (!allowedTypes.includes(fileData.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.jpeg, *.jpg, *.png เท่านั้น');
} else {
const data = await this.uploadService.uploadImage(formData).toPromise();
if (data) {
this.modelCourse.thumbnail = data.body.fileId;
}
}
}
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('เกิดข้อผิดพลาดในการอัปโหลดรูปภาพ');
}
}
onSelectFile(event: any) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
const file: File = event.target.files[0];
reader.readAsDataURL(event.target.files[0]); // read file as data url
reader.onload = (event) => { // called once readAsDataURL is completed
if (event) {
// Changed allowed types from 'application/json' to Excel types based on your HTML comment
// Please double check the exact MIME types for .xlsx and .xlsm
const allowedTypes = [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
'application/vnd.ms-excel.addin.macroEnabled.12', // .xlsm (sometimes used, check if correct)
'application/vnd.ms-excel' // Older .xls
];
if (!allowedTypes.includes(file.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.xlsx, *.xlsm เท่านั้น');
} else {
let base64 = event.target!.result as string;
this.modelCourse.courseObj = base64.split(',')[1];
}
}
};
}
}
applyFilterAndPagination() {
this.listCourse = this.originalListCourse.filter(x =>
x.thName.toLowerCase().includes(this.search.toLowerCase()) ||
x.engName.toLowerCase().includes(this.search.toLowerCase())
);
const totalPagesAfterFilter = Math.ceil(this.listCourse.length / this.pageSize);
if (this.pageIndex >= totalPagesAfterFilter && totalPagesAfterFilter > 0) {
this.pageIndex = totalPagesAfterFilter - 1;
} else if (totalPagesAfterFilter === 0) {
this.pageIndex = 0;
}
const startIndex = this.pageIndex * this.pageSize;
const endIndex = startIndex + this.pageSize;
this.paginatedCourses = this.listCourse.slice(startIndex, endIndex);
}
async downloadFile(logId: string) {
try {
const data = await this.courseService.downloadFile(logId).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.json");
}
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถดาวน์โหลดไฟล์ได้');
}
}
deleteFile(item: CourseModel) {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการลบข้อมูลนี้หรือไม่",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willDelete: boolean) => {
if (willDelete) {
this.courseService.deleteCourse(item).subscribe(result => {
if (result) {
swal("ลบสำเร็จ!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getListCourse();
} else {
swal("สำเร็จบางส่วน/ข้อผิดพลาด!!", "มีการลบข้อมูลบางส่วนไม่สำเร็จ หรือมีข้อผิดพลาด", "warning");
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
openAlertModalWithMatDialog(message?: string) {
this.dialog.open(AlertModalComponent, {
data: { message: message ? message : "" },
width: '400px',
disableClose: true,
});
}
onUpdate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการอัพเดทข้อมูลหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willUpdate: boolean) => {
if (willUpdate) {
console.log(this.modelCourse);
this.courseService.createCourse(this.modelCourse).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "อัพเดทข้อมูลสำเร็จ", "success");
this.closeDialog();
this.getListCourse();
} else {
swal('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.courseService.createCourse(this.modelCourse).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสมาชิก", "success");
this.closeDialog();
this.getListCourse();
} else {
swal('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onSumit() {
if (this.checkEdit) {
this.onUpdate();
} else {
this.onCreate();
}
}
async getCourseById(targetModal: NgbModal, id: string) {
try {
const data = await this.courseService.getCourseById(id).toPromise();
this.modelCourse = new CourseModel(data!);
if (data) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
});
}
} catch (error) {
console.error('Error loading data:', error);
}
}
async getListCourse() {
try {
const data = await this.courseService.getListCourse().toPromise();
this.originalListCourse = data!.map(x => new CourseModel(x));
this.applyFilterAndPagination();
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการหลักสูตรได้');
}
}
get totalFilteredItems(): number {
return this.listCourse.length;
}
get totalPages(): number {
return Math.ceil(this.totalFilteredItems / this.pageSize);
}
get startItemIndex(): number {
if (this.totalFilteredItems === 0) return 0;
return this.pageIndex * this.pageSize + 1;
}
get endItemIndex(): number {
const end = Math.min((this.pageIndex + 1) * this.pageSize, this.totalFilteredItems);
return end;
}
goToPage(page: number) {
if (page >= 0 && page < this.totalPages) {
this.pageIndex = page;
this.applyFilterAndPagination();
} else if (this.totalPages === 0 && page === 0) {
this.pageIndex = 0;
this.applyFilterAndPagination();
}
}
onSearchChange() {
this.pageIndex = 0;
this.applyFilterAndPagination();
}
get pages(): number[] {
const pages: number[] = [];
const startPage = Math.max(0, this.pageIndex - Math.floor(this.maxPagesToShow / 2));
const endPage = Math.min(this.totalPages - 1, startPage + this.maxPagesToShow - 1);
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
}
ngOnInit() {
this.getListCourse();
this.getListTag();
this.getListGroup();
}
async getListGroup() {
try {
const data = await this.groupService.getList().toPromise();
this.listGroup = data!.map(x => new GroupModel(x));
} catch (error) {
console.error('Error loading groups:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการกลุ่มได้');
}
}
selectGroup() {
let findData = this.listGroup.find(x => x.groupId === this.modelCourse.group.groupId);
this.modelCourse.group = findData ? new GroupModel(findData) : new GroupModel({});
}
closeBtnClick() {
this.modalService.dismissAll();
}
async getListTag() {
try {
const data = await this.tagService.getList().toPromise();
this.listTag = data!.map(x => new TagModel(x));
} catch (error) {
console.error('Error loading tags:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการแท็กได้');
}
}
openEmployeeDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
openEditExcelDialog(item?: CourseModel) {
if (item) {
this.courseService.getCourseById(item.logId).toPromise().then(data => {
if (data) {
this.modelCourse = new CourseModel(data!);
this.checkEdit = true;
}
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '1100px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Course dialog closed with result:', result);
if (result === true) {
this.getListCourse();
}
});
}).catch(error => {
console.error('Error loading data for edit:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลเพื่อแก้ไขได้');
});
} else {
this.modelCourse = new CourseModel({});
this.checkEdit = false;
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '800px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Course dialog closed with result:', result);
if (result === true) {
this.getListCourse();
}
});
}
}
closeDialog() {
if (this.dialogRef) {
this.dialogRef.close();
}
}
openLink(url: string) {
window.open(url, "_blank");
}
}
\ No newline at end of file
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
<button class="btn btn-primary ml-auto" (click)="openModal(editTemplateModal)">เพิ่มไฟล์เอกสาร</button>
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of filterListDoc() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)" ></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId,'tha')"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId,'eng')"></i></td>
<td class="text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == '0'" ngbTooltip="รออนุมัติ" container="body" tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fas fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == '1'" ngbTooltip="เปิดใช้งาน" container="body" tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fas fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == '2'" ngbTooltip="ไม่อนุมัติ" container="body" tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fas fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<button type="button" [disabled]="data.status != 0" class="btn btn-circle btn-primary rounded-circle btn-sm mr-2" (click)="openModal(editTemplateModal,data)" placement="top" ngbTooltip="แก้ไข">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listDoc.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<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}}
</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)="openEditExcelDialog()"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาบริษัท"
aria-label=".form-control-sm example" [(ngModel)]="search" (ngModelChange)="onSearchChange()">
</div>
</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 text-white" style="background-color: #49b6f5;">
<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">รายละเอียด</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">สถานะ</th>
<th scope="col" class="text-start">การจัดการ</th>
</tr>
</thead>
<tbody>
<tr class="border border-defaultborder dark:border-defaultborder/10"
*ngFor="let data of paginatedDocs; let i = index">
<td>
<div>
<span class="block mb-1">
{{pageIndex * pageSize + i + 1}}
</span>
</div>
</td>
<td>
<div class="flex items-center">
<span class="p-3 me-1" style="width: 200px;">
<img src="{{data.getImage()}}" (click)="openEmployeeDialog(data.getImage())" id="profile-img"
class="border-radius-1 cursor-pointer" style="width: 180px; height: 120px; object-fit: cover;">
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thName }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thDesc }}
</span>
</div>
</td>
<td style="justify-items: center;">
<div>
<span class="block mb-1">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0 cursor-pointer active: hover:text-danger" (click)="openLink(data.link1)"
style="font-size: 20px;"></i>
</span>
</div>
</td>
<td style="justify-items: center;">
<div>
<span class="block mb-1">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0 cursor-pointer active hover:text-success"
(click)="downloadFile(data.logId,'tha')" style="font-size: 20px;"></i>
</span>
</div>
</td>
<td style="justify-items: center;">
<div>
<span class="block mb-1">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0 cursor-pointer active hover:text-success"
(click)="downloadFile(data.logId,'eng')" style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<ng-container *ngIf="data.status === 0">
<span class="badge bg-amber-300 text-white">
รออนุมัติ </span>
</ng-container>
<ng-container *ngIf="data.status === 1">
<span class="badge bg-success text-white">
เปิดใช้งาน </span>
</ng-container>
<ng-container *ngIf="data.status === 2">
<span class="badge bg-gray-500 text-white">
ไม่อนุมัติ </span>
</ng-container>
</div>
</td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="openEditExcelDialog(data)"
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)="deleteFile(data)"
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>
<tr *ngIf="paginatedDocs.length === 0">
<td colspan="9" class="text-center py-4">ไม่พบข้อมูล</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto">
<div class="mb-2 sm:mb-0">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="goToPage(0)">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0 flex">
<li class="page-item" [class.disabled]="pageIndex === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex - 1)">
{{ 'Previous' | translate }}
</a>
</li>
<ng-container *ngFor="let p of pages">
<li class="page-item" [class.active]="p === pageIndex">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" [class.active]="p === pageIndex"
(click)="goToPage(p)">
{{ p + 1 }} </a>
</li>
</ng-container>
<li class="page-item" [class.disabled]="pageIndex === totalPages - 1 || totalPages === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex + 1)">
{{ 'Next' | translate }}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #editTemplateModal let-modal>
<div class="modal-headtitle ti-modal-header flex justify-between items-center p-4" style="background-color: #fefbfb;">
<h3 class="modal-title text-sm font-semibold text-defaulttextcolor" id="edittemplateLabel">
{{ checkEdit ? ('แก้ไขไฟล์เอกสาร' | translate) : ('เพิ่มไฟล์เอกสาร' | translate) }}
</h3>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeDialog()" #closeModal> <span class="sr-only">{{ 'Close' | translate }}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="w-full flex justify-end">
<div class="absolute flex">
<div class="px-1">
</div>
</div>
</div>
<mat-dialog-content style="padding: 0px; padding-left: 20px; padding-right: 20px;">
<div class="box p-9 top-4">
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-6 col-span-12">
<label for="nameth" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (ไทย)' | translate }}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameth" [(ngModel)]="modelDoc.thName" name="thName">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="nameeng" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (อังกฤษ)' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameeng" [(ngModel)]="modelDoc.engName" name="engName">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="thDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (ไทย)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="thDesc" [(ngModel)]="modelDoc.thDesc" rows="3" name="thDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="engDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (อังกฤษ)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="engDesc" [(ngModel)]="modelDoc.engDesc" rows="3" name="engDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'ภาษาที่รองรับ' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="langSupport" [(ngModel)]="modelDoc.langSupport" name="langSupport">
<option value="THA">ไทย</option>
<option value="ENG">English</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="fileUpload" class="block text-primary mb-2 font-bold font-14">{{ 'ไฟล์อัพโหลด' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="fileUpload" (change)="onSelectFile($event,'th')">
<button *ngIf="modelDoc.thDocObj && checkEdit" (click)="downloadFile(modelDoc.logId,'tha')"
class="ti-btn ti-btn-primary flex-shrink-0 px-4 py-2 rounded-md transition-all duration-200">
<i class="fas fa-download mr-1"></i> {{ 'ดาวน์โหลด' | translate }}
</button>
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.xlsx *.xlsm
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="fileUpload" class="block text-primary mb-2 font-bold font-14">{{ 'ไฟล์อัพโหลด' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="fileUpload" (change)="onSelectFile($event,'eng')">
<button *ngIf="modelDoc.engDocObj && checkEdit" (click)="downloadFile(modelDoc.logId,'eng')"
class="ti-btn ti-btn-primary flex-shrink-0 px-4 py-2 rounded-md transition-all duration-200">
<i class="fas fa-download mr-1"></i> {{ 'ดาวน์โหลด' | translate }}
</button>
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.xlsx *.xlsm
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="imageUpload" class="block text-primary mb-2 font-bold font-14">{{ 'รูปภาพตัวอย่าง' | translate
}}</label>
<div class="flex items-center gap-2"> <input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="imageUpload" (change)="onUploadImage($event)">
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.jpeg, *.jpg, *.png
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="linkExample" class="block text-primary mb-2 font-bold font-14">{{ 'Link ตัวอย่าง' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="linkExample" [(ngModel)]="modelDoc.link1" name="link1">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="tags" class="block text-primary mb-2 font-bold font-14">{{ 'แท็ก' | translate }}</label>
<tag-input [theme]="'bootstrap'" [(ngModel)]='modelDoc.tags' placeholder="{{ '+แท็ก' | translate }}"
[onlyFromAutocomplete]="true" secondaryPlaceholder="{{ 'กดปุ่ม Enter เพื่อเพิ่มแท็กใหม่' | translate }}"
[addOnBlur]="true" [clearOnBlur]="true" class="w-full" name="tags">
<tag-input-dropdown [autocompleteItems]="listTag" [showDropdownIfEmpty]="true">
</tag-input-dropdown>
</tag-input>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'กลุ่ม' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="group" [(ngModel)]="modelDoc.group.groupId" (change)="selectGroup()" name="group">
<option *ngFor="let item of listGroup" [ngValue]="item.groupId">{{item.thName}}</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="remark" class="block text-primary mb-2 font-bold font-14">{{ 'หมายเหตุ' | translate }}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="remark" [(ngModel)]="modelDoc.remark" rows="4" name="remark"></textarea>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions style="justify-content: end;">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle" (click)="closeDialog()">
{{ 'Cancel' | translate }}
</button>
<button type="submit" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!modelDoc.thName||!modelDoc.engName"
[disabled]="!modelDoc.thName||!modelDoc.engName" (click)="onSumit()">
{{ 'Save' | translate }}
</button>
</mat-dialog-actions>
</ng-template>
<!-- <ng-template #editTemplateModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">{{checkEdit?"แก้ไขไฟล์เอกสาร":"เพิ่มไฟล์เอกสาร"}}</h5>
<button type="button" class="close" (click)="closeBtnClick()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="nameth" class="col-sm-4 col-form-label">ชื่อ (ไทย)</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="nameth" [(ngModel)]="modelDoc.thName">
</div>
</div>
<div class="form-group row">
<label for="nameeng" class="col-sm-4 col-form-label">ชื่อ (อังกฤษ)</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="nameeng" [(ngModel)]="modelDoc.engName">
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">รายละเอียด (ไทย)</label>
<div class="col-sm-8">
<textarea class="form-control" [(ngModel)]="modelDoc.thDesc"></textarea>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">รายละเอียด (อังกฤษ)</label>
<div class="col-sm-8">
<textarea class="form-control" [(ngModel)]="modelDoc.engDesc"></textarea>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">ภาษาที่รองรับ</label>
<div class="col-sm-8">
<select class="custom-select" [(ngModel)]="modelDoc.langSupport">
<option value="THA">ไทย</option>
<option value="ENG">English</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">ไฟล์อัพโหลด (ไทย)</label>
<div class="{{!modelDoc.thDocObj||!checkEdit ? 'col-sm-8' : 'col-sm-6'}}">
<input type="file" class="form-control" (change)="onSelectFile($event,'th')">
<div class="msg-detail">ไฟล์ที่อนุญาต:*.doc</div>
</div>
<div class="col-auto" *ngIf="modelDoc.thDocObj&&checkEdit">
<button class="btn btn-primary" (click)="downloadFile(modelDoc.logId,'tha')"><i class="fas fa-download" ></i></button>
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">ไฟล์อัพโหลด (อังกฤษ)</label>
<div class="{{!modelDoc.engDocObj||!checkEdit ? 'col-sm-8' : 'col-sm-6'}}">
<input type="file" class="form-control" (change)="onSelectFile($event,'eng')">
<div class="msg-detail">ไฟล์ที่อนุญาต:*.doc</div>
</div>
<div class="col-auto" *ngIf="modelDoc.engDocObj&&checkEdit">
<button class="btn btn-primary" (click)="downloadFile(modelDoc.logId,'eng')"><i class="fas fa-download" ></i></button>
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">รูปภาพตัวอย่าง</label>
<div class="col-sm-8">
<input type="file" class="form-control" (change)="onUploadImage($event)">
<div class="msg-detail">ไฟล์ที่อนุญาต:*.jpeg, *.jpg, *.png</div>
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">Link ตัวอย่าง</label>
<div class="col-sm-8">
<input type="text" class="form-control" id="nameeng" [(ngModel)]="modelDoc.link1">
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">แท็ก</label>
<div class="col-sm-8">
<tag-input [theme]="'bootstrap'" [(ngModel)]='modelDoc.tags' placeholder="+แท็ก" [onlyFromAutocomplete]="true"
secondaryPlaceholder="กดปุ่ม Enter เพื่อเพิ่มแท็กใหม่" [addOnBlur]="true" [clearOnBlur]="true">
<tag-input-dropdown [autocompleteItems]="listTag" [showDropdownIfEmpty]="true">
</tag-input-dropdown>
</tag-input>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">กลุ่ม</label>
<div class="col-sm-8">
<select class="custom-select" [(ngModel)]="modelDoc.group.groupId" (change)="selectGroup()">
<option *ngFor="let item of listGroup" [ngValue]="item.groupId">{{item.thName}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">หมายเหตุ</label>
<div class="col-sm-8">
<textarea class="form-control"[(ngModel)]="modelDoc.remark"></textarea>
</div>
</div>
<div class="modal-footer ">
<button type="submit" class="btn btn-info" (click)="onSumit()" [disabled]="!modelDoc.thName||!modelDoc.engName">บันทึก</button>
<button type="button" class="btn btn-danger" (click)="closeBtnClick()">ปิด</button>
</div>
</div>
</ng-template> -->
\ No newline at end of file
::ng-deep ng2-dropdown-menu {
z-index: 9999 !important;
.ng2-dropdown-menu {
z-index: 9999 !important;
ng2-menu-item {
z-index: 9999 !important;
}
}
}
\ No newline at end of file
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component'; // ตรวจสอบว่ามี ConfirmModalComponent นี้อยู่จริง
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { OpenImageComponent } from '../open-image/open-image.component'; // ตรวจสอบว่ามี OpenImageComponent นี้อยู่จริง
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { TagInputModule } from 'ngx-chips';
import { UploadService } from '../../../services/upload.service';
import { TagService } from '../../../services/tag.service';
import { TagModel } from '../../../models/tag.mmodel';
import { GroupModel } from '../../../models/group.mmodel';
import { GroupService } from '../../../services/group.service';
import { DocumentModel } from '../../../models/document.model';
import { DocumentService } from '../../../services/document.service';
import saveAs from 'file-saver';
import swal from 'sweetalert';
@Component({
selector: 'app-list-doc',
templateUrl: './list-doc.component.html',
styleUrls: ['./list-doc.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
TagInputModule,
],
})
export class ListDocComponent implements OnInit {
@ViewChild("editTemplateModal") editTemplateModalRef!: TemplateRef<any>;
dialogRef: any;
page = 1; // อาจจะไม่จำเป็นต้องใช้โดยตรงแล้ว แต่ยังคงไว้หากมีส่วนอื่นใช้
pageSize = 10;
pageIndex: number = 0;
maxPagesToShow: number = 3;
originalListDoc: DocumentModel[] = []; // เก็บข้อมูลเอกสารทั้งหมดที่โหลดมาจาก API
listDoc: DocumentModel[] = []; // เก็บข้อมูลเอกสารที่ถูกกรองด้วย search term แล้ว
paginatedDocs: DocumentModel[] = []; // เก็บข้อมูลเอกสารสำหรับหน้าปัจจุบันที่แสดงในตาราง
modelDoc: DocumentModel = new DocumentModel({});
checkEdit: boolean = false;
search: string = '';
listTag: TagModel[] = [];
listGroup: GroupModel[] = [];
constructor(
private modalService: NgbModal, // ยังคง NgbModal ไว้เผื่อมีการใช้งานอื่น ๆ ที่ไม่ได้เปลี่ยน
private documentService: DocumentService,
private uploadService: UploadService,
private tagService: TagService,
private groupService: GroupService,
private dialog: MatDialog, // MatDialog สำหรับ Material Dialogs
) {}
async onUploadImage(event: any) {
try {
if (event.target.files.length > 0) {
let fileData = event.target.files[0];
const formData = new FormData();
formData.append('file', fileData);
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
if (!allowedTypes.includes(fileData.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.jpeg, *.jpg, *.png เท่านั้น');
} else {
const data = await this.uploadService.uploadImage(formData).toPromise();
if (data) {
this.modelDoc.thumbnail = data.body.fileId;
}
}
}
} catch (error) {
console.error('Error loading data:', error);
}
}
onSelectFile(event: any, lang: string) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
const file: File = event.target.files[0];
reader.readAsDataURL(event.target.files[0]); // read file as data url
reader.onload = (event) => { // called once readAsDataURL is completed
if (event) {
const allowedTypes = ['application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/msword'];
if (!allowedTypes.includes(file.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.doc เท่านั้น');
} else {
let base64 = event.target!.result as string;
if (lang === 'th') {
this.modelDoc.thDocObj = base64.split(',')[1];
} else {
this.modelDoc.engDocObj = base64.split(',')[1];
}
}
}
};
}
}
// เมธอดหลักสำหรับกรองข้อมูลและแบ่งหน้า
applyFilterAndPagination() {
// 1. กรองข้อมูลจาก originalListDoc ด้วย search term
this.listDoc = this.originalListDoc.filter(x =>
x.thName.toLowerCase().includes(this.search.toLowerCase()) ||
x.engName.toLowerCase().includes(this.search.toLowerCase())
);
// 2. ปรับ pageIndex หากหน้าปัจจุบันอยู่นอกช่วงข้อมูลที่ถูกกรองแล้ว
const totalPagesAfterFilter = Math.ceil(this.listDoc.length / this.pageSize);
if (this.pageIndex >= totalPagesAfterFilter && totalPagesAfterFilter > 0) {
this.pageIndex = totalPagesAfterFilter - 1; // ไปยังหน้าสุดท้ายที่มีข้อมูล
} else if (totalPagesAfterFilter === 0) { // ถ้าไม่มีข้อมูลเลย ให้เป็นหน้าแรก
this.pageIndex = 0;
}
// 3. แบ่งข้อมูลตาม pageIndex และ pageSize เพื่อแสดงบนหน้าปัจจุบัน
const startIndex = this.pageIndex * this.pageSize;
const endIndex = startIndex + this.pageSize;
this.paginatedDocs = this.listDoc.slice(startIndex, endIndex);
}
async downloadFile(logId: string, lang: string) {
try {
const data = await this.documentService.downloadFile(logId, lang).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.doc");
}
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถดาวน์โหลดไฟล์ได้');
}
}
deleteFile(item: DocumentModel) {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการลบข้อมูลนี้หรือไม่",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willDelete: boolean) => {
if (willDelete) {
this.documentService.deleteExcel(item).subscribe(result => {
if (result) {
swal("ลบสำเร็จ!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
swal("สำเร็จบางส่วน/ข้อผิดพลาด!!", "มีการลบข้อมูลบางส่วนไม่สำเร็จ หรือมีข้อผิดพลาด", "warning");
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
openAlertModalWithMatDialog(message?: string) {
this.dialog.open(AlertModalComponent, {
data: { message: message ? message : "" },
width: '400px',
disableClose: true,
});
}
onUpdate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการอัพเดทข้อมูลหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willUpdate: boolean) => {
if (willUpdate) {
console.log(this.modelDoc);
this.documentService.createDoc(this.modelDoc).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "อัพเดทข้อมูลสำเร็จ", "success");
this.closeDialog(); // ปิด MatDialog
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
swal('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.documentService.createDoc(this.modelDoc).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสมาชิก", "success");
this.closeDialog(); // ปิด MatDialog
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
swal('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onSumit() {
if (this.checkEdit) {
this.onUpdate();
} else {
this.onCreate();
}
}
// Method นี้ไม่ได้ถูกใช้โดยตรงใน openEditExcelDialog แล้ว แต่ยังคงไว้เผื่อการใช้งานอื่น
async getExcelById(targetModal: NgbModal, id: string) {
try {
const data = await this.documentService.getDocById(id).toPromise();
this.modelDoc = new DocumentModel(data!);
if (data) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
});
}
} catch (error) {
console.error('Error loading data:', error);
}
}
// openModal นี้เป็นการใช้ NgbModal ซึ่งอาจจะไม่ได้ใช้แล้วหากเปลี่ยนไปใช้ MatDialog ทั้งหมด
openModal(targetModal: NgbModal, item?: DocumentModel) {
if (item) {
this.getExcelById(targetModal, item.logId); // อันนี้ยังเรียก NgbModal อยู่
this.checkEdit = true;
} else {
this.modelDoc = new DocumentModel({});
this.checkEdit = false;
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
});
}
}
getStatus(status: string): string {
if (status === '0') {
return 'รออนุมัติ';
} else if (status === '1') {
return 'เปิดใช้งาน';
} else if (status === '2') {
return 'ไม่อนุมัติ';
} else {
return 'ไม่ทราบสถานะ'; // ควรมีค่า default หรือจัดการ error ที่นี่
}
}
async getListDoc() {
try {
const data = await this.documentService.getListDoc().toPromise();
this.originalListDoc = data!.map(x => new DocumentModel(x)); // เก็บข้อมูลทั้งหมดที่นี่
this.applyFilterAndPagination(); // หลังจากได้ข้อมูลแล้ว ค่อยนำมา filter และ pagination
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลเอกสารได้');
}
}
// Getters สำหรับ Pagination (ใช้ this.listDoc.length ซึ่งเป็นข้อมูลที่ถูกกรองแล้ว)
get totalFilteredItems(): number {
return this.listDoc.length;
}
get totalPages(): number {
return Math.ceil(this.totalFilteredItems / this.pageSize);
}
get startItemIndex(): number {
if (this.totalFilteredItems === 0) return 0;
return this.pageIndex * this.pageSize + 1;
}
get endItemIndex(): number {
const end = Math.min((this.pageIndex + 1) * this.pageSize, this.totalFilteredItems);
return end;
}
// เมธอดสำหรับเปลี่ยนหน้า
goToPage(page: number) {
if (page >= 0 && page < this.totalPages) {
this.pageIndex = page;
this.applyFilterAndPagination(); // เมื่อเปลี่ยนหน้า ให้เรียกกรองและแบ่งหน้าใหม่
} else if (this.totalPages === 0 && page === 0) {
// กรณีไม่มีข้อมูลเลย ให้ pageIndex เป็น 0 และเรียก applyFilterAndPagination()
this.pageIndex = 0;
this.applyFilterAndPagination();
}
}
// เมธอดที่เรียกเมื่อค่า search เปลี่ยน
onSearchChange() {
this.pageIndex = 0; // รีเซ็ตไปหน้าแรกเมื่อค้นหาใหม่
this.applyFilterAndPagination(); // เรียกกรองและแบ่งหน้าใหม่
}
// สำหรับแสดงเลขหน้าใน pagination
get pages(): number[] {
const pages: number[] = [];
const startPage = Math.max(0, this.pageIndex - Math.floor(this.maxPagesToShow / 2));
const endPage = Math.min(this.totalPages - 1, startPage + this.maxPagesToShow - 1);
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
}
ngOnInit() {
this.getListDoc(); // โหลดข้อมูลเอกสารครั้งแรก
this.getListTag();
this.getListGroup();
}
async getListGroup() {
try {
const data = await this.groupService.getList().toPromise();
this.listGroup = data!.map(x => new GroupModel(x));
} catch (error) {
console.error('Error loading groups:', error);
}
}
selectGroup() {
let findData = this.listGroup.find(x => x.groupId === this.modelDoc.group.groupId);
this.modelDoc.group = findData ? new GroupModel(findData) : new GroupModel({});
}
async getListTag() {
try {
const data = await this.tagService.getList().toPromise();
this.listTag = data!.map(x => new TagModel(x));
} catch (error) {
console.error('Error loading tags:', error);
}
}
openEmployeeDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
openEditExcelDialog(item?: DocumentModel) {
if (item) {
this.documentService.getDocById(item.logId).toPromise().then(data => {
if (data) {
this.modelDoc = new DocumentModel(data!);
this.checkEdit = true;
}
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '1100px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Excel dialog closed with result:', result);
if (result === true) {
this.getListDoc(); // รีเฟรชรายการเฉพาะเมื่อสำเร็จ
}
});
}).catch(error => {
console.error('Error loading data for edit:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลเพื่อแก้ไขได้');
});
} else {
this.modelDoc = new DocumentModel({});
this.checkEdit = false;
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '800px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Excel dialog closed with result:', result);
if (result === true) {
this.getListDoc(); // รีเฟรชรายการเฉพาะเมื่อสำเร็จ
}
});
}
}
closeDialog() {
if (this.dialogRef) {
this.dialogRef.close();
}
}
// closeBtnClick นี้เป็นการใช้ NgbModal.dismissAll() ซึ่งอาจจะไม่ได้ใช้แล้วหากเปลี่ยนไปใช้ MatDialog ทั้งหมด
closeBtnClick() {
this.modalService.dismissAll();
}
openLink(url: string) {
window.open(url, "_blank");
}
}
\ No newline at end of file
<app-page-header [title]="'รายการเอกสาร Excel'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการอัพโหลดเอกสาร Excel'"></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}}
</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 hover:scale-105 transition duration-300"
(click)="openEditExcelDialog()"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาบริษัท"
aria-label=".form-control-sm example" [(ngModel)]="search" (ngModelChange)="onSearchChange()">
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive" *ngIf="checkType !== '1'">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr class="border-b border-defaultborder text-white" style="background-color: #49b6f5;">
<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">รายละเอียด</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">การจัดการ</th>
</tr>
</thead>
<tbody>
<tr class="border border-defaultborder dark:border-defaultborder/10"
*ngFor="let data of paginatedExcels; let i = index">
<td>
<div>
<span class="block mb-1">
{{pageIndex * pageSize + i + 1}}
</span>
</div>
</td>
<td>
<div class="flex items-center">
<span class="p-3 me-1" style="width: 200px;">
<img src="{{data.getImage()}}" (click)="openEmployeeDialog(data.getImage())" id="profile-img"
class="border-radius-1 cursor-pointer" style="width: 180px; height: 120px; object-fit: cover;">
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thName }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thDesc }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0 cursor-pointer active: hover:text-danger hover:scale-125 transition duration-300" (click)="openLink(data.link1)"
style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0 cursor-pointer active hover:text-success hover:scale-125 transition duration-300"
(click)="downloadFile(data.logId)" style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<ng-container *ngIf="data.status === 0">
<span class="badge bg-amber-300 text-white">
รออนุมัติ </span>
</ng-container>
<ng-container *ngIf="data.status === 1">
<span class="badge bg-success text-white">
เปิดใช้งาน </span>
</ng-container>
<ng-container *ngIf="data.status === 2">
<span class="badge bg-gray-500 text-white">
ไม่อนุมัติ </span>
</ng-container>
</div>
</td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="openEditExcelDialog(data)"
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)="deleteFile(data)"
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>
<tr *ngIf="paginatedExcels.length === 0">
<td colspan="9" class="text-center py-4">ไม่พบข้อมูล</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto">
<div class="mb-2 sm:mb-0">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="goToPage(0)">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0 flex">
<li class="page-item" [class.disabled]="pageIndex === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex - 1)">
{{ 'Previous' | translate }}
</a>
</li>
<ng-container *ngFor="let p of pages">
<li class="page-item" [class.active]="p === pageIndex">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" [class.active]="p === pageIndex"
(click)="goToPage(p)">
{{ p + 1 }} </a>
</li>
</ng-container>
<li class="page-item" [class.disabled]="pageIndex === totalPages - 1 || totalPages === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex + 1)">
{{ 'Next' | translate }}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box p-4" *ngIf="checkType === '1'">
<div class="flex flex-wrap -mx-2">
<div class="w-full">
<div class="py-3 px-2">
<input type="text"
class="block w-full md:w-1/4 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 shadow-sm"
placeholder="ค้นหา" [(ngModel)]="search" (ngModelChange)="onSearchChange()" />
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/3 xl:w-1/3 px-2 mb-4" *ngFor="let data of paginatedExcels; let i = index">
<div
class="group bg-white rounded-lg overflow-hidden shadow-lg border-4 border-blue-600 h-full flex flex-col transform transition-all duration-300 ease-in-out hover:scale-[1.02] hover:shadow-xl">
<div class="p-4 flex justify-center items-center flex-shrink-0">
<img
class="w-full h-full object-cover rounded-md shadow-md transform transition-transform duration-300 group-hover:scale-105 cursor-pointer"
style="width: 400px; height: 200px; max-width: 100%;" src="{{ data.getImage() }}" alt="{{ data.thName }}"
(click)="openEmployeeDialog(data.getImage())" />
</div>
<div class="p-4 flex-grow">
<span class="text-xl font-semibold text-gray-800 mb-2" style="font-size: 18px;">
{{ data.thName }}
</span>
<p class="text-gray-700 text-sm mb-3">{{ data.thDesc }}</p>
<div class="mb-0 flex items-center justify-center sm:justify-start gap-2 w-1/2 mt-5">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ตัวอย่างวิธีใช้งาน'" (click)="openLink(data.link1)"
style="background-color: rgb(76, 117, 207, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-1/2">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด'" (click)="downloadFile(data.logId)"
style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
</div>
<div class="px-4 py-3 border-t border-gray-200 text-right flex-shrink-0">
<small class="text-gray-500 text-xs">วันที่อัพโหลด {{ coverDate(data.uploadDate) }}
{{ data.uploadTime }}</small>
</div>
</div>
</div>
<div class="w-full text-center py-4" *ngIf="paginatedExcels.length === 0">ไม่พบข้อมูล</div>
</div>
</div>
<ng-template #editTemplateModal let-modal>
<div class="modal-headtitle ti-modal-header flex justify-between items-center p-4" style="background-color: #fefbfb;">
<h3 class="modal-title text-sm font-semibold text-defaulttextcolor" id="edittemplateLabel">
{{ checkEdit ? ('แก้ไขไฟล์ Excel' | translate) : ('เพิ่มไฟล์ Excel' | translate) }}
</h3>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeDialog()" #closeModal> <span class="sr-only">{{ 'Close' | translate }}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="w-full flex justify-end">
<div class="absolute flex">
<div class="px-1">
</div>
</div>
</div>
<mat-dialog-content style="padding: 0px; padding-left: 20px; padding-right: 20px;">
<div class="box p-9 top-4">
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-6 col-span-12">
<label for="nameth" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (ไทย)' | translate }}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameth" [(ngModel)]="modelExcel.thName" name="thName">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="nameeng" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อ (อังกฤษ)' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameeng" [(ngModel)]="modelExcel.engName" name="engName">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="thDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (ไทย)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="thDesc" [(ngModel)]="modelExcel.thDesc" rows="3" name="thDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="engDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (อังกฤษ)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="engDesc" [(ngModel)]="modelExcel.engDesc" rows="3" name="engDesc"></textarea>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="dbSupport" class="block text-primary mb-2 font-bold font-14">{{ 'ดาต้าเบสที่รองรับ' | translate
}}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="dbSupport" [(ngModel)]="modelExcel.dbSupport" name="dbSupport">
<option value="SQLServer">SQLServer</option>
<option value="PostgreSQL">PostgreSQL</option>
<option value="Oracle">Oracle</option>
</select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="linkExample" class="block text-primary mb-2 font-bold font-14">{{ 'Link ตัวอย่าง' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="linkExample" [(ngModel)]="modelExcel.link1" name="link1">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="fileUpload" class="block text-primary mb-2 font-bold font-14">{{ 'ไฟล์อัพโหลด' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="fileUpload" (change)="onSelectFile($event)">
<button *ngIf="modelExcel.excelObj && checkEdit" (click)="downloadFile(modelExcel.logId)"
class="ti-btn ti-btn-primary flex-shrink-0 px-4 py-2 rounded-md transition-all duration-200">
<i class="fas fa-download mr-1"></i> {{ 'ดาวน์โหลด' | translate }}
</button>
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.xlsx *.xlsm
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="imageUpload" class="block text-primary mb-2 font-bold font-14">{{ 'รูปภาพตัวอย่าง' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="imageUpload" (change)="onUploadImage($event)">
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.jpeg, *.jpg, *.png
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="tags" class="block text-primary mb-2 font-bold font-14">{{ 'แท็ก' | translate }}</label>
<tag-input [theme]="'bootstrap'" [(ngModel)]='modelExcel.tags' placeholder="{{ '+แท็ก' | translate }}"
[onlyFromAutocomplete]="true" secondaryPlaceholder="{{ 'กดปุ่ม Enter เพื่อเพิ่มแท็กใหม่' | translate }}"
[addOnBlur]="true" [clearOnBlur]="true" class="w-full" name="tags">
<tag-input-dropdown [autocompleteItems]="listTag" [showDropdownIfEmpty]="true">
</tag-input-dropdown>
</tag-input>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'กลุ่ม' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="group" [(ngModel)]="modelExcel.group.groupId" (change)="selectGroup()" name="group">
<option *ngFor="let item of listGroup" [ngValue]="item.groupId">{{item.thName}}</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="block text-primary mb-2 font-bold font-14">{{ 'แสดงรูปแบบ Pivot' | translate }}</label>
<div class="flex items-center gap-4 mt-2">
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="pivot0" name="radio-pivot" [value]="0" [(ngModel)]="modelExcel.isPivot">
<label class="ml-2 text-gray-700" for="pivot0">{{ 'ใช่' | translate }}</label>
</div>
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="pivot1" name="radio-pivot" [value]="1" [(ngModel)]="modelExcel.isPivot">
<label class="ml-2 text-gray-700" for="pivot1">{{ 'ไม่ใช่' | translate }}</label>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="block text-primary mb-2 font-bold font-14">{{ 'แสดงรูปแบบ DataGrid' | translate }}</label>
<div class="flex items-center gap-4 mt-2">
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="datagrid0" name="datagridCheck" [value]="0" [(ngModel)]="modelExcel.isDataGrid">
<label class="ml-2 text-gray-700" for="datagrid0">{{ 'ใช่' | translate }}</label>
</div>
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="datagrid1" name="datagridCheck" [value]="1" [(ngModel)]="modelExcel.isDataGrid">
<label class="ml-2 text-gray-700" for="datagrid1">{{ 'ไม่ใช่' | translate }}</label>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="remark" class="block text-primary mb-2 font-bold font-14">{{ 'หมายเหตุ' | translate }}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="remark" [(ngModel)]="modelExcel.remark" rows="4" name="remark"></textarea>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions style="justify-content: end;">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle" (click)="closeDialog()">
{{ 'Cancel' | translate }}
</button>
<button type="submit" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!modelExcel.thName||!modelExcel.engName"
[disabled]="!modelExcel.thName||!modelExcel.engName" (click)="onSumit()">
{{ 'Save' | translate }}
</button>
</mat-dialog-actions>
</ng-template>
\ No newline at end of file
::ng-deep ng2-dropdown-menu {
z-index: 9999 !important;
.ng2-dropdown-menu {
z-index: 9999 !important;
ng2-menu-item {
z-index: 9999 !important;
}
}
}
\ No newline at end of file
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ExcelModel } from '../../../models/excel.model';
import { ExcelService } from '../../../services/excel.service';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { UploadService } from '../../../services/upload.service';
import { TagService } from '../../../services/tag.service';
import { TagModel } from '../../../models/tag.mmodel';
import { GroupModel } from '../../../models/group.mmodel';
import { OpenImageComponent } from '../open-image/open-image.component';
import { GroupService } from '../../../services/group.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import saveAs from 'file-saver';
import { TagInputModule } from 'ngx-chips';
import swal from 'sweetalert';
@Component({
selector: 'app-list-excel',
templateUrl: './list-excell.component.html', // Note: original was list-excell.component.html
styleUrls: ['./list-excell.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
TagInputModule,
],
})
export class ListExcelComponent implements OnInit {
@ViewChild("editTemplateModal") editTemplateModalRef!: TemplateRef<any>;
@ViewChild("listexcel") listexcel: any;
page = 1;
pageSize = 10;
pageIndex: number = 0;
maxPagesToShow: number = 3;
originalListExcel: ExcelModel[] = [];
listExcel: ExcelModel[] = [];
paginatedExcels: ExcelModel[] = [];
modelExcel: ExcelModel = new ExcelModel({});
checkEdit: boolean = false;
search: string = '';
checkType: string = '';
dialogRef: any;
listTag: TagModel[] = [];
listGroup: GroupModel[] = [];
constructor(
private excelService: ExcelService,
private uploadService: UploadService,
private tagService: TagService,
private groupService: GroupService,
private dialog: MatDialog,
) {
}
applyFilterAndPagination() {
this.listExcel = this.originalListExcel.filter(x =>
x.thName.toLowerCase().includes(this.search.toLowerCase()) ||
x.engName.toLowerCase().includes(this.search.toLowerCase())
);
const totalPagesAfterFilter = Math.ceil(this.listExcel.length / this.pageSize);
if (this.pageIndex >= totalPagesAfterFilter && totalPagesAfterFilter > 0) {
this.pageIndex = totalPagesAfterFilter - 1;
} else if (totalPagesAfterFilter === 0) {
this.pageIndex = 0;
}
const startIndex = this.pageIndex * this.pageSize;
const endIndex = startIndex + this.pageSize;
this.paginatedExcels = this.listExcel.slice(startIndex, endIndex);
}
async onUploadImage(event: any) {
try {
if (event.target.files.length > 0) {
let fileData = event.target.files[0];
const formData = new FormData();
formData.append('file', fileData);
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
if (!allowedTypes.includes(fileData.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.jpeg, *.jpg, *.png เท่านั้น'); // Using MatDialog alert
} else {
const data = await this.uploadService.uploadImage(formData).toPromise();
if (data) {
this.modelExcel.thumbnail = data.body.fileId;
}
}
}
} catch (error) {
console.error('Error uploading image:', error);
this.openAlertModalWithMatDialog('เกิดข้อผิดพลาดในการอัปโหลดรูปภาพ');
}
}
async getListTag() {
try {
const data = await this.tagService.getList().toPromise();
this.listTag = data!.map(x => new TagModel(x));
} catch (error) {
console.error('Error loading tags:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการแท็กได้');
}
}
async getListGroup() {
try {
const data = await this.groupService.getList().toPromise();
this.listGroup = data!.map(x => new GroupModel(x));
} catch (error) {
console.error('Error loading groups:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการกลุ่มได้');
}
}
selectGroup() {
let findData = this.listGroup.find(x => x.groupId == this.modelExcel.group.groupId);
this.modelExcel.group = findData ? new GroupModel(findData) : new GroupModel({});
}
onSelectFile(event: any) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
const file: File = event.target.files[0];
console.log("🚀 ~ ListExcelComponent ~ onSelectFile ~ file:", file);
reader.readAsDataURL(event.target.files[0]);
reader.onload = (event) => {
if (event) {
const allowedTypes = [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel.sheet.macroEnabled.12'
];
if (!allowedTypes.includes(file.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.xlsx *.xlsm เท่านั้น');
} else {
let base64 = event.target!.result as string;
this.modelExcel.excelObj = base64.split(',')[1];
this.modelExcel.fileType = file.name.split('.')[file.name.split('.').length - 1];
}
}
};
}
}
async downloadFile(logId: string) {
try {
const data = await this.excelService.downloadFile(logId).toPromise();
if (data) {
const fileName = `${logId}.${this.modelExcel.fileType || 'xlsx'}`;
saveAs(new Blob([data]), fileName);
}
} catch (error) {
console.error('Error downloading file:', error);
this.openAlertModalWithMatDialog('ไม่สามารถดาวน์โหลดไฟล์ได้');
}
}
deleteFile(item: ExcelModel) {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการลบข้อมูลนี้หรือไม่",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willDelete: boolean) => {
if (willDelete) {
this.excelService.deleteExcel(item).subscribe(result => {
if (result) {
swal("ลบสำเร็จ!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getListExcel();
} else {
swal("สำเร็จบางส่วน/ข้อผิดพลาด!!", "มีการลบข้อมูลบางส่วนไม่สำเร็จ หรือมีข้อผิดพลาด", "warning");
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
openAlertModalWithMatDialog(message?: string) {
this.dialog.open(AlertModalComponent, {
data: { message: message ? message : "" },
width: '400px',
disableClose: true,
});
}
onUpdate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการอัพเดทข้อมูลหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willUpdate: boolean) => {
if (willUpdate) {
console.log(this.modelExcel);
this.excelService.createExcel(this.modelExcel).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "อัพเดทข้อมูลสำเร็จ", "success");
this.closeDialog();
this.getListExcel();
} else {
swal('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.excelService.createExcel(this.modelExcel).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสมาชิก", "success");
this.closeDialog();
this.getListExcel();
} else {
swal('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
// onUpdate() {
// swal({
// title: "คุณแน่ใจหรือไม่?",
// text: "คุณต้องการอัพเดทข้อมูลหรือไม่",
// icon: "warning",
// dangerMode: false,
// buttons: ["ยกเลิก", "ยืนยัน"],
// }).then((willUpdate: boolean) => {
// if (willUpdate) {
// this.excelService.createExcel(this.modelExcel).subscribe({
// next: (res) => {
// console.log('Excel update response:', res);
// if (res) {
// // this.openAlertModalWithMatDialog('อัพเดทข้อมูลสำเร็จ');
// } else {
// this.openAlertModalWithMatDialog('ไม่สามารถอัพเดทข้อมูลได้ (res is false/null)');
// }
// this.closeDialog();
// this.getListExcel();
// },
// error: (error) => {
// this.openAlertModalWithMatDialog(error.message || 'เกิดข้อผิดพลาดในการอัปเดทข้อมูล');
// this.closeDialog();
// },
// });
// } else {
// this.closeDialog();
// }
// });
// }
// onCreate() {
// swal({
// title: "คุณแน่ใจหรือไม่?",
// text: "คุณต้องการบันทึกหรือไม่",
// icon: "warning",
// dangerMode: false,
// buttons: ["ยกเลิก", "ยืนยัน"],
// }).then((willSave: boolean) => {
// if (willSave) {
// this.excelService.createExcel(this.modelExcel).subscribe({
// next: (res) => {
// if (res) {
// // this.openAlertModalWithMatDialog('บันทึกข้อมูลสำเร็จ'); // <--- ลบบรรทัดนี้ออก
// } else {
// this.openAlertModalWithMatDialog('ไม่สามารถสร้างเอกสารได้ (res is false/null)');
// }
// this.closeDialog();
// this.getListExcel();
// },
// error: (error) => {
// this.openAlertModalWithMatDialog(error.message || 'เกิดข้อผิดพลาดในการสร้างเอกสาร');
// this.closeDialog();
// },
// });
// } else {
// this.closeDialog();
// }
// });
// }
onSumit() {
if (this.checkEdit) {
this.onUpdate();
} else {
this.onCreate();
}
}
async getExcelById(id: string) { // <--- แก้ไขตรงนี้
try {
const data = await this.excelService.getExcelById(id).toPromise();
this.modelExcel = new ExcelModel(data!);
console.log("🚀 ~ ListExcelComponent ~ getExcelById ~ this.modelExcel:", this.modelExcel);
if (data) {
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '1100px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Excel dialog opened by getExcelById closed with result:', result);
if (result === true) {
this.getListExcel();
}
});
}
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลได้'); // เพิ่มการแจ้งเตือน
}
}
openLink(url: string) {
window.open(url, "_blank");
}
getStatus(status: string): string {
if (status === '0') {
return 'รออนุมัติ';
} else if (status === '1') {
return 'เปิดใช้งาน';
} else if (status === '2') {
return 'ไม่อนุมัติ';
} else {
return 'ไม่ทราบสถานะ';
}
}
async getListExcel() {
try {
const data = await this.excelService.getListExcel().toPromise();
this.originalListExcel = data!.map(x => new ExcelModel(x));
this.applyFilterAndPagination();
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการ Excel ได้');
}
}
get totalFilteredItems(): number {
return this.listExcel.length;
}
get totalPages(): number {
return Math.ceil(this.totalFilteredItems / this.pageSize);
}
get startItemIndex(): number {
if (this.totalFilteredItems === 0) return 0;
return this.pageIndex * this.pageSize + 1;
}
get endItemIndex(): number {
const end = Math.min((this.pageIndex + 1) * this.pageSize, this.totalFilteredItems);
return end;
}
goToPage(page: number) {
if (page >= 0 && page < this.totalPages) {
this.pageIndex = page;
this.applyFilterAndPagination();
} else if (this.totalPages === 0 && page === 0) {
this.pageIndex = 0;
this.applyFilterAndPagination();
}
}
onSearchChange() {
this.pageIndex = 0;
this.applyFilterAndPagination();
}
get pages(): number[] {
const pages = [];
const startPage = Math.max(0, this.pageIndex - Math.floor(this.maxPagesToShow / 2));
const endPage = Math.min(this.totalPages - 1, startPage + this.maxPagesToShow - 1);
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
}
ngOnInit() {
this.getListExcel();
this.getListTag();
this.getListGroup();
}
openEmployeeDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
coverDate(date: string) {
return date.split('-').reverse().join('/');
}
openEditExcelDialog(item?: ExcelModel) {
if (item) {
this.excelService.getExcelById(item.logId).toPromise().then(data => {
if (data) {
this.modelExcel = new ExcelModel(data!);
this.checkEdit = true;
}
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '1100px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Excel dialog closed with result:', result);
if (result === true) {
this.getListExcel();
}
});
}).catch(error => {
console.error('Error loading data for edit:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลเพื่อแก้ไขได้');
});
} else {
this.modelExcel = new ExcelModel({});
this.checkEdit = false;
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '800px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Excel dialog closed with result:', result);
if (result === true) {
this.getListExcel();
}
});
}
}
closeDialog() {
if (this.dialogRef) {
this.dialogRef.close();
}
}
}
\ No newline at end of file
<app-page-header [title]="'รายการวิทเจ็ด'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการอัพโหลดเอกสาร Widget'"></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}}
</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)="openEditExcelDialog()"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาบริษัท"
aria-label=".form-control-sm example" [(ngModel)]="search" (ngModelChange)="onSearchChange()">
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive" *ngIf="checkType !== '1'">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr class="border-b border-defaultborder text-white" style="background-color: #49b6f5;">
<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">รายละเอียด</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">การจัดการ</th>
</tr>
</thead>
<tbody>
<tr class="border border-defaultborder dark:border-defaultborder/10"
*ngFor="let data of paginatedWidgets; let i = index">
<td>
<div>
<span class="block mb-1">
{{pageIndex * pageSize + i + 1}} </span>
</div>
</td>
<td>
<div class="flex items-center">
<span class="p-3 me-1" style="width: 200px;">
<img src="{{data.getImage()}}" (click)="openEmployeeDialog(data.getImage())" id="profile-img"
class="border-radius-1 cursor-pointer" style="width: 180px; height: 120px; object-fit: cover;">
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.widgetTname }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
{{ data.thDesc }}
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0 cursor-pointer active: hover:text-danger hover:scale-125 transition duration-300"
(click)="openLink(data.link1)" style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<span class="block mb-1">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0 cursor-pointer active hover:text-success hover:scale-125 transition duration-300"
(click)="downloadFile(data.widgetId)" style="font-size: 20px;"></i>
</span>
</div>
</td>
<td>
<div>
<ng-container *ngIf="data.status === 0">
<span class="badge bg-amber-300 text-white">
รออนุมัติ </span>
</ng-container>
<ng-container *ngIf="data.status === 1">
<span class="badge bg-success text-white">
เปิดใช้งาน </span>
</ng-container>
<ng-container *ngIf="data.status === 2">
<span class="badge bg-gray-500 text-white">
ไม่อนุมัติ </span>
</ng-container>
</div>
</td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="openEditExcelDialog(data)"
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)="deleteFile(data)"
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>
<tr *ngIf="paginatedWidgets.length === 0">
<td colspan="9" class="text-center py-4">ไม่พบข้อมูล</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto">
<div class="mb-2 sm:mb-0">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="goToPage(0)">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0 flex">
<li class="page-item" [class.disabled]="pageIndex === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex - 1)">
{{ 'Previous' | translate }}
</a>
</li>
<ng-container *ngFor="let p of pages">
<li class="page-item" [class.active]="p === pageIndex">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" [class.active]="p === pageIndex"
(click)="goToPage(p)">
{{ p + 1 }} </a>
</li>
</ng-container>
<li class="page-item" [class.disabled]="pageIndex === totalPages - 1 || totalPages === 0">
<a class="page-link px-3 py-[0.375rem] cursor-pointer" (click)="goToPage(pageIndex + 1)">
{{ 'Next' | translate }}
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #editTemplateModal let-modal>
<div class="modal-headtitle ti-modal-header flex justify-between items-center p-4" style="background-color: #fefbfb;">
<h3 class="modal-title text-sm font-semibold text-defaulttextcolor" id="edittemplateLabel">
{{ checkEdit ? ('แก้ไขไฟล์ Widget' | translate) : ('เพิ่มไฟล์ Widget' | translate) }}
</h3>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeDialog()" #closeModal> <span class="sr-only">{{ 'Close' | translate }}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="w-full flex justify-end">
<div class="absolute flex">
<div class="px-1">
</div>
</div>
</div>
<mat-dialog-content style="padding: 0px; padding-left: 20px; padding-right: 20px;">
<div class="box p-9 top-4">
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-6 col-span-12">
<label for="nameth" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อวิทเจ็ด (ไทย)' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameth" [(ngModel)]="modelWidget.widgetTname" name="widgetTname">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="nameeng" class="block text-primary mb-2 font-bold font-14">{{ 'ชื่อวิทเจ็ด (อังกฤษ)' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="nameeng" [(ngModel)]="modelWidget.widgetEname" name="widgetEname">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="thDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (ไทย)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="thDesc" [(ngModel)]="modelWidget.thDesc" rows="3" name="thDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="engDesc" class="block text-primary mb-2 font-bold font-14">{{ 'รายละเอียด (อังกฤษ)' | translate
}}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="engDesc" [(ngModel)]="modelWidget.engDesc" rows="3" name="engDesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'ภาษาที่รองรับ' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="status" [(ngModel)]="modelWidget.status" name="status">
<option [value]="0">Private</option>
<option [value]="1">Public</option>
</select>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="dbSupport" class="block text-primary mb-2 font-bold font-14">{{ 'ดาต้าเบสที่รองรับ' | translate
}}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="dbSupport" [(ngModel)]="modelWidget.dbSupport" name="dbSupport">
<option value="SQLServer">SQLServer</option>
<option value="PostgreSQL">PostgreSQL</option>
<option value="Oracle">Oracle</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="fileUpload" class="block text-primary mb-2 font-bold font-14">{{ 'ไฟล์อัพโหลด' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="fileUpload" (change)="onSelectFile($event)">
<button *ngIf="modelWidget.widgetObj && checkEdit" (click)="downloadFile(modelWidget.widgetId)"
class="ti-btn ti-btn-primary flex-shrink-0 px-4 py-2 rounded-md transition-all duration-200">
<i class="fas fa-download mr-1"></i> {{ 'ดาวน์โหลด' | translate }}
</button>
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.xlsx *.xlsm
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="imageUpload" class="block text-primary mb-2 font-bold font-14">{{ 'รูปภาพตัวอย่าง' | translate
}}</label>
<div class="flex items-center gap-2">
<input type="file"
class="form-control flex-grow p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="imageUpload" (change)="onUploadImage($event)">
</div>
<div class="msg-detail text-sm text-red mt-1"> ไฟล์ที่อนุญาต:*.jpeg, *.jpg, *.png
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="linkExample" class="block text-primary mb-2 font-bold font-14">{{ 'Link ตัวอย่าง' | translate
}}</label>
<input type="text"
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="linkExample" [(ngModel)]="modelWidget.link1" name="link1">
</div>
<div class="xl:col-span-12 col-span-12">
<label for="tags" class="block text-primary mb-2 font-bold font-14">{{ 'แท็ก' | translate }}</label>
<tag-input [theme]="'bootstrap'" [(ngModel)]='modelWidget.tags' placeholder="{{ '+แท็ก' | translate }}"
[onlyFromAutocomplete]="true" secondaryPlaceholder="{{ 'กดปุ่ม Enter เพื่อเพิ่มแท็กใหม่' | translate }}"
[addOnBlur]="true" [clearOnBlur]="true" class="w-full" name="tags">
<tag-input-dropdown [autocompleteItems]="listTag" [showDropdownIfEmpty]="true">
</tag-input-dropdown>
</tag-input>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="group" class="block text-primary mb-2 font-bold font-14">{{ 'กลุ่ม' | translate }}</label>
<select
class="custom-select w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="group" [(ngModel)]="modelWidget.group.groupId" (change)="selectGroup()" name="group">
<option *ngFor="let item of listGroup" [ngValue]="item.groupId">{{item.thName}}</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="block text-primary mb-2 font-bold font-14">{{ 'แสดงรูปแบบ Pivot' | translate }}</label>
<div class="flex items-center gap-4 mt-2">
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="pivot0" name="radio-pivot" [value]="0" [(ngModel)]="modelWidget.isPivot">
<label class="ml-2 text-gray-700" for="pivot0">{{ 'ใช่' | translate }}</label>
</div>
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="pivot1" name="radio-pivot" [value]="1" [(ngModel)]="modelWidget.isPivot">
<label class="ml-2 text-gray-700" for="pivot1">{{ 'ไม่ใช่' | translate }}</label>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="block text-primary mb-2 font-bold font-14">{{ 'แสดงรูปแบบ DataGrid' | translate }}</label>
<div class="flex items-center gap-4 mt-2">
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="datagrid0" name="datagridCheck" [value]="0" [(ngModel)]="modelWidget.isDataGrid">
<label class="ml-2 text-gray-700" for="datagrid0">{{ 'ใช่' | translate }}</label>
</div>
<div class="flex items-center">
<input type="radio" class="form-radio h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
id="datagrid1" name="datagridCheck" [value]="1" [(ngModel)]="modelWidget.isDataGrid">
<label class="ml-2 text-gray-700" for="datagrid1">{{ 'ไม่ใช่' | translate }}</label>
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="remark" class="block text-primary mb-2 font-bold font-14">{{ 'หมายเหตุ' | translate }}</label>
<textarea
class="form-control w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200"
id="remark" [(ngModel)]="modelWidget.remark" rows="4" name="remark"></textarea>
</div>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions style="justify-content: end;">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle" (click)="closeDialog()">
{{ 'Cancel' | translate }}
</button>
<button type="submit" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!modelWidget.widgetTname||!modelWidget.widgetEname"
[disabled]="!modelWidget.widgetTname||!modelWidget.widgetEname" (click)="onSumit()">
{{ 'Save' | translate }}
</button>
</mat-dialog-actions>
</ng-template>
\ No newline at end of file
::ng-deep ng2-dropdown-menu {
z-index: 9999 !important;
.ng2-dropdown-menu {
z-index: 9999 !important;
ng2-menu-item {
z-index: 9999 !important;
}
}
}
\ No newline at end of file
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { UploadService } from '../../../services/upload.service';
import { TagService } from '../../../services/tag.service';
import { TagModel } from '../../../models/tag.mmodel';
import { GroupModel } from '../../../models/group.mmodel';
import { OpenImageComponent } from '../open-image/open-image.component';
import { GroupService } from '../../../services/group.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import saveAs from 'file-saver';
import { TagInputModule } from 'ngx-chips';
import swal from 'sweetalert';
import { WidgetModel } from '../../../models/widgets.model';
import { WidgetService } from '../../../services/widgets.service';
@Component({
selector: 'app-list-widgets',
templateUrl: './list-widgets.component.html',
styleUrls: ['./list-widgets.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
TagInputModule,
],
})
export class ListWidgetsComponent implements OnInit {
@ViewChild("editTemplateModal") editTemplateModalRef!: TemplateRef<any>;
checkType: string = '';
page = 1;
pageSize = 10;
pageIndex: number = 0;
maxPagesToShow: number = 3;
// Initialize with empty arrays to prevent undefined errors before data loads
listWidgets: WidgetModel[] = []; // This will now store the filtered list
originallistWidgets: WidgetModel[] = []; // This will store the raw data from API
paginatedWidgets: WidgetModel[] = []; // This will store the sliced data for the current page
dialogRef: any;
modelWidget: WidgetModel = new WidgetModel({});
checkEdit: boolean = false;
search: string = '';
listTag: TagModel[] = [];
listGroup: GroupModel[] = [];
constructor(private modalService: NgbModal, private widgetService: WidgetService,
private uploadService: UploadService,
private tagService: TagService,
private groupService: GroupService,
private dialog: MatDialog,) {
}
// Central method for filtering and pagination
applyFilterAndPagination() {
// Ensure `originallistWidgets` is the source for filtering
this.listWidgets = this.originallistWidgets.filter(x =>
x.widgetTname.toLowerCase().includes(this.search.toLowerCase()) ||
x.widgetEname.toLowerCase().includes(this.search.toLowerCase())
);
const totalPagesAfterFilter = Math.ceil(this.listWidgets.length / this.pageSize);
if (this.pageIndex >= totalPagesAfterFilter && totalPagesAfterFilter > 0) {
this.pageIndex = totalPagesAfterFilter - 1;
} else if (totalPagesAfterFilter === 0) {
this.pageIndex = 0;
}
const startIndex = this.pageIndex * this.pageSize;
const endIndex = startIndex + this.pageSize;
this.paginatedWidgets = this.listWidgets.slice(startIndex, endIndex);
}
onSelectFile(event: any) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
const file: File = event.target.files[0];
console.log("🚀 ~ listWidgetsComponent ~ onSelectFile ~ file:", file)
reader.readAsDataURL(event.target.files[0]); // read file as data url
reader.onload = (event) => { // called once readAsDataURL is completed
if (event) {
const allowedTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel.sheet.macroEnabled.12'];
if (!allowedTypes.includes(file.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.xlsx *.xlsm เท่านั้น');
} else {
let base64 = event.target!.result as string;
this.modelWidget.widgetObj = base64.split(',')[1];
this.modelWidget.fileType = file.name.split('.')[file.name.split('.').length - 1];
}
}
};
}
}
async downloadFile(logId: string) {
try {
// Assuming modelWidget.fileType is available and correct after a file has been selected/uploaded
// If not, you might need to get the file type from the service or infer it.
const data = await this.widgetService.downloadFile(logId).toPromise();
if (data) {
const fileName = `${logId}.${this.modelWidget.fileType || 'xlsx'}`; // Fallback to 'xlsx'
saveAs(new Blob([data]), fileName);
}
} catch (error) {
console.error('Error downloading file:', error);
this.openAlertModalWithMatDialog('ไม่สามารถดาวน์โหลดไฟล์ได้');
}
}
async getListGroup() {
try {
const data = await this.groupService.getList().toPromise();
this.listGroup = data!.map(x => new GroupModel(x));
} catch (error) {
console.error('Error loading groups:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการกลุ่มได้');
}
}
selectGroup() {
let findData = this.listGroup.find(x => x.groupId === this.modelWidget.group.groupId);
this.modelWidget.group = findData ? new GroupModel(findData) : new GroupModel({});
}
async onUploadImage(event: any) {
try {
if (event.target.files.length > 0) {
let fileData = event.target.files[0];
const formData = new FormData();
formData.append('file', fileData);
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
if (!allowedTypes.includes(fileData.type)) {
this.openAlertModalWithMatDialog('อัพโหลดได้เฉพาะไฟล์ *.jpeg, *.jpg, *.png เท่านั้น');
} else {
const data = await this.uploadService.uploadImage(formData).toPromise();
if (data) {
this.modelWidget.picture = data.body.fileId;
}
}
}
} catch (error) {
console.error('Error uploading image:', error);
this.openAlertModalWithMatDialog('เกิดข้อผิดพลาดในการอัปโหลดรูปภาพ');
}
}
deleteFile(item: WidgetModel) {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการลบข้อมูลนี้หรือไม่",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willDelete: boolean) => {
if (willDelete) {
this.widgetService.deleteWidget(item).subscribe(result => {
if (result) {
swal("ลบสำเร็จ!!", "บันทึกข้อมูลสำเร็จ", "success");
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
swal("สำเร็จบางส่วน/ข้อผิดพลาด!!", "มีการลบข้อมูลบางส่วนไม่สำเร็จ หรือมีข้อผิดพลาด", "warning");
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
openAlertModalWithMatDialog(message?: string) {
this.dialog.open(AlertModalComponent, {
data: { message: message ? message : "" },
width: '400px',
disableClose: true,
});
}
onUpdate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการอัพเดทข้อมูลหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willUpdate: boolean) => {
if (willUpdate) {
console.log(this.modelWidget);
this.widgetService.createWidget(this.modelWidget).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "อัพเดทข้อมูลสำเร็จ", "success");
this.closeDialog();
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
swal('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.widgetService.createWidget(this.modelWidget).subscribe(res => {
if (res) {
swal("บันทึกสำเร็จ!!", "บันทึกข้อมูลสมาชิก", "success");
this.closeDialog();
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
swal('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
console.error("เกิดข้อผิดพลาดในการบันทึก/อัปเดต:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถบันทึก/อัปเดตข้อมูลได้", "error");
});
}
});
}
onSumit() {
if (this.checkEdit) {
this.onUpdate();
} else {
this.onCreate();
}
}
// This method openModal with NgbModal is currently unused as MatDialog is used for edit template.
// If not explicitly needed for other NgbModal usage, it can be removed.
async getWidgetById(targetModal: NgbModal, id: string) {
try {
const data = await this.widgetService.getWidgetById(id).toPromise();
this.modelWidget = new WidgetModel(data!);
if (data) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
});
}
} catch (error) {
console.error('Error loading data:', error);
}
}
getStatus(status: string): string {
if (status === '0') {
return 'Private';
} else if (status === '1') {
return 'Public';
} else {
return 'ไม่ทราบสถานะ';
}
}
async getListWidgets() {
try {
const data = await this.widgetService.getListWidgets().toPromise();
this.originallistWidgets = data!.map(x => new WidgetModel(x)); // Populate original list
this.applyFilterAndPagination(); // Apply filter and pagination after fetching
} catch (error) {
console.error('Error loading data:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการ Widget ได้');
}
}
get totalFilteredItems(): number {
return this.listWidgets.length; // `listWidgets` now holds the filtered count
}
get totalPages(): number {
return Math.ceil(this.totalFilteredItems / this.pageSize);
}
get startItemIndex(): number {
if (this.totalFilteredItems === 0) return 0;
return this.pageIndex * this.pageSize + 1;
}
get endItemIndex(): number {
const end = Math.min((this.pageIndex + 1) * this.pageSize, this.totalFilteredItems);
return end;
}
goToPage(page: number) {
if (page >= 0 && page < this.totalPages) {
this.pageIndex = page;
this.applyFilterAndPagination(); // Re-apply filter and pagination
} else if (this.totalPages === 0 && page === 0) {
this.pageIndex = 0;
this.applyFilterAndPagination();
}
}
onSearchChange() {
this.pageIndex = 0; // Reset to the first page when search changes
this.applyFilterAndPagination(); // Re-apply filter and pagination
}
get pages(): number[] {
const pages: number[] = [];
const startPage = Math.max(0, this.pageIndex - Math.floor(this.maxPagesToShow / 2));
const endPage = Math.min(this.totalPages - 1, startPage + this.maxPagesToShow - 1);
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
}
ngOnInit() {
this.getListWidgets(); // Initial data fetch and display
this.getListTag();
this.getListGroup();
}
async getListTag() {
try {
// Assuming tagService.getListWidgets() is the correct method for tags
const data = await this.tagService.getList().toPromise(); // Assuming general getList
this.listTag = data!.map(x => new TagModel(x));
} catch (error) {
console.error('Error loading tags:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดรายการแท็กได้');
}
}
openEmployeeDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
openEditExcelDialog(item?: WidgetModel) {
if (item) {
this.widgetService.getWidgetById(item.widgetId).toPromise().then(data => {
if (data) {
this.modelWidget = new WidgetModel(data!);
this.checkEdit = true;
}
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '1100px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Widget dialog closed with result:', result);
if (result === true) {
this.getListWidgets(); // Refresh list on success
}
});
}).catch(error => {
console.error('Error loading data for edit:', error);
this.openAlertModalWithMatDialog('ไม่สามารถโหลดข้อมูลเพื่อแก้ไขได้');
});
} else {
this.modelWidget = new WidgetModel({});
this.checkEdit = false;
this.dialogRef = this.dialog.open(this.editTemplateModalRef, {
width: '800px',
disableClose: true,
});
this.dialogRef.afterClosed().subscribe((result: boolean) => {
console.log('Edit Widget dialog closed with result:', result);
if (result === true) {
this.getListWidgets(); // Refresh list on success
}
});
}
}
closeDialog() {
if (this.dialogRef) {
this.dialogRef.close();
}
}
closeBtnClick() {
this.modalService.dismissAll();
}
openLink(url: string) {
window.open(url, "_blank");
}
}
\ No newline at end of file
<app-page-header [title]="'รายการเอกสารผ่านการอนุมัติ'" [activeTitle]="'รายการเอกสารผ่านการอนุมัติ'" [title1]="'อนุมัติหลักสูตร'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="row justify-content-center">
<div class="col-md-4 " *ngFor="let c of testdata|slice: (page-1) * pageSize : (page-1) * pageSize + pageSize">
<div class="card border border-2">
<div class="card-body text-center">
<img src="{{ c.img }}" class="rounded-circle border p-1" width="100">
<h3 class="card-title mt-3 mb-0">{{c.name}}</h3>
</div>
<div class="d-flex justify-content-between bg-light border-top p-3">
<div>
<span class="align-middle">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div >
<button class="btn btn-info btn-sm text-nowrap" (click)="openView(c.id)">รายละเอียด</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
<div class="max-w-7xl mx-auto">
<div class="box p-4">
<h4 class="text-xl font-semibold text-gray-800 mb-6">รายการเอกสาร</h4>
<!-- Flex แทน Grid -->
<div class="flex flex-wrap justify-center gap-6">
<div
class=" bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition duration-300 ease-in-out flex flex-col" style="width: 30%;"
*ngFor="let c of testdata | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize"
>
<div class="p-8 text-center">
<div class="w-24 h-24 mx-auto rounded-full bg-green-100 flex items-center justify-center shadow-inner">
<img src="{{ c.img }}" class="" alt="" />
</div>
<h3 class="text-2xl font-bold text-gray-800 mt-6 mb-2">{{ c.name }}</h3>
</div>
<!-- Footer -->
<div class="mt-auto">
<div class="flex justify-between items-center bg-gray-50 border-t border-gray-200 p-4">
<div>
<span class="text-sm text-gray-700">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div>
<button
class="bg-primary inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md shadow-md
active hover:bg-success hover:scale-110 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
(click)="openView(c.id)"
>
รายละเอียด
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ApprovedListComponent } from './approved-list.component';
describe('ApprovedListComponent', () => {
let component: ApprovedListComponent;
let fixture: ComponentFixture<ApprovedListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ApprovedListComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ApprovedListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { Router, RouterModule } from '@angular/router';
import { DocumentService } from '../../../../services/document.service';
import { CourseService } from '../../../../services/course.service';
import { ExcelService } from '../../../../services/excel.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../../../../../shared/shared.module';
@Component({
selector: 'app-approved-list',
templateUrl: './approved-list.component.html',
styleUrls: ['./approved-list.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
TranslateModule,
SharedModule,
],
})
export class ApprovedListComponent implements OnInit {
constructor(
private routes: Router,
private excelService:ExcelService,private documentService:DocumentService,private courseService:CourseService
) {}
page = 1;
pageSize = 10;
testdata: {
id:string;
img: String;
name: String;
details: String;
document: String;
}[] = [];
ngOnInit() {
this.testdata = [
{
id:'1',
img: "assets/images/icons/excel.png",
name: "Excel",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'2',
img: "assets/images/icons/document.png",
name: "Document",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'3',
img: "assets/images/icons/course.png",
name: "Course",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
];
this.getListCount();
}
async getListCount(){
try {
const countExcel = await this.excelService.getCountContent().toPromise();
const countDoc = await this.documentService.getCountContent().toPromise();
const countCourse = await this.courseService.getCountContent().toPromise();
this.testdata.forEach((x ,i) => {
if(i == 0){
x.document = (countExcel ?? 0).toString()
}else if(i == 1){
x.document = (countDoc ?? 0).toString()
}else if(i == 2){
x.document = (countCourse ?? 0).toString()
}
})
} catch (error) {
console.error('Error loading data:', error);
}
}
openView(id:string){
if(id =='1'){
this.routes.navigate(['/admin/view-list-excel/1']);
}else if(id =='2'){
this.routes.navigate(['/admin/view-list-doc/1']);
}else if(id =='3'){
this.routes.navigate(['/admin/view-list-course/1']);
}
}
}
<app-page-header [title]="'รายการหลักสูตร'" [activeTitle]="'รายการเอกสาร'" [title1]="'รายการหลักสูตร'"></app-page-header>
<!-- <div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการหลักสูตร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col">ประเภทหลักสูตร</th>
<th scope="col">ลิงค์</th>
<th scope="col">สถานะ</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listCourse.length == 0">
<td colspan="{{checkType == '1' ? '9' : '8'}}" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListCourse() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td class="text-wrap">{{ data.thDesc }}</td>
<td>{{data.courseType}}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.courseId)"></i></td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listCourse.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div>
<div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListCourse()">
<div class="card border-5 border border-course h-100 shadow" >
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.courseId)"><i class="fas fa-download mr-1"></i> ดาวน์โหลด <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}} {{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i> ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5" >
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<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">
รายการหลักสูตร
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="!text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col">ประเภทหลักสูตร</th>
<th scope="col">ลิงค์</th>
<th scope="col">สถานะ</th>
<th scope="col" class="!text-center">วันที่อัพโหลด</th>
<th scope="col" class="!text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="!text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listCourse.length == 0">
<td [attr.colspan]="checkType == '1' ? 8 : 7" class="!text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListCourse() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="!text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())" style="width: 250px; max-width: none;"></td>
<td>{{ data.thName }}</td>
<td class="text-wrap">{{ data.thDesc }}</td>
<td>{{data.courseType}}</td>
<td class="!text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary" (click)="openLink(data.link1)"></i></td>
<td class="!text-center"> <i class="fa fa-download text-base font-bold cursor-pointer hover:text-primary" (click)="downloadFile(data.courseId)"></i></td>
<td class="!text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="!text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="!text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
<tr *ngIf="filterListCourse().length === 0">
<td [attr.colspan]="checkType == '1' ? 10 : 9" class="!text-center py-4">
<ng-container *ngIf="search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
<ng-container *ngIf="!search">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListCourse().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListCourse().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListCourse().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListCourse().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListCourse().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute } from '@angular/router';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { firstValueFrom } from 'rxjs';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { CourseContentModel } from '../../../../../models/course-content.model';
import { CourseService } from '../../../../../services/course.service';
declare var require: any
import FileSaver from 'file-saver';
@Component({
selector: 'app-view-list-course',
templateUrl: './view-list-course.component.html',
styleUrls: ['./view-list-course.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ViewListCourseComponent implements OnInit {
page = 0;
pageSize = 10;
listCourse: CourseContentModel[] = []
search: string = ''
checkType: string = '0'
pagedItems: CourseContentModel[];
constructor(private modalService: NgbModal, private courseService: CourseService, private activatedRoute: ActivatedRoute) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
get totalItems(): number {
return this.search == ''
? this.listCourse.length
: this.filterListCourse().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: CourseContentModel[] = this.search === '' ? this.listCourse : this.filterListCourse();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
async downloadFile(logId: string) {
try {
const data = await this.courseService.downloadFileContent(logId).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.json");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
filterListCourse() {
return this.listCourse.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListCourse() {
try {
const data = await firstValueFrom(this.courseService.getListCourseContent('0'));
this.listCourse = data.map(x => new CourseContentModel(x))
console.log("🚀 ~ ViewListCourseComponent ~ getListCourse ~ this.listCourse :", this.listCourse)
} catch (error) {
console.error('Error loading data:', error);
}
}
deleteFile(item: CourseContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.courseService.deleteCourseContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListCourse();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
ngOnInit() {
this.getListCourse();
}
openLink(url: string) {
window.open(url, "_blank");
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
coverDate(date: string) {
return date.split('-').reverse().join('/')
}
}
<app-page-header [title]="'รายการเอกสาร'" [activeTitle]="'รายการเอกสาร'" [title1]="'รายการเอกสาร'"></app-page-header>
<!-- <div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listDoc.length == 0">
<td colspan="{{checkType == '1' ? '9' : '10'}}" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListDoc() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.docId,'tha')"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.docId,'eng')"></i></td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listDoc.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div>
<div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListDoc()">
<div class="card border-5 border border-doc h-100 shadow" >
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.docId,'tha')"><i class="fas fa-download mr-1"></i> ดาวน์โหลด (ไทย) <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}} {{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.docId,'eng')"><i class="fas fa-download mr-1"></i> ดาวน์โหลด (อังกฤษ) <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}} {{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i> ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5" >
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<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">
รายการเอกสาร
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="!text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="!text-center">ลิงค์</th>
<th scope="col" class="!text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="!text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="!text-center">วันที่อัพโหลด</th>
<th scope="col" class="!text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="!text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listDoc.length == 0">
<td [attr.colspan]="checkType == '1' ? 8 : 7" class="!text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListDoc() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="!text-center">{{i+1}}</td>
<td><img class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())" style="width: 250px; max-width: none;"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="!text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary" (click)="openLink(data.link1)"></i></td>
<td class="!text-center"> <i class="fa fa-download text-base font-bold cursor-pointer hover:text-primary" (click)="downloadFile(data.docId,'tha')"></i></td>
<td class="!text-center"> <i class="fa fa-download text-base font-bold cursor-pointer hover:text-primary" (click)="downloadFile(data.docId,'eng')"></i></td>
<td class="!text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="!text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="!text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fa fa-trash"></i></button>
</td>
</tr>
<tr *ngIf="filterListDoc().length === 0">
<td [attr.colspan]="checkType == '1' ? 10 : 9" class="!text-center py-4">
<ng-container *ngIf="search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
<ng-container *ngIf="!search">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListDoc().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListDoc().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListDoc().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListDoc().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListDoc().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute } from '@angular/router';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { firstValueFrom } from 'rxjs';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { DocumentContentModel } from '../../../../../models/document-content.model';
import { DocumentService } from '../../../../../services/document.service';
declare var require: any
import FileSaver from 'file-saver';
@Component({
selector: 'app-view-list-doc',
templateUrl: './view-list-doc.component.html',
styleUrls: ['./view-list-doc.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ViewListDocComponent implements OnInit {
page = 0;
pageSize = 10;
listDoc: DocumentContentModel[] = []
search: string = ''
checkType: string = '0'
pagedItems: DocumentContentModel[] = [];
constructor(private modalService: NgbModal, private documentService: DocumentService, private activatedRoute: ActivatedRoute) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
get totalItems(): number {
return this.search == ''
? this.listDoc.length
: this.filterListDoc().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: DocumentContentModel[] = this.search === '' ? this.listDoc : this.filterListDoc();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
deleteFile(item: DocumentContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.documentService.deleteExcelContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListDoc();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
async downloadFile(logId: string, lang: string) {
try {
const data = await this.documentService.downloadFileContent(logId, lang).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.doc");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
filterListDoc() {
return this.listDoc.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListDoc() {
try {
const data = await firstValueFrom(this.documentService.getListExcelContent('0'));
this.listDoc = data.map(x => new DocumentContentModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
ngOnInit() {
this.getListDoc();
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
openLink(url: string) {
window.open(url, "_blank");
}
coverDate(date: string) {
return date.split('-').reverse().join('/')
}
}
<app-page-header [title]="'รายการ Excel'" [activeTitle]="'รายการเอกสาร'" [title1]="'รายการ Excel'"></app-page-header>
<!-- <div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการ Excel</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listExcel.length == 0">
<td [attr.colspan]="checkType == '1' ? 8 : 7" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr
*ngFor="let data of filterListExcel() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.excelId)"></i></td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listExcel.length" [maxSize]="3"
[rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div>
<div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListExcel()">
<div class="card border-5 border border-excel h-100 shadow">
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.excelId)"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i>
ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5">
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<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">
รายการ Excel
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="!text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="!text-center">ลิงค์</th>
<th scope="col" class="!text-center">ไฟล์</th>
<th scope="col" class="!text-center">วันที่อัพโหลด</th>
<th scope="col" class="!text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="!text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listExcel.length == 0">
<td [attr.colspan]="checkType == '1' ? 8 : 7" class="!text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListExcel() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="!text-center">{{i+1}}</td>
<td><img class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"
style="width: 250px; max-width: none;"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="!text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary"
(click)="openLink(data.link1)"></i></td>
<td class="!text-center"> <i
class="fa fa-download text-base font-bold cursor-pointer hover:text-primary"
(click)="downloadFile(data.excelId)"></i>
</td>
<td class="!text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="!text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="!text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fa fa-trash"></i></button>
</td>
</tr>
<tr *ngIf="filterListExcel().length === 0">
<td [attr.colspan]="checkType == '1' ? 10 : 9" class="!text-center py-4">
<ng-container *ngIf="search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
<ng-container *ngIf="!search">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListExcel().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListExcel().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListExcel().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListExcel().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListExcel().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute } from '@angular/router';
import { ExcelContentModel } from '../../../../../models/excel-content.model';
import { ExcelService } from '../../../../../services/excel.service';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { firstValueFrom } from 'rxjs';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
declare var require: any
import FileSaver from 'file-saver';
@Component({
selector: 'app-view-list-excel',
templateUrl: './view-list-excel.component.html',
styleUrls: ['./view-list-excel.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ViewListExcelComponent implements OnInit {
page = 0;
pageSize = 10;
listExcel: ExcelContentModel[] = []
search: string = ''
checkType: string = '0'
pagedItems: ExcelContentModel[] = [];
constructor(private modalService: NgbModal, private excelService: ExcelService, private activatedRoute: ActivatedRoute,) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
get totalItems(): number {
return this.search == ''
? this.listExcel.length
: this.filterListExcel().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: ExcelContentModel[] = this.search === '' ? this.listExcel : this.filterListExcel();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
deleteFile(item: ExcelContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.excelService.deleteExcelContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListExcel();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
async downloadFile(logId: string) {
try {
const data = await this.excelService.downloadFileContent(logId).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.xlsx");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
getStatus(status: string): string {
if (status === '0') {
return 'รออนุมัติ';
} else if (status === '1') {
return 'เปิดใช้งาน';
} else if (status === '2') {
return 'ไม่อนุมัติ';
} else {
return 'ไม่ทราบสถานะ'; // สำหรับกรณีที่ไม่ตรงกับเงื่อนไขใดเลย
}
}
filterListExcel() {
return this.listExcel.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListExcel() {
try {
const data = await firstValueFrom(this.excelService.getListExcelContent('0'));
this.listExcel = data.map(x => new ExcelContentModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
ngOnInit() {
this.getListExcel();
}
openLink(url: string) {
window.open(url, "_blank");
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
coverDate(date: string) {
return date.split('-').reverse().join('/')
}
}
/* 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 { ManagementComponent } from './management.component';
describe('ManagementComponent', () => {
let component: ManagementComponent;
let fixture: ComponentFixture<ManagementComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ManagementComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ManagementComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-management',
templateUrl: './management.component.html',
styleUrls: ['./management.component.css']
})
export class ManagementComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<app-page-header [title]="'อนุมัติหลักสูตร'" [activeTitle]="'รายการเอกสารรอการอนุมัติ'"
[title1]="'อนุมัติหลักสูตร'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการหลักสูตร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col">ประเภทหลักสูตร</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์</th>
<th scope="col" class="text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listCourse.length == 0">
<td colspan="9" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListCourse() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td class="text-wrap">{{ data.thDesc }}</td>
<td>{{data.courseType}}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId)"></i></td>
<td class="text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body" tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fas fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body" tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fas fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body" tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fas fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<button type="button" class="btn btn-circle btn-success rounded-circle btn-sm mr-2" (click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="onCancelApprove(data)"><i class="fas fa-times"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listCourse.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<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">
รายการหลักสูตร
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="!text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col">ประเภทหลักสูตร</th>
<th scope="col" class="!text-center">ลิงค์</th>
<th scope="col" class="!text-center">ไฟล์</th>
<th scope="col" class="!text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let data of filterListCourse() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="!text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td class="text-wrap">{{ data.thDesc }}</td>
<td>{{data.courseType}}</td>
<td class="!text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary"
(click)="openLink(data.link1)"></i></td>
<td class="!text-center"> <i
class="fa fa-download text-base font-bold cursor-pointer hover:text-primary"
(click)="downloadFile(data.logId)"></i></td>
<td class="!text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body"
tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fa fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body"
tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class="fa fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body"
tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fa fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<div class="flex space-x-2 justify-center">
<button type="button"
class="bg-success hover:bg-success text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onCancelApprove(data)"><i class="fas fa-times"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
<tr *ngIf="filterListCourse().length === 0">
<td [attr.colspan]="9" class="!text-center py-4">
<ng-container *ngIf="listCourse.length === 0">
<p>ไม่พบข้อมูล</p>
</ng-container>
<ng-container *ngIf="listCourse.length > 0 && search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListCourse().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListCourse().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListCourse().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListCourse().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListCourse().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { CourseModel } from '../../../../../models/course.model';
import { CourseService } from '../../../../../services/course.service';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { firstValueFrom } from 'rxjs';
declare var require: any
import FileSaver from 'file-saver';
@Component({
selector: 'app-approve-course',
templateUrl: './approve-course.component.html',
styleUrls: ['./approve-course.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ApproveCourseComponent implements OnInit {
page = 0;
pageSize = 10;
listCourse: CourseModel[] = []
search: string = ''
pagedItems: CourseModel[] = [];
constructor(private modalService: NgbModal, private courseService: CourseService) {
}
get totalItems(): number {
return this.search == ''
? this.listCourse.length
: this.filterListCourse().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: CourseModel[] = this.search === '' ? this.listCourse : this.filterListCourse();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
async downloadFile(logId: string) {
try {
const data = await this.courseService.downloadFile(logId).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.json");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
filterListCourse() {
return this.listCourse.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListCourse() {
try {
const data = await firstValueFrom(this.courseService.getListCourse('0'));
this.listCourse = data.map(x => new CourseModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
onApprove(item: CourseModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการอนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 1
this.courseService.approve(item).subscribe(result => {
if (result) {
this.modalService.dismissAll()
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListCourse();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
onCancelApprove(item: CourseModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการไม่อนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 2
this.courseService.approve(item).subscribe(result => {
if (result) {
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListCourse();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
ngOnInit() {
this.getListCourse();
}
deleteFile(item: CourseModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.courseService.deleteCourse(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListCourse();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
// this.modalService.dismissAll()
}, reason => {
// this.modalService.dismissAll()
})
}
openLink(url: string) {
window.open(url, "_blank");
}
}
<app-page-header [title]="'อนุมัติเอกสาร'" [activeTitle]="'รายการเอกสารรอการอนุมัติ'"
[title1]="'อนุมัติเอกสาร'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">รหัสเอกสาร</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listDoc.length == 0">
<td colspan="9" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListDoc() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openEmployeeModal(data.getImage())"></td>
<td >{{data.id}}</td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId,'tha')"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId,'eng')"></i></td>
<td class="text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body" tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fas fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body" tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fas fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body" tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fas fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<button type="button" class="btn btn-circle btn-success rounded-circle btn-sm mr-2" (click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="onCancelApprove(data)"><i class="fas fa-times"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listDoc.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<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">
รายการเอกสาร
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead class="bg-info text-white">
<tr>
<th class="!text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<!-- <th scope="col">รหัสเอกสาร</th> -->
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="!text-center">ลิงค์</th>
<th scope="col" class="!text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="!text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="!text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of filterListDoc() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="!text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())"></td>
<!-- <td >{{data.id}}</td> -->
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="!text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary"
(click)="openLink(data.link1)"></i></td>
<td class="!text-center"> <i
class="fa fa-download text-base font-bold cursor-pointer hover:text-primary"
(click)="downloadFile(data.logId,'tha')"></i></td>
<td class="!text-center"> <i
class="fa fa-download text-base font-bold cursor-pointer hover:text-primary"
(click)="downloadFile(data.logId,'eng')"></i></td>
<td class="!text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body"
tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fa fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body"
tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fa fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body"
tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fa fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<div class="flex space-x-2 justify-center">
<button type="button"
class="bg-success hover:bg-success text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onCancelApprove(data)"><i class="fa fa-times"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
<tr *ngIf="filterListDoc().length === 0">
<td [attr.colspan]="9" class="!text-center py-4">
<ng-container *ngIf="listDoc.length === 0">
<p>ไม่พบข้อมูล</p>
</ng-container>
<ng-container *ngIf="listDoc.length > 0 && search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListDoc().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListDoc().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListDoc().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListDoc().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListDoc().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { DocumentModel } from '../../../../../models/document.model';
import { DocumentService } from '../../../../../services/document.service';
import { firstValueFrom } from 'rxjs';
declare var require: any
import FileSaver from 'file-saver';
@Component({
selector: 'app-approve-doc',
templateUrl: './approve-doc.component.html',
styleUrls: ['./approve-doc.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ApproveDocComponent implements OnInit {
page = 0;
pageSize = 10;
listDoc: DocumentModel[] = []
search: string = ''
pagedItems: DocumentModel[] = [];
constructor(private modalService: NgbModal, private documentService: DocumentService) {
}
get totalItems(): number {
return this.search == ''
? this.listDoc.length
: this.filterListDoc().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: DocumentModel[] = this.search === '' ? this.listDoc : this.filterListDoc();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
async downloadFile(logId: string, lang: string) {
try {
const data = await this.documentService.downloadFile(logId, lang).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.doc");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
openLink(url: string) {
window.open(url, "_blank");
}
filterListDoc() {
return this.listDoc.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListDoc() {
try {
const data = await firstValueFrom(this.documentService.getListDoc('0'));
this.listDoc = data.map(x => new DocumentModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
onApprove(item: DocumentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการอนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 1
this.documentService.approve(item).subscribe(result => {
if (result) {
this.modalService.dismissAll()
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListDoc();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
onCancelApprove(item: DocumentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการไม่อนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 2
this.documentService.approve(item).subscribe(result => {
if (result) {
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListDoc();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
ngOnInit() {
this.getListDoc();
}
deleteFile(item: DocumentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.documentService.deleteExcel(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListDoc();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
// this.modalService.dismissAll()
}, reason => {
// this.modalService.dismissAll()
})
}
}
<app-page-header [title]="'อนุมัติ Excel'" [activeTitle]="'รายการเอกสารรอการอนุมัติ'"
[title1]="'อนุมัติ Excel'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการ Excel</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">รหัสเอกสาร</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์</th>
<th scope="col" class="text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listExcel.length == 0">
<td colspan="8" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr
*ngFor="let data of filterListExcel() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())"></td>
<td >{{data.id}}</td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.logId)"></i></td>
<td class="text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body"
tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fas fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body"
tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fas fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body"
tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fas fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<button type="button" class="btn btn-circle btn-success rounded-circle btn-sm mr-2"
(click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2"
(click)="onCancelApprove(data)"><i class="fas fa-times"></i></button>
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listExcel.length" [maxSize]="3"
[rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<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">
รายการ Excel
</div>
<div class="flex flex-wrap gap-4">
<!-- Buttons -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]='search'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<!-- <th scope="col">รหัสเอกสาร</th> -->
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์</th>
<th scope="col" class="text-center">สถานะ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let data of filterListExcel() | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())"></td>
<!-- <td >{{data.id}}</td> -->
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="ri-links-line text-base font-bold cursor-pointer hover:text-primary"
(click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fa fa-download text-base font-bold cursor-pointer hover:text-primary"
(click)="downloadFile(data.logId)"></i></td>
<td class="text-center">
<span class="fa-stack rt_anim" *ngIf="data.status == 0" ngbTooltip="รออนุมัติ" container="body"
tooltipClass="myhrcolor-1">
<i class="fa fa-circle fa-stack-2x text-info"></i>
<i class=" fa fa-hourglass-half fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 1" ngbTooltip="เปิดใช้งาน" container="body"
tooltipClass="myhrcolor-2">
<i class="fa fa-circle fa-stack-2x text-success"></i>
<i class=" fa fa-check fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack rt_anim" *ngIf="data.status == 2" ngbTooltip="ไม่อนุมัติ" container="body"
tooltipClass="myhrcolor-3">
<i class="fa fa-circle fa-stack-2x text-danger"></i>
<i class="fa fa-times fa-stack-1x fa-inverse" style="font-size: 1.25em;"></i>
</span>
</td>
<td>
<div class="flex space-x-2 justify-center">
<button type="button"
class="bg-success hover:bg-success text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onApprove(data)"><i class="fa fa-check"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="onCancelApprove(data)"><i class="fa fa-times"></i></button>
<button type="button"
class="bg-danger hover:bg-danger text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fa fa-trash"></i></button>
</div>
</td>
</tr>
<tr *ngIf="filterListExcel().length === 0">
<td [attr.colspan]="9" class="!text-center py-4">
<ng-container *ngIf="listExcel.length === 0">
<p>ไม่พบข้อมูล</p>
</ng-container>
<ng-container *ngIf="listExcel.length > 0 && search">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListExcel().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto" [(ngModel)]="pageSize"
(ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListExcel().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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]" href="javascript:void(0);">{{page +
1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < filterListExcel().length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < filterListExcel().length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < filterListExcel().length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { OpenImageComponent } from '../../../open-image/open-image.component';
import { ConfirmModalComponent } from '../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../alert-modal/alert-modal.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../../shared/shared.module';
import { MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { ExcelModel } from '../../../../../models/excel.model';
import { ExcelService } from '../../../../../services/excel.service';
import { firstValueFrom } from 'rxjs';
declare var require: any
import FileSaver from 'file-saver';
import { TemplateModel } from '../../../../../models/template.model';
@Component({
selector: 'app-approve-excel',
templateUrl: './approve-excel.component.html',
styleUrls: ['./approve-excel.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ApproveExcelComponent implements OnInit {
page = 0;
pageSize = 10;
listExcel: ExcelModel[] = []
search: string = ''
pagedItems: ExcelModel[] = [];
constructor(private modalService: NgbModal, private excelService: ExcelService) {
}
get totalItems(): number {
return this.search == ''
? this.listExcel.length
: this.filterListExcel().length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.pageSize);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.page = index;
this.updatePagedItems();
}
updatePagedItems() {
const data: ExcelModel[] = this.search === '' ? this.listExcel : this.filterListExcel();
const start = this.page * this.pageSize;
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
openEmployeeModal(image: string) {
const modalRef = this.modalService.open(OpenImageComponent, {
centered: true,
windowClass: 'my-dialog-img-preview'
})
modalRef.componentInstance.linkImage = image
modalRef.result.then(result => {
}, reason => {
this.modalService.dismissAll()
})
}
async downloadFile(logId: string) {
try {
const data = await this.excelService.downloadFile(logId).toPromise();
if (data) {
FileSaver.saveAs(new Blob([data]), "file_download.xlsx");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
filterListExcel() {
return this.listExcel.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListExcel() {
try {
const data = await firstValueFrom(this.excelService.getListExcel('0'));
this.listExcel = data.map(x => new ExcelModel(x));
} catch (error) {
console.error('Error loading data:', error);
this.listExcel = [];
}
}
onApprove(item: ExcelModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการอนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 1
this.excelService.approve(item).subscribe(result => {
if (result) {
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListExcel();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
onCancelApprove(item: ExcelModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการไม่อนุมัติข้อมูลหรือไม่'
modalRef.result.then(result => {
item.status = 2
this.excelService.approve(item).subscribe(result => {
if (result) {
this.modalService.dismissAll()
this.openAlertModal('บันทึกข้อมูลสำเร็จ')
this.getListExcel();
} else {
this.openAlertModal('ไม่สามารถบันทึกข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
ngOnInit() {
this.getListExcel();
console.log('search:', this.search, 'length:', this.search?.length)
}
deleteFile(item: ExcelModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.excelService.deleteExcel(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListExcel();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
// this.modalService.dismissAll()
}, reason => {
// this.modalService.dismissAll()
})
}
openLink(url: string) {
window.open(url, "_blank");
}
}
<app-page-header [title]="'รายการเอกสารรอการอนุมัติ'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'รายการเอกสารรอการอนุมัติ'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสารรอการอนุมัติ</h4>
<div class="row justify-content-center">
<div class="col-md-4 " *ngFor="let c of testdata|slice: (page-1) * pageSize : (page-1) * pageSize + pageSize">
<div class="card border border-2">
<div class="card-body text-center">
<img src="{{ c.img }}" class="rounded-circle border p-1" width="100">
<h3 class="card-title mt-3 mb-0">{{c.name}}</h3>
</div>
<div class="d-flex justify-content-between bg-light border-top p-3">
<div>
<span class="align-middle">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div >
<button class="btn btn-info btn-sm text-nowrap" (click)="openView(c.id)">รายละเอียด</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
<div class="max-w-7xl mx-auto">
<div class="box p-4">
<h4 class="text-xl font-semibold text-gray-800 mb-6">รายการเอกสารรอการอนุมัติ</h4>
<!-- Flex แทน Grid -->
<div class="flex flex-wrap justify-center gap-6">
<div
class=" bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition duration-300 ease-in-out flex flex-col" style="width: 30%;"
*ngFor="let c of testdata | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize"
>
<div class="p-8 text-center">
<div class="w-24 h-24 mx-auto rounded-full bg-green-100 flex items-center justify-center shadow-inner">
<img src="{{ c.img }}" class="" alt="" />
</div>
<h3 class="text-2xl font-bold text-gray-800 mt-6 mb-2">{{ c.name }}</h3>
</div>
<!-- Footer -->
<div class="mt-auto">
<div class="flex justify-between items-center bg-gray-50 border-t border-gray-200 p-4">
<div>
<span class="text-sm text-gray-700">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div>
<button
class="bg-primary inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md shadow-md
active hover:bg-success hover:scale-110 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
(click)="openView(c.id)"
>
รายละเอียด
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { FormBuilder, FormGroup, FormsModule, Validators } from "@angular/forms";
import { Router, RouterModule } from "@angular/router";
import { ExcelService } from "../../../../services/excel.service";
import { DocumentService } from "../../../../services/document.service";
import { CourseService } from "../../../../services/course.service";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";
import { SharedModule } from "../../../../../shared/shared.module";
@Component({
selector: "app-portal-category-list-approve",
templateUrl: "./portal-category-list-approve.component.html",
styleUrls: ["./portal-category-list-approve.component.scss"],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
TranslateModule,
SharedModule,
],
})
export class PortalCategoryListApproveComponent implements OnInit {
constructor(
private modalService: NgbModal,
private router: Router,
private excelService: ExcelService, private documentService: DocumentService, private courseService: CourseService
) { }
page = 1;
pageSize = 7;
testdata: {
id: string
img: String;
name: String;
details: String;
document: String;
}[] = [];
ngOnInit() {
this.testdata = [
{
id: '1',
img: "assets/images/icons/excel.png",
name: "Excel",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '2',
img: "assets/images/icons/document.png",
name: "Document",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '3',
img: "assets/images/icons/course.png",
name: "Course",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
];
this.getListCount();
}
openView(id: string) {
if (id == '1') {
this.router.navigate(['/admin/approve-excel']);
} else if (id == '2') {
this.router.navigate(['/admin/approve-doc']);
} else if (id == '3') {
this.router.navigate(['/admin/approve-course']);
}
}
async getListCount() {
try {
const countExcel = await this.excelService.getCount().toPromise();
const countDoc = await this.documentService.getCount().toPromise();
const countCourse = await this.courseService.getCount().toPromise();
this.testdata.forEach((x, i) => {
if (i == 0) {
x.document = (countExcel ?? 0).toString();
} else if (i == 1) {
x.document = (countDoc ?? 0).toString();
} else if (i == 2) {
x.document = (countCourse ?? 0).toString();
}
});
} catch (error) {
console.error('Error loading data:', error);
}
}
openModal(targetModal: NgbModal) {
this.modalService.open(targetModal, {
centered: true,
backdrop: "static",
});
}
closeBtnClick() {
this.modalService.dismissAll();
}
routerLink(path: String, type: String) {
this.router.navigate([path, { type: type }])
}
}
/* 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 { MyportalComponent } from './myportal.component';
describe('MyportalComponent', () => {
let component: MyportalComponent;
let fixture: ComponentFixture<MyportalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MyportalComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyportalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-myportal',
templateUrl: './myportal.component.html',
styleUrls: ['./myportal.component.css']
})
export class MyportalComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<div class="p-4">
<img [src]="linkImage" alt="Image Preview" class="w-full h-auto max-w-full block mx-auto" />
<div class="mt-4 text-right">
<button
(click)="onClose()"
class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 transition-colors duration-200"
>
ปิด
</button>
</div>
</div>
\ No newline at end of file
import { Component, OnInit, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; // นำเข้า MAT_DIALOG_DATA และ MatDialogRef
@Component({
selector: 'app-open-image',
templateUrl: './open-image.component.html',
styleUrls: ['./open-image.component.scss']
})
export class OpenImageComponent implements OnInit {
linkImage: string = '';
constructor(
@Inject(MAT_DIALOG_DATA) public data: { linkImage: string },
public dialogRef: MatDialogRef<OpenImageComponent>
) { }
ngOnInit(): void {
if (this.data && this.data.linkImage) {
this.linkImage = this.data.linkImage;
}
}
onClose(): void {
this.dialogRef.close();
}
}
\ No newline at end of file
<app-page-header [title]="'รายการเอกสาร'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'รายการเอกสาร'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="row justify-content-center">
<div class="col-lg-4 col-md-6 col-sm-6 col-12" *ngFor="let c of testdata|slice: (page-1) * pageSize : (page-1) * pageSize + pageSize">
<div class="card border-border-2">
<div class="card-body text-center">
<img src="{{ c.img }}" class="rounded-circle border p-1" width="100">
<h3 class="card-title mt-3 mb-0">{{c.name}}</h3>
</div>
<div class="d-flex justify-content-between bg-light border-top p-3">
<div>
<span class="align-middle">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div >
<button class="btn btn-info btn-sm text-nowrap" (click)="openView(c.id)">รายละเอียด</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
<div class="max-w-7xl mx-auto">
<div class="box p-4">
<h4 class="text-xl font-semibold text-gray-800 mb-6">รายการเอกสาร</h4>
<!-- Flex แทน Grid -->
<div class="flex flex-wrap justify-center gap-6">
<div
class=" bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition duration-300 ease-in-out flex flex-col" style="width: 30%;"
*ngFor="let c of testdata | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize"
>
<div class="p-8 text-center">
<div class="w-24 h-24 mx-auto rounded-full bg-green-100 flex items-center justify-center shadow-inner">
<img src="{{ c.img }}" class="" alt="" />
</div>
<h3 class="text-2xl font-bold text-gray-800 mt-6 mb-2">{{ c.name }}</h3>
</div>
<!-- Footer -->
<div class="mt-auto">
<div class="flex justify-between items-center bg-gray-50 border-t border-gray-200 p-4">
<div>
<span class="text-sm text-gray-700">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div>
<button
class="bg-primary inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700
text-white text-sm font-medium rounded-md shadow-md ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50
active hover:bg-success hover:scale-110 transition duration-300"
(click)="openView(c.id)"
>
รายละเอียด
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { Router, RouterModule } from '@angular/router';
import { ExcelService } from '../../../services/excel.service';
import { DocumentService } from '../../../services/document.service';
import { CourseService } from '../../../services/course.service';
import { WidgetService } from '../../../services/widgets.service';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
@Component({
selector: 'app-portal-category-list',
templateUrl: './portal-category-list.component.html',
styleUrls: ['./portal-category-list.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
TranslateModule,
SharedModule,
],
})
export class PortalCategoryListComponent implements OnInit {
constructor(
private routes: Router,
private excelService: ExcelService, private documentService: DocumentService, private courseService: CourseService, private widgetService: WidgetService
) { }
page = 1;
pageSize = 10;
testdata: {
id: string;
img: String;
name: String;
details: String;
document: String;
}[] = [];
ngOnInit() {
this.testdata = [
{
id: '1',
img: "assets/images/icons/excel.png",
name: "Excel",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '2',
img: "assets/images/icons/document.png",
name: "Document",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '3',
img: "assets/images/icons/course.png",
name: "Course",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '4',
img: "assets/images/icons/widget.png",
name: "Widgets",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id: '5',
img: "assets/images/faces/1.jpg",
name: "BI",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
];
this.getListCount();
}
async getListCount() {
try {
const countExcel = await this.excelService.getCountContent().toPromise();
const countDoc = await this.documentService.getCountContent().toPromise();
const countCourse = await this.courseService.getCountContent().toPromise();
const countWidgets = await this.widgetService.getCount().toPromise();
this.testdata.forEach((x, i) => {
if (i == 0) {
x.document = countExcel!.toString()
} else if (i == 1) {
x.document = countDoc!.toString()
} else if (i == 2) {
x.document = countCourse!.toString()
} else if (i == 3) {
x.document = countWidgets!.toString()
}
})
} catch (error) {
console.error('Error loading data:', error);
}
}
openView(id: string) {
if (id == '1') {
this.routes.navigate(['/admin/view-list-excel']);
} else if (id == '2') {
this.routes.navigate(['/admin/view-list-doc']);
} else if (id == '3') {
this.routes.navigate(['/admin/view-list-course']);
} else if (id == '4') {
this.routes.navigate(['/admin/view-list-widgets']);
}
}
}
<app-page-header [title]="'รายการอัพโหลดเอกสาร'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการอัพโหลดเอกสาร'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการอัพโหลดเอกสาร</h4>
<div class="row justify-content-center">
<div class="col-md-4 " *ngFor="let c of testdata|slice: (page-1) * pageSize : (page-1) * pageSize + pageSize">
<div class="card border border-2">
<div class="card-body text-center">
<img src="{{ c.img }}" class="rounded-circle border p-1" width="100">
<h3 class="card-title mt-3 mb-0">{{c.name}}</h3>
</div>
<div class="d-flex justify-content-between bg-light border-top p-3">
<div>
<span class="align-middle">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div >
<button class="btn btn-info btn-sm text-nowrap" (click)="openView(c.id)">รายละเอียด</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
<div class="max-w-7xl mx-auto">
<div class="box p-4">
<h4 class="text-xl font-semibold text-gray-800 mb-6">รายการเอกสาร</h4>
<!-- Flex แทน Grid -->
<div class="flex flex-wrap justify-center gap-6">
<div
class=" bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition duration-300 ease-in-out flex flex-col"
style="width: 30%;" *ngFor="let c of testdata | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize">
<div class="p-8 text-center">
<div class="w-24 h-24 mx-auto rounded-full bg-green-100 flex items-center justify-center shadow-inner">
<img src="{{ c.img }}" class="" alt="" />
</div>
<h3 class="text-2xl font-bold text-gray-800 mt-6 mb-2">{{ c.name }}</h3>
</div>
<!-- Footer -->
<div class="mt-auto">
<div class="flex justify-between items-center bg-gray-50 border-t border-gray-200 p-4">
<div>
<span class="text-sm text-gray-700">จำนวนเอกสาร {{ c.document }} ฉบับ</span>
</div>
<div>
<button
class="bg-primary inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md shadow-md
active hover:bg-success hover:scale-110 transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
(click)="openView(c.id)">
รายละเอียด
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add File -->
<!-- <ng-template #editTemplateModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">Add File Type Category</h5>
<button type="button" class="close" (click)="closeBtnClick()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="edittemplate" (ngSubmit)="onSubmit()">
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">ชื่อประเภทไฟล์</label>
<div class="col-sm-8">
<input type="text" class="form-control" formControlName="Name" id="name" (blur)=logValidationErrors(edittemplate)>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">รายละเอียด</label>
<div class="col-sm-8">
<textarea class="form-control"></textarea>
</div>
</div>
<div class="form-group row">
<label for="name" class="col-sm-4 col-form-label">รูปประเภทไฟล์</label>
<div class="col-sm-8">
<input type="file" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="closeBtnClick()">Close</button>
<button type="submit" class="btn btn-primary" >Save</button>
</div>
</form>
</div>
</ng-template> -->
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { Router, RouterModule } from '@angular/router';
import { ExcelService } from '../../../services/excel.service';
import { DocumentService } from '../../../services/document.service';
import { CourseService } from '../../../services/course.service';
import { WidgetService } from '../../../services/widgets.service';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { SharedModule } from '../../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'app-portal-create-category',
templateUrl: './portal-create-category.component.html',
styleUrls: ['./portal-create-category.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
TranslateModule,
SharedModule,
],
})
export class PortalCreateCategoryComponent implements OnInit {
constructor(private modalService: NgbModal,private routes: Router,
private excelService:ExcelService,
private documentService:DocumentService,
private courseService:CourseService,
private widgetService:WidgetService
) {}
page = 1;
pageSize = 10;
testdata: {
id:string;
img: String;
name: String;
details: String;
document: String;
}[] = [];
ngOnInit() {
this.testdata = [
{
id:'1',
img: "assets/images/icons/excel.png",
name: "Excel",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'2',
img: "assets/images/icons/document.png",
name: "Document",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'3',
img: "assets/images/icons/course.png",
name: "Course",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'4',
img: "assets/images/icons/widget.png",
name: "Widgets",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
},
{
id:'5',
img: "assets/images/faces/1.jpg",
name: "BI",
details: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
document: "0"
}
];
this.getListCount();
}
async getListCount(){
try {
const countExcel = await this.excelService.getCount().toPromise();
const countDoc = await this.documentService.getCount().toPromise();
const countCourse = await this.courseService.getCount().toPromise();
const countWidgets= await this.widgetService.getCount().toPromise();
this.testdata.forEach((x ,i) => {
if(i == 0){
x.document = countExcel!.toString()
}else if(i == 1){
x.document = countDoc!.toString()
}else if(i == 2){
x.document = countCourse!.toString()
}else if(i == 3){
x.document = countWidgets!.toString()
}
})
} catch (error) {
console.error('Error loading data:', error);
}
}
openView(id:string){
if(id =='1'){
this.routes.navigate(['/admin/list-excell']);
}else if(id =='2'){
this.routes.navigate(['/admin/list-doc']);
}else if(id =='3'){
this.routes.navigate(['/admin/list-course']);
}else if(id =='4'){
this.routes.navigate(['/admin/list-widgets']);
}
}
openModal(targetModal: NgbModal) {
this.modalService.open(targetModal, {
centered: true,
backdrop: "static",
});
}
closeBtnClick() {
this.modalService.dismissAll();
}
}
<app-page-header [title]="'เพิ่มรายงาน Excel'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'เพิ่มรายงาน Excel'"></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">
รายการเอกสาร
</div>
<div class="flex flex-wrap gap-4">
<!-- Search By -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Search By</label>
<select class="form-control h-100" [(ngModel)]="searchBy">
<option style="color: red" [class.d-none]="searchBy==''" [value]="''">
{{searchBy!=''?'ยกเลิก':'--- เลือก ---'}}</option>
<option [value]="'templateId'">รหัสกลุ่มแม่แบบ</option>
<option [value]="'module'">โมดูล</option>
<option [value]="'tname'">ชื่อกลุ่มรายงาน (ภาษาไทย)</option>
<option [value]="'ename'">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</option>
<option [value]="'tdesc'">รายละเอียด (ภาษาไทย)</option>
<option [value]="'edesc'">รายละเอียด (ภาษาอังกฤษ)</option>
</select>
</div>
<!-- Condition -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Condition</label>
<select class="form-control h-100" [(ngModel)]="condition">
<option style="color: red" [class.d-none]="condition==''" [value]="''">
{{condition!=''?'ยกเลิก':'--- เลือก ---'}}</option>
<option value="includes">คำในประโยค</option>
<option value="lt">น้อยกว่า</option>
<option value="gt">มากกว่า</option>
<option value="eq">เท่ากับ</option>
<option value="lte">น้อยกว่าเท่ากับ</option>
<option value="gte">มากกว่าเท่ากับ</option>
<option value="neq">ไม่เท่ากับ</option>
</select>
</div>
<!-- Key Value -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Key Value</label>
<input class="form-control h-100" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]="searchValue">
</div>
<!-- Buttons -->
<div class="flex items-end gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full"
style="margin-bottom: unset;" (click)="openTemplate.clear();templateListSearch()">
<i class="ri-search-line font-semibold align-middle"></i>ค้นหา
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-success-full"
style="margin-bottom: unset;" (click)="openAddGroupModal()">
<i class="ri-add-line font-semibold align-middle"></i>Add Group
</a>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน (ภาษาไทย)</th>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</th>
<th class="text-center" scope="col" style="min-width: 200px">สร้างโดย</th>
<th class="text-center" scope="col" style="min-width:120px">วันที่สร้าง</th>
<th class="text-center" scope="col">สร้างเมื่อ</th>
<th class="text-center" scope="col" style="min-width: 120px;">โมดูล</th>
<th class="text-center" scope="col">Add file</th>
</tr>
</thead>
<tbody *ngIf="!template.filter.length">
<tr style="background-color:#ebf2f6">
<td colspan="7" class="text-center">ไม่พบข้อมูล</td>
</tr>
</tbody>
<tbody id="tableBody" *ngIf="isSearching || template.filter.length">
<tr *ngIf="isSearching">
<td colspan="7" class="loading">
<div class="spinner"></div>
<div class="spinner"></div>
<div class="spinner"></div>
<span>กำลังค้นหา...</span>
</td>
</tr>
<ng-container *ngIf="!isSearching">
<ng-container
*ngFor="let items of template.filter | slice: page * pageSize : (page + 1) * pageSize; let i = index">
<tr (mouseenter)="tableHover.set(items.templateId,!tableHover.get(items.templateId))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(items.templateId)?'rgb(201 223 235)':'#ebf2f6'}">
<td colspan="6" class="font-16 font-medium">
<span class="cursor-pointer text-info" style="text-decoration: underline;"
(click)="openAddGroupModal(items)">
{{items.tname}}
</span>
<span *ngIf="items.tdesc">
-{{items.tdesc}}
</span>
</td>
<td class="text-center">
<ng-container *ngIf="items.templateFile.length">
<ng-container *ngIf="!openTemplate.get(items.templateId)">
<a aria-label="anchor" title="เปิด"
(click)="openTemplate.set(items.templateId, true)"
(mouseenter)="buttonHover.set(items.templateId, true)"
(mouseleave)="buttonHover.set(items.templateId, false)"
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-arrow-right-line"></i></a>
</ng-container>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<a aria-label="anchor" title="ปิด" (click)="
openTemplate.set(items.templateId,false)"
(mouseenter)="buttonHover.set(items.templateId+'close',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
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-arrow-down-line"></i></a>
</ng-container>
&nbsp;
</ng-container>
<a aria-label="anchor" title="เพิ่ม EXCCHILD"
(click)="openAddChildModal(items,'add')"
(mouseenter)="buttonHover.set(items.templateId+'add',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
class="ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-success/10 text-success hover:bg-success hover:text-white hover:border-success"><i
class="ri-save-2-line"></i></a>
&nbsp;
<a aria-label="anchor" title="ลบ" (click)="deleteTemplate(items)"
(mouseenter)="buttonHover.set(items.templateId+'delete',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
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>
</td>
</tr>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<ng-container
*ngFor="let item of items.templateFile | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<tr (mouseenter)="tableHover.set(item.fileName,!tableHover.get(item.fileName))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.fileName)?'rgb(201 223 235)':'#ffffff'}">
<ng-container *ngIf="downloadTemplateFileLoading.get(item.fileName)">
<td colspan="7" class="text-center">
<div *ngFor="let item of [1,2,3]"
class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</td>
</ng-container>
<ng-container *ngIf="!downloadTemplateFileLoading.get(item.fileName)">
<td style="white-space: normal !important;word-break: break-word;">
<i *ngIf="item.menuActive=='0'"
class="fa fa-times text-danger cursor-pointer"
style="text-decoration: underline;"
(click)="menuActiveTemplateFile(item)"></i>
<i *ngIf="item.menuActive=='1'"
class="fa fa-check text-success cursor-pointer"
style="text-decoration: underline;"
(click)="menuActiveTemplateFile(item)"></i>
&nbsp;
<span class="cursor-pointer text-info"
style="text-decoration: underline;"
(click)="openAddChildModal(item,'edit')">
{{item.tdesc}}
</span>
</td>
<td style="white-space: normal !important;word-break: break-word;">
{{item.edesc}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
<!-- {{item.createBy.thFullName}}</td> -->
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).date}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).time}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{item.module}}</td>
<td class="text-center" class="text-center">
<a aria-label="anchor" title="Download File"
(mouseenter)="buttonHover.set(item.fileName+'copy',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
(click)="downloadTemplateFile(item)"
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-file-download-line"></i></a>
&nbsp;
<a aria-label="anchor" title="Print"
(mouseenter)="buttonHover.set(item.fileName+'print',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
(click)="openPrintModal(item);"
class="ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-warning/10 text-warning hover:bg-warning hover:text-white hover:border-warning"><i
class="ri-printer-line"></i></a>
&nbsp;
<a aria-label="anchor" title="ลบ"
(mouseenter)="buttonHover.set(item.fileName+'delete',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
(click)="deleteTemplateFile(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>
</td>
</ng-container>
</tr>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="template.filter.length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto"
[(ngModel)]="pageSize" (ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{template.filter.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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{page + 1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < template.filter.length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < template.filter.length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < template.filter.length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="flex justify-end items-center flex-wrap gap-4 mt-3">
<div><i class="fa fa-times text-danger"></i> = used on menu</div>
<div><i class="fa fa-check text-success"></i> = unused on menu</div>
<div><i class="ri-file-download-line text-info"></i> = download files</div>
<div><i class="ri-printer-line text-warning"></i> = print report</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #addGroupModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
EXCHEAD, Template header excel
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalAddGroup()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<label for="deal-title" class="form-label">รหัสกลุ่มแม่แบบ</label>
<input type="text" class="form-control !bg-input-readonly" id="deal-title" placeholder=""
value="AUTO-001" disabled [value]="bodyTemplate.data.templateId">
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-title" class="form-label">ชื่อกลุ่มรายงาน (ภาษาไทย)
<span class="required text-danger">*</span>
</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="bodyTemplate.data.tname">
<div class="text-danger" *ngIf="!bodyTemplate.data.tname">
กรอกชื่อกลุ่มรายงานภาษาไทย
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-title" class="form-label">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)
<span class="required text-danger">*</span>
</label>
<input type="text" class="form-control" id="deal-title" placeholder=""
[(ngModel)]="bodyTemplate.data.ename">
<div class="text-danger" *ngIf="!bodyTemplate.data.ename">
Enter report group name in English
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-title" class="form-label">รายละเอียด (ภาษาไทย)</label>
<textarea type="text" class="form-control" id="deal-title"
placeholder="กรอกรายละเอียดภาษาไทย..." [(ngModel)]="bodyTemplate.data.tdesc"></textarea>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="deal-title" class="form-label">รายละเอียด (ภาษาอังกฤษ)</label>
<textarea type="text" class="form-control" id="deal-title"
placeholder="Enter description in English..."
[(ngModel)]="bodyTemplate.data.edesc"></textarea>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">
โมดูล
<span class="required text-danger">*</span>
</label>
<select class="form-select" required [disabled]="bodyTemplate.status=='edit'"
[(ngModel)]="bodyTemplate.data.module">
<option style="color: red" [class.d-none]="bodyTemplate.data.module==''" [value]="''">
{{bodyTemplate.data.module!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of module.data" [ngValue]="item.code">{{item.tdesc}}
</option>
</select>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">
ชื่อบริษัท
<span class="required text-danger">*</span>
</label>
<select class="form-select" required [disabled]="bodyTemplate.status=='edit'"
[(ngModel)]="bodyTemplate.data.companyId">
<option style="color: red" [class.d-none]="bodyTemplate.data.companyId==''" [value]="''">
{{bodyTemplate.data.companyId!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of itemsList" [ngValue]="item.companyId">{{item.companyName}}</option>
</select>
</div>
</div>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-center gap-3 mb-3 mt-3">
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalAddGroup()">
{{'Cancel' | translate}}
</button>
<button type="button"
[class.ti-btn-disabled]="!bodyTemplate.data.tname || !bodyTemplate.data.ename ||!bodyTemplate.data.module"
[disabled]="!bodyTemplate.data.tname || !bodyTemplate.data.ename ||!bodyTemplate.data.module"
(click)="postTemplate()" class="ti-btn bg-primary text-white !font-medium">{{'Save' |
translate}}</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #addChildModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
EXCCHILD, Template file excel
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModaladdChild()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="flex flex-col space-y-4">
<!-- ตัวเลือกไฟล์จาก Portal -->
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<div class="flex items-center gap-2 mb-2">
<input type="radio" class="custom-control-input cursor-pointer" name="excelFile"
id="excelFilePortal" [checked]="templateFileType=='portal'"
(click)="templateFileTypeChange()">
<label class="custom-control-label cursor-pointer font-bold" for="excelFilePortal">
เลือกเอ็กเซลล์ไฟล์จาก Portal
<span *ngIf="templateFileType=='portal'" class="text-red">*</span>
</label>
</div>
<div *ngIf="templateFileType=='portal'" class="flex items-center w-full col-span-2">
<input *ngIf="templateFile.portalId" type="text"
class="form-control bg-white w-full flex-grow" style="border-radius: 0px;"
[value]="templateFile.portalId" readonly>
<button type="button"
class="hs-dropdown-toggle ti-btn ti-btn-primary-full flex-shrink-0 !mb-0"
style="border-radius: 0.1rem;" (click)="openExcelPortalModal()">
<i class="ri-search-line font-semibold align-middle"></i>
</button>
</div>
</div>
<!-- อัปโหลดไฟล์ -->
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<div class="flex items-center gap-2 mb-2">
<input type="radio" class="custom-control-input cursor-pointer" name="excelFile"
id="excelFileUpload" [checked]="templateFileType=='upload'"
(click)="templateFileTypeChange()">
<label class="custom-control-label cursor-pointer font-bold" for="excelFileUpload">
อัพโหลดเอ็กเซลล์ไฟล์
<span *ngIf="templateFileType=='upload'" style="color: red">*</span>
</label>
</div>
<div *ngIf="templateFileType=='upload'" class="flex items-center gap-2 truncate">
<input #fileInput type="file" accept=".xlsx" (change)="onFileSelected($event)"
style="display: none;" />
<button (click)="fileInput.click()"
class="px-2 py-1 border border-black rounded bg-white hover:bg-gray-100 shadow-sm font-bold whitespace-nowrap">
เลือกไฟล์
</button>
<span *ngIf="templateFileName"
class="truncate block max-w-[200px] overflow-hidden whitespace-nowrap text-ellipsis">
{{ templateFileName }}
</span>
</div>
</div>
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<div
class="col-span-3 lg:col-span-5 sm:inline-flex sm:items-center space-y-2 sm:space-y-0 sm:space-x-3 w-full rtl:space-x-reverse">
<label class="ti-form-label block mb-1 font-bold" style="width: 50%;">รายละเอียด (ภาษาไทย)
</label>
<input type="text" id="input-label" class="ti-form-input w-full "
[(ngModel)]="templateFile.tdesc">
</div>
</div>
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<div
class="col-span-3 lg:col-span-5 sm:inline-flex sm:items-center space-y-2 sm:space-y-0 sm:space-x-3 w-full rtl:space-x-reverse">
<label class="ti-form-label block mb-1 font-bold" style="width: 50%;">รายละเอียด
(ภาษาอังกฤษ)
</label>
<input type="text" id="input-label" class="ti-form-input w-full "
[(ngModel)]="templateFile.edesc">
</div>
</div>
<!-- แบ่งปัน -->
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<label class="ti-form-label block mb-1 font-bold">แบ่งปัน</label>
<div class="flex gap-4">
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="share"
id="shareNo" [checked]="templateFile.isShare=='0'"
(click)="templateFile.isShare='0'">
<label class="custom-control-label cursor-pointer" for="shareNo">ไม่ใช่</label>
</div>
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="share"
id="shareYes" [checked]="templateFile.isShare=='1'"
(click)="templateFile.isShare='1'">
<label class="custom-control-label cursor-pointer" for="shareYes">ใช่</label>
</div>
</div>
</div>
<!-- แสดง Pivot -->
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<label class="ti-form-label block mb-1 font-bold">แสดงรูปแบบ pivot</label>
<div class="flex gap-4">
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="pivot"
id="pivotNo" [checked]="templateFile.isPivot=='0'"
(click)="templateFile.isPivot='0'">
<label class="custom-control-label cursor-pointer" for="pivotNo">ไม่ใช่</label>
</div>
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="pivot"
id="pivotYes" [checked]="templateFile.isPivot=='1'"
(click)="templateFile.isPivot='1'">
<label class="custom-control-label cursor-pointer" for="pivotYes">ใช่</label>
</div>
</div>
</div>
<!-- แสดง DataGrid -->
<div class="sm:grid grid-cols-3 gap-x-6 space-y-4 lg:space-y-0 mt-5">
<label class="ti-form-label block mb-1 font-bold">แสดงรูปแบบ datagrid</label>
<div class="flex gap-4">
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="datagt"
id="datagtNo" [checked]="templateFile.isDataGrid=='0'"
(click)="templateFile.isDataGrid='0'">
<label class="custom-control-label cursor-pointer" for="datagtNo">ไม่ใช่</label>
</div>
<div class="flex items-center gap-2">
<input type="radio" class="custom-control-input cursor-pointer" name="datagt"
id="datagtYes" [checked]="templateFile.isDataGrid=='1'"
(click)="templateFile.isDataGrid='1'">
<label class="custom-control-label cursor-pointer" for="datagtYes">ใช่</label>
</div>
</div>
</div>
</div>
</div>
<div class="border-t mt-3">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModaladdChild()">
{{'Cancel' | translate}}
</button>
<button type="submit"
[class.ti-btn-disabled]="(!templateFile.portalId&&!templateFileName)||!templateFile.tdesc"
[disabled]="(!templateFile.portalId&&!templateFileName)||!templateFile.tdesc"
(click)="postTemplateFile()" class="ti-btn bg-primary text-white !font-medium">{{'Save' |
translate}}</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #excel_portalModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
EXCEL_PORTAL
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalexcel_portal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="flex flex-col space-y-4">
<div class="flex flex-wrap items-start justify-between gap-4">
<div class="flex flex-wrap gap-4">
<div class="flex flex-col min-w-[200px]">
<select class="custom-select" [(ngModel)]="excelPortalSearch.groupId">
<option value="" *ngIf="excelPortalSearch.groupId==''" disabled selected hidden>
เลือกกลุ่ม</option>
<option style="color: red" [class.d-none]="excelPortalSearch.groupId==''"
[value]="''">
{{excelPortalSearch.groupId!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of excelPortalGroup.data" [ngValue]="item.groupId">
{{item.thName}}
</option>
</select>
</div>
<div class="flex flex-col min-w-[200px]">
<select class="custom-select" [(ngModel)]="excelPortalSearch.tags">
<option value="" *ngIf="excelPortalSearch.tags==''" disabled selected hidden>เลือก
tags</option>
<option style="color: red" [class.d-none]="excelPortalSearch.tags==''" [value]="''">
{{excelPortalSearch.tags!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of excelPortalTags.data" [ngValue]="item.value">
{{item.value}}
</option>
</select>
</div>
</div>
<div class="flex flex-col min-w-[200px]">
<input type="text" class="form-control" placeholder="ค้นหา"
[(ngModel)]="excelPortalSearch.search">
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table mb-0 no-wrap v-middle " style="width: 100%;">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">Ex.</th>
<th class="text-center" scope="col">VDO</th>
<th class="text-center" scope="col" style="min-width: 200px">Name</th>
<th class="text-center" scope="col" style="min-width: 200px">Description</th>
<th class="text-center" scope="col">Database Support</th>
<th class="text-center" scope="col">Pivot</th>
<th class="text-center" scope="col">DataGrid</th>
<th class="text-center" scope="col">Download</th>
<th class="text-center" scope="col">Last Download</th>
<th class="text-center" scope="col">Upload Date</th>
</tr>
</thead>
<tbody *ngIf="!excelPortalFilter().length">
<tr style="background-color:#ebf2f6;">
<td colspan="10" class="text-center align-middle">ไม่พบข้อมูล</td>
</tr>
</tbody>
<tbody *ngIf="excelPortalFilter().length">
<ng-container
*ngFor="let item of excelPortalFilter() | slice: pageModal * pageSizeModal : (pageModal+1) * pageSizeModal ; let i = index">
<tr (mouseenter)="tableHover.set(item.excelId,!tableHover.get(item.excelId))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.excelId)?'rgb(201 223 235)':'#ebf2f6'}">
<td><img *ngIf="item.getImg()" class="border p-1" style="width: auto;"
[src]="item.getImg()">
</td>
<td>
<button *ngIf="item.link1" type="button"
class="hs-dropdown-toggle ti-btn ti-btn-primary-full"
(click)="openVideoModal(item.link1)"><i
class="ri-search-line font-semibold align-middle"></i></button>
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.thName}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.thDesc}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.dbSupport}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.isPivot}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.isDataGrid}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.dwTime}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.downloadDate}} {{item.downloadTime}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.uploadDate}} {{item.uploadTime}}
</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="box-footer !border-t-0">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="excelPortalFilter().length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{excelPortalFilter().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="pageModal > 0" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal - 1; updatePagedItems()">{{'Previous' |
translate}}</a>
</li>
<li class="page-item" *ngIf="pageModal - 1 >= 0">
<a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
(click)="pageModal = pageModal - 1; updatePagedItems()">{{pageModal}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageModal + 1}}</a>
</li>
<li class="page-item"
*ngIf="(pageModal + 1) * pageSizeModal < excelPortalFilter().length">
<a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
(click)="pageModal = pageModal + 1; updatePagedItems()">{{pageModal + 2}}</a>
</li>
<li class="page-item"
*ngIf="(pageModal + 2) * pageSizeModal < excelPortalFilter().length">
<a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
(click)="pageModal = pageModal + 2; updatePagedItems()">{{pageModal + 3}}</a>
</li>
<li *ngIf="(pageModal + 1) * pageSizeModal < excelPortalFilter().length"
class="page-item"><a class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal + 1; updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="border-t mt-3">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalexcel_portal()">
{{'Cancel' | translate}}
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #videoModal let-modal>
<div style="width: 100%; height: 100%;">
<iframe style="width: 100%; height: 100%;" [src]="videoLink | safeUrl" frameborder="0"
allow="autoplay; encrypted-media" allowfullscreen>
</iframe>
</div>
</ng-template>
<ng-template #printModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
Excel Report
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalprintModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="row">
<div class="d-flex col-12 justify-content-center" *ngIf="loading||loadingExcel">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<ng-container *ngIf="!loading&&!loadingExcel">
<ng-container *ngFor="let item of variableSheet; let i=index">
<div class="flex items-center mb-4">
<!-- Label -->
<label class="w-1/4 text-right pr-4 font-semibold text-sm">
{{ item.label }}
</label>
<!-- Input -->
<div class="w-3/4">
<ng-container [ngSwitch]="item.type">
<!-- Text -->
<input *ngSwitchCase="'text'" type="text" class="form-input w-full"
[(ngModel)]="item.value" />
<!-- List -->
<select *ngSwitchCase="'list'" class="form-select w-full"
[(ngModel)]="item.value">
<option *ngFor="let list of item.option" [value]="list.value">
{{ list.text }}
</option>
</select>
<!-- Radio -->
<div *ngSwitchCase="'radio'" class="flex flex-wrap gap-4">
<label *ngFor="let radioItem of item.option"
class="inline-flex items-center">
<input type="radio" class="form-radio mr-2"
[name]="'group-' + item.label" [id]="radioItem.text+radioItem.value"
[value]="radioItem.value" [(ngModel)]="item.value" />
{{ radioItem.text }}
</label>
</div>
<!-- Help -->
<div *ngSwitchCase="'help'" class="flex items-stretch">
<input type="text" readonly class="form-input w-1/2 bg-white cursor-pointer"
[value]="item.value.tdesc" (click)="openModalData(item)" />
<button
class="bg-primary hover:bg-primary text-white px-3 flex items-center justify-center rounded-none border"
type="button" (click)="openModalData(item)">
<i class="ri-search-line font-semibold align-middle"></i>
</button>
<button type="button"
class="bg-red hover:bg-red text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="item.value={id:'',tdesc:'',edesc:''}">
<i class="fa fa-times text-xs"></i>
</button>
</div>
<!-- Calendar -->
<div *ngSwitchCase="'calendar'" class="flex items-stretch">
<!-- Input -->
<input class="form-input w-1/2 bg-white cursor-pointer rounded-r-none"
style="border-color: #e9edf6;" placeholder="dd.mm.yyyy" name="dp1"
ngbDatepicker #d1="ngbDatepicker" [(ngModel)]="select[item.key]"
readonly (click)="d1.toggle()"
(ngModelChange)="formatNgbDate(item.key, select[item.key])"
#c1="ngModel" (change)="validateDate(c1)" container="body">
<!-- Calendar Button -->
<button type="button"
class="bg-white hover:bg-primary text-primary hover:text-white px-3 flex items-center justify-center rounded-none border"
style="border-color: #154c9c;" (click)="d1.toggle()">
<i class="ri-calendar-2-line"></i>
</button>
<!-- Clear Button -->
<button type="button"
class="bg-red hover:bg-red text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="select[item.key]=null ;formatNgbDate(item.key)">
<i class="fa fa-times text-xs"></i>
</button>
</div>
</ng-container>
</div>
</div>
</ng-container>
<!-- <div *ngIf="variableSheet.length&&!loadingExcel"
class="col-12 justify-content-center align-content-center d-flex" style="margin-bottom: 1rem;">
<button type="submit" class="btn btn-info waves-effect waves-light btn-w-100"
(click)="dowloadExcelReport()">
{{"Print" }}
</button>
</div>
<div *ngIf="loadingExcel" class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div> -->
<div *ngIf="!variableSheet.length"
class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;margin-top: 1rem;">
<div
class="col-3 justify-content-center text-center font-weight-bold control-label col-form-label font-14">
{{'No Data Found' }}
</div>
</div>
<div class="row col-12 flex justify-center"
*ngIf="variableSheet.length&&(excelReport.isDataGrid=='1'||excelReport.isPivot=='1')">
<div class="col-12 d-flex justify-content-center align-content-center">
<button type="button" *ngIf="excelReport.isDataGrid=='1'"
class="ti-btn ti-btn-primary-full" (click)="getExcelData('grid')">Datagrid</button>
<ng-container *ngIf="excelReport.isDataGrid=='1'&&excelReport.isPivot=='1'">
&nbsp;
</ng-container>
<button type="button" *ngIf="excelReport.isPivot=='1'"
class="ti-btn ti-btn-primary-full" (click)="getExcelData('pivot')">Pivot</button>
</div>
</div>
</ng-container>
</div>
</div>
<div class="border-t mt-3">
<div class="ti-modal-footer flex justify-end gap-1 mb-3 mt-3 mr-3">
<ng-container *ngIf="variableSheet.length">
<button type="submit" class="ti-btn ti-btn-info-full waves-effect waves-light btn-w-100"
[disabled]="loadingExcel" (click)="dowloadExcelReport()">
{{"Print" }}
</button>
</ng-container>
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalprintModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #modalData let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
{{modalDetail.text.cardHead }}
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalmodalData()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="d-flex mb-1">
<input type="text" placeholder="{{'systemcode.search' }} {{modalDetail.text.search[0]}}"
class=" form-control w-75 border-color-gray-full-focus" [(ngModel)]='searchModal'>
</div>
<div class="table-responsive">
<table class="table table-hover table-striped-myhr table-sm mb-0 no-wrap v-middle "
style="width: 100%">
<thead class="bg-info ">
<tr class="text-white font-weight-normal">
<th class="font-weight-normal text-center" scope="col"
*ngFor="let item of modalDetail.text.tableHead">
{{item}}
</th>
</tr>
</thead>
<tbody *ngIf="!valueDetailFilter().length">
<tr>
<td colspan="9" class="font-weight-normal text-center">
{{"No Data Found" }}
</td>
</tr>
</tbody>
<tbody *ngIf="valueDetailFilter().length">
<tr class="cursor-pointer"
*ngFor="let item of valueDetailFilter() | slice: pageModal * pageSizeModal : (pageModal+1) * pageSizeModal; let i=index"
(click)="selectData(item);closeModalmodalData()"
(mouseenter)="tableHover.set(item.id,!tableHover.get(item.id))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.id)?'rgb(201 223 235)':'#ffffff'}">
<td class="align-middle text-center">
{{item.id}}
</td>
<td class="align-middle text-center">
{{item.tdesc}}
</td>
<td class="align-middle text-center">
{{item.edesc}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer !border-t-0">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="valueDetailFilter().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto"
[(ngModel)]="pageSizeModal" (ngModelChange)="pageModal">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"Items per page"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{valueDetailFilter().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="pageModal>0" class="page-item {{pageModal==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal-1;updatePagedItems()">{{'Previous' |
translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageModal-1>0"
(click)="pageModal = pageModal-2;updatePagedItems()">{{pageModal-1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageModal>0 && ((pageModal-1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length))"
(click)="pageModal = pageModal-1;updatePagedItems()">{{pageModal}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageModal +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageModal+1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
(click)="pageModal = pageModal+1;updatePagedItems()">{{pageModal +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageModal+2)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
(click)="pageModal = pageModal+2;updatePagedItems()">{{pageModal +3}}</a></li>
<li *ngIf="(pageModal+1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
class="page-item"><a class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalmodalData()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #gridModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
GridData
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalgridModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<app-datagrid-syncfution #pivotComponent [searchSettings]="searchSettings" [dataSource]="dataList" [columns]="columns"
[gridLayout]="setPerspective" [sendLayout]="gridLayout.stimulate" (layout)="gridLayout.data=$event">
</app-datagrid-syncfution>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button *ngIf="!loadingExcel" type="button" class="ti-btn ti-btn-info-full"
(click)="gridLayout.stimulate=!gridLayout.stimulate;savePerspective('grid')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="ti-btn ti-btn-danger-full text-white !font-medium"
(click)="closeModalgridModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<!-- <ng-template #gridModals let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">GridData</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<app-datagrid-syncfution [searchSettings]="searchSettings" [dataSource]="dataList" [columns]="columns"
[gridLayout]="setPerspective" [sendLayout]="gridLayout.stimulate" (layout)="gridLayout.data=$event">
</app-datagrid-syncfution>
</div>
<div class="modal-footer">
<button *ngIf="!loadingExcel" type="button" class="btn btn-info"
(click)="gridLayout.stimulate=!gridLayout.stimulate;savePerspective('grid')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="btn btn-danger" (click)="modal.close()">
{{"ปิด"}}
</button>
</div>
</ng-template> -->
<ng-template #pivotModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
Pivot
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalpivotModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<app-pivot-syncfution [dataSource]="dataList" [columns]="pivotColumns"
[templateId]="excelReport.templateId" [fileName]="excelReport.fileName"
[pivotLayout]="setPerspective" [sendLayout]="pivotLayout.stimulate"
(layout)="pivotLayout.data=$event">
</app-pivot-syncfution>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button *ngIf="!loadingExcel" type="button" class="ti-btn ti-btn-info-full"
(click)="pivotLayout.stimulate=!pivotLayout.stimulate;savePerspective('pivot')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="ti-btn ti-btn-danger-full text-white !font-medium"
(click)="closeModalpivotModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<!-- <ng-template #pivotModals let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">Pivot</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<app-pivot-syncfution [dataSource]="dataList" [columns]="pivotColumns" [templateId]="excelReport.templateId"
[fileName]="excelReport.fileName" [pivotLayout]="setPerspective" [sendLayout]="pivotLayout.stimulate"
(layout)="pivotLayout.data=$event">
</app-pivot-syncfution>
</div>
<div class="modal-footer">
<button *ngIf="!loadingExcel" type="button" class="btn btn-info"
(click)="pivotLayout.stimulate=!pivotLayout.stimulate;savePerspective('pivot')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="btn btn-danger" (click)="modal.close()">
{{"ปิด"}}
</button>
</div>
</ng-template> -->
\ No newline at end of file
.ti-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom-width: 1px;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
::ng-deep [ngbDatepickerDayView].today {
background-color: #e0f2fe !important;
}
::ng-deep [ngbDatepickerDayView].bg-primary {
background-color: rgb(var(--primary)) !important;
color: #ffffff !important;
}
/* ให้ datepicker popup ทะลุ modal ได้ */
::ng-deep ngb-datepicker,
::ng-deep .ngb-datepicker-container {
z-index: 2000 !important;
}
/* เผื่อ modal มี overflow ซ่อนอยู่ */
::ng-deep .ti-modal-content {
overflow: visible !important;
}
\ No newline at end of file
import { ChangeDetectorRef, Component, ElementRef, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { forkJoin, Subscription } from 'rxjs';
import { NgbDate, NgbDatepickerModule, NgbModal, NgbModalRef, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ColumnModel } from '@syncfusion/ej2-angular-grids';
import * as FileSaver from 'file-saver';
import { MyTemplateFileModel, MyTemplateModel, TemplateFileModel, TemplateModel } from '../../../../models/template.model';
import { ModuleModel, MyModuleModel } from '../../../../models/module.model';
import { ExcelPortalGroupModel, ExcelPortalModel, ExcelPortalTagsModel, MyExcelPortalGroupModel, MyExcelPortalModel, MyExcelPortalTagsModel } from '../../../../models/excel-portal.model';
import { ExcelReportService } from '../../../../services/excel-report.service';
import { CustomCubeService } from '../../../../services/custom-cube.service';
import { AlertModalComponent } from '../../../../../alert-modal/alert-modal.component';
import { ConfirmModalComponent } from '../../../../../confirm-modal/confirm-modal.component';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../shared/shared.module';
import { MatPaginator } from '@angular/material/paginator';
import { QuillModule } from 'ngx-quill';
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { FileUploadModule } from 'ng2-file-upload';
import { FormsModule, NgModel } from '@angular/forms';
import { DatagridSyncfutionComponent } from '../../../../../datagrid-syncfution/datagrid-syncfution.component';
import { PivotSyncfutionComponent } from "../../../../../pivot-syncfution/pivot-syncfution.component";
import { SafeUrlPipe } from '../../../../../../pipe/safe-url.pipe';
import Swal from 'sweetalert2';
import { DatasourceTableModel, MyDatasourceTableModel } from '../../../../models/datasource-table.model';
import { DatasourseTableService } from '../../../../services/datasourse-table.service';
import swal from 'sweetalert';
export interface ModalDetail {
text: { cardHead: string, search: string[], tableHead: string[] }
}
interface ValueDetailItem {
id: string;
tdesc: string;
edesc: string;
}
@Component({
standalone: true,
selector: 'app-excel-list',
templateUrl: './excel-list.component.html',
styleUrls: ['./excel-list.component.scss'],
imports: [
CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
RouterModule,
FileUploadModule,
QuillModule,
MatDialogModule,
NgbPaginationModule,
DatagridSyncfutionComponent,
PivotSyncfutionComponent,
NgbDatepickerModule,
SafeUrlPipe
],
})
export class ExcelListComponent implements OnInit {
template: { data: TemplateModel[], filter: TemplateModel[], loading: boolean } = { data: [], filter: [], loading: false }
templateFile: TemplateFileModel = new MyTemplateFileModel()
module: { data: ModuleModel[], loading: boolean } = { data: [], loading: false }
pagedItems: TemplateModel[] = [];
pageIndex: number = 0;
itemsPerPage: number = 10;
page = 0
pageSize = 10
pageModal = 0
pageSizeModal = 10
searchBy = ''
condition = ''
searchValue = ''
openTemplate: Map<string, boolean> = new Map<string, boolean>()
bodyTemplate: {
status: 'add' | 'edit',
data: {
templateId: string,
tname: string,
ename: string,
tdesc: string,
edesc: string,
module: string,
companyId: string,
}
} = {
status: 'add',
data: {
templateId: '',
tname: '',
ename: '',
tdesc: '',
edesc: '',
module: '',
companyId: ''
}
}
excelPortalSearch: {
groupId: string,
tags: string,
search: string
} = {
groupId: '',
tags: '',
search: ''
}
excelPortal: { data: ExcelPortalModel[], loading: boolean } = { data: [], loading: false }
excelPortalGroup: { data: ExcelPortalGroupModel[], loading: boolean } = { data: [], loading: false }
excelPortalTags: { data: ExcelPortalTagsModel[], loading: boolean } = { data: [], loading: false }
videoLink = ""
templateFileType: 'portal' | 'upload' = 'portal'
templateFileName = ''
tableHover: Map<string, boolean> = new Map<string, boolean>()
buttonHover: Map<string, boolean> = new Map<string, boolean>()
addChildModalRef?: NgbModalRef
@ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;
downloadTemplateFileLoading: Map<string, boolean> = new Map<string, boolean>()
changeDate = new Date();
select: any = {}
excelReport?: any
variableSheet: any = []
loading = false
loadingExcel = false
valueDetail: ValueDetailItem[] = [];
modalDetail: ModalDetail = {
text: {
cardHead: '',
search: [],
tableHead: []
}
}
keySelect = ""
searchModal = ""
getTemplateFileSubscription?: Subscription
searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
columns: ColumnModel[] = []
pivotColumns: any[] = []
dataList: any[] = []
gridLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
pivotLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
setPerspective = ""
isSearching = false;
modalRef: any;
modalRefaddChild: any;
modalRefexcel_portal: MatDialogRef<any>;
modalRefvideoModal: any;
modalRefprintModal: any;
modalRefmodalData: any;
modalRefgridModal: MatDialogRef<any>;
modalRefpivotModal: MatDialogRef<any>;
searchText: string = '';
itemsList: DatasourceTableModel[] = [];
@ViewChild('addGroupModal') addGroupModal!: TemplateRef<any>;
@ViewChild('addChildModal') addChildModal!: TemplateRef<any>;
@ViewChild('excel_portalModal') excel_portalModal!: TemplateRef<any>;
@ViewChild('videoModal') videoModal!: TemplateRef<any>;
@ViewChild('printModal') printModal!: TemplateRef<any>;
@ViewChild('modalData') modalData!: TemplateRef<any>;
@ViewChild('gridModal') gridModal!: TemplateRef<any>;
@ViewChild('pivotModal') pivotModal!: TemplateRef<any>;
constructor(private excelReportService: ExcelReportService,
private modalService: NgbModal,
private customCubeService: CustomCubeService,
private cdr: ChangeDetectorRef,
private modal: MatDialog,
private datasourceTable: DatasourseTableService ) {
}
ngOnInit(): void {
this.getExcelList()
this.getModuleList()
this.getExcelPortalList()
this.getExcelPortalgGroupList()
this.getExcelPortalTagsList()
this.getData()
}
get totalItems(): number {
return this.searchText == ''
? this.template.data.length
: this.template.filter.length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.itemsPerPage);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.pageIndex = index;
this.updatePagedItems();
}
updatePagedItems() {
const data = this.searchText == '' ? this.template.data : this.template.filter;
const start = this.page * this.pageSize; // ถ้า page = 1 → เริ่มที่ index 10
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
getExcelPortalList() {
this.excelPortal.loading = true
this.excelReportService.getExcelPortalList().subscribe(response => {
this.excelPortal.data = response.map(e => new MyExcelPortalModel(e))
this.excelPortal.loading = false
}, error => {
this.excelPortal.loading = false
})
}
excelPortalFilter() {
const search = this.excelPortalSearch.search?.trim();
let result = this.excelPortal.data.filter(x =>
x.group?.groupId?.includes(this.excelPortalSearch.groupId) ||
x.tags?.some(e => e.value?.includes(this.excelPortalSearch.tags))
);
if (search) {
result = result.filter(x =>
(x.thName).includes(search) ||
(x.thDesc).includes(search) ||
(x.dbSupport).includes(search) ||
String(x.isPivot ?? 0).includes(search) ||
String(x.isDataGrid ?? 0).includes(search) ||
((x.downloadDate + x.downloadTime)).includes(search) ||
((x.uploadDate + x.uploadTime)).includes(search)
);
}
return result;
}
getExcelPortalgGroupList() {
this.excelPortalGroup.loading = true
this.excelReportService.getExcelPortalgGroupList().subscribe(response => {
this.excelPortalGroup.data = response.map(e => new MyExcelPortalGroupModel(e))
this.excelPortalGroup.loading = false
}, error => {
this.excelPortalGroup.loading = false
})
}
getExcelPortalTagsList() {
this.excelPortalTags.loading = true
this.excelReportService.getExcelPortalTagsList().subscribe(response => {
this.excelPortalTags.data = response.map(e => new MyExcelPortalTagsModel(e))
this.excelPortalTags.loading = false
}, error => {
this.excelPortalTags.loading = false
})
}
getExcelList() {
this.template.loading = true
this.excelReportService.getExcelList().subscribe(response => {
this.template.data = response.map(e => new MyTemplateModel(e))
this.template.filter = response.map(e => new MyTemplateModel(e))
this.template.loading = false
}, error => {
this.template.loading = false
})
}
getModuleList() {
this.module.loading = true
this.excelReportService.getModuleList().subscribe(response => {
this.module.data = response.map(e => new MyModuleModel(e))
this.module.loading = false
}, error => {
this.module.loading = false
})
}
postTemplate() {
console.log("wait for function")
this.modalRef?.close()
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
}, reason => {
})
}
openAddGroupModal(data?: TemplateModel) {
if (data) {
this.bodyTemplate.status = 'edit';
this.bodyTemplate.data = {
templateId: data.templateId,
tname: data.tname,
ename: data.ename,
tdesc: data.tdesc,
edesc: data.edesc,
module: data.module,
companyId: data.companyId
};
} else {
this.bodyTemplate.status = 'add';
this.bodyTemplate.data = {
templateId: '',
tname: '',
ename: '',
tdesc: '',
edesc: '',
module: '',
companyId: ''
};
}
this.openModalAddGroup()
}
deleteTemplate(template: TemplateModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.excelReportService.deleteTemplate(template).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
openAddChildModal(data: TemplateModel | TemplateFileModel, status: 'add' | 'edit') {
if (status == 'add') {
this.templateFile = new MyTemplateFileModel({ templateId: data.templateId, module: data.module })
this.templateFileType = 'portal'
this.templateFileName = ''
} else if (status == 'edit') {
this.templateFile = new MyTemplateFileModel(data)
this.templateFileType = this.templateFile.portalId ? 'portal' : 'upload'
this.templateFileName = this.templateFile.portalId ? '' : this.templateFile.fileName
this.templateFile.realFileNameOld = this.templateFile.fileName
}
this.openModaladdChild()
}
openExcelPortalModal() {
this.pageModal = 0;
this.pageSizeModal = 5;
this.modalRefexcel_portal = this.modal.open(this.excel_portalModal, {
width: '1500px',
height: '700px',
maxWidth: 'none',
disableClose: true,
data: {}
});
this.modalRefexcel_portal.afterClosed().subscribe(result => {
if (result?.excelId) {
this.templateFile.portalId = result.excelId;
}
});
}
openVideoModal(videoLink: string) {
this.videoLink = this.convertToEmbedUrl(videoLink);
this.modalRefvideoModal = this.modal.open(this.videoModal, {
width: '50%',
height: '50%',
panelClass: 'video-preview-dialog',
backdropClass: 'video-backdrop'
});
}
convertToEmbedUrl(youtubeUrl: string): string {
try {
const url = new URL(youtubeUrl);
if (url.hostname.includes('youtube.com') && url.searchParams.has('v')) {
const videoId = url.searchParams.get('v');
return `https://www.youtube.com/embed/${videoId}`;
}
if (url.hostname === 'youtu.be') {
const videoId = url.pathname.slice(1); // เอา path หลัง youtu.be/
return `https://www.youtube.com/embed/${videoId}`;
}
} catch (e) {
// ถ้า url ผิดรูปแบบ
console.warn('Invalid YouTube URL:', youtubeUrl);
}
// ถ้าไม่เข้าเงื่อนไขใดเลย ให้คืนลิงก์เดิม
return youtubeUrl;
}
templateListSearch() {
this.isSearching = true;
setTimeout(() => {
if (!this.searchBy || !this.condition || !this.searchValue) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e));
this.isSearching = false;
return;
}
const conditionMap: { [key: string]: (a: any, b: any) => boolean } = {
includes: (a, b) => (a || '').toString().toLowerCase().includes((b || '').toString().toLowerCase()),
lt: (a, b) => parseFloat(a) < parseFloat(b),
gt: (a, b) => parseFloat(a) > parseFloat(b),
eq: (a, b) => a == b,
lte: (a, b) => parseFloat(a) <= parseFloat(b),
gte: (a, b) => parseFloat(a) >= parseFloat(b),
neq: (a, b) => a != b,
};
const compareFn = conditionMap[this.condition];
if (!compareFn) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e));
this.isSearching = false;
return;
}
this.template.filter = this.template.data
.filter(item => {
const value = (item as any)[this.searchBy];
return compareFn(value, this.searchValue);
})
.map(e => new MyTemplateModel(e));
this.isSearching = false;
}, 1000); // delay mock loading
}
onFileSelected(event: any) {
const file: File = event.target.files[0];
if (!file) {
alert('กรุณาเลือกไฟล์');
return;
}
const allowedExtensions = ['xls', 'xlsx'];
const fileExtension = file.name.split('.').pop()?.toLowerCase();
if (!allowedExtensions.includes(fileExtension || '')) {
alert('รองรับเฉพาะไฟล์ Excel (.xls, .xlsx) เท่านั้น');
return;
}
const reader = new FileReader();
reader.onload = () => {
const base64String = (reader.result as string).split(',')[1]; // เอาเฉพาะส่วน base64
// ✅ สร้าง payload
const payload = {
filename: file.name,
filetype: file.type,
fileExtension: fileExtension,
data: base64String
};
this.templateFileName = payload.filename
this.templateFile.fileData = payload.data
this.templateFile.fileType = payload.fileExtension || ''
this.fileInput.nativeElement.value = '';
};
reader.readAsDataURL(file); // อ่านแบบ Base64
}
postTemplateFile() {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการบันทึกข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.addChildModalRef?.close()
this.templateFile.createDate = new Date().toISOString();
this.excelReportService.postTemplateFile(this.templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
menuActiveTemplateFile(templateFile: TemplateFileModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการ' + (templateFile.menuActive == '1' ? 'ปิด' : 'เปิด') + 'การใช้งานหรือไม่'
modalConfirmRef.result.then(result => {
templateFile.menuActive = templateFile.menuActive == '1' ? '0' : '1'
this.excelReportService.postTemplateFile(templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
formatISOToLocal(isoString: string) {
const date = new Date(isoString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); // เดือนเริ่มที่ 0
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return { date: `${day}-${month}-${year}`, time: `${hours}:${minutes}` };
}
deleteTemplateFile(templateFile: TemplateFileModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.excelReportService.deleteTemplateFile(templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
downloadTemplateFile(templateFile: TemplateFileModel) {
this.downloadTemplateFileLoading.set(templateFile.fileName, true)
this.excelReportService.downloadTemplateFile(templateFile.fileName).subscribe((response: any) => {
const blob = new Blob([response], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, templateFile.fileName);
this.downloadTemplateFileLoading.clear()
}, error => {
this.downloadTemplateFileLoading.clear()
this.openAlertModal(error.message)
})
}
openPrintModal(templateFile: TemplateFileModel) {
this.getTemplateFile(templateFile)
this.openModalprintModal()
}
getTemplateFile(templateFile: TemplateFileModel) {
this.loading = true
this.excelReport = undefined
this.variableSheet = []
this.valueDetail = []
this.getTemplateFileSubscription = this.excelReportService.getTemplateFile(templateFile.templateId, templateFile.fileName).subscribe(response => {
this.excelReport = response
this.getExcelColumn()
if (this.excelReport.param) {
Object.entries(this.excelReport.param.variableSheet).forEach(([key, value]) => {
const data = value as any
if (data.type == 'text') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key
})
} else if (data.type == 'list') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'radio') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'help') {
this.variableSheet.push({ ...data, value: data.valueDefault ? { id: data.valueDefault, tdesc: data.valueDefault, edesc: data.valueDefault } : { id: "", tdesc: "", edesc: "" }, key: key })
} else if (data.type == 'calendar') {
this.variableSheet.push({ ...data, value: data.valueDefault || '', key: key })
const [d, m, y] = data.valueDefault ? data.valueDefault.split('-').map(Number) : [null, null, null]
this.select[key] = y ? new NgbDate(y, m, d) : null
}
})
}
this.loading = false
this.cdr.detectChanges()
}, (err) => {
this.loading = false
})
}
openModalData(data: any) {
this.searchModal = ''
this.page = 0
this.pageSize = 10
this.valueDetail = data.valueDetail.map((x: any) => ({ id: x.id || '', tdesc: x.tdesc || '', edesc: x.edesc || '' }))
this.keySelect = data.key
this.modalDetail = {
text: {
cardHead: "Table " + data.table,
search: ["Table " + data.table],
tableHead: ['ID', 'detailTH', 'detailENG']
}
}
this.openModalmodalData()
}
valueDetailFilter(): ValueDetailItem[] {
const search = this.searchModal.toLowerCase();
return this.valueDetail.filter(item =>
item.id.toLowerCase().includes(search) ||
item.tdesc.toLowerCase().includes(search) ||
item.edesc.toLowerCase().includes(search)
);
}
selectData(data: any) {
const item = this.variableSheet.find((i: any) => i.key === this.keySelect);
if (item) item.value = data;
}
formatNgbDate(key: string, date?: NgbDate) {
if (date) {
const day = String(date.day).padStart(2, '0');
const month = String(date.month).padStart(2, '0');
const year = date.year;
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = `${day}-${month}-${year}`
} else {
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = ''
}
}
validateDate(control: NgModel) {
if (!control.value) {
// ว่าง หรือวันที่ไม่ถูกต้อง
this.openAlertModal('กรุณาเลือกวันที่ให้ถูกต้อง');
}
}
dowloadExcelReport() {
this.loadingExcel = true
const fileName = this.excelReport.param.excelFile
const param = this.excelReport.param.variableName
const data = this.variableSheet.map((item: any) => {
if (item.type == 'help') {
return "__" + item.key + "=" + item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
return "__" + item.key + "=" + item.value
}
return ""
}).join('|')
const body = {
fileName: fileName,
paramObj: data
}
this.excelReportService.printExcelReport(body).subscribe((res: any) => {
const blob = new Blob([res], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, fileName);
this.loadingExcel = false
this.cdr.detectChanges()
}, (err) => {
this.loadingExcel = false
this.openAlertModal(err.message)
this.cdr.detectChanges()
})
}
openGridModal(targetModal: TemplateRef<any>, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
openPivotModal(targetModal: TemplateRef<any>, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
ngOnDestroy(): void {
this.getTemplateFileSubscription?.unsubscribe()
}
templateFileTypeChange() {
if (this.templateFileName || this.templateFile?.portalId) {
Swal.fire({
title: 'ยืนยันการดำเนินการ',
text: 'คุณต้องการยกเลิกไฟล์ที่เลือกหรือไม่',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'ใช่, ยกเลิกไฟล์',
cancelButtonText: 'ไม่',
confirmButtonColor: '#d33', // สีปุ่มยืนยัน (แดง)
cancelButtonColor: '#3085d6', // สีปุ่มยกเลิก (น้ำเงิน)
reverseButtons: true
}).then((result) => {
if (result.isConfirmed) {
this.onToggleTemplateFile();
}
});
} else {
this.onToggleTemplateFile();
}
}
onToggleTemplateFile() {
if (this.templateFileType == 'portal') {
this.templateFileType = 'upload'
this.templateFile.portalId = ''
} else if (this.templateFileType == 'upload') {
this.templateFileType = 'portal';
this.templateFile.fileData = '';
this.templateFile.fileType = '';
this.templateFileName = ''
}
}
getExcelColumn() {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
}
this.searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
this.columns = []
this.pivotColumns = []
this.customCubeService.getExcelColumn(body).subscribe((response: any) => {
this.searchSettings = {
fields: response.map((e: any) => e.split(':')[0]),
operator: 'contains',
ignoreCase: false
};
response.forEach((e: any, i: number) => {
const [field, headerText] = e.split(':')
this.columns.push({
field: field,
headerText: headerText,
type: "string",
isPrimaryKey: i == 0,
})
this.pivotColumns.push({
name: field,
caption: headerText
})
});
}, error => {
})
}
getExcelData(type: 'grid' | 'pivot') {
this.loadingExcel = true
this.dataList = []
this.setPerspective = ''
const body: any = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
};
this.variableSheet.forEach((item: any, index: number) => {
const i = index + 1;
body[`key${i}`] = item.key;
if (item.type == 'help') {
body[`val${i}`] = item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
body[`val${i}`] = item.value
}
});
forkJoin({
excelData: this.customCubeService.getExcelData(body),
excelPerspective: this.customCubeService.getExcelPerspective(body),
}).subscribe(response => {
this.dataList = response.excelData as any
this.loadingExcel = false
console.log('🟢 dataList:', this.dataList); // ตรวจสอบว่ามีข้อมูลไหม
console.log('🟢 pivotStr:', (response.excelPerspective as any).pivotStr); // ตรวจสอบ layout ที่ส่งมา
console.log('🟢 columns:', this.pivotColumns); // ดูว่า columns ถูกเซ็ตหรือยัง
if (type === 'grid') {
this.modalRefgridModal = this.modal.open(this.gridModal, {
width: '1000px',
height: '700px',
data: {
gridData: (response.excelPerspective as any).dataGridStr,
dataList: this.dataList
},
disableClose: true,
autoFocus: false
});
} else if (type === 'pivot') {
this.modalRefpivotModal = this.modal.open(this.pivotModal, {
width: '1000px',
height: '580px',
data: {
pivotData: (response.excelPerspective as any).pivotStr,
dataList: this.dataList
},
disableClose: true,
autoFocus: false
});
}
this.cdr.detectChanges()
}, error => {
this.loadingExcel = false
})
}
savePerspective(type: 'grid' | 'pivot') {
if (type == 'grid') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: this.gridLayout.data,
pivotStr: "",
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
} else if (type == 'pivot') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: '',
pivotStr: this.pivotLayout.data,
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
}
}
openModalAddGroup() {
this.modalRef = this.modal.open(this.addGroupModal, {
width: '1000px',
height: '500px'
})
}
closeModalAddGroup() {
this.modalRef?.close()
}
openModaladdChild() {
this.modalRefaddChild = this.modal.open(this.addChildModal, {
width: '700px',
height: '500px'
})
}
closeModaladdChild() {
this.modalRefaddChild?.close()
}
openModalexcel_portal() {
this.modalRefexcel_portal = this.modal.open(this.excel_portalModal, {
width: '1500px',
height: '700px'
})
}
closeModalexcel_portal() {
this.modalRefexcel_portal?.close()
}
openModalvideoModal() {
this.modalRefvideoModal = this.modal.open(this.videoModal, {
width: '1500px',
height: '700px'
})
}
closeModalvideoModal() {
this.modalRefvideoModal?.close()
}
openModalprintModal() {
this.modalRefprintModal = this.modal.open(this.printModal, {
width: '700px',
height: '300px'
})
}
closeModalprintModal() {
this.modalRefprintModal?.close()
}
openModalmodalData() {
this.modalRefmodalData = this.modal.open(this.modalData, {
width: '800px',
height: '580px'
})
}
closeModalmodalData() {
this.modalRefmodalData?.close()
}
closeModalgridModal() {
this.modalRefgridModal?.close()
}
closeModalpivotModal() {
this.modalRefpivotModal?.close()
}
getData() {
this.datasourceTable.getList().subscribe({
next: (response: DatasourceTableModel[]) => {
this.itemsList = response.map((x: any) => new MyDatasourceTableModel(x));
console.log('ข้อมูล (itemsList)', this.itemsList);
this.updatePagedItems();
},
error: (error) => {
console.error('error cant get position', error);
swal("ข้อผิดพลาด", "ไม่สามารถดึงข้อมูลได้", "error");
}
});
}
}
\ No newline at end of file
<app-page-header [title]="'เปิด-ปิด การใช้รายงาน Excel'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'เปิด-ปิด การใช้รายงาน Excel'"></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">
รายการเอกสาร
</div>
<div class="flex flex-wrap gap-4">
<!-- Search By -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Search By</label>
<select class="form-control h-100" [(ngModel)]="searchBy">
<option style="color: red" [class.d-none]="searchBy==''" [value]="''">
{{searchBy!=''?'ยกเลิก':'--- เลือก ---'}}</option>
<option [value]="'templateId'">รหัสกลุ่มแม่แบบ</option>
<option [value]="'module'">โมดูล</option>
<option [value]="'tname'">ชื่อกลุ่มรายงาน (ภาษาไทย)</option>
<option [value]="'ename'">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</option>
<option [value]="'tdesc'">รายละเอียด (ภาษาไทย)</option>
<option [value]="'edesc'">รายละเอียด (ภาษาอังกฤษ)</option>
</select>
</div>
<!-- Condition -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Condition</label>
<select class="form-control h-100" [(ngModel)]="condition">
<option style="color: red" [class.d-none]="condition==''" [value]="''">
{{condition!=''?'ยกเลิก':'--- เลือก ---'}}</option>
<option value="includes">คำในประโยค</option>
<option value="lt">น้อยกว่า</option>
<option value="gt">มากกว่า</option>
<option value="eq">เท่ากับ</option>
<option value="lte">น้อยกว่าเท่ากับ</option>
<option value="gte">มากกว่าเท่ากับ</option>
<option value="neq">ไม่เท่ากับ</option>
</select>
</div>
<!-- Key Value -->
<div class="d-flex flex-column" style="min-width: 200px;">
<label class="mb-1 font-medium text-sm">Key Value</label>
<input class="form-control h-100" type="text" placeholder="ค้นหา"
aria-label=".form-control-sm example" [(ngModel)]="searchValue">
</div>
<!-- Buttons -->
<div class="flex items-end gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full"
style="margin-bottom: unset;" (click)="openTemplate.clear();templateListSearch()">
<i class="ri-search-line font-semibold align-middle"></i>ค้นหา
</a>
<!-- <a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-success-full"
style="margin-bottom: unset;" (click)="openAddGroupModal()">
<i class="ri-add-line font-semibold align-middle"></i>Add Group
</a> -->
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน(Thai)</th>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</th>
<th class="text-center" scope="col" style="min-width: 200px">สร้างโดย</th>
<th class="text-center" scope="col" style="min-width:120px">วันที่สร้าง</th>
<th class="text-center" scope="col">สร้างเมื่อ</th>
<th class="text-center" scope="col" style="min-width: 120px;">โมดูล</th>
<th class="text-center" scope="col">Add file</th>
</tr>
</thead>
<tbody *ngIf="!template.filter.length">
<tr style="background-color:#ebf2f6">
<td colspan="7" class="text-center">ไม่พบข้อมูล</td>
</tr>
</tbody>
<tbody id="tableBody" *ngIf="isSearching || template.filter.length">
<tr *ngIf="isSearching">
<td colspan="7" class="loading">
<div class="spinner"></div>
<div class="spinner"></div>
<div class="spinner"></div>
<span>กำลังค้นหา...</span>
</td>
</tr>
<ng-container *ngIf="!isSearching">
<ng-container
*ngFor="let items of template.filter | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<tr (mouseenter)="tableHover.set(items.templateId,!tableHover.get(items.templateId))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(items.templateId)?'rgb(201 223 235)':'#ebf2f6'}">
<td colspan="6" class="font-16 font-medium">
{{items.tname}}<span *ngIf="items.tdesc">
-{{items.tdesc}}
</span>
</td>
<td class="text-center">
<ng-container *ngIf="items.templateFile.length">
<ng-container *ngIf="!openTemplate.get(items.templateId)">
<button type="button" title="เปิด" (click)="
openTemplate.set(items.templateId,true)"
(mouseenter)="buttonHover.set(items.templateId+'open',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
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-arrow-right-line"></i>
</button>
</ng-container>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<button type="button" title="ปิด" (click)="
openTemplate.set(items.templateId,false)"
(mouseenter)="buttonHover.set(items.templateId+'close',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
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-arrow-down-line"></i>
</button>
</ng-container>
</ng-container>
</td>
</tr>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<ng-container
*ngFor="let item of items.templateFile | slice: page * pageSize : (page+1) * pageSize ; let i = index">
<tr (mouseenter)="tableHover.set(item.fileName,!tableHover.get(item.fileName))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.fileName)?'rgb(201 223 235)':'#ffffff'}">
<td style="white-space: normal !important;word-break: break-word;">
{{item.tdesc}}
</td>
<td style="white-space: normal !important;word-break: break-word;">
{{item.edesc}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
<!-- {{item.createBy.thFullName}}</td> -->
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).date}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).time}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{item.module}}</td>
<td class="text-center">
<i *ngIf="item.menuActive=='0'"
class="fa fa-times text-danger cursor-pointer"
style="text-decoration: underline; font-size: medium;"
(click)="menuActiveTemplateFile(item)"></i>
<i *ngIf="item.menuActive=='1'"
class="fa fa-check text-success cursor-pointer"
style="text-decoration: underline; font-size: medium;"
(click)="menuActiveTemplateFile(item)"></i>
&nbsp;
<a aria-label="anchor" title="Print"
(mouseenter)="buttonHover.set(item.fileName+'print',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
class="ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-warning/10 text-warning hover:bg-warning hover:text-white hover:border-warning"
(click)="openPrintModal(item);">
<i class="ri-printer-line"></i></a>
</td>
</tr>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="template.filter.length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto"
[(ngModel)]="pageSize" (ngModelChange)="page">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{template.filter.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="page > 0" class="page-item {{page==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page - 1; updatePagedItems()">{{'Previous' | translate}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="page > 0" (click)="page = page - 1; updatePagedItems()">{{page}}</a>
</li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{page + 1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 1) * pageSize < template.filter.length"
(click)="page = page + 1; updatePagedItems()">{{page + 2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(page + 2) * pageSize < template.filter.length"
(click)="page = page + 2; updatePagedItems()">{{page + 3}}</a></li>
<li *ngIf="(page + 1) * pageSize < template.filter.length" class="page-item"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="page = page + 1; updatePagedItems()">{{'Next' | translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="flex justify-end items-center flex-wrap gap-4 mt-3">
<div><i class="fa fa-times text-danger"></i> = used on menu</div>
<div><i class="fa fa-check text-success"></i> = unused on menu</div>
<!-- <div><i class="ri-file-download-line text-info"></i> = download files</div> -->
<div><i class="ri-printer-line text-warning"></i> = print report</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #printModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
Excel Report
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalprintModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="row">
<div class="d-flex col-12 justify-content-center" *ngIf="loading||loadingExcel">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<ng-container *ngIf="!loading&&!loadingExcel">
<ng-container *ngFor="let item of variableSheet; let i=index">
<div class="flex items-center mb-4">
<!-- Label -->
<label class="w-1/4 text-right pr-4 font-semibold text-sm">
{{ item.label }}
</label>
<!-- Input -->
<div class="w-3/4">
<ng-container [ngSwitch]="item.type">
<!-- Text -->
<input *ngSwitchCase="'text'" type="text" class="form-input w-full"
[(ngModel)]="item.value" />
<!-- List -->
<select *ngSwitchCase="'list'" class="form-select w-full"
[(ngModel)]="item.value">
<option *ngFor="let list of item.option" [value]="list.value">
{{ list.text }}
</option>
</select>
<!-- Radio -->
<div *ngSwitchCase="'radio'" class="flex flex-wrap gap-4">
<label *ngFor="let radioItem of item.option"
class="inline-flex items-center">
<input type="radio" class="form-radio mr-2"
[name]="'group-' + item.label" [id]="radioItem.text+radioItem.value"
[value]="radioItem.value" [(ngModel)]="item.value" />
{{ radioItem.text }}
</label>
</div>
<!-- Help -->
<div *ngSwitchCase="'help'" class="flex items-stretch">
<input type="text" readonly class="form-input w-1/2 bg-white cursor-pointer"
[value]="item.value.tdesc" (click)="openModalData(item)" />
<button
class="bg-primary hover:bg-primary text-white px-3 flex items-center justify-center rounded-none border"
type="button" (click)="openModalData(item)">
<i class="ri-search-line font-semibold align-middle"></i>
</button>
<button type="button"
class="bg-red hover:bg-red text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="item.value={id:'',tdesc:'',edesc:''}">
<i class="fa fa-times text-xs"></i>
</button>
</div>
<!-- Calendar -->
<div *ngSwitchCase="'calendar'" class="flex items-stretch">
<!-- Input -->
<input class="form-input w-1/2 bg-white cursor-pointer rounded-r-none"
style="border-color: #e9edf6;" placeholder="dd.mm.yyyy" name="dp1"
ngbDatepicker #d1="ngbDatepicker" [(ngModel)]="select[item.key]"
readonly (click)="d1.toggle()"
(ngModelChange)="formatNgbDate(item.key, select[item.key])"
#c1="ngModel" (change)="validateDate(c1)" container="body">
<!-- Calendar Button -->
<button type="button"
class="bg-white hover:bg-primary text-primary hover:text-white px-3 flex items-center justify-center rounded-none border"
style="border-color: #154c9c;" (click)="d1.toggle()">
<i class="ri-calendar-2-line"></i>
</button>
<!-- Clear Button -->
<button type="button"
class="bg-red hover:bg-red text-white text-sm ml-5 w-10 h-10 flex items-center justify-center rounded-full"
(click)="select[item.key]=null ;formatNgbDate(item.key)">
<i class="fa fa-times text-xs"></i>
</button>
</div>
</ng-container>
</div>
</div>
</ng-container>
<!-- <div *ngIf="variableSheet.length&&!loadingExcel"
class="col-12 justify-content-center align-content-center d-flex" style="margin-bottom: 1rem;">
<button type="submit" class="btn btn-info waves-effect waves-light btn-w-100"
(click)="dowloadExcelReport()">
{{"Print" }}
</button>
</div>
<div *ngIf="loadingExcel" class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div> -->
<div *ngIf="!variableSheet.length"
class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;margin-top: 1rem;">
<div
class="col-3 justify-content-center text-center font-weight-bold control-label col-form-label font-14">
{{'No Data Found' }}
</div>
</div>
<div class="row col-12 flex justify-center"
*ngIf="variableSheet.length&&(excelReport.isDataGrid=='1'||excelReport.isPivot=='1')">
<div class="col-12 d-flex justify-content-center align-content-center">
<button type="button" *ngIf="excelReport.isDataGrid=='1'"
class="ti-btn ti-btn-primary-full" (click)="getExcelData('grid')">Datagrid</button>
<ng-container *ngIf="excelReport.isDataGrid=='1'&&excelReport.isPivot=='1'">
&nbsp;
</ng-container>
<button type="button" *ngIf="excelReport.isPivot=='1'"
class="ti-btn ti-btn-primary-full" (click)="getExcelData('pivot')">Pivot</button>
</div>
</div>
</ng-container>
</div>
</div>
<div class="border-t mt-3">
<div class="ti-modal-footer flex justify-end gap-1 mb-3 mt-3 mr-3">
<ng-container *ngIf="variableSheet.length">
<button type="submit" class="ti-btn ti-btn-info-full waves-effect waves-light btn-w-100"
[disabled]="loadingExcel" (click)="dowloadExcelReport()">
{{"Print" }}
</button>
</ng-container>
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalprintModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #modalData let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
{{modalDetail.text.cardHead }}
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalmodalData()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<div class="d-flex mb-1">
<input type="text" placeholder="{{'systemcode.search' }} {{modalDetail.text.search[0]}}"
class=" form-control w-75 border-color-gray-full-focus" [(ngModel)]='searchModal'>
</div>
<div class="table-responsive">
<table class="table table-hover table-striped-myhr table-sm mb-0 no-wrap v-middle "
style="width: 100%">
<thead class="bg-info ">
<tr class="text-white font-weight-normal">
<th class="font-weight-normal text-center" scope="col"
*ngFor="let item of modalDetail.text.tableHead">
{{item}}
</th>
</tr>
</thead>
<tbody *ngIf="!valueDetailFilter().length">
<tr>
<td colspan="9" class="font-weight-normal text-center">
{{"No Data Found" }}
</td>
</tr>
</tbody>
<tbody *ngIf="valueDetailFilter().length">
<tr class="cursor-pointer"
*ngFor="let item of valueDetailFilter() | slice: pageModal * pageSizeModal : (pageModal+1) * pageSizeModal; let i=index"
(click)="selectData(item);closeModalmodalData()"
(mouseenter)="tableHover.set(item.id,!tableHover.get(item.id))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.id)?'rgb(201 223 235)':'#ffffff'}">
<td class="align-middle text-center">
{{item.id}}
</td>
<td class="align-middle text-center">
{{item.tdesc}}
</td>
<td class="align-middle text-center">
{{item.edesc}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="box-footer !border-t-0">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="valueDetailFilter().length > 0">
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto"
[(ngModel)]="pageSizeModal" (ngModelChange)="pageModal=1">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"รายการต่อหน้า"}}: {{item}}
</option>
</select>
</div>
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{valueDetailFilter().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="pageModal>0" class="page-item {{pageModal==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal-1;updatePagedItems()">{{'Previous' |
translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageModal-1>0"
(click)="pageModal = pageModal-2;updatePagedItems()">{{pageModal-1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageModal>0 && ((pageModal-1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length))"
(click)="pageModal = pageModal-1;updatePagedItems()">{{pageModal}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageModal +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageModal+1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
(click)="pageModal = pageModal+1;updatePagedItems()">{{pageModal +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageModal+2)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
(click)="pageModal = pageModal+2;updatePagedItems()">{{pageModal +3}}</a></li>
<li *ngIf="(pageModal+1)*10 < (searchText == '' ? valueDetailFilter().length : valueDetailFilter().length)"
class="page-item"><a class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageModal = pageModal+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button type="button" class="ti-btn bg-danger text-white !font-medium"
(click)="closeModalmodalData()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #gridModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
GridData
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalgridModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<app-datagrid-syncfution [searchSettings]="searchSettings" [dataSource]="dataList" [columns]="columns"
[gridLayout]="setPerspective" [sendLayout]="gridLayout.stimulate" (layout)="gridLayout.data=$event">
</app-datagrid-syncfution>
</div>
<div class="border-t">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button *ngIf="!loadingExcel" type="button" class="ti-btn ti-btn-info-full"
(click)="gridLayout.stimulate=!gridLayout.stimulate;savePerspective('grid')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="ti-btn ti-btn-danger-full text-white !font-medium"
(click)="closeModalgridModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #pivotModal let-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 flex justify-between items-center p-5">
<h6 class="ti-modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
Pivot
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeModalpivotModal()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4 mt-3">
<app-pivot-syncfution [dataSource]="dataList" [columns]="pivotColumns"
[templateId]="excelReport.templateId" [fileName]="excelReport.fileName"
[pivotLayout]="setPerspective" [sendLayout]="pivotLayout.stimulate"
(layout)="pivotLayout.data=$event">
</app-pivot-syncfution>
</div>
<div class="border-t mt-3">
<div class="ti-modal-footer flex justify-end gap-3 mb-3 mt-3 mr-3">
<button *ngIf="!loadingExcel" type="button" class="ti-btn ti-btn-info-full"
(click)="pivotLayout.stimulate=!pivotLayout.stimulate;savePerspective('pivot')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="ti-btn ti-btn-danger-full text-white !font-medium"
(click)="closeModalpivotModal()">
ปิด
</button>
</div>
</div>
</div>
</div>
</ng-template>
\ No newline at end of file
.ti-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom-width: 1px;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
::ng-deep [ngbDatepickerDayView].today {
background-color: #e0f2fe !important;
}
::ng-deep [ngbDatepickerDayView].bg-primary {
background-color: rgb(var(--primary)) !important;
color: #ffffff !important;
}
/* ให้ datepicker popup ทะลุ modal ได้ */
::ng-deep ngb-datepicker,
::ng-deep .ngb-datepicker-container {
z-index: 2000 !important;
}
/* เผื่อ modal มี overflow ซ่อนอยู่ */
::ng-deep .ti-modal-content {
overflow: visible !important;
}
import { ChangeDetectorRef, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { NgbDate, NgbDatepickerModule, NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import * as FileSaver from 'file-saver';
import { IDataOptions } from '@syncfusion/ej2-angular-pivotview';
import { forkJoin, Subscription } from 'rxjs';
import { ColumnModel } from '@syncfusion/ej2-grids';
import { MyTemplateFileModel, MyTemplateModel, TemplateFileModel, TemplateModel } from '../../../../models/template.model';
import { ModuleModel } from '../../../../models/module.model';
import { ExcelReportService } from '../../../../services/excel-report.service';
import { CustomCubeService } from '../../../../services/custom-cube.service';
import { ConfirmModalComponent } from '../../../../../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../../../../../alert-modal/alert-modal.component';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, NgModel } from '@angular/forms';
import { FileUploadModule } from 'ng2-file-upload';
import { QuillModule } from 'ngx-quill';
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { DatagridSyncfutionComponent } from '../../../../../datagrid-syncfution/datagrid-syncfution.component';
import { PivotSyncfutionComponent } from '../../../../../pivot-syncfution/pivot-syncfution.component';
import { SafeUrlPipe } from '../../../../../../pipe/safe-url.pipe';
export interface ModalDetail {
text: { cardHead: string, search: string[], tableHead: string[] }
}
interface ValueDetailItem {
id: string;
tdesc: string;
edesc: string;
}
@Component({
standalone: true,
imports: [
CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
RouterModule,
FileUploadModule,
QuillModule,
MatDialogModule,
NgbPaginationModule,
DatagridSyncfutionComponent,
PivotSyncfutionComponent,
NgbDatepickerModule
],
selector: 'app-excel-report-toggle',
templateUrl: './excel-report-toggle.component.html',
styleUrls: ['./excel-report-toggle.component.scss']
})
export class ExcelReportToggleComponent implements OnInit {
template: { data: TemplateModel[], filter: TemplateModel[], loading: boolean } = { data: [], filter: [], loading: false }
templateFile: TemplateFileModel = new MyTemplateFileModel()
module: { data: ModuleModel[], loading: boolean } = { data: [], loading: false }
pagedItems: TemplateModel[] = [];
pageIndex: number = 0;
itemsPerPage: number = 10;
page = 0
pageSize = 10
pageModal = 0
pageSizeModal = 10
searchBy = ''
condition = ''
searchValue = ''
openTemplate: Map<string, boolean> = new Map<string, boolean>()
bodyTemplate: {
status: 'add' | 'edit',
data: {
templateId: string,
tname: string,
ename: string,
tdesc: string,
edesc: string,
module: string
}
} = {
status: 'add',
data: {
templateId: '',
tname: '',
ename: '',
tdesc: '',
edesc: '',
module: ''
}
}
excelPortalSearch: {
groupId: string,
tags: string,
search: string
} = {
groupId: '',
tags: '',
search: ''
}
tableHover: Map<string, boolean> = new Map<string, boolean>()
buttonHover: Map<string, boolean> = new Map<string, boolean>()
changeDate = new Date();
select: any = {}
excelReport?: any
variableSheet: any = []
loading = false
loadingExcel = false
valueDetail: ValueDetailItem[] = [];
modalDetail: ModalDetail = {
text: {
cardHead: '',
search: [],
tableHead: []
}
}
keySelect = ""
searchModal = ""
getTemplateFileSubscription?: Subscription
searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
columns: ColumnModel[] = []
pivotColumns: any[] = []
dataList: any[] = []
gridLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
pivotLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
setPerspective = ''
isSearching = false;
modalRefprintModal: any;
modalRefmodalData: any;
modalRefgridModal: MatDialogRef<any>;
modalRefpivotModal: MatDialogRef<any>;
searchText: string = '';
@ViewChild('printModal') printModal!: TemplateRef<any>;
@ViewChild('modalData') modalData!: TemplateRef<any>;
@ViewChild('gridModal') gridModal!: TemplateRef<any>;
@ViewChild('pivotModal') pivotModal!: TemplateRef<any>;
constructor(private excelReportService: ExcelReportService,
private modalService: NgbModal,
private customCubeService: CustomCubeService,
private cdr: ChangeDetectorRef,
private modal: MatDialog) {
}
ngOnInit(): void {
this.getExcelList()
}
get totalItems(): number {
return this.searchText == ''
? this.template.data.length
: this.template.filter.length;
}
get totalPages(): number {
return Math.ceil(this.totalItems / this.itemsPerPage);
}
get totalPagesArray(): number[] {
return Array(this.totalPages).fill(0);
}
goToPage(index: number): void {
if (index < 0 || index >= this.totalPages) return;
this.pageIndex = index;
this.updatePagedItems();
}
updatePagedItems() {
const data = this.searchText == '' ? this.template.data : this.template.filter;
const start = this.page * this.pageSize; // ถ้า page = 1 → เริ่มที่ index 10
const end = start + this.pageSize;
this.pagedItems = data.slice(start, end);
}
getExcelList() {
this.template.loading = true
this.excelReportService.getExcelList().subscribe(response => {
this.template.data = response.map(e => new MyTemplateModel(e))
this.template.filter = response.map(e => new MyTemplateModel(e))
this.template.loading = false
}, error => {
this.template.loading = false
})
}
templateListSearch() {
this.isSearching = true;
setTimeout(() => {
if (!this.searchBy || !this.condition || !this.searchValue) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e));
this.isSearching = false;
return;
}
const conditionMap: { [key: string]: (a: any, b: any) => boolean } = {
includes: (a, b) => (a || '').toString().toLowerCase().includes((b || '').toString().toLowerCase()),
lt: (a, b) => parseFloat(a) < parseFloat(b),
gt: (a, b) => parseFloat(a) > parseFloat(b),
eq: (a, b) => a == b,
lte: (a, b) => parseFloat(a) <= parseFloat(b),
gte: (a, b) => parseFloat(a) >= parseFloat(b),
neq: (a, b) => a != b,
};
const compareFn = conditionMap[this.condition];
if (!compareFn) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e));
this.isSearching = false;
return;
}
this.template.filter = this.template.data
.filter(item => {
const value = (item as any)[this.searchBy];
return compareFn(value, this.searchValue);
})
.map(e => new MyTemplateModel(e));
this.isSearching = false;
}, 1000); // delay mock loading
}
menuActiveTemplateFile(templateFile: TemplateFileModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการ' + (templateFile.menuActive == '1' ? 'ปิด' : 'เปิด') + 'การใช้งานหรือไม่'
modalConfirmRef.result.then(result => {
templateFile.menuActive = templateFile.menuActive == '1' ? '0' : '1'
this.excelReportService.postTemplateFile(templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
formatISOToLocal(isoString: string) {
const date = new Date(isoString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); // เดือนเริ่มที่ 0
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return { date: `${day}-${month}-${year}`, time: `${hours}:${minutes}` };
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
}, reason => {
})
}
openPrintModal(templateFile: TemplateFileModel) {
this.getTemplateFile(templateFile)
this.openModalprintModal()
}
getTemplateFile(templateFile: TemplateFileModel) {
this.loading = true
this.excelReport = undefined
this.variableSheet = []
this.valueDetail = []
this.getTemplateFileSubscription = this.excelReportService.getTemplateFile(templateFile.templateId, templateFile.fileName).subscribe(response => {
this.excelReport = response
this.getExcelColumn()
if (this.excelReport.param) {
Object.entries(this.excelReport.param.variableSheet).forEach(([key, value]) => {
const data = value as any
if (data.type == 'text') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key
})
} else if (data.type == 'list') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'radio') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'help') {
this.variableSheet.push({ ...data, value: data.valueDefault ? { id: data.valueDefault, tdesc: data.valueDefault, edesc: data.valueDefault } : { id: "", tdesc: "", edesc: "" }, key: key })
} else if (data.type == 'calendar') {
this.variableSheet.push({ ...data, value: data.valueDefault || '', key: key })
const [d, m, y] = data.valueDefault ? data.valueDefault.split('-').map(Number) : [null, null, null]
this.select[key] = y ? new NgbDate(y, m, d) : null
}
})
}
this.loading = false
this.cdr.detectChanges()
}, (err) => {
this.loading = false
})
}
openModalData(data: any) {
this.searchModal = ''
this.page = 0
this.pageSize = 10
this.valueDetail = data.valueDetail.map((x: any) => ({ id: x.id || '', tdesc: x.tdesc || '', edesc: x.edesc || '' }))
this.keySelect = data.key
this.modalDetail = {
text: {
cardHead: "Table " + data.table,
search: ["Table " + data.table],
tableHead: ['ID', 'detailTH', 'detailENG']
}
}
this.openModalmodalData()
}
valueDetailFilter(): ValueDetailItem[] {
return this.valueDetail.filter((item: any) =>
item.id.toLowerCase().includes(this.searchModal.toLowerCase()) ||
item.tdesc.toLowerCase().includes(this.searchModal.toLowerCase()) ||
item.edesc.toLowerCase().includes(this.searchModal.toLowerCase())
)
}
selectData(data: any) {
const item = this.variableSheet.find((i: any) => i.key === this.keySelect);
if (item) item.value = data;
}
formatNgbDate(key: string, date?: NgbDate) {
if (date) {
const day = String(date.day).padStart(2, '0');
const month = String(date.month).padStart(2, '0');
const year = date.year;
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = `${day}-${month}-${year}`
} else {
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = ''
}
}
validateDate(control: NgModel) {
if (!control.value) {
// ว่าง หรือวันที่ไม่ถูกต้อง
this.openAlertModal('กรุณาเลือกวันที่ให้ถูกต้อง');
}
}
dowloadExcelReport() {
this.loadingExcel = true
const fileName = this.excelReport.param.excelFile
const param = this.excelReport.param.variableName
const data = this.variableSheet.map((item: any) => {
if (item.type == 'help') {
return "__" + item.key + "=" + item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
return "__" + item.key + "=" + item.value
}
return ""
}).join('|')
const body = {
fileName: fileName,
paramObj: data
}
this.excelReportService.printExcelReport(body).subscribe((res: any) => {
const blob = new Blob([res], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, fileName);
this.loadingExcel = false
this.cdr.detectChanges()
}, (err) => {
this.loadingExcel = false
this.openAlertModal(err.message)
this.cdr.detectChanges()
})
}
openGridModal(targetModal: NgbModal, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
openPivotModal(targetModal: NgbModal, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
ngOnDestroy(): void {
this.getTemplateFileSubscription?.unsubscribe()
}
getExcelColumn() {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
}
this.searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
this.columns = []
this.pivotColumns = []
this.customCubeService.getExcelColumn(body).subscribe((response: any) => {
this.searchSettings = {
fields: response.map((e: any) => e.split(':')[0]),
operator: 'contains',
ignoreCase: false
};
response.forEach((e: any, i: number) => {
const [field, headerText] = e.split(':')
this.columns.push({
field: field,
headerText: headerText,
type: "string",
isPrimaryKey: i == 0,
})
this.pivotColumns.push({
name: field,
caption: headerText
})
});
}, error => {
})
}
getExcelData(type: 'grid' | 'pivot') {
this.loadingExcel = true
this.dataList = []
const body: any = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
};
this.variableSheet.forEach((item: any, index: number) => {
const i = index + 1;
body[`key${i}`] = item.key;
if (item.type == 'help') {
body[`val${i}`] = item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
body[`val${i}`] = item.value
}
});
forkJoin({
excelData: this.customCubeService.getExcelData(body),
excelPerspective: this.customCubeService.getExcelPerspective(body),
}).subscribe(response => {
this.dataList = response.excelData as any
this.loadingExcel = false
if (type == 'grid') {
this.modalRefgridModal = this.modal.open(this.gridModal, {
width: '1000px',
height: '700px',
data: {
gridData: (response.excelPerspective as any).dataGridStr,
dataList: this.dataList
},
disableClose: true,
autoFocus: false
});
} else if (type == 'pivot') {
this.modalRefpivotModal = this.modal.open(this.pivotModal, {
width: '1000px',
height: '580px',
data: {
pivotData: (response.excelPerspective as any).pivotStr,
dataList: this.dataList
},
disableClose: true,
autoFocus: false
});
}
this.cdr.detectChanges()
}, error => {
this.loadingExcel = false
})
}
savePerspective(type: 'grid' | 'pivot') {
if (type == 'grid') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: this.gridLayout.data,
pivotStr: "",
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
} else if (type == 'pivot') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: '',
pivotStr: this.pivotLayout.data,
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
}
}
openModalprintModal() {
this.modalRefprintModal = this.modal.open(this.printModal, {
width: '700px',
height: '300px'
})
}
closeModalprintModal() {
this.modalRefprintModal?.close()
}
openModalmodalData() {
this.modalRefmodalData = this.modal.open(this.modalData, {
width: '800px',
height: '580px'
})
}
closeModalmodalData() {
this.modalRefmodalData?.close()
}
closeModalgridModal() {
this.modalRefgridModal?.close()
}
closeModalpivotModal() {
this.modalRefpivotModal?.close()
}
}
\ No newline at end of file
/* 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 { SetExcelReportsComponent } from './set-excel-reports.component';
describe('SetExcelReportsComponent', () => {
let component: SetExcelReportsComponent;
let fixture: ComponentFixture<SetExcelReportsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SetExcelReportsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SetExcelReportsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-set-excel-reports',
templateUrl: './set-excel-reports.component.html',
styleUrls: ['./set-excel-reports.component.css']
})
export class SetExcelReportsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<app-page-header [title]="'รายการหลักสูตร'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการหลักสูตร'"></app-page-header>
<!-- <div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการหลักสูตร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search" >
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col">ประเภทหลักสูตร</th>
<th scope="col">ลิงค์</th>
<th scope="col">สถานะ</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listCourse.length == 0">
<td colspan="{{checkType == '1' ? '9' : '8'}}" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr *ngFor="let data of filterListCourse() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openDialog(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td class="text-wrap">{{ data.thDesc }}</td>
<td>{{data.courseType}}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.courseId)"></i></td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2" (click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listCourse.length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<!-- <div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListCourse()">
<div class="card border-5 border border-course h-100 shadow">
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}" (click)="openDialog(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.courseId)"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i>
ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5">
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<div class="box p-4">
<div *ngIf="checkType != '1'" class="flex flex-wrap -mx-2">
<div class="w-full">
<div class="py-3 px-2">
<input type="text"
class="block w-full md:w-1/4 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 shadow-sm"
placeholder="ค้นหา" [(ngModel)]="search" />
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/3 xl:w-1/3 px-2 mb-4" *ngFor="let data of filterListCourse()">
<div
class="group bg-white rounded-lg overflow-hidden shadow-lg border-4 border-blue-600 h-full flex flex-col transform transition-all duration-300 ease-in-out hover:scale-[1.02] hover:shadow-xl">
<div class="p-4 flex justify-center items-center flex-shrink-0">
<img
class="w-full h-full object-cover rounded-md shadow-md transform transition-transform duration-300 group-hover:scale-105 cursor-pointer"
style="width: 400px; height: 200px; max-width: 100%;" src="{{ data.getImage() }}" alt="{{ data.thName }}"
(click)="openDialog(data.getImage())" />
</div>
<div class="p-4 flex-grow">
<span class="text-xl font-semibold text-gray-800 mb-2" style="font-size: 18px;">
{{ data.thName }}
</span>
<p class="text-gray-700 text-sm mb-3">{{ data.thDesc }}</p>
<div class="mb-0 flex items-center justify-center sm:justify-start gap-2 w-1/2 mt-5">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ตัวอย่างวิธีใช้งาน'" (click)="openLink(data.link1)"
style="background-color: rgb(76, 117, 207, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-1/2">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด'" (click)="downloadFile(data.courseId)"
style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
<small class="text-gray-500 text-xs ml-auto hidden sm:block flex-shrink-0" *ngIf="data.dwTime > 0">{{
coverDate(data.downloadDate) }} {{ data.downloadTime }} (
{{ data.dwTime }} ครั้ง)</small>
</div>
</div>
<div class="px-4 py-3 border-t border-gray-200 text-right flex-shrink-0">
<small class="text-gray-500 text-xs">วันที่อัพโหลด {{ coverDate(data.uploadDate) }}
{{ data.uploadTime }}</small>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { CourseContentModel } from '../../../models/course-content.model';
import { CourseService } from '../../../services/course.service';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
import { OpenImageComponent } from '../open-image/open-image.component';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import saveAs from 'file-saver';
@Component({
selector: 'app-view-list-course',
templateUrl: './view-list-course.component.html',
styleUrls: ['./view-list-course.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
NgbPaginationModule,
TranslateModule,
],
})
export class ViewListCourseComponent implements OnInit {
page = 1;
pageSize = 10;
listCourse: CourseContentModel[] = []
dialogRef: any;
search: string = ''
checkType: string = '0'
constructor(private modalService: NgbModal, private courseService: CourseService, private activatedRoute: ActivatedRoute, private dialog: MatDialog,) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
openDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
// openEmployeeModal(image: string) {
// const modalRef = this.modalService.open(OpenImageComponent, {
// centered: true,
// windowClass: 'my-dialog-img-preview'
// })
// modalRef.componentInstance.linkImage = image
// modalRef.result.then(result => {
// }, reason => {
// this.modalService.dismissAll()
// })
// }
async downloadFile(logId: string) {
try {
const data = await this.courseService.downloadFileContent(logId).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.json");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
// async downloadFile(logId: string) {
// try {
// const data = await this.courseService.downloadFileContent(logId).toPromise();
// if (data) {
// FileSaver.saveAs(new Blob([data]), "file_download.json");
// }
// } catch (error) {
// console.error('Error loading data:', error);
// }
// }
filterListCourse() {
return this.listCourse.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListCourse() {
try {
const data = await this.courseService.getListCourseContent().toPromise();
this.listCourse = data!.map(x => new CourseContentModel(x))
console.log("🚀 ~ ViewListCourseComponent ~ getListCourse ~ this.listCourse :", this.listCourse)
} catch (error) {
console.error('Error loading data:', error);
}
}
deleteFile(item: CourseContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.courseService.deleteCourseContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListCourse();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
ngOnInit() {
this.getListCourse();
}
openLink(url: string) {
window.open(url, "_blank");
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
coverDate(date: string) {
return date.split('-').reverse().join('/')
}
}
<app-page-header [title]="'รายการเอกสาร'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการเอกสาร'"></app-page-header>
<!-- <div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์ (ไทย)</th>
<th scope="col" class="text-center">ไฟล์ (อังกฤษ)</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listDoc.length == 0">
<td colspan="{{checkType == '1' ? '9' : '10'}}" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr
*ngFor="let data of filterListDoc() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}" (click)="openDialog(data.getImage())">
</td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.docId,'tha')"></i>
</td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.docId,'eng')"></i>
</td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listDoc.length" [maxSize]="3"
[rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div> -->
<!-- <div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListDoc()">
<div class="card border-5 border border-doc h-100 shadow">
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}" (click)="openDialog(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.docId,'tha')"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด (ไทย) <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.docId,'eng')"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด (อังกฤษ) <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i>
ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5">
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<div class="box p-4">
<div *ngIf="checkType != '1'" class="flex flex-wrap -mx-2">
<div class="w-full">
<div class="py-3 px-2">
<input type="text"
class="block w-full md:w-1/4 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 shadow-sm"
placeholder="ค้นหา" [(ngModel)]="search" />
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/3 xl:w-1/3 px-2 mb-4" *ngFor="let data of filterListDoc()">
<div
class="group bg-white rounded-lg overflow-hidden shadow-lg border-4 border-blue-600 h-full flex flex-col transform transition-all duration-300 ease-in-out hover:scale-[1.02] hover:shadow-xl">
<div class="p-4 flex justify-center items-center flex-shrink-0">
<img
class="w-full h-full object-cover rounded-md shadow-md transform transition-transform duration-300 group-hover:scale-105 cursor-pointer"
style="width: 400px; height: 200px; max-width: 100%;" src="{{ data.getImage() }}" alt="{{ data.thName }}"
(click)="openDialog(data.getImage())" />
</div>
<div class="p-4 flex-grow">
<span class="text-xl font-semibold text-gray-800 mb-2" style="font-size: 18px;">
{{ data.thName }}
</span>
<p class="text-gray-700 text-sm mb-3">{{ data.thDesc }}</p>
<div class="mb-0 flex items-center justify-center sm:justify-start gap-2 w-1/2 mt-5">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ตัวอย่างวิธีใช้งาน'" (click)="openLink(data.link1)"
style="background-color: rgb(76, 117, 207, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-full mt-2">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด (ไทย)'" (click)="downloadFile(data.docId, 'tha')"
style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 cursor-pointer hover:opacity-90 focus:outline-none focus:ring-1 focus:ring-green-700 transition-all duration-200"
readonly />
<small class="text-gray-500 text-xs ml-auto hidden sm:block flex-shrink-0" *ngIf="data.dwTime > 0">
{{coverDate(data.downloadDate)}} {{data.downloadTime}} ( {{data.dwTime}} ครั้ง)
</small>
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-full">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด (อังกฤษ)'" (click)="downloadFile(data.docId, 'eng')"
style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 cursor-pointer hover:opacity-90 focus:outline-none focus:ring-1 focus:ring-green-700 transition-all duration-200"
readonly />
<small class="text-gray-500 text-xs ml-auto hidden sm:block flex-shrink-0" *ngIf="data.dwTime > 0">
{{coverDate(data.downloadDate)}} {{data.downloadTime}} ( {{data.dwTime}} ครั้ง)
</small>
</div>
</div>
<div class="px-4 py-3 border-t border-gray-200 text-right flex-shrink-0">
<small class="text-gray-500 text-xs">วันที่อัพโหลด {{ coverDate(data.uploadDate) }}
{{ data.uploadTime }}</small>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { DocumentContentModel } from '../../../models/document-content.model';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
import { OpenImageComponent } from '../open-image/open-image.component';
import { DocumentService } from '../../../services/document.service';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { saveAs } from 'file-saver';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'app-view-list-doc',
templateUrl: './view-list-doc.component.html',
styleUrls: ['./view-list-doc.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
NgbPaginationModule,
TranslateModule,
],
})
export class ViewListDocComponent implements OnInit {
page = 1;
pageSize = 10;
listDoc: DocumentContentModel[] = []
search: string = ''
dialogRef: any;
checkType: string = '0'
constructor(private modalService: NgbModal, private documentService: DocumentService, private activatedRoute: ActivatedRoute, private dialog: MatDialog,) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
openDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
deleteFile(item: DocumentContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalRef.result.then(result => {
this.documentService.deleteExcelContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ')
this.getListDoc();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้')
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
async downloadFile(logId: string, lang: string) {
try {
const data = await this.documentService.downloadFileContent(logId, lang).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.doc");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
filterListDoc() {
return this.listDoc.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()))
}
async getListDoc() {
try {
const data = await this.documentService.getListExcelContent().toPromise();
this.listDoc = data!.map(x => new DocumentContentModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
ngOnInit() {
this.getListDoc();
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
openLink(url: string) {
window.open(url, "_blank");
}
coverDate(date: string) {
return date.split('-').reverse().join('/')
}
}
<app-page-header [title]="'รายการเอกสาร Excel'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการเอกสาร Excel'"></app-page-header>
<div class="row" *ngIf="checkType == '1'">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการ Excel</h4>
<div class="d-flex mb-3 mt-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
<div class="table-responsive">
<table class="table table-striped mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">#</th>
<th scope="col">รูปภาพ</th>
<th scope="col">ชื่อ</th>
<th scope="col">รายละเอียด</th>
<th scope="col" class="text-center">ลิงค์</th>
<th scope="col" class="text-center">ไฟล์</th>
<th scope="col" class="text-center">วันที่อัพโหลด</th>
<th scope="col" class="text-center">วันล่าสุดดาวน์โหลด</th>
<th scope="col" class="text-center">จำนวนดาวน์โหลด</th>
<th scope="col" *ngIf="checkType == '1'"></th>
</tr>
</thead>
<tbody>
<tr *ngIf="listExcel.length == 0">
<td [colSpan]="checkType == '1' ? 8 : 7" class="text-center">ไม่พบข้อมูล</td>
</tr>
<tr
*ngFor="let data of filterListExcel() | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<td class="text-center">{{i+1}}</td>
<td><img width="100" class="border p-1" src="{{data.getImage()}}"
(click)="openDialog(data.getImage())"></td>
<td>{{ data.thName }}</td>
<td>{{ data.thDesc }}</td>
<td class="text-center"><i class="fas fa-link pointer" (click)="openLink(data.link1)"></i></td>
<td class="text-center"> <i class="fas fa-download pointer" (click)="downloadFile(data.excelId)"></i></td>
<td class="text-center">{{data.uploadDate}} {{data.uploadTime}}</td>
<td class="text-center">{{data.downloadDate}} {{data.downloadTime}}</td>
<td class="text-center">{{data.dwTime}} ครั้ง</td>
<td *ngIf="checkType == '1'">
<button type="button" class="btn btn-circle btn-danger rounded-circle btn-sm mr-2"
(click)="deleteFile(data)" placement="top" ngbTooltip="ลบ"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="listExcel.length" [maxSize]="3"
[rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
</div>
</div>
<!-- <div class="row" *ngIf="checkType != '1'">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListExcel()">
<div class="card border-5 border border-excel h-100 shadow">
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.thName }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.excelId)"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i>
ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5">
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<div class="box p-4">
<div *ngIf="checkType != '1'" class="flex flex-wrap -mx-2">
<div class="w-full">
<div class="py-3 px-2">
<input type="text"
class="block w-full md:w-1/4 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 shadow-sm"
placeholder="ค้นหา" [(ngModel)]="search" />
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/3 xl:w-1/3 px-2 mb-4" *ngFor="let data of filterListExcel()">
<div
class="group bg-white rounded-lg overflow-hidden shadow-lg border-4 border-blue-600 h-full flex flex-col transform transition-all duration-300 ease-in-out hover:scale-[1.02] hover:shadow-xl">
<div class="p-4 flex justify-center items-center flex-shrink-0">
<img
class="w-full h-full object-cover rounded-md shadow-md transform transition-transform duration-300 group-hover:scale-105 cursor-pointer"
style="width: 400px; height: 200px; max-width: 100%;" src="{{ data.getImage() }}" alt="{{ data.thName }}"
(click)="openDialog(data.getImage())" />
</div>
<div class="p-4 flex-grow">
<span class="text-xl font-semibold text-gray-800 mb-2" style="font-size: 18px;">
{{ data.thName }}
</span>
<p class="text-gray-700 text-sm mb-3">{{ data.thDesc }}</p>
<div class="mb-0 flex items-center justify-center sm:justify-start gap-2 w-1/2 mt-5">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ตัวอย่างวิธีใช้งาน'" (click)="openLink(data.link1)" style="background-color: rgb(76, 117, 207, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-1/2">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด'" (click)="downloadFile(data.excelId)" style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
<small class="text-gray-500 text-xs ml-auto hidden sm:block flex-shrink-0" *ngIf="data.dwTime > 0">{{
coverDate(data.downloadDate) }} {{ data.downloadTime }} (
{{ data.dwTime }} ครั้ง)</small>
</div>
</div>
<div class="px-4 py-3 border-t border-gray-200 text-right flex-shrink-0">
<small class="text-gray-500 text-xs">วันที่อัพโหลด {{ coverDate(data.uploadDate) }}
{{ data.uploadTime }}</small>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { ExcelService } from '../../../services/excel.service';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { ExcelContentModel } from '../../../models/excel-content.model';
import { OpenImageComponent } from '../open-image/open-image.component';
import { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../shared/shared.module';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { saveAs } from 'file-saver';
@Component({
selector: 'app-view-list-excel',
templateUrl: './view-list-excel.component.html',
styleUrls: ['./view-list-excel.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
RouterModule,
NgSelectModule,
SharedModule,
MatDialogModule,
TranslateModule,
NgbPaginationModule,
],
})
export class ViewListExcelComponent implements OnInit {
page = 1;
pageSize = 10;
listExcel: ExcelContentModel[] = [];
search: string = '';
checkType: string = '0';
dialogRef: any;
constructor(
private modalService: NgbModal,
private excelService: ExcelService,
private activatedRoute: ActivatedRoute,
private dialog: MatDialog,
) {
this.activatedRoute.paramMap.subscribe(result => {
this.checkType = result.get("type")!;
});
}
openDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
// openEmployeeModal(image: string) {
// const modalRef = this.modalService.open(OpenImageComponent, {
// centered: true,
// windowClass: 'my-dialog-img-preview'
// });
// modalRef.componentInstance.linkImage = image;
// modalRef.result.then(result => {
// }, reason => {
// this.modalService.dismissAll();
// });
// }
deleteFile(item: ExcelContentModel) {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
});
modalRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่';
modalRef.result.then(result => {
this.excelService.deleteExcelContent(item).subscribe(result => {
if (result) {
this.openAlertModal('ลบข้อมูลสำเร็จ');
this.getListExcel();
} else {
this.openAlertModal('ไม่สามารถลบข้อมูลได้');
}
}, error => {
this.openAlertModal(error.message);
});
}, reject => { });
}
async downloadFile(logId: string) {
try {
const data = await this.excelService.downloadFileContent(logId).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.xlsx");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
getStatus(status: string): string {
if (status == '0') {
return 'รออนุมัติ';
} else if (status == '1') {
return 'เปิดใช้งาน';
} else if (status == '2') {
return 'ไม่อนุมัติ';
}
return '';
}
filterListExcel() {
return this.listExcel.filter(x => x.thName.toLowerCase().includes(this.search.toLowerCase()) || x.engName.toLowerCase().includes(this.search.toLowerCase()));
}
async getListExcel() {
try {
const data = await this.excelService.getListExcelContent().toPromise();
this.listExcel = data!.map(x => new ExcelContentModel(x));
} catch (error) {
console.error('Error loading data:', error);
}
}
ngOnInit() {
this.getListExcel();
}
openLink(url: string) {
window.open(url, "_blank");
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
});
modalRef.componentInstance.message = message ? message : "";
modalRef.result.then(result => {
this.modalService.dismissAll();
}, reason => {
this.modalService.dismissAll();
});
}
coverDate(date: string) {
return date.split('-').reverse().join('/');
}
}
\ No newline at end of file
<app-page-header [title]="'รายการวิทเจ็ท'" [activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'รายการวิทเจ็ท'"></app-page-header>
<!-- <div class="row">
<div class="col-12">
<div class="py-3">
<input type="text" class="form-control w-25" placeholder="ค้นหา" [(ngModel)]="search">
</div>
</div>
<div class="col-sm-6 col-md-4 col-lg-4 mb-3" *ngFor="let data of filterListWidget()">
<div class="card border-5 border border-widget h-100 shadow">
<div class=" p-2 border-5">
<img width="100" class="card-img-top cover" src="{{data.getImage()}}"
(click)="openEmployeeModal(data.getImage())">
</div>
<div class="card-body">
<h4 class="card-title">{{ data.widgetTname }}</h4>
<p class="card-text ">{{ data.thDesc }}</p>
<p class="text-info pointer mb-0" (click)="downloadFile(data.widgetId)"><i class="fas fa-download mr-1"></i>
ดาวน์โหลด <small class="text-muted" *ngIf="data.dwTime > 0">{{coverDate(data.downloadDate)}}
{{data.downloadTime}} ( {{data.dwTime}} ครั้ง)</small></p>
<p class="text-info pointer mb-0" (click)="openLink(data.link1)"><i class="fas fa-link mr-1"></i>
ตัวอย่างวิธีใช้งาน</p>
</div>
<div class="card-footer border-bottom-5">
<small class="text-muted">วันที่อัพโหลด {{coverDate(data.uploadDate)}} {{data.uploadTime}}</small>
</div>
</div>
</div>
</div> -->
<div class="box p-4">
<div class="flex flex-wrap -mx-2">
<div class="w-full">
<div class="py-3 px-2">
<input type="text"
class="block w-full md:w-1/4 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 shadow-sm"
placeholder="ค้นหา" [(ngModel)]="search" />
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/3 xl:w-1/3 px-2 mb-4" *ngFor="let data of filterListWidget()">
<div
class="group bg-white rounded-lg overflow-hidden shadow-lg border-4 border-blue-600 h-full flex flex-col transform transition-all duration-300 ease-in-out hover:scale-[1.02] hover:shadow-xl">
<div class="p-4 flex justify-center items-center flex-shrink-0">
<img
class="w-full h-full object-cover rounded-md shadow-md transform transition-transform duration-300 group-hover:scale-105 cursor-pointer"
style="width: 400px; height: 200px; max-width: 100%;" src="{{ data.getImage() }}" alt="{{ data.widgetTname }}"
(click)="openDialog(data.getImage())" />
</div>
<div class="p-4 flex-grow">
<span class="text-xl font-semibold text-gray-800 mb-2" style="font-size: 18px;">
{{ data.widgetTname }}
</span>
<p class="text-gray-700 text-sm mb-3">{{ data.thDesc }}</p>
<div class="mb-0 flex items-center justify-center sm:justify-start gap-2 w-1/2 mt-5">
<i class="fa fa-link text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ตัวอย่างวิธีใช้งาน'" (click)="openLink(data.link1)"
style="background-color: rgb(76, 117, 207, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
<div class="mb-2 flex items-center justify-center sm:justify-start gap-2 w-1/2">
<i class="fa fa-download text-blue-600 text-base flex-shrink-0" aria-hidden="true"></i>
<input type="text" [value]="'ดาวน์โหลด'" (click)="downloadFile(data.widgetId)"
style="background-color: rgb(34, 197, 94, 1); color: #FFF;"
class="flex-grow border border-gray-300 rounded-md px-2 py-1 text-blue-600 cursor-pointer hover:underline focus:outline-none focus:ring-1 focus:ring-blue-500 transition-all duration-200"
readonly />
</div>
</div>
<div class="px-4 py-3 border-t border-gray-200 text-right flex-shrink-0">
<small class="text-gray-500 text-xs">วันที่อัพโหลด {{ coverDate(data.uploadDate) }}
{{ data.uploadTime }}</small>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { OpenImageComponent } from '../open-image/open-image.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { SharedModule } from '../../../../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { NgSelectModule } from '@ng-select/ng-select';
import saveAs from 'file-saver';
import { WidgetModel } from '../../../models/widgets.model';
import { WidgetService } from '../../../services/widgets.service';
@Component({
selector: 'app-view-list-widgets',
templateUrl: './view-list-widgets.component.html',
styleUrls: ['./view-list-widgets.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
NgSelectModule,
SharedModule,
MatDialogModule,
NgbPaginationModule,
TranslateModule,
],
})
export class ViewListWidgetsComponent implements OnInit {
page = 1;
pageSize = 10;
listWidget:WidgetModel[]=[]
search:string = ''
dialogRef: any;
constructor(private modalService: NgbModal,private widgetService:WidgetService, private dialog: MatDialog,) {
}
openDialog(image: string) {
const dialogConfig = {
width: '750px',
disableClose: false,
data: {
linkImage: image
},
panelClass: 'my-dialog-img-preview',
};
this.dialogRef = this.dialog.open(OpenImageComponent, dialogConfig);
this.dialogRef.afterClosed().subscribe((result: any) => {
console.log('The dialog was closed', result);
}, (reason: any) => {
});
}
async downloadFile(logId: string) {
try {
const data = await this.widgetService.downloadFile(logId).toPromise();
if (data) {
saveAs(new Blob([data]), "file_download.xlsx");
}
} catch (error) {
console.error('Error loading data:', error);
}
}
// getStatus(status: string) {
// if (status == '0') {
// return 'Private'
// } else if (status == '1') {
// return 'Public'
// }
// }
filterListWidget(){
return this.listWidget.filter(x => x.widgetTname.toLowerCase().includes(this.search.toLowerCase())||x.widgetEname.toLowerCase().includes(this.search.toLowerCase()))
}
async getListExcel(){
try {
const data = await this.widgetService.getListWidgets().toPromise();
this.listWidget = data!.map(x => new WidgetModel(x))
} catch (error) {
console.error('Error loading data:', error);
}
}
ngOnInit() {
this.getListExcel();
}
openLink(url:string){
window.open(url, "_blank");
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
this.modalService.dismissAll()
}, reason => {
this.modalService.dismissAll()
})
}
coverDate(date:string){
return date.split('-').reverse().join('/')
}
}
......@@ -7,137 +7,137 @@ import { QuillModule } from 'ngx-quill';
export const myportal: Routes = [
{
path: 'myportal', children: [
path: 'myskill-x', children: [
//////////////MyPortal/////////////////
{
path: 'portal-category-list',
loadComponent: () =>
import('../common/myportal/portal-category-list/portal-category-list.component').then((m) => m.PortalCategoryListComponent),
import('../myskill-x/myportal/portal-category-list/portal-category-list.component').then((m) => m.PortalCategoryListComponent),
},
{
path: 'view-list-excel',
loadComponent: () =>
import('../common/myportal/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
import('../myskill-x/myportal/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
},
{
path: 'view-list-doc',
loadComponent: () =>
import('../common/myportal/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
import('../myskill-x/myportal/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
},
{
path: 'view-list-course',
loadComponent: () =>
import('../common/myportal/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
import('../myskill-x/myportal/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
},
{
path: 'view-list-widgets',
loadComponent: () =>
import('../common/myportal/view-list-widgets/view-list-widgets.component').then((m) => m.ViewListWidgetsComponent),
import('../myskill-x/myportal/view-list-widgets/view-list-widgets.component').then((m) => m.ViewListWidgetsComponent),
},
{
path: 'excel-report',
loadComponent: () =>
import('../common/myportal/excel-report/excel-report.component').then((m) => m.ExcelReportComponent),
import('../myskill-x/myportal/excel-report/excel-report.component').then((m) => m.ExcelReportComponent),
},
{
path: 'portal-create-category',
loadComponent: () =>
import('../common/myportal/portal-create-category/portal-create-category.component').then((m) => m.PortalCreateCategoryComponent),
import('../myskill-x/myportal/portal-create-category/portal-create-category.component').then((m) => m.PortalCreateCategoryComponent),
},
{
path: 'list-excell',
loadComponent: () =>
import('../common/myportal/list-excell/list-excell.component').then((m) => m.ListExcelComponent),
import('../myskill-x/myportal/list-excell/list-excell.component').then((m) => m.ListExcelComponent),
},
{
path: 'list-course',
loadComponent: () =>
import('../common/myportal/list-course/list-course.component').then((m) => m.ListCourseComponent),
import('../myskill-x/myportal/list-course/list-course.component').then((m) => m.ListCourseComponent),
},
{
path: 'list-doc',
loadComponent: () =>
import('../common/myportal/list-doc/list-doc.component').then((m) => m.ListDocComponent),
import('../myskill-x/myportal/list-doc/list-doc.component').then((m) => m.ListDocComponent),
},
{
path: 'list-widgets',
loadComponent: () =>
import('../common/myportal/list-widgets/list-widgets.component').then((m) => m.ListWidgetsComponent),
import('../myskill-x/myportal/list-widgets/list-widgets.component').then((m) => m.ListWidgetsComponent),
},
{
path: 'excel-list',
loadComponent: () =>
import('../common/myportal/set-excel-reports/excel-list/excel-list.component').then((m) => m.ExcelListComponent),
import('../myskill-x/myportal/set-excel-reports/excel-list/excel-list.component').then((m) => m.ExcelListComponent),
},
{
path: 'datasource-table',
loadComponent: () =>
import('../common/myportal/datasource-table/datasource-table.component').then((m) => m.DatasourceTableComponent),
import('../myskill-x/myportal/datasource-table/datasource-table.component').then((m) => m.DatasourceTableComponent),
},
{
path: 'excel-report-toggle',
loadComponent: () =>
import('../common/myportal/set-excel-reports/excel-report-toggle/excel-report-toggle.component').then((m) => m.ExcelReportToggleComponent),
import('../myskill-x/myportal/set-excel-reports/excel-report-toggle/excel-report-toggle.component').then((m) => m.ExcelReportToggleComponent),
},
{
path: 'management',
loadComponent: () =>
import('../common/myportal/management/management.component').then((m) => m.ManagementComponent),
import('../myskill-x/myportal/management/management.component').then((m) => m.ManagementComponent),
},
{
path: 'portal-category-list-approve',
loadComponent: () =>
import('../common/myportal/management/portal-category-list-approve/portal-category-list-approve.component').then((m) => m.PortalCategoryListApproveComponent),
import('../myskill-x/myportal/management/portal-category-list-approve/portal-category-list-approve.component').then((m) => m.PortalCategoryListApproveComponent),
},
{
path: 'approve-excel',
loadComponent: () =>
import('../common/myportal/management/portal-category-list-approve/approve-excel/approve-excel.component').then((m) => m.ApproveExcelComponent),
import('../myskill-x/myportal/management/portal-category-list-approve/approve-excel/approve-excel.component').then((m) => m.ApproveExcelComponent),
},
{
path: 'approve-doc',
loadComponent: () =>
import('../common/myportal/management/portal-category-list-approve/approve-doc/approve-doc.component').then((m) => m.ApproveDocComponent),
import('../myskill-x/myportal/management/portal-category-list-approve/approve-doc/approve-doc.component').then((m) => m.ApproveDocComponent),
},
{
path: 'approve-course',
loadComponent: () =>
import('../common/myportal/management/portal-category-list-approve/approve-course/approve-course.component').then((m) => m.ApproveCourseComponent),
import('../myskill-x/myportal/management/portal-category-list-approve/approve-course/approve-course.component').then((m) => m.ApproveCourseComponent),
},
{
path: 'approved-list',
loadComponent: () =>
import('../common/myportal/management/approved-list/approved-list.component').then((m) => m.ApprovedListComponent),
import('../myskill-x/myportal/management/approved-list/approved-list.component').then((m) => m.ApprovedListComponent),
},
{
path: 'view-list-excel',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
import('../myskill-x/myportal/management/approved-list/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
},
{
path: 'view-list-excel/:type',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
import('../myskill-x/myportal/management/approved-list/view-list-excel/view-list-excel.component').then((m) => m.ViewListExcelComponent),
},
{
path: 'view-list-doc',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
import('../myskill-x/myportal/management/approved-list/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
},
{
path: 'view-list-doc/:type',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
import('../myskill-x/myportal/management/approved-list/view-list-doc/view-list-doc.component').then((m) => m.ViewListDocComponent),
},
{
path: 'view-list-course',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
import('../myskill-x/myportal/management/approved-list/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
},
{
path: 'view-list-course/:type',
loadComponent: () =>
import('../common/myportal/management/approved-list/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
import('../myskill-x/myportal/management/approved-list/view-list-course/view-list-course.component').then((m) => m.ViewListCourseComponent),
},
]
}
......
......@@ -363,7 +363,7 @@
</div>
<div class="">
<a routerLink="/myportal/portal-category-list"
<a routerLink="/myskill-x/portal-category-list"
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/logoallHR/mySkill-x.png" alt="miscrosoft"
class="leading-[1.75] text-2xl !h-[1.75rem] align-middle flex justify-center mx-auto">
......
......@@ -114,7 +114,7 @@ export class SidebarComponent {
this.currentUrl = this.router.url
this.isCommonRoute = this.currentUrl.includes('/admin');
this.isInstallerRoute = this.currentUrl.includes('/company');
this.isMyportalRoute = this.currentUrl.includes('/myportal');
this.isMyportalRoute = this.currentUrl.includes('/myskill-x');
this.isMylearnRoute = this.currentUrl.includes('/mylearn');
this.isMyJobRoute = this.currentUrl.includes('/myjob');
this.isMyhrLiteRoute = this.currentUrl.includes('/myhr-lite');
......@@ -149,7 +149,7 @@ export class SidebarComponent {
checkInitialUrl(): void {
this.isCommonRoute = this.currentUrl.includes('/admin');
this.isInstallerRoute = this.currentUrl.includes('/company');
this.isMyportalRoute = this.currentUrl.includes('/myportal');
this.isMyportalRoute = this.currentUrl.includes('/myskill-x');
this.isMylearnRoute = this.currentUrl.includes('/mylearn');
this.isMyJobRoute = this.currentUrl.includes('/myjob');
this.isMyhrLiteRoute = this.currentUrl.includes('/myhr-lite');
......@@ -161,7 +161,7 @@ export class SidebarComponent {
console.log('Initial URL:', this.currentUrl);
console.log('Is Common Route:', this.isCommonRoute);
console.log('Is Installer Route:', this.isInstallerRoute);
console.log('Is Myportal Route:', this.isMyportalRoute);
console.log('Is Myskill-X Route:', this.isMyportalRoute);
console.log('mylearn routes',this.isMylearnRoute);
console.log('myhrlite routes',this.isMyhrLiteRoute);
console.log('myface routes',this.isMyFaceRoute);
......@@ -197,7 +197,7 @@ export class SidebarComponent {
// Update the boolean values
this.isCommonRoute = this.currentUrl.includes('/admin');
this.isInstallerRoute = this.currentUrl.includes('/company');
this.isMyportalRoute = this.currentUrl.includes('/myportal');
this.isMyportalRoute = this.currentUrl.includes('/myskill-x');
this.isMylearnRoute = this.currentUrl.includes('/mylearn');
this.isMyJobRoute = this.currentUrl.includes('/myjob');
this.isMyhrLiteRoute = this.currentUrl.includes('/myhr-lite');
......
......@@ -224,45 +224,45 @@ export class NavService implements OnDestroy {
{ headTitle: 'MyPortal' },
{
icon: 'receipt',
path: '/myportal/portal-category-list',
path: '/myskill-x/portal-category-list',
title: 'รายการเอกสาร',
type: 'link',
},
{
icon: 'receipt',
path: '/myportal/portal-create-category',
path: '/myskill-x/portal-create-category',
title: 'รายการอัพโหลดเอกสาร',
type: 'link',
},
{
icon: 'cog',
path: '/myportal/management',
path: '/myskill-x/management',
title: 'การจัดการ',
type: 'sub',
children: [
{ path: '/myportal/portal-category-list-approve', title: 'รายการรอการอนุมัติ', type: 'link' },
{ path: '/myportal/approved-list', title: 'รายการผ่านการอนุมัติ', type: 'link' }
{ path: '/myskill-x/portal-category-list-approve', title: 'รายการรอการอนุมัติ', type: 'link' },
{ path: '/myskill-x/approved-list', title: 'รายการผ่านการอนุมัติ', type: 'link' }
],
},
{
icon: 'user',
path: '/myportal/set-excel-reports',
path: '/myskill-x/set-excel-reports',
title: 'ตั้งรายงานเอ็กเซล',
type: 'sub',
children: [
{ path: '/myportal/excel-list', title: 'เพิ่มรายงาน Excel', type: 'link' },
{ path: '/myportal/excel-report-toggle', title: 'เปิด-ปิด การใช้รายงาน Excel', type: 'link' }
{ path: '/myskill-x/excel-list', title: 'เพิ่มรายงาน Excel', type: 'link' },
{ path: '/myskill-x/excel-report-toggle', title: 'เปิด-ปิด การใช้รายงาน Excel', type: 'link' }
],
},
{
icon: 'data',
path: '/myportal/datasource-table',
path: '/myskill-x/datasource-table',
title: 'DataSource Table',
type: 'link',
},
{
icon: 'file',
path: '/myportal/excel-report',
path: '/myskill-x/excel-report',
title: 'รายงาน Excel',
type: 'link',
},
......
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