Commit 3ad57f67 by pantakan konthang

myLearn-manage PDPA

parent 38b859c0
import { TranslateService } from "@ngx-translate/core";
import { BaseModel } from "./base.model";
export interface PdpaModel_myLearn {
version: string;
startDate: string;
endDate: string;
requestConsent: string;
requestConsentEng: string;
memberPdpa: any[];
status: number;
getName(): string
getStatus(): string
}
export class MyPdpaModel_myLearn extends BaseModel
implements PdpaModel_myLearn {
version: string;
startDate: string;
endDate: string;
requestConsent: string;
requestConsentEng: string;
memberPdpa: any[];
status: number;
constructor(data?: Partial<PdpaModel_myLearn>,
translateService?: TranslateService) {
super(data, translateService);
this.version = data?.version!;
this.startDate = data?.startDate!;
this.endDate = data?.endDate!;
this.requestConsent = data?.requestConsent!;
this.requestConsentEng = data?.requestConsentEng!;
this.memberPdpa = data?.memberPdpa!;
this.status = data?.status!
// this.status = data?.status!;
}
getName(): string {
return this.translateService.currentLang == "th"
? this.requestConsent
: this.requestConsentEng;
}
getStatus(): string {
if (this.status == 1) {
return this.translateService.instant('Active');
} else {
return this.translateService.instant('Unactive');
}
}
}
...@@ -11,7 +11,12 @@ export const mylearn: Routes = [ ...@@ -11,7 +11,12 @@ export const mylearn: Routes = [
path: 'dashboard', path: 'dashboard',
loadComponent: () => loadComponent: () =>
import('./dashboard/dashboard.component').then((m) => m.DashboardComponent), import('./dashboard/dashboard.component').then((m) => m.DashboardComponent),
} },
{
path: 'mylearn-pdpa-manage',
loadComponent: () =>
import('./pdpa-manage/pdpa-manage.component').then((m) => m.PdpaManageComponent),
},
] ]
} }
] ]
...@@ -24,6 +29,6 @@ export const mylearn: Routes = [ ...@@ -24,6 +29,6 @@ export const mylearn: Routes = [
declarations: [MylearnComponent], declarations: [MylearnComponent],
exports: [RouterModule], exports: [RouterModule],
}) })
export class MylearnModule { export class MylearnModule {
static routes = mylearn; static routes = mylearn;
} }
.page {
display: flex;
height: auto;
flex-direction: column;
}
/* Base styling for the modal body content */
.ti-modal-body-content {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Form labels */
.ti-form-label {
font-size: 0.95rem;
color: #333;
}
/* Form inputs */
.ti-form-input {
border: 1px solid #d1d5db;
border-radius: 4px;
padding: 0.6rem 1rem;
font-size: 1rem;
transition: all 0.2s ease-in-out;
}
.ti-form-input:focus {
outline: none;
border-color: #3b82f6; /* Example primary color */
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}
/* Read-only input styling */
.bg-input-readonly {
background-color: #e5e7eb;
cursor: not-allowed;
}
/* Error text */
.text-danger {
color: #ef4444;
}
/* Tab button styling */
.tab-btn {
display: flex;
justify-content: center;
align-items: center;
width: 120px; /* Consistent width for tabs */
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-bottom: none; /* Remove bottom border for active tab effect */
border-top-left-radius: 4px;
border-top-right-radius: 4px;
font-size: 0.95rem;
font-weight: 500;
text-align: center;
color: #6b7280;
transition: all 0.2s ease-in-out;
text-decoration: none; /* Remove underline from anchor tags */
}
.tab-btn:hover {
color: #111827;
background-color: #f3f4f6;
}
/* Active tab styling */
.tab-btn.hs-tab-active {
background-color: #3b82f6; /* Example primary color */
border-color: #3b82f6;
color: #ffffff;
}
/* Custom primary color (replace with your actual primary color if different) */
.text-primary {
color: #3b82f6;
}
.bg-primary {
background-color: #3b82f6;
}
<!-- <p>
pdpa-manage works!
</p> -->
<ng-container *ngIf="!edit">
<app-page-header [title]="'จัดการ PDPA'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'จัดการ PDPA'"></app-page-header>
<div class="grid grid-cols-12 gap-6">
<div class="xl:col-span-12 col-span-12">
<div class="box">
<div class="box-header justify-between">
<div class="box-title">
{{ 'All List' | translate}} <span
class="badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle">{{itemsList.length}}</span>
</div>
<div class="flex flex-wrap gap-2">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2" (click)="new()"
data-hs-overlay="#modal-detail"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Add Version' |
translate}}
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-success-full me-2"
*ngIf="someSelected" (click)="adjustSelect(1)"><i
class="ri-user-follow-line font-semibold align-middle"></i>{{ 'Active' |
translate}}
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-secondary-full me-2"
*ngIf="someSelected" (click)="adjustSelect(0)"><i
class="ri-user-unfollow-line font-semibold align-middle"></i>{{ 'Unactive' |
translate}}
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-danger-full me-2" *ngIf="someSelected"
(click)="deleteSelect()"><i class="ri-delete-bin-line font-semibold align-middle"></i>{{ 'Delete' |
translate}}
</a>
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาเวอร์ชั่น"
aria-label=".form-control-sm example" [(ngModel)]='searchTerm'>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr class="border-b border-defaultborder">
<th scope="col" class="!text-start">
<input class="form-check-input check-all" type="checkbox" id="all-products"
(change)="toggleAll($event)" [checked]="allSelected" aria-label="...">
</th>
<th scope="col" class="text-start">{{ 'Version' | translate}}</th>
<th scope="col" class="text-start">{{ 'Start Date' | translate}}</th>
<th scope="col" class="text-start">{{ 'End Date' | translate}}</th>
<th scope="col" class="text-start">{{ 'Status' | translate}}</th>
<th scope="col" class="text-start">{{ 'Action' | translate}}</th>
<th scope="col" class="text-start"></th>
</tr>
</thead>
<tbody>
@if (filterList.length > 0) {
@for(item of filterList;track filterList){
<tr class="border border-defaultborder dark:border-defaultborder/10">
<td class="product-checkbox"><input class="form-check-input" type="checkbox"
[checked]="selectedItems.get(item.version)" (change)="onCheckboxChange(item.version)"
aria-label="..." value="">
</td>
<td>
<div>
<span class="block">{{item.version}}</span>
</div>
</td>
<td>
<div>
<span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{getDateFormatted(item.startDate)}}</span>
</div>
</td>
<td>
<div>
<span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{getDateFormatted(item.endDate)}}</span>
</div>
</td>
<td> <span
class="badge bg-{{ item.status == 1 ? 'primary' : 'danger'}} text-white">{{item.getStatus()}}</span>
</td>
<td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="view(item)"
class="ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info"><i
class="ri-pencil-line"></i></a>
<!-- <a aria-label="anchor" href="javascript:void(0);"
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> -->
<a aria-label="anchor" href="javascript:void(0);" (click)="deleteVersion(item)"
class="ti-btn ti-btn-wave product-btn !gap-0 !m-0 bg-danger/10 text-danger hover:bg-danger hover:text-white hover:border-danger"><i
class="ri-delete-bin-line"></i></a>
</div>
</td>
</tr>
}
} @else {
<tr>
<td [attr.colspan]="6" class="text-center py-4">
<ng-container *ngIf="itemsList.length === 0 && !searchTerm">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
<ng-container *ngIf="itemsList.length > 0 && filterList.length === 0 && searchTerm">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem] cursor-pointer"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Start:: Add Version Oldest -->
<!-- <div id="modal-detail" class="hs-overlay hidden ti-modal [--overlay-backdrop:static]">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
{{ (modalStatus == 'add' ? ('Create' | translate) : ('Edit' | translate)) + ' ' + ('User' | translate) }}
สร้างเวอร์ชั่น
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-detail" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<label for="deal-title" class="form-label">{{'version' | translate}}</label>
<input type="text" class="form-control" [ngClass]="{ '!bg-input-readonly': modalStatus === 'edit' }"
id="deal-title" placeholder="" [(ngModel)]="pdpa.version" [readonly]="modalStatus === 'edit'">
<div class="text-danger" *ngIf="!pdpa.version">
{{'{{'Please fill in information' | translate}}' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{'วันที่เริ่ม' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder="" [(ngModel)]="pdpa.startDate">
<div class="text-danger" *ngIf="!pdpa.startDate">
{{'{{'Please fill in information' | translate}}' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12" *ngIf="modalStatus=='add'">
<label for="deal-title" class="form-label">{{'วันที่สินสุด' | translate}}</label>
<input type="text" class="form-control" id="deal-title" placeholder="" [(ngModel)]="pdpa.endDate">
<div class="text-danger" *ngIf="!pdpa.endDate">
{{'{{'Please fill in information' | translate}}' | translate}}
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">{{'Status' | translate}}</label>
<ng-select name="choices-multiple-remove-button2" id="choices-multiple-remove-button2" placeholder=""
[(ngModel)]="pdpa.status">
<ng-option [value]="0">{{'Unactive' | translate}}</ng-option>
<ng-option [value]="1">{{'Active' | translate}}</ng-option>
</ng-select>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button type="button" class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-detail">
{{'Cancel' | translate}}
</button>
<button type="button" (click)="save()" class="ti-btn bg-primary text-white !font-medium">{{'Save' |
translate}}</button>
</div>
</div>
</div>
</div> -->
<!-- End:: Add Version Oldest -->
<ng-template #modalDetail let-modal>
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header flex justify-between items-center p-5">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
{{ (modalStatus == 'add' ? ('Create' | translate) : ('Edit' | translate)) + ' ' + ('Version' | translate) }}
:
<span class="text-danger" *ngIf="pdpa.version">({{ pdpa.version }})</span>
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="modalRef.close()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body-content pt-6 pr-6 pl-6 pb-3 mb-4">
<!-- <div class="flex items-end justify-start md:justify-end">
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full w-full md:w-auto"
(click)="openModalConsent()" data-hs-overlay="#modal-detail">
<i class="ri-add-line font-semibold align-middle"></i>
{{ 'Consent List' | translate }}
</a>
</div> -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="flex flex-col">
<label class="ti-form-label mb-2 text-primary font-bold">{{'Version' | translate}}
<span class="text-danger">*
<ng-container *ngIf="modalStatus=='add'&&checkPrimary()">
เวอร์ชั่นซ้ำ
</ng-container>
</span>
</label>
<input type="text" class="ti-form-input w-full" [(ngModel)]="pdpa.version" />
<div class="text-danger text-sm mt-1" *ngIf="!pdpa.version">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="flex flex-col">
<label class="ti-form-label mb-2 text-primary font-bold">{{ 'Status' | translate }} </label>
<div class="flex items-center space-x-6">
<label class="inline-flex items-center space-x-2">
<input type="radio" name="status" [(ngModel)]="pdpa.status" [value]= "1" />
<span>{{ 'Active' | translate }}</span>
</label>
<label class="inline-flex items-center space-x-2">
<input type="radio" name="status" [(ngModel)]="pdpa.status" [value]= "0" />
<span>{{ 'Unactive' | translate }}</span>
</label>
</div>
<div class="text-danger text-sm mt-1" *ngIf="pdpa.status === null || pdpa.status === undefined">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="flex flex-col">
<label class="ti-form-label mb-2 text-primary font-bold">{{'Start Date' | translate}} </label>
<input type="date" class="ti-form-input w-full" [(ngModel)]="pdpa.startDate" />
<div class="text-danger text-sm mt-1" *ngIf="!pdpa.startDate">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="flex flex-col">
<label class="ti-form-label mb-2 text-primary font-bold">{{'End Date' | translate}}</label>
<input type="date" class="ti-form-input w-full" [(ngModel)]="pdpa.endDate" />
<div class="text-danger text-sm mt-1" *ngIf="!pdpa.endDate">
{{'Please fill in information' | translate}}
</div>
</div>
</div>
<div class="body-content pt-0">
<nav class="flex space-x-2 border-b border-gray-200 mb-4">
<a (click)="currentTab = 1" class="tab-btn"
[ngClass]="{ '!bg-primary border-primary !text-white': currentTab === 1 }" href="javascript:void(0);">
{{ 'Thai' | translate }}
</a>
<a (click)="currentTab = 2" class="tab-btn"
[ngClass]="{ '!bg-primary border-primary !text-white': currentTab === 2 }" href="javascript:void(0);">
{{ 'Eng' | translate }}
</a>
<!-- <a (click)="currentTab = 3" class="tab-btn"
[ngClass]="{ '!bg-primary border-primary !text-white': currentTab === 3 }" href="javascript:void(0);">
{{ 'China' | translate }}
</a> -->
</nav>
<div>
<div *ngIf="currentTab == 1">
<quill-editor [modules]="quillConfig" [styles]="{ 'height': '500px', 'overflow-y': 'auto' }"
[(ngModel)]="pdpa.requestConsent" [ngModelOptions]="{ standalone: true }"
theme="snow"></quill-editor>
</div>
<div *ngIf="currentTab == 2">
<quill-editor [modules]="quillConfig" [styles]="{ 'height': '500px', 'overflow-y': 'auto' }"
[(ngModel)]="pdpa.requestConsentEng" [ngModelOptions]="{ standalone: true }"
theme="snow"></quill-editor>
</div>
</div>
</div>
</div>
<div class="ti-modal-footer flex justify-center gap-4 mb-3">
<button type="button" class="hs-dropdown-toggle ti-btn bg-danger align-middle text-white !font-medium"
(click)="modalRef.close()" #closeModal>
{{'Cancel' | translate}}
</button>
<button type="button" (click)="save()" [disabled]="isSaving" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!pdpa.version ||!pdpa.startDate ||!pdpa.endDate" [disabled]="!pdpa.version ||!pdpa.startDate ||!pdpa.endDate">
{{ isSaving ? ('Saving...' | translate) : ('Save' | translate) }}
</button>
</div>
</div>
</div>
</ng-template>
<ng-template #ConsentList let-modal>
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header flex justify-between items-center p-5">
<h6 class="modal-title text-[1rem] font-semibold text-defaulttextcolor" id="mail-ComposeLabel">
{{"Consent List | translate"}}
</h6>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="modalRefConsent.close()" #closeModal>
<span class="sr-only">{{'Close' | translate}}</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body-content pt-6 pr-6 pl-6 pb-3 mb-4">
<div class="table-responsive">
<table class="table whitespace-nowrap min-w-full ti-custom-table-hover">
<thead>
<tr class="border-b border-defaultborder">
<th scope="col" class="!text-start">
<input class="form-check-input check-all" type="checkbox" id="all-products"
(change)="toggleAll($event)" [checked]="allSelected" aria-label="...">
</th>
<th scope="col" class="text-start">{{ 'Fullname' | translate}}</th>
<th scope="col" class="text-start">{{ 'Mobile' | translate}}</th>
<th scope="col" class="text-start">{{ 'Consent Date' | translate}}</th>
<th scope="col" class="text-start"></th>
</tr>
</thead>
<tbody>
@if (filterListConsent.length > 0) {
@for(item of filterListConsent;track filterListConsent){
<tr class="border border-defaultborder dark:border-defaultborder/10">
<td class="product-checkbox"><input class="form-check-input" type="checkbox"
[checked]="selectedItems.get(item.profileId)" (change)="onCheckboxChange(item.profileId)"
aria-label="..." value="">
</td>
<td>
<div class="flex items-center">
<span class="avatar avatar-sm p-1 me-1 bg-light !rounded-full">
<img [src]="item.getPicture()" alt="" id="profile-img">
</span>
<div class="ms-2">
<p class="font-semibold mb-0 flex items-center text-primary"><a (click)="viewConsent(item)">
{{item.thFullName}}</a></p>
<!-- <p class="text-[0.75rem] text-muted mb-0">{{item.memberId}}</p> -->
</div>
</div>
</td>
<td>
<div>
<span class="block"><i
class="ri-phone-line me-2 align-middle text-[.875rem] text-[#8c9097] dark:text-white/50 inline-flex"></i>{{item.phoneCurrent}}</span>
</div>
</td>
<td><span class="badge bg-info/10 text-primary"><i class="bi bi-clock me-1"></i>{{item.consentDate
| date
: 'medium'}}</span>
</td>
</tr>
}
} @else {
<tr>
<td [attr.colspan]="6" class="text-center py-4">
<ng-container *ngIf="itemsListConsent.length === 0 && !searchTerm">
<p>กำลังโหลดข้อมูล หรือไม่มีข้อมูลเลย...</p>
</ng-container>
<ng-container *ngIf="itemsListConsent.length > 0 && filterListConsent.length === 0 && searchTerm">
<p>ไม่พบข้อมูลที่ค้นหา...</p>
</ng-container>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filterListConsent.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filterListConsent.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsListConsent.length : filterListConsent.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsListConsent.length : filterListConsent.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsListConsent.length : filterListConsent.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsListConsent.length : filterListConsent.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="ti-modal-footer flex justify-center gap-4 mb-3">
<button type="button" class="hs-dropdown-toggle ti-btn bg-danger align-middle text-white !font-medium"
(click)="modalRefConsent.close()" #closeModal>
{{'Previous' | translate}}
</button>
<!-- <button type="button" (click)="save()" [disabled]="isSaving"
class="ti-btn bg-primary text-white !font-medium">
{{ isSaving ? ('Saving...' | translate) : ('Save' | translate) }}
</button> -->
</div>
</div>
</div>
</ng-template>
</ng-container>
<!-- <ng-container *ngIf="edit">
<app-pdpa-config [pdpaconfig]="pdpaconfig" (sendEdit)="edit=false"></app-pdpa-config>
</ng-container> -->
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { PdpaManageComponent } from './pdpa-manage.component';
describe('PdpaManageComponent', () => {
let component: PdpaManageComponent;
let fixture: ComponentFixture<PdpaManageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PdpaManageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PdpaManageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
// import { Component, OnInit } from '@angular/core';
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, ElementRef, TemplateRef, ViewChild, OnInit } from '@angular/core';
import { NgSelectModule } from "@ng-select/ng-select";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { FormsModule } from "@angular/forms";
import swal from 'sweetalert';
import { MatPaginator, PageEvent } from "@angular/material/paginator";
import { FileUploadModule } from 'ng2-file-upload';
import { FileItem, FileUploader, ParsedResponseHeaders } from "ng2-file-upload";
import { SharedModule } from "../../../shared/shared.module";
import { UserProfileModel } from "../../models/user.model";
import { UserRoleModel } from "../../models/user-role-model";
import { TokenService } from "../../../shared/services/token.service";
import { environment } from "../../../../environments/environment";
import { PdpaService } from "../../services/pdpa.service";
import { Router } from "@angular/router";
import { QuillModule } from "ngx-quill";
import { MatDialog } from "@angular/material/dialog";
import { MyPdpaConfigModel, PdpaConfigModel } from '../../models/pdpaConfig.model';
import { MyPdpaModel, PdpaModel } from "../../models/pdpa.model";
import { MyPdpaModel_myLearn, PdpaModel_myLearn } from "../../models/pdpaMylearn.model";
@Component({
selector: 'app-pdpa-manage',
templateUrl: './pdpa-manage.component.html',
styleUrls: ['./pdpa-manage.component.css'],
imports: [
CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
FileUploadModule,
QuillModule
],
standalone: true,
})
export class PdpaManageComponent implements OnInit {
quillConfig = {
toolbar: [
['link'],
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'align': [] }],
['clean'],
]
};
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('modalDetail') modalDetail!: TemplateRef<any>;
@ViewChild('ConsentList') ConsentList!: TemplateRef<any>;
modalRef: any;
modalRefConsent: any;
// pdpaconfig!: MyPdpaConfigModel;
pdpaconfig!: MyPdpaModel_myLearn;
edit = false
currentTab = 1
allSelected = false;
someSelected = false;
// itemsList: MyPdpaConfigModel[] = []
// filterList: MyPdpaConfigModel[] = []
// pdpa: MyPdpaConfigModel = new MyPdpaConfigModel()
itemsList: MyPdpaModel_myLearn[] = []
filterList: MyPdpaModel_myLearn[] = []
pdpa: MyPdpaModel_myLearn = new MyPdpaModel_myLearn()
selectedItems = new Map<string, boolean>();
pageIndex = 0;
modalStatus: "add" | "edit" = "add"
isSaving = false;
consentList: PdpaModel[] = [];
itemsListConsent: MyPdpaModel[] = []
filterListConsent: MyPdpaModel[] = []
consent: MyPdpaModel = new MyPdpaModel()
isActiveExist: boolean = false;
get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(val: string) {
this.pageIndex = 0;
this.allSelected = false
this._searchTerm = val;
if (val != '') {
this.filterList = this.filter(val);
} else {
this.updatePagedItems()
}
}
_searchTerm = "";
constructor(
private pdpaService: PdpaService,
public translate: TranslateService,
private modal: MatDialog)
{
}
ngOnInit(): void {
this.pdpaService.getConfigList_myLearn().subscribe( result => {
console.log("myLearn",result);
this.itemsList = result.map(item => new MyPdpaModel_myLearn(item, this.translate));
this.filterList = [...this.itemsList];
})
// this.pdpaService.getConfigList().subscribe(res => {
// this.itemsList = res.map(item => new MyPdpaConfigModel(item, this.translate));
// this.filterList = [...this.itemsList];
// });
this.pdpaService.getPdpaList().subscribe(res => {
this.consentList = res.map(item => new MyPdpaModel(item, this.translate));
this.filterListConsent = [...this.itemsListConsent];
});
}
filter(v: string): MyPdpaModel_myLearn[] {
const search = v.toLowerCase();
return this.itemsList.filter(x => {
const startDateStr = x.startDate ? new Date(x.startDate).toLocaleDateString() : '';
const endDateStr = x.endDate ? new Date(x.endDate).toLocaleDateString() : '';
const statusStr = x.status ? x.status.toString() : '';
return (x.version?.toLowerCase().includes(search)) ||
(startDateStr.toLowerCase().includes(search)) ||
(endDateStr.toLowerCase().includes(search)) ||
(statusStr.toLowerCase().includes(search));
});
}
new() {
this.modalStatus = 'add'
this.pdpa = new MyPdpaModel_myLearn();
this.openModal();
this.pdpa.status = 0;
}
view(item: MyPdpaModel_myLearn) {
this.modalStatus = 'edit';
this.pdpa = new MyPdpaModel_myLearn(item, this.translate);
this.pdpa.startDate = this.unwrapDate(this.pdpa.startDate);
this.pdpa.endDate = this.unwrapDate(this.pdpa.endDate);
// this.formatDatesForDatetimeLocal(this.pdpa);
this.openModal();
}
viewConsent(item: MyPdpaModel) {
this.modalStatus = 'edit';
this.consent = new MyPdpaModel(item, this.translate);
}
private formatDatesForDatetimeLocal(consentModel: MyPdpaModel_myLearn): void {
if (consentModel.startDate) {
consentModel.startDate = consentModel.startDate.substring(0, 16);
}
if (consentModel.endDate) {
consentModel.endDate = consentModel.endDate.substring(0, 16);
}
}
// ----------------------------------------------------
save() {
this.pdpaService.getConfigList_myLearn().subscribe({
next: configs => {
const isActiveExist = configs.some(cfg => cfg.status === 1 && cfg.version !== this.pdpa.version);
if (isActiveExist && this.pdpa.status === 1) {
// ถ้ามีเวอร์ชันอื่นที่เปิดใช้งานอยู่แล้ว และกำลังจะเปิดอีกเวอร์ชัน
swal({
title: "มีรายการที่เปิดใช้งานอยู่แล้ว",
text: "คุณกำลังจะเปิดใช้งานเวอร์ชันใหม่ แต่มีเวอร์ชันอื่นที่เปิดใช้งานอยู่แล้ว?",
icon: "warning",
buttons: ["ยกเลิก", "ยืนยัน"],
dangerMode: false,
}).then((confirm: boolean) => {
if (confirm) {
const pdpaSend = this.transformPdpaDates(this.pdpa);
if (this.modalStatus === 'add') {
this.pdpaService.save_pdpa_myLearn(pdpaSend,"").subscribe(() => {
swal("บันทึกสำเร็จ", "บันทึกเวอร์ชั่นสำเร็จแล้ว", "success");
this.ngOnInit();
this.childModal?.nativeElement.click();
});
} else if (this.modalStatus === 'edit') {
// const respone = new MyPdpaModel_myLearn(this.pdpa);
console.log("11",pdpaSend);
// this.pdpaService.save_pdpa_myLearn(pdpaSend,"").subscribe(() => {
// swal("อัปเดตสำเร็จ", "บันทึกเวอร์ชั่นสำเร็จแล้ว", "success");
// this.ngOnInit();
// this.childModal?.nativeElement.click();
// });
}
}
});
} else {
// กรณีทั่วไป
swal({
title: "ยืนยันการบันทึก",
text: "คุณต้องการบันทึกหรือไม่?",
icon: "warning",
buttons: ["ยกเลิก", "ยืนยัน"],
dangerMode: false,
}).then((willSave: boolean) => {
if (willSave) {
const pdpaSend = this.transformPdpaDates(this.pdpa);
if (this.modalStatus === 'add') {
// console.log("111",pdpaSend);
this.pdpaService.save_pdpa_myLearn(pdpaSend,"").subscribe(() => {
swal("บันทึกสำเร็จ", "บันทึกเวอร์ชั่นสำเร็จแล้ว", "success");
this.ngOnInit();
this.childModal?.nativeElement.click();
});
} else if (this.modalStatus === 'edit') {
// const respone = new MyPdpaConfigModel(this.pdpa);
this.pdpaService.save_pdpa_myLearn(pdpaSend,"").subscribe(() => {
swal("อัปเดตสำเร็จ", "บันทึกเวอร์ชั่นสำเร็จแล้ว", "success");
this.ngOnInit();
this.childModal?.nativeElement.click();
});
}
}
});
}
},
error: (error) => {
console.error('Error: ', error);
}
});
}
deleteVersion(item: PdpaModel_myLearn) {
const versionText = `${this.translate.instant('Version')}: ${item.version || 'เวอร์ชั่นที่จะลบเป็นค่าว่าง'}`;
swal({
title: "Are you sure?",
text: `Confirm to delete :\n${versionText}\!`,
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes,Delete it!"],
}).then((willDelete: any) => {
if (willDelete) {
this.pdpaService.delete_pdpa_myLearn(item).subscribe(result => {
swal("Delete Success!!", "ลบข้อมูลสำเร็จ", "success");
this.ngOnInit();
});
}
});
}
deleteSelect() {
let pdpaConfig = '';
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version) as MyPdpaModel_myLearn;
if (pdpa) {
pdpaConfig += `${this.translate.instant('Version')}: ${pdpa.version || 'เวอร์ชั่นที่จะลบเป็นค่าว่าง'}\n`;
}
}
});
swal({
title: "Are you sure?",
text: pdpaConfig,
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes, Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version);
if (pdpa) {
this.pdpaService.delete_pdpa_myLearn(pdpa).subscribe(result => {
swal("Delete Success!!", "ลบข้อมูลที่เลือกสำเร็จ", "success");
this.ngOnInit();
});
}
}
});
}
});
}
adjustSelect(status: number) {
this.pdpaService.getConfigList_myLearn().subscribe({
next: configs => {
this.isActiveExist = configs.some(cfg => cfg.status === 1 && cfg.version !== this.pdpa.version);
if (status === 1) {
let isSelectedAlreadyActive = false;
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(item => item.version === version);
if (pdpa && pdpa.status === 1) {
isSelectedAlreadyActive = true;
}
}
});
if (isSelectedAlreadyActive) {
// แจ้งเตือนว่ามีเวอร์ชันที่เลือกเปิดอยู่แล้ว
swal({
title: "เวอร์ชันนี้เปิดใช้งานอยู่แล้ว",
text: "หนึ่งในเวอร์ชันที่คุณเลือกได้ถูกเปิดใช้งานอยู่แล้ว คุณต้องการดำเนินการต่อหรือไม่?",
icon: "info",
buttons: ["ยกเลิก", "ยืนยัน"],
dangerMode: false,
}).then((confirm: boolean) => {
if (confirm) {
this.proceedUpdate(status);
}
});
return;
}
if (this.isActiveExist) {
// แจ้งเตือนว่ามีเวอร์ชันอื่นเปิดใช้งานอยู่แล้ว
swal({
title: "มีเวอร์ชันอื่นที่เปิดใช้งานอยู่แล้ว",
text: "คุณกำลังจะเปิดใช้งานเวอร์ชันใหม่ แต่มีเวอร์ชันอื่นที่เปิดใช้งานอยู่แล้ว?",
icon: "warning",
buttons: ["ยกเลิก", "ยืนยัน"],
dangerMode: false,
}).then((confirm: boolean) => {
if (confirm) {
let pdpaConfig = ''; // เก็บข้อมูลเวอร์ชันที่เลือก
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version);
if (pdpa) {
pdpaConfig += `${this.translate.instant('Version')}: ${pdpa.version}\n`;
}
}
});
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version);
if (pdpa) {
const cleanPdpa = {
version: pdpa.version,
startDate: pdpa.startDate,
endDate: pdpa.endDate,
status: status,
requestConsentThai: pdpa.requestConsent,
requestConsentEng: pdpa.requestConsentEng,
// requestConsentChina: pdpa.requestConsentChina
};
// this.pdpaService.update(cleanPdpa).subscribe(result => {
// swal("บันทึกสำเร็จ!", "ข้อมูลถูกบันทึกเรียบร้อยแล้ว", "success");
// this.ngOnInit(); // รีเฟรชข้อมูลหลังจากอัปเดต
// });
}
}
});
}
});
return;
}
}
this.proceedUpdate(status);
},
error: err => {
console.error('Failed to load config list', err);
}
});
}
proceedUpdate(status: number) {
let title = "คุณแน่ใจหรือไม่?";
let pdpaConfig = ''; // เก็บข้อมูลเวอร์ชันที่เลือก
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version);
if (pdpa) {
pdpaConfig += `${this.translate.instant('Version')}: ${pdpa.version}\n`;
}
}
});
swal({
title: title,
text: pdpaConfig,
icon: "warning",
dangerMode: false,
buttons: ["ยกเลิก", "ยืนยัน"],
}).then((willProceed: any) => {
if (willProceed) {
this.selectedItems.forEach((isSelected, version) => {
if (isSelected) {
const pdpa = this.itemsList.find(pdpa => pdpa.version === version);
if (pdpa) {
const cleanPdpa = {
version: pdpa.version,
startDate: pdpa.startDate,
endDate: pdpa.endDate,
status: status.toString(),
requestConsentThai: pdpa.requestConsent,
requestConsentEng: pdpa.requestConsentEng,
// requestConsentChina: pdpa.requestConsentChina
};
// this.pdpaService.update(cleanPdpa).subscribe(result => {
// swal("บันทึกสำเร็จ!", "ข้อมูลถูกบันทึกเรียบร้อยแล้ว", "success");
// this.ngOnInit(); // รีเฟรชข้อมูลหลังจากอัปเดต
// });
}
}
});
}
});
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList = this.itemsList.slice(startIndex, endIndex);
}
toggleAll(event: any) {
this.allSelected = event.target.checked;
this.selectedItems.clear();
this.itemsList.forEach(item => {
this.selectedItems.set(item.version, this.allSelected);
});
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.version));
}
onCheckboxChange(version: string) {
const isSelected = this.selectedItems.get(version) || false;
this.selectedItems.set(version, !isSelected);
this.allSelected = this.itemsList.every(item => this.selectedItems.get(item.version));
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.version));
}
openModal() {
this.modalRef = this.modal.open(this.modalDetail, {
width: '1700px',
height: '1000px'
})
}
closeModal() {
this.modalRef.close()
}
openModalConsent() {
this.modalRefConsent = this.modal.open(this.ConsentList, {
width: '1500px',
height: '750px'
})
}
closeModalConsent() {
this.modalRefConsent.close()
}
filterEmp(empId: string) {
this.pdpa = this.itemsList.filter(e => e.version == empId)[0]
}
checkPrimary() {
return this.itemsList.find(x => x.version == this.pdpa.version)
}
getDateFormatted(dateObject: any) {
let dateData = JSON.parse(dateObject)
return dateData
}
changeDateFormatSend(dateStr: string): string{
// รับ format "YYYY-MM-DD" แล้วคืน "\"DD-MM-YYYY\""
const [year, month, day] = dateStr.split('-');
return `\"${day}-${month}-${year}\"`;
}
transformPdpaDates(pdpa: any): any {
const clone = { ...pdpa };
// ตรวจสอบและแปลงเฉพาะกรณีที่ยังไม่ได้แปลง
const isRawFormat = (dateStr: string) =>
/^\d{4}-\d{2}-\d{2}$/.test(dateStr); // ตรวจว่ามีรูปแบบ YYYY-MM-DD
if (clone.startDate && isRawFormat(clone.startDate)) {
const [year, month, day] = clone.startDate.split("-");
clone.startDate = `\"${day}-${month}-${year}\"`;
}
if (clone.endDate && isRawFormat(clone.endDate)) {
const [year, month, day] = clone.endDate.split("-");
clone.endDate = `\"${day}-${month}-${year}\"`;
}
return clone;
// const clone = { ...pdpa };
// if (clone.startDate) {
// clone.startDate = this.changeDateFormatSend(clone.startDate);
// }
// if (clone.endDate) {
// clone.endDate = this.changeDateFormatSend(clone.endDate);
// }
// return clone;
}
unwrapDate(quotedDate: string): string {
// เอา \"25-07-2025\" → 2025-07-25
if (!quotedDate) return '';
const match = quotedDate.match(/"?(\d{2})-(\d{2})-(\d{4})"?/);
if (!match) return quotedDate;
const [, day, month, year] = match;
return `${year}-${month}-${day}`;
}
}
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { map, Observable } from 'rxjs';
import { PdpaConsentModel, PdpaModel } from '../models/pdpa.model'; import { PdpaConsentModel, PdpaModel } from '../models/pdpa.model';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { PdpaSummaryModel } from '../models/pdpaSummary.model'; import { PdpaSummaryModel } from '../models/pdpaSummary.model';
import { MyPdpaConfigModel, PdpaConfigModel } from '../models/pdpaConfig.model'; import { MyPdpaConfigModel, PdpaConfigModel } from '../models/pdpaConfig.model';
import { ResponseModel } from '../models/base.model'; import { ResponseModel } from '../models/base.model';
import { MyPdpaModel_myLearn, PdpaModel_myLearn } from '../models/pdpaMylearn.model';
import { TranslateService } from '@ngx-translate/core';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class PdpaService { export class PdpaService {
constructor(private http: HttpClient) { } constructor(private http: HttpClient,private translateService: TranslateService) { }
getPdpaList(): Observable<PdpaModel[]> { getPdpaList(): Observable<PdpaModel[]> {
return this.http.get<PdpaModel[]>(`${environment.baseUrl}/pdpa/consent-profile/lists`); return this.http.get<PdpaModel[]>(`${environment.baseUrl}/pdpa/consent-profile/lists`);
...@@ -68,4 +70,33 @@ export class PdpaService { ...@@ -68,4 +70,33 @@ export class PdpaService {
getLastVersion(): Observable<PdpaConfigModel> { getLastVersion(): Observable<PdpaConfigModel> {
return this.http.get<PdpaConfigModel>(`${environment.baseUrl}/pdpa/consent-profile/last`); return this.http.get<PdpaConfigModel>(`${environment.baseUrl}/pdpa/consent-profile/last`);
} }
// myLearn
myLearnUrl = "https://mylearn-uat.myhr.co.th/api"
// getPdpaList_myLearn(){
// return this.http.get<any>(this.myLearnUrl + "/pdpa" + "/lists?companyId=" );
// }
// getConfigList_myLearn(){
// return this.http.get<any>(this.myLearnUrl + "/pdpa" + "/lists?companyId=");
// }
getConfigList_myLearn(): Observable<PdpaModel_myLearn[]> {
return this.http.get<PdpaModel_myLearn[]>(this.myLearnUrl + "/pdpa" + "/lists?companyId=")
.pipe(
map((e) => e.map((e) => new MyPdpaModel_myLearn(e, this.translateService)))
);
}
save_pdpa_myLearn(body: PdpaModel_myLearn, conSent: string) {
return this.http.post<PdpaModel_myLearn>(this.myLearnUrl+"/pdpa?consent="+conSent!, new MyPdpaModel_myLearn(body));
}
delete_pdpa_myLearn(body: PdpaModel_myLearn) {
const options = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
body: new MyPdpaModel_myLearn(body),
};
return this.http.delete<ResponseModel>(this.myLearnUrl+"/pdpa?companyId=", options);
}
} }
...@@ -343,7 +343,7 @@ ...@@ -343,7 +343,7 @@
</div> </div>
<div class=""> <div class="">
<a routerLink="/mylearn/dashboard" <a routerLink="/mylearn/mylearn-pdpa-manage"
class="p-4 items-center related-app block text-center rounded-sm hover:bg-gray-50 dark:hover:bg-black/20"> class="p-4 items-center related-app block text-center rounded-sm hover:bg-gray-50 dark:hover:bg-black/20">
<img src="./assets/images/logoallHR/mylearn-logo.png" alt="miscrosoft" <img src="./assets/images/logoallHR/mylearn-logo.png" alt="miscrosoft"
class="leading-[1.75] text-2xl !h-[1.75rem] align-middle flex justify-center mx-auto"> class="leading-[1.75] text-2xl !h-[1.75rem] align-middle flex justify-center mx-auto">
......
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