Commit 5f100770 by DESKTOP-E3GSHH7\myhr

อัพโหลดรายการเอกสาร excel

parent b5c35c21
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
"ng2-file-upload": "^8.0.0", "ng2-file-upload": "^8.0.0",
"ng2-nouislider": "^2.0.0", "ng2-nouislider": "^2.0.0",
"ngx-bar-rating": "^6.0.0", "ngx-bar-rating": "^6.0.0",
"ngx-chips": "^3.0.0",
"ngx-color-picker": "^16.0.0", "ngx-color-picker": "^16.0.0",
"ngx-colors": "^3.5.3", "ngx-colors": "^3.5.3",
"ngx-countup": "^13.1.0", "ngx-countup": "^13.1.0",
...@@ -12755,6 +12756,15 @@ ...@@ -12755,6 +12756,15 @@
"@angular/core": "^19.0.0" "@angular/core": "^19.0.0"
} }
}, },
"node_modules/ng2-material-dropdown": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ng2-material-dropdown/-/ng2-material-dropdown-1.0.0.tgz",
"integrity": "sha512-waKgEkKQwGZT0dqRAd3ZW+wueYs5Xw9owiPBSMdg5jru5DcKDpUqFaADMqqI7HPuzJCxNJSln6nXX1tMYDbXGA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
}
},
"node_modules/ng2-nouislider": { "node_modules/ng2-nouislider": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ng2-nouislider/-/ng2-nouislider-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ng2-nouislider/-/ng2-nouislider-2.0.0.tgz",
...@@ -12783,6 +12793,23 @@ ...@@ -12783,6 +12793,23 @@
"rxjs": ">=6.0.0" "rxjs": ">=6.0.0"
} }
}, },
"node_modules/ngx-chips": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ngx-chips/-/ngx-chips-3.0.0.tgz",
"integrity": "sha512-hBJg4m9kCtCfersQef7vr2Ve6j9ntFSoB5ktF8MyOQnpONAriIp46c3Tb2wdf/lMEB2Xmc45snfJKnxKh3zimg==",
"license": "MIT",
"dependencies": {
"ng2-material-dropdown": ">=1.0.0",
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/animations": ">=10.1.5",
"@angular/common": ">=10.1.5",
"@angular/compiler": ">=10.1.5",
"@angular/core": ">=10.1.5",
"@angular/forms": ">=10.1.5"
}
},
"node_modules/ngx-color-picker": { "node_modules/ngx-color-picker": {
"version": "16.0.0", "version": "16.0.0",
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-16.0.0.tgz", "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-16.0.0.tgz",
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
"@ngx-translate/core": "^15.0.0", "@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0", "@ngx-translate/http-loader": "^8.0.0",
"@syncfusion/ej2-angular-base": "^29.2.4", "@syncfusion/ej2-angular-base": "^29.2.4",
"@syncfusion/ej2-angular-charts": "^29.2.4",
"@syncfusion/ej2-angular-dropdowns": "^29.2.4", "@syncfusion/ej2-angular-dropdowns": "^29.2.4",
"@syncfusion/ej2-angular-grids": "^29.2.4", "@syncfusion/ej2-angular-grids": "^29.2.4",
"@syncfusion/ej2-angular-inputs": "^29.2.4", "@syncfusion/ej2-angular-inputs": "^29.2.4",
...@@ -45,7 +46,6 @@ ...@@ -45,7 +46,6 @@
"@syncfusion/ej2-dropdowns": "^29.2.4", "@syncfusion/ej2-dropdowns": "^29.2.4",
"@syncfusion/ej2-grids": "^29.2.4", "@syncfusion/ej2-grids": "^29.2.4",
"@syncfusion/ej2-inputs": "^29.2.4", "@syncfusion/ej2-inputs": "^29.2.4",
"@syncfusion/ej2-angular-charts": "^29.2.4",
"@tailwindcss/forms": "^0.5.7", "@tailwindcss/forms": "^0.5.7",
"@types/google.maps": "^3.58.1", "@types/google.maps": "^3.58.1",
"@types/googlemaps": "^3.43.3", "@types/googlemaps": "^3.43.3",
...@@ -76,6 +76,7 @@ ...@@ -76,6 +76,7 @@
"ng2-file-upload": "^8.0.0", "ng2-file-upload": "^8.0.0",
"ng2-nouislider": "^2.0.0", "ng2-nouislider": "^2.0.0",
"ngx-bar-rating": "^6.0.0", "ngx-bar-rating": "^6.0.0",
"ngx-chips": "^3.0.0",
"ngx-color-picker": "^16.0.0", "ngx-color-picker": "^16.0.0",
"ngx-colors": "^3.5.3", "ngx-colors": "^3.5.3",
"ngx-countup": "^13.1.0", "ngx-countup": "^13.1.0",
...@@ -120,4 +121,4 @@ ...@@ -120,4 +121,4 @@
"tailwindcss": "^3.3.2", "tailwindcss": "^3.3.2",
"typescript": "~5.2.2" "typescript": "~5.2.2"
} }
} }
\ No newline at end of file
...@@ -114,6 +114,31 @@ export const admin: Routes = [ ...@@ -114,6 +114,31 @@ export const admin: Routes = [
import('./myportal/excel-report/excel-report.component').then((m) => m.ExcelReportComponent), import('./myportal/excel-report/excel-report.component').then((m) => m.ExcelReportComponent),
}, },
{ {
path: 'portal-create-category',
loadComponent: () =>
import('./myportal/portal-create-category/portal-create-category.component').then((m) => m.PortalCreateCategoryComponent),
},
{
path: 'list-excell',
loadComponent: () =>
import('./myportal/list-excell/list-excell.component').then((m) => m.ListExcelComponent),
},
{
path: 'list-course',
loadComponent: () =>
import('./myportal/list-course/list-course.component').then((m) => m.ListCourseComponent),
},
{
path: 'list-doc',
loadComponent: () =>
import('./myportal/list-doc/list-doc.component').then((m) => m.ListDocComponent),
},
{
path: 'list-widgets',
loadComponent: () =>
import('./myportal/list-widgets/list-widgets.component').then((m) => m.ListWidgetsComponent),
},
{
path: 'excel-list', path: 'excel-list',
loadComponent: () => loadComponent: () =>
import('./myportal/set-excel-reports/excel-list/excel-list.component').then((m) => m.ExcelListComponent), import('./myportal/set-excel-reports/excel-list/excel-list.component').then((m) => m.ExcelListComponent),
......
<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" (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" (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) {
this.openAlertModalWithMatDialog('ลบข้อมูลสำเร็จ');
this.getListCourse();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถลบข้อมูลได้');
}
}, 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) {
this.openAlertModalWithMatDialog('อัพเดทข้อมูลสำเร็จ');
this.closeDialog();
this.getListCourse();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.courseService.createCourse(this.modelCourse).subscribe(res => {
if (res) {
this.openAlertModalWithMatDialog('บันทึกข้อมูลสำเร็จ');
this.closeDialog();
this.getListCourse();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
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" (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"
(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"
(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) {
this.openAlertModalWithMatDialog('ลบข้อมูลสำเร็จ');
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถลบข้อมูลได้');
}
}, 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) {
this.openAlertModalWithMatDialog('อัพเดทข้อมูลสำเร็จ');
this.closeDialog(); // ปิด MatDialog
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.documentService.createDoc(this.modelDoc).subscribe(res => {
if (res) {
this.openAlertModalWithMatDialog('บันทึกข้อมูลสำเร็จ');
this.closeDialog(); // ปิด MatDialog
this.getListDoc(); // โหลดข้อมูลใหม่ทั้งหมดและใช้ applyFilterAndPagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
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"
(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" (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" (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 { ConfirmModalComponent } from '../confirm-modal/confirm-modal.component';
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,
NgbPaginationModule,
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 modalService: NgbModal,
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) {
this.openAlertModalWithMatDialog('ลบข้อมูลสำเร็จ');
this.getListExcel();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถลบข้อมูลได้');
}
}, 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) {
this.openAlertModalWithMatDialog('อัพเดทข้อมูลสำเร็จ');
this.closeDialog();
this.getListExcel();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.excelService.createExcel(this.modelExcel).subscribe(res => {
if (res) {
this.openAlertModalWithMatDialog('บันทึกข้อมูลสำเร็จ');
this.closeDialog();
this.getListExcel();
} else {
this.openAlertModalWithMatDialog('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
onSumit() {
if (this.checkEdit) {
this.onUpdate();
} else {
this.onCreate();
}
}
async getExcelById(targetModal: NgbModal, 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.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
});
}
} catch (error) {
console.error('Error loading data:', error);
}
}
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) => {
});
}
closeBtnClick() {
this.modalService.dismissAll();
}
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"
(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"
(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) {
this.openAlertModalWithMatDialog('ลบข้อมูลสำเร็จ');
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถลบข้อมูลได้');
}
}, 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) {
this.openAlertModalWithMatDialog('อัพเดทข้อมูลสำเร็จ');
this.closeDialog();
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถอัพเดทข้อมูลได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
onCreate() {
swal({
title: "คุณแน่ใจหรือไม่?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willSave: boolean) => {
if (willSave) {
this.widgetService.createWidget(this.modelWidget).subscribe(res => {
if (res) {
this.openAlertModalWithMatDialog('บันทึกข้อมูลสำเร็จ');
this.closeDialog();
this.getListWidgets(); // Re-fetch and re-apply filter/pagination
} else {
this.openAlertModalWithMatDialog('ไม่สามารถสร้างเอกสารได้');
}
}, error => {
this.openAlertModalWithMatDialog(error.message);
});
}
});
}
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>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<div class="bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition duration-300 ease-in-out"
*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>
<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 transition duration-150 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>
<!-- 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> -->
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();
}
}
import { Injectable } from '@angular/core';
import { environment } from "../../../environments/environment";
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { GroupModel } from '../models/group.mmodel';
@Injectable({
providedIn: 'root'
})
export class GroupService {
url = environment.url
createStatus: boolean = true
constructor(private http: HttpClient) { }
getList(): Observable<GroupModel[]> {
return this.http.get<GroupModel[]>(this.url + "portal-group/lists")
}
}
import { Injectable } from '@angular/core';
import { environment } from "../../../environments/environment";
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { TagModel } from '../models/tag.mmodel';
@Injectable({
providedIn: 'root'
})
export class TagService {
url = environment.url
createStatus: boolean = true
constructor(private http: HttpClient) { }
getList(): Observable<TagModel[]> {
return this.http.get<TagModel[]>(this.url + "tag/lists")
}
getListWidgets(): Observable<TagModel[]> {
return this.http.get<TagModel[]>(this.url + "widget-tag/lists")
}
}
import { Injectable } from '@angular/core';
import { environment } from "../../../environments/environment";
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UploadService {
url = environment.url
createStatus: boolean = true
constructor(private http: HttpClient) { }
uploadImage(file: any): Observable<any> {
return this.http.post<any>(this.url + 'files/upload/image', file, { observe: 'response' })
}
}
...@@ -133,12 +133,18 @@ export class NavService implements OnDestroy { ...@@ -133,12 +133,18 @@ export class NavService implements OnDestroy {
}, },
{ headTitle: 'MyPortal' }, { headTitle: 'MyPortal' },
{ {
icon: 'list-ul', icon: 'receipt',
path: '/admin/portal-category-list', path: '/admin/portal-category-list',
title: 'รายการเอกสาร', title: 'รายการเอกสาร',
type: 'link', type: 'link',
}, },
{ {
icon: 'receipt',
path: '/admin/portal-create-category',
title: 'รายการอัพโหลดเอกสาร',
type: 'link',
},
{
icon: 'cog', icon: 'cog',
path: '/admin/management', path: '/admin/management',
title: 'การจัดการ', title: 'การจัดการ',
...@@ -149,7 +155,7 @@ export class NavService implements OnDestroy { ...@@ -149,7 +155,7 @@ export class NavService implements OnDestroy {
], ],
}, },
{ {
icon: 'slider', icon: 'user',
path: '/admin/set-excel-reports', path: '/admin/set-excel-reports',
title: 'ตั้งรายงานเอ็กเซล', title: 'ตั้งรายงานเอ็กเซล',
type: 'sub', type: 'sub',
......
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