Commit eed0a650 by Mon

Create User

parent 38ede92a
......@@ -40,11 +40,11 @@
class="badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle">{{UserList.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()" (click)="openDialog()" data-hs-overlay="#modal-detail"><i
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2"
(click)="new()" (click)="openDialogcearteUser()"><i
class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a> -->
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-danger-full me-2"
*ngIf="someSelected" (click)="deleteSelect()"><i
......@@ -411,4 +411,184 @@
{{'Cancel' | translate}}
</button>
</mat-dialog-actions>
</ng-template>
<ng-template #CreateUser 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="mail-ComposeLabel">
<span *ngIf="action === 'add'">{{ 'Create' | translate }}</span>{{ 'สิทธิ์การใช้งาน' | translate }}
<!-- <span *ngIf="action === 'edit'">{{ 'Edit' | translate }}</span> -->
</h3>
<button type="button" class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
(click)="closeDialogcearteUser()" #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-4">
<div class="xl:col-span-12 col-span-12 justify-items-center">
<div class="relative w-[200px] h-[200px] overflow-hidden rounded-md group mx-auto">
<img [src]="selectModel.member.getPictureUrl() === '' ? 'assets/images/media/default-pic-gX.png' : selectModel.member.getPictureUrl()"
alt="Profile Picture"
class="w-full h-full block transition-all duration-300 ease-in-out group-hover:blur-md border-color">
<div class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-30 opacity-0 transition-opacity duration-300 ease-in-out group-hover:opacity-100 cursor-pointer"
(click)="triggerFileInput()">
<input #profileChangeInput ng2FileSelect [uploader]="uploaderProfile" type="file"
name="photo" class="hidden" id="profile-change-input">
<button
class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-lg opacity-100 transition-opacity duration-300 ease-in-out">
<i class="fe fe-camera text-[.625rem]"></i> Upload
</button>
</div>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="thFirstname" class="text-primary mt-4 font-bold font-14">{{'ชื่อ(ไทย)' |
translate}}</label>
<input type="text" class="form-control" id="thFirstname" placeholder=""
[(ngModel)]="selectModel.member.thFirstname">
<div class="text-danger" *ngIf="!selectModel.member.thFirstname">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="thLastname" class="text-primary mt-4 font-bold font-14">{{'นามสกุล(ไทย)' |
translate}}</label>
<input type="text" class="form-control" id="thLastname" placeholder=""
[(ngModel)]="selectModel.member.thLastname">
<div class="text-danger" *ngIf="!selectModel.member.thLastname">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="engFirstname" class="text-primary mt-4 font-bold font-14">{{'ชื่อ(อังกฤษ)' |
translate}}</label>
<input type="text" class="form-control" id="engFirstname" placeholder=""
[(ngModel)]="selectModel.member.engFirstname">
<div class="text-danger" *ngIf="!selectModel.member.engFirstname">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="engLastname" class="text-primary mt-4 font-bold font-14">{{'นามสกุล(อังกฤษ)' |
translate}}</label>
<input type="text" class="form-control" id="engLastname" placeholder=""
[(ngModel)]="selectModel.member.engLastname">
<div class="text-danger" *ngIf="!selectModel.member.engLastname">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="sex" class="mt-4 font-bold font-14 text-primary">
{{ 'เพศ' | translate }}
</label>
<ng-select class="form-control" [(ngModel)]="selectModel.member.sex" [items]="sexList"
bindLabel="tdesc" placeholder="เลือกเพศ">
</ng-select>
<!-- <div class="text-danger"
*ngIf="selectModel.member.sex === null || selectModel.member.sex === undefined">
{{ 'Please fill in information' | translate }}
</div> -->
</div>
<div class="xl:col-span-6 col-span-12">
<label for="tel" class="text-primary mt-4 font-bold font-14">{{'เบอร์โทรศัพท์' |
translate}}</label>
<input type="text" class="form-control" id="tel" placeholder=""
[(ngModel)]="selectModel.member.tel">
<!-- <div class="text-danger" *ngIf="!selectModel.member.tel">
{{'Please fill in information' | translate}}
</div> -->
</div>
<div class="xl:col-span-12 col-span-12">
<label for="email" class="text-primary mt-4 font-bold font-14">
{{ 'อีเมล์' | translate }}
</label>
<input type="email" class="form-control" id="email" placeholder="example@domain.com"
[(ngModel)]="selectModel.member.email" name="email" #emailCtrl="ngModel" required
pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.com$">
<div class="text-danger" *ngIf="!selectModel.member.email">
{{ 'Please fill in information' | translate }}
</div>
<div class="text-danger" *ngIf="emailCtrl.invalid && emailCtrl.touched">
<div *ngIf="emailCtrl.errors?.['pattern']">
{{ 'กรุณากรอก email ให้ถูกต้องในลักษณะนี้ เช่น @xxx.com' | translate }}
</div>
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="username" class="text-primary mt-4 font-bold font-14">{{'Username' |
translate}}</label>
<input type="text" class="form-control" id="username" placeholder=""
[(ngModel)]="selectModel.username">
<div class="text-danger" *ngIf="!selectModel.username">
{{'Please fill in information' | translate}}
</div>
</div>
<div class="xl:col-span-6 col-span-12">
<label for="password" class="text-primary mt-4 font-bold font-14">
{{ 'Password' | translate }}
</label>
<div class="relative">
<input [type]="showPassword ? 'text' : 'password'" class="form-control pr-10" id="password"
placeholder="กรอกรหัสผ่าน" [(ngModel)]="selectModel.password" name="password"
#passwordCtrl="ngModel" required pattern="^(?=.*[A-Z]|.*[@$!%*?&]).{6,}$">
<span class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer"
(click)="togglePassword()">
<i class="fe" [ngClass]="showPassword ? 'fe-eye-off' : 'fe-eye'"></i>
</span>
</div>
<div class="text-danger" *ngIf="!selectModel.password">
{{ 'Please fill in information' | translate }}
</div>
<div class="text-danger" *ngIf="passwordCtrl.invalid && passwordCtrl.touched">
<!-- <div *ngIf="passwordCtrl.errors?.['required']">
{{ 'Please fill in information' | translate }}
</div> -->
<div *ngIf="passwordCtrl.errors?.['pattern']">
{{ 'กรุณากรอกรหัสผ่านให้ครบ 6 ตัว เเละกรอกตัวอักขระพิเศษหรืออักษรพิมพ์ใหญ่ 1 ตัว เช่น (@$!%*?&A-Z)' | translate }}
</div>
</div>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="mt-4 font-bold font-14 text-primary">
{{ 'สิทธิ์การใช้งาน' | translate }}
</label>
<ng-select class="form-control" [items]="RoleOptions" bindLabel="label" bindValue="value"
[(ngModel)]="menuSelected" placeholder="เลือกสิทธิ์การใช้งาน">
</ng-select>
<div class="text-danger" *ngIf="!menuSelected">
{{ 'Please fill in information' | translate }}
</div>
</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)="closeDialogcearteUser()">
{{'Cancel' | translate}}
</button>
<button type="button" (click)="createUser()" class="ti-btn bg-primary text-white !font-medium"
[class.ti-btn-disabled]="!menuSelected || menuSelected === 0 || !selectModel.username || !selectModel.password || !selectModel.member.email"
[disabled]="!menuSelected || menuSelected === 0 || !selectModel.username || !selectModel.password || !selectModel.member.email">
{{'Save'| translate}}
</button>
</mat-dialog-actions>
</ng-template>
\ No newline at end of file
......@@ -11,4 +11,14 @@
::ng-deep .ng-select .ng-value-container .ng-value .ng-value-icon {
color: #fff !important; /* ไอคอนกากบาท (x) เป็นสีขาว */
}
/* placeholder ตอนยังไม่ได้เลือกค่า */
::ng-deep .ng-select .ng-select-container .ng-placeholder {
color: #6c757d !important; /* เทาอ่อน */
}
/* ความสูงกล่อง */
::ng-deep .ng-select .ng-select-container {
height: 40px !important;
}
\ No newline at end of file
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { SharedModule } from '../../../../shared/shared.module';
import { CommonModule } from '@angular/common';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import swal from 'sweetalert';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ProfileModel, MyProfileModel, MyRoleModel, RoleModel } from '../../../models/mylearn/user.model';
import { ProfileModel, MyProfileModel, MyRoleModel, RoleModel, MySexModel, MyMarryModel, MyEmpStatusModel } from '../../../models/mylearn/user.model';
import { UserRoleService } from '../../../services/mylearn/user-role.service';
import { NgSelectModule } from '@ng-select/ng-select';
import { MyhrcompanyService } from '../../../services/mylearn/myhrcompany.service';
......@@ -13,6 +13,9 @@ import { HrcompanyModel } from '../../../models/mylearn/myhrcompany.model';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { UserService } from '../../../services/mylearn/user.service';
import { MyUserAccountModel, UserAccountModel } from '../../../models/mylearn/user-account.model';
import { FileItem, FileUploader, FileUploadModule, ParsedResponseHeaders } from 'ng2-file-upload';
import { environment } from '../../../../../environments/environment';
import { TokenService } from '../../../../shared/services/token.service';
@Component({
selector: 'app-user-authorization',
......@@ -26,11 +29,18 @@ import { MyUserAccountModel, UserAccountModel } from '../../../models/mylearn/us
FormsModule,
NgSelectModule,
MatDialogModule,
FileUploadModule,
],
})
export class UserAuthorizationComponent implements OnInit {
sexList = [
{ code: '1', tdesc: 'ชาย', edesc: 'Male' },
{ code: '2', tdesc: 'หญิง', edesc: 'Female' }
];
@ViewChild("userModal") userModal: any;
@ViewChild("Profile") Profile: any;
@ViewChild("CreateUser") CreateUser: any;
@ViewChild('profileChangeInput') profileChangeInputRef!: ElementRef;
UserList: UserAccountModel[] = [];
filterList: UserAccountModel[] = [];
......@@ -40,23 +50,27 @@ export class UserAuthorizationComponent implements OnInit {
companyId: string;
// เลือกหลาย roleuser
// menuMultiSelected: number[] = [];
menuSelected: number = 0;
menuSelected: number | null = null;
RoleList: RoleModel[] = [];
MyRoleList: MyRoleModel[] = [];
RoleOptions: { value: number; label: string }[] = [];
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
selectedMemberId: string = "";
selectedMemberName: string = "";
action = "new";
dialogRef: any;
dialogRefProfile: any;
dialogRefcreateuser: any;
_searchTerm = "";
pageIndex = 0;
allSelected = false;
someSelected = false;
selectedItems = new Map<string, boolean>();
showPassword: boolean = false;
selectedMenu: string[] = [];
......@@ -82,8 +96,60 @@ export class UserAuthorizationComponent implements OnInit {
private route: ActivatedRoute,
private myhrcompanyservice: MyhrcompanyService,
private dialog: MatDialog,
private tokenService: TokenService,
) { }
uploadConfig() {
this.uploaderProfile = new FileUploader({
url: environment.baseUrls + "/files/upload-image",
isHTML5: true,
authToken: this.tokenService.getToken()!,
});
this.uploaderProfile.onAfterAddingFile = (fileItem: FileItem) => {
fileItem.withCredentials = false;
this.uploadErrorMsg = "";
while (this.uploaderProfile!.queue.length > 1) {
this.uploaderProfile!.queue[0].remove();
}
if (fileItem.file.size > 5000000) {
this.uploadErrorMsg = "maximum file size 5mb.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
if (fileItem.file.type!.indexOf("image") === -1) {
this.uploadErrorMsg = "please upload image only.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
fileItem.isCancel = true;
return;
}
fileItem.upload();
};
this.uploaderProfile.onCompleteItem = (
item: FileItem,
response: string,
publish: number,
headers: ParsedResponseHeaders
) => {
if (item.isSuccess) {
const res = JSON.parse(response);
console.log("res", res);
this.selectModel.member.picture = res.resultObject;
swal(res.message, "บันทึกสำเร็จ", "success");
console.log('After Upload, selectModel is:', this.selectModel);
} else {
this.uploadErrorMsg = "cannot upload file.";
swal("Opp!!", "ไม่สามารถอัพโหลดได้", "info");
}
};
}
ngOnInit() {
const id = this.route.snapshot.paramMap.get('id')!;
this.myhrcompanyservice.getByCompany(id).subscribe(company => {
......@@ -98,27 +164,35 @@ export class UserAuthorizationComponent implements OnInit {
this.action = 'add';
this.selectModel = new MyUserAccountModel();
this.selectModel.member = new MyProfileModel();
this.selectModel.member.memberId = "";
// ✅ ต้องส่ง 0,companyId ตอนสร้างใหม่
this.selectModel.member.memberId = `0,${this.companyId}`;
this.selectModel.username = "";
this.selectModel.password = "";
this.selectModel.member.thFirstname = "";
this.selectModel.member.thLastname = "";
this.selectModel.member.engFirstname = "";
this.selectModel.member.engLastname = "";
this.selectModel.member.thFullName = "";
this.selectModel.member.engFullName = "";
this.selectModel.member.email = "";
this.selectModel.member.tel = "";
this.selectModel.member.picture = "";
this.selectModel.member.pictureBackground = "";
this.selectModel.member.status = 1;
// ค่า default object
this.selectModel.member.sex = new MySexModel({ code: "", tdesc: "", edesc: "" }, this.translate);
this.selectModel.member.marryId = new MyMarryModel({ code: "", tdesc: "", edesc: "" }, this.translate);
this.selectModel.member.empstatusId = new MyEmpStatusModel(
{ empstatusId: 0, empstatusCode: "", empstatusName: "", empstatusEngName: "", companyId: this.companyId },
this.translate
);
this.selectModel.role.roleId = 0;
this.menuSelected = 0;
this.menuSelected = null;
}
// getRoleOptions() {
// if (!this.companyId) return;
// this.userroleservice.getListsByCompany(this.companyId).subscribe({
// next: (roles: RoleModel[]) => {
// this.RoleOptions = roles.map(r => ({
// value: r.roleId,
// label: `${r.thName} (${r.engName})`
// }));
// console.log("✅ RoleOptions:", this.RoleOptions);
// },
// error: (err) => {
// console.error("โหลด role ไม่ได้:", err);
// swal("ข้อผิดพลาด", "ไม่สามารถโหลดสิทธิ์การใช้งานได้", "error");
// }
// });
// }
getRoleOptions() {
this.companyId = this.route.snapshot.paramMap.get('id')!;
......@@ -355,4 +429,89 @@ export class UserAuthorizationComponent implements OnInit {
this.closeDialogProfile();
}
openDialogcearteUser() {
this.dialogRefcreateuser = this.dialog.open(this.CreateUser, {
width: '700px',
disableClose: false,
});
}
closeDialogcearteUser() {
this.dialogRefcreateuser.close();
}
triggerFileInput(): void {
if (this.profileChangeInputRef) {
this.profileChangeInputRef.nativeElement.click();
}
}
togglePassword() {
this.showPassword = !this.showPassword;
}
createUser() {
const cleanUsername = (this.selectModel.username || "").split(",")[0].trim();
const selectedRole = this.MyRoleList.find(r => r.roleId === this.menuSelected);
if (!selectedRole) {
swal("กรุณาเลือกสิทธิ์การใช้งาน", "คุณต้องเลือก role ก่อนสร้างผู้ใช้", "error");
return;
}
const payload: any = {
username: `${cleanUsername},${this.companyId}`,
password: this.selectModel.password,
companyId: this.companyId,
status: 1,
roleOth: JSON.stringify([selectedRole.roleCode]),
member: {
memberId: `0,${this.companyId}`,
thFirstname: this.selectModel.member?.thFirstname || "",
thLastname: this.selectModel.member?.thLastname || "",
engFirstname: this.selectModel.member?.engFirstname || "",
engLastname: this.selectModel.member?.engLastname || "",
email: this.selectModel.member.email,
tel: this.selectModel.member.tel || "",
companyId: this.companyId,
picture: this.selectModel.member.picture || "",
status: 1,
sex: this.selectModel.member.sex,
},
role: {
roleId: selectedRole.roleId,
roleCode: selectedRole.roleCode,
thName: selectedRole.thName,
engName: selectedRole.engName,
menu: selectedRole["menu"] || "[]",
companyId: this.companyId
}
};
console.log("🚀 create payload", payload);
swal({
title: "ยืนยันการสร้างผู้ใช้?",
text: "คุณต้องการสร้างผู้ใช้งานใหม่หรือไม่",
icon: "warning",
buttons: ["ยกเลิก", "ยืนยัน"],
dangerMode: false,
}).then((willSave: any) => {
if (willSave) {
this.userservice.save(payload).subscribe(
(result: any) => {
if (result.state === "FAIL") {
swal("ข้อผิดพลาด!!", result.message, "error");
} else {
swal("สร้างผู้ใช้สำเร็จ!!", "ผู้ใช้งานถูกเพิ่มเข้าระบบแล้ว", "success");
this.ngOnInit();
this.closeDialogcearteUser();
}
},
(error) => {
console.error("เกิดข้อผิดพลาดในการสร้างผู้ใช้:", error);
swal("ข้อผิดพลาด!!", "ไม่สามารถสร้างผู้ใช้งานได้", "error");
}
);
}
});
}
}
......@@ -43,9 +43,6 @@ export class UserService {
.pipe(map(list => list.map(e => new MyUserAccountModel(e, this.translate))));
}
// create(user: ProfileModel): Observable<ResponseModel> {
// return this.http.post<ResponseModel>(this.urlApi, new MyProfileModel(user));
// }
save(user: UserAccountModel): Observable<ResponseModel> {
return this.http.post<ResponseModel>(this.urlApi, new MyUserAccountModel (user));
}
......
......@@ -228,19 +228,19 @@ export class NavService implements OnDestroy {
// type: 'link',
// },
{
icon: 'news bx-flip-horizontal',
icon: 'briefcase',
path: '/mylearn/myhrcompany',
title: 'จัดการบริษัท',
type: 'link',
},
{
icon: 'receipt',
icon: 'user',
path: '/mylearn/management-user',
title: 'การจัดการผู้ใช้งาน',
type: 'link',
},
{
icon: 'receipt',
icon: 'repeat',
path: '/mylearn/course-transfer',
title: 'โอนย้ายคอร์ส',
type: 'link',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment