Commit 3f575b1c by DESKTOP-E0VCCBD\zedan

update

parent 284738a8
......@@ -48,7 +48,7 @@ async def create_borrow_transaction(db: AsyncSession, borrow_in: BorrowTransacti
)
# ถ้าสถานะเป็น "approved" ให้ทำการตรวจสอบและตัดสต็อกทันที
if status == "approved":
if status == "approved" or status == "repair" or status == "remove":
if pe_db.quantity_in_project < borrow_in.quantity_borrowed:
raise HTTPException(status_code=400, detail="Not enough equipment to approve")
pe_db.quantity_in_project -= borrow_in.quantity_borrowed
......@@ -56,6 +56,7 @@ async def create_borrow_transaction(db: AsyncSession, borrow_in: BorrowTransacti
new_borrow.approved_by = borrow_in.approved_by # ถ้ามีการส่ง admin id
new_borrow.approved_at = datetime.utcnow()
db.add(pe_db)
try:
db.add(new_borrow)
......
# myproject/controllers/borrow_controller.py
from typing import List, Optional
from uuid import UUID
from datetime import datetime
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.exc import SQLAlchemyError
from ..models.project_equipment import ProjectEquipment
from ..models.project_member import ProjectMember
from ..models.equipment_repair import BorrowRepair
from ..models.equipment import Equipment
from ..schemas.equipment_repair_schema import BorrowRepairBase
# CREATE
async def create_borrow_repair(db: AsyncSession, borrow_in: BorrowRepairBase):
"""
สร้าง BorrowTransaction โดยที่ถ้าสถานะเป็น 'approved'
ระบบจะตัดจำนวนในสต็อกทันที (deduct stock) และบันทึกสถานะเป็น 'approved'
Admin เป็นผู้สร้าง transaction นี้และระบุ memberId ที่เบิกอุปกรณ์
"""
# หา ProjectEquipment ที่อ้างอิง
pe_db = await db.get(ProjectEquipment, borrow_in.peId)
if not pe_db:
raise HTTPException(status_code=404, detail="ProjectEquipment not found")
# ตรวจสอบว่า member อยู่ใน project หรือไม่
result_pm = await db.execute(
select(ProjectMember).where(
ProjectMember.memberId == borrow_in.memberId,
ProjectMember.projectId == pe_db.projectId
)
)
pm_db = result_pm.scalar_one_or_none()
if not pm_db:
raise HTTPException(status_code=403, detail="Member is not in this project")
# กำหนดสถานะเริ่มต้นจาก input ถ้ามี, ถ้าไม่ระบุให้เป็น "requested"
status = borrow_in.status if borrow_in.status else "requested"
new_borrow = BorrowRepair(
memberId=borrow_in.memberId,
peId=borrow_in.peId,
quantity_borrowed=borrow_in.quantity_borrowed,
status=status
)
# ถ้าสถานะเป็น "approved" ให้ทำการตรวจสอบและตัดสต็อกทันที
if status == "approved":
if pe_db.quantity_in_project < borrow_in.quantity_borrowed:
raise HTTPException(status_code=400, detail="Not enough equipment to approve")
pe_db.quantity_in_project -= borrow_in.quantity_borrowed
# บันทึกการตัดสต็อก (อาจเพิ่ม approved_by, approved_at ได้ที่นี่)
new_borrow.approved_by = borrow_in.approved_by # ถ้ามีการส่ง admin id
new_borrow.approved_at = datetime.utcnow()
db.add(pe_db)
try:
db.add(new_borrow)
await db.commit()
await db.refresh(new_borrow)
except SQLAlchemyError as e:
await db.rollback()
raise HTTPException(status_code=400, detail=str(e.orig))
return new_borrow
# UPDATE
async def update_borrow_repair(db: AsyncSession, borrow_id: UUID, borrow_up: BorrowRepairBase):
borrow_db = await db.get(BorrowRepair, borrow_id)
if not borrow_db:
raise HTTPException(status_code=404, detail="BorrowRepair not found")
try:
# ถ้า user ส่ง status ใหม่มา
if borrow_up.status:
# ------------------------------------------------------------------
# 1) กรณี "approved"
# ------------------------------------------------------------------
if borrow_up.status == "approved":
# ต้องเป็นคำขอที่ยังอยู่ในสถานะ requested ก่อน
if borrow_db.status != "requested":
raise HTTPException(
status_code=400,
detail="Cannot approve a transaction that is not in 'requested' status."
)
# หา projectEquipment
pe_db = await db.get(ProjectEquipment, borrow_db.peId)
if not pe_db:
raise HTTPException(status_code=404, detail="ProjectEquipment not found")
# ตรวจสอบจำนวน
if pe_db.quantity_in_project < borrow_db.quantity_borrowed:
raise HTTPException(status_code=400, detail="Not enough equipment to approve")
# ตัดจำนวนใน project_equipment
pe_db.quantity_in_project -= borrow_db.quantity_borrowed
db.add(pe_db)
# เปลี่ยนสถานะเป็น approved
borrow_db.status = "approved"
# หากต้องการเก็บว่าใครเป็นคน approved และเวลาใด
# borrow_db.approved_by = current_admin_id
# borrow_db.approved_at = datetime.utcnow()
if borrow_up.status == "repair":
# ต้องเป็นคำขอที่ยังอยู่ในสถานะ requested ก่อน
if borrow_db.status != "returned":
raise HTTPException(
status_code=400,
detail="Cannot approve a transaction that is not in 'repair' status."
)
# หา projectEquipment
pe_db = await db.get(ProjectEquipment, borrow_db.peId)
if not pe_db:
raise HTTPException(status_code=404, detail="ProjectEquipment not found")
# ตรวจสอบจำนวน
if pe_db.quantity_in_project < borrow_db.quantity_borrowed:
raise HTTPException(status_code=400, detail="Not enough equipment to approve")
# ตัดจำนวนใน project_equipment
pe_db.quantity_in_project -= borrow_db.quantity_borrowed
db.add(pe_db)
# เปลี่ยนสถานะเป็น approved
borrow_db.status = "approved"
# เปลี่ยนสถานะเป็น repair
borrow_db.status = "repair"
# หากต้องการเก็บว่าใครเป็นคน approved และเวลาใด
# borrow_db.approved_by = current_admin_id
# borrow_db.approved_at = datetime.utcnow()
# ------------------------------------------------------------------
# 2) กรณี "returned"
# ------------------------------------------------------------------
elif borrow_up.status == "returned":
# หา projectEquipment
pe_db = await db.get(ProjectEquipment, borrow_db.peId)
if not pe_db:
raise HTTPException(status_code=404, detail="ProjectEquipment not found")
# หาอุปกรณ์
result_eq = await db.execute(select(Equipment).where(Equipment.equipmentId == pe_db.equipmentId))
eq_db = result_eq.scalar_one_or_none()
if not eq_db:
raise HTTPException(status_code=404, detail="Equipment not found")
# ถ้าอุปกรณ์เป็นประเภทคืนได้ (is_returnable == True) ก็คืนจำนวน
if eq_db.is_returnable:
pe_db.quantity_in_project += borrow_db.quantity_borrowed
db.add(pe_db)
borrow_db.status = "returned"
borrow_db.returned_date = datetime.utcnow()
# ------------------------------------------------------------------
# 3) กรณีสถานะอื่น ๆ (เช่น rejected, borrowed, ฯลฯ)
# ------------------------------------------------------------------
else:
borrow_db.status = borrow_up.status
# ถ้ามีฟิลด์อื่น เช่น quantity_borrowed หรืออื่น ๆ ก็อัปเดตที่นี่
# if borrow_up.quantity_borrowed is not None:
# borrow_db.quantity_borrowed = borrow_up.quantity_borrowed
# ...
db.add(borrow_db)
await db.commit()
await db.refresh(borrow_db)
except SQLAlchemyError as e:
await db.rollback()
raise HTTPException(status_code=400, detail=str(e.orig))
return borrow_db
# GET ALL
async def get_all_borrow_repair(db: AsyncSession):
result = await db.execute(select(BorrowRepair))
return result.scalars().all()
# GET BY ID
async def get_borrow_repair_by_id(db: AsyncSession, borrow_id: UUID):
return await db.get(BorrowRepair, borrow_id)
# DELETE
async def delete_borrow_repair(db: AsyncSession, borrow_id: UUID):
borrow_db = await db.get(BorrowRepair, borrow_id)
if not borrow_db:
raise HTTPException(status_code=404, detail="BorrowRepair not found")
try:
await db.delete(borrow_db)
await db.commit()
except SQLAlchemyError as e:
await db.rollback()
raise HTTPException(status_code=400, detail=str(e.orig))
return {"message": "BorrowRepair deleted"}
# NEW API: Search BorrowRepairs by query parameters
async def search_borrow_repair(
db: AsyncSession,
member_id: Optional[UUID] = None,
peId: Optional[UUID] = None,
status: Optional[str] = None,
date_from: Optional[datetime] = None,
date_to: Optional[datetime] = None
) -> List[BorrowRepair]:
"""
ค้นหา BorrowRepair โดยสามารถกรองตาม:
- member_id: รหัสสมาชิกที่เบิกอุปกรณ์
- status: สถานะของการเบิก (requested, approved, returned, rejected, etc.)
- date_from และ date_to: ช่วงวันของ created_at
"""
query = select(BorrowRepair)
if member_id:
query = query.where(BorrowRepair.memberId == member_id)
if peId:
query = query.where(BorrowRepair.peId == peId)
if status:
query = query.where(BorrowRepair.status == status)
if date_from:
query = query.where(BorrowRepair.created_at >= date_from)
if date_to:
query = query.where(BorrowRepair.created_at <= date_to)
result = await db.execute(query)
return result.scalars().all()
\ No newline at end of file
from sqlalchemy import Column, String, Integer, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import func
from datetime import datetime
from uuid import uuid4
from sqlalchemy.orm import relationship
from ..config.database import Base
class BorrowRepair(Base):
__tablename__ = "borrow_transactions"
borrowId = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
peId = Column(UUID(as_uuid=True), ForeignKey("project_equipment.peId"), nullable=False)
memberId = Column(UUID(as_uuid=True), ForeignKey("member.memberId"), nullable=False)
quantity_borrowed = Column(Integer, nullable=False, default=1)
status = Column(String(50), nullable=False, default="repair") # requested, approved, rejected, borrowed, returned
returned_date = Column(DateTime, nullable=True)
created_at = Column(DateTime, nullable=False, server_default=func.now())
# Approval fields
approved_by = Column(UUID(as_uuid=True), ForeignKey("member.memberId"), nullable=True)
approved_at = Column(DateTime, nullable=True)
rejected_reason = Column(String, nullable=True)
# Relationships
# Relationship สำหรับผู้เบิก
member = relationship("Member", foreign_keys=[memberId], back_populates="borrow_transactions", lazy="joined")
# Relationship สำหรับ ProjectEquipment
project_equipment = relationship("ProjectEquipment", back_populates="borrow_transactions", lazy="joined")
# Relationship สำหรับผู้อนุมัติ
approved_by_member = relationship("Member", foreign_keys=[approved_by], back_populates="borrow_transactions", lazy="joined")
# myproject/routes/borrow_routes.py
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status
from typing import List, Optional
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from ..config.database import get_db
from ..controllers.equipment_repair_controller import (
create_borrow_repair,
update_borrow_repair,
get_all_borrow_repair,
get_borrow_repair_by_id,
delete_borrow_repair,
search_borrow_repair
)
from ..schemas.equipment_repair_schema import (
BorrowRepairBase,
BorrowRepairResponse,
)
router = APIRouter()
# CREATE (พนักงานยืมอุปกรณ์)
@router.post("/", response_model=BorrowRepairResponse, status_code=status.HTTP_201_CREATED)
async def create_borrow_endpoint(
borrow_in: BorrowRepairBase,
db: AsyncSession = Depends(get_db)
):
new_borrow = await create_borrow_repair(db, borrow_in)
return new_borrow
# UPDATE (เช่น คืนอุปกรณ์)
@router.put("/{borrowrepairId}", response_model=BorrowRepairResponse)
async def update_borrow_endpoint(
borrowId: UUID,
borrow_up: BorrowRepairBase,
db: AsyncSession = Depends(get_db)
):
updated_borrow = await update_borrow_repair(db, borrowId, borrow_up)
return updated_borrow
# GET ALL
@router.get("/", response_model=List[BorrowRepairResponse])
async def get_all_borrows_endpoint(db: AsyncSession = Depends(get_db)):
return await get_all_borrow_repair(db)
# GET BY ID
@router.get("/{borrowrepairId}", response_model=BorrowRepairResponse)
async def get_borrow_by_id_endpoint(
borrowId: UUID,
db: AsyncSession = Depends(get_db)
):
tx = await get_borrow_repair_by_id(db, borrowId)
if not tx:
raise HTTPException(status_code=404, detail="BorrowTransaction not found")
return tx
# DELETE
@router.delete("/{borrowrepairId}")
async def delete_borrow_endpoint(
borrowId: UUID,
db: AsyncSession = Depends(get_db)
):
return await delete_borrow_repair(db, borrowId)
# NEW API: Search BorrowTransactions by query parameters
@router.get("/search/borrow", response_model=List[BorrowRepairResponse])
async def search_borrow_endpoint(
member_id: Optional[UUID] = None,
peId: Optional[UUID] = None,
status: Optional[str] = None,
date_from: Optional[datetime] = None,
date_to: Optional[datetime] = None,
db: AsyncSession = Depends(get_db)
):
results = await search_borrow_repair(db, member_id,peId, status, date_from, date_to)
return results
\ No newline at end of file
# myproject/schemas/borrow_schema.py
from pydantic import BaseModel
from uuid import UUID
from typing import Optional
from datetime import datetime
from .member_schema import MemberResponse # Schema สำหรับ Member
from .project_equipment_schema import ProjectEquipmentResponse # Schema สำหรับ ProjectEquipment
class BorrowRepairBase(BaseModel):
peId: Optional[UUID] = None
quantity_borrowed: int
status: str = "borrowed" # เช่น requested, borrowed, returned
returned_date: Optional[datetime] = None
memberId: UUID
approved_by : Optional[UUID] = None
class BorrowRepairCreate(BorrowRepairBase):
peId: UUID
memberId: UUID
class BorrowRepairResponse(BorrowRepairBase):
borrowId: UUID
created_at: datetime
peId: UUID
memberId: UUID
# แสดงข้อมูลของสมาชิกที่เบิกอุปกรณ์ (จาก memberId)
member: Optional[MemberResponse] = None
# แสดงข้อมูลของ project_equipment ที่ผูกกับ peId ซึ่งจะมีข้อมูลอุปกรณ์ (ผ่าน relationship ใน ORM)
project_equipment: Optional[ProjectEquipmentResponse] = None
# แสดงข้อมูลของสมาชิกที่อนุมัติ (จาก approved_by)
approved_by_member: Optional[MemberResponse] = None
class Config:
orm_mode = True
\ No newline at end of file
......@@ -99,29 +99,38 @@
<button type="button" class="ti-btn ti-btn-primary btn-wave waves-effect waves-light">Place Bid</button>
</div> -->
</div>
<div
class="box-footer border-block-start-dashed dark:border-defaultborder/10 text-center"
>
<div class="box-footer border-block-start-dashed dark:border-defaultborder/10 text-center">
<div class="btn-list">
<div class="btn-list">
<div class="flex justify-center gap-2"> <!-- เพิ่ม flex และ gap -->
<button
data-hs-overlay="#modal-stock"
type="button"
aria-label="button"
(click)="viewStock(item)"
class="ti-btn ti-btn-sm ti-btn-warning me-[0.375rem]"
class="ti-btn ti-btn-sm ti-btn-success"
>
<i class="ri-store-line"></i>
</button>
<button
data-hs-overlay="#modal-stock-his"
(click)="viewHisStock(item)"
type="button"
aria-label="button"
class="ti-btn ti-btn-sm ti-btn-danger me-0"
class="ti-btn ti-btn-sm ti-btn-danger"
>
<i class="ri-time-line"></i>
</button>
<button
data-hs-overlay="#modal-repair"
type="button"
aria-label="button"
(click)="viewStock(item)"
class="ti-btn ti-btn-sm ti-btn-warning"
>
<i class="ri-tools-line"></i>
</button>
</div>
</div>
</div>
......@@ -379,6 +388,113 @@
</div>
<!-- Start:: New Deal -->
<div id="modal-repair" class="hs-overlay hidden ti-modal" *ngIf="selectModel">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6
class="modal-title text-[1rem] font-semibold text-defaulttextcolor"
id="mail-ComposeLabel"
>
ซ่อม/ซำรุด
</h6>
<button
type="button"
class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#modal-repair"
>
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="xl:col-span-12 col-span-12">
<img
[src]="selectModel.equipment.getPicture()"
class="!rounded-t-md"
alt="..."
style="width: 100%; height: auto; object-fit: cover"
/>
</div>
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<label for="deal-name" class="form-label">ชื่ออุปกรณ์</label>
<input
readonly
type="text"
class="form-control"
id="deal-name"
placeholder="ชื่ออุปกรณ์"
[(ngModel)]="selectModel.equipment.equipmentName"
/>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="deal-name" class="form-label">S/N</label>
<input
readonly
type="text"
class="form-control"
id="deal-name"
placeholder="S/N"
[(ngModel)]="selectModel.equipment.serialNumber"
/>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="input-label" class="form-label">{{
"สถานะ" | translate
}}</label>
<!-- <ng-select [items]="customerList" bindLabel="companyThName" [(ngModel)]="projectModel.customer">
</ng-select> -->
<select class="mb-4 sm:mb-0 form-select !py-3" id="inlineFormSelectPref"
[(ngModel)]="borrowSelect.status">
<option ngValue="repair" selected>ส่งซ่อม</option>
<option ngValue="remove" selected>นำอุปกรณ์ออก</option>
</select>
<!-- <select class="mb-4 sm:mb-0 form-select !py-3" id="inlineFormSelectPref"
[(ngModel)]="projectModel.customer">
<option [ngValue]="null" selected>-</option>
<option *ngFor="let item of customerList" [ngValue]="item">{{item.getCompanyName()}}</option>
</select> -->
</div>
<div class="xl:col-span-12 col-span-12">
<label for="deal-name" class="form-label">จำนวน</label>
<input
type="number"
class="form-control"
id="deal-name"
placeholder="จำนวนที่ชำรุด"
[(ngModel)]="borrowSelect.quantity_borrowed"
/>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button
#closeModalStock
type="button"
class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#modal-repair"
>
ยกเลิก
</button>
<button
type="button"
class="ti-btn bg-primary text-white !font-medium"
(click)="saveRepair()"
>
บันทึก
</button>
</div>
</div>
</div>
</div>
<!-- Start:: New Deal -->
<div
id="modal-stock-his"
class="hs-overlay hidden ti-modal"
......
......@@ -279,6 +279,37 @@ export class AdminProjectEquirementComponent {
}
saveRepair() {
// console.log(this.selectStock)
swal({
title: "Are you sure?",
text: "คุณต้องการบันทึกหรือไม่",
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
this.borrowTransactionsService.save({
"peId": this.selectModel.peId,
"quantity_borrowed": this.borrowSelect.quantity_borrowed,
"status": this.borrowSelect.status,
"memberId": this.tokenService.getUser().member.memberId,
"approved_by": this.tokenService.getUser().member.memberId
}).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit()
this.closeModalStock?.nativeElement.click()
}, (error: any) => {
swal("Fail!!", error.error.detail, "info");
})
}
});
}
stock(item: EquipmentModel) {
if (item.quantity > 0) {
swal({
......
<app-page-header [title]="'จัดการโครงการ'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'จัดการโครงการ'"></app-page-header>
<app-page-header [title]="'เลือกโครงการ'" [activeTitle]="'ผู้ดูแลโปรเจค'" [title1]="'เลือกโครงการ'"></app-page-header>
<div class="grid grid-cols-12 gap-6">
......@@ -10,10 +10,10 @@
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()"
<!--<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2" (click)="new()"
data-hs-overlay="#modal-detail"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
</a> -->
<!-- <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' |
......@@ -26,10 +26,10 @@
</a> -->
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-danger-full me-2" *ngIf="someSelected"
<!-- <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>
</a> -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาโครงการ"
aria-label=".form-control-sm example" [(ngModel)]='searchTerm'>
......@@ -69,7 +69,7 @@
class="font-semibold text-[.875rem] block text-truncate project-list-title">{{item.project_name}}</a>
<span class="text-[#8c9097] dark:text-white/50 block text-[0.75rem]">{{item.project_code}}</span>
</div>
<div class="hs-dropdown ti-dropdown">
<!-- <div class="hs-dropdown ti-dropdown">
<a aria-label="anchor" href="javascript:void(0);" class="ti-btn ti-btn-sm ti-btn-light !mb-0"
aria-expanded="false">
<i class="fe fe-more-vertical"></i>
......@@ -82,7 +82,7 @@
<li><a class="ti-dropdown-item" (click)="delete(item)"><i
class="ri-delete-bin-line me-1 align-middle inline-flex"></i>Delete</a></li>
</ul>
</div>
</div> -->
</div>
<div class="box-body">
......
import { map } from 'rxjs/operators';
import { ProjectMemberModel } from './../../models/project-members';
import { ProjectMemberService } from './../../services/project-members.service';
import { Component, ElementRef, ViewChild } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
......@@ -49,6 +52,8 @@ export class AdminProjectHomeComponent {
pageIndex = 0;
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
projectId = ""
projectList: ProjectModel[]
get searchTerm(): string {
return this._searchTerm;
}
......@@ -64,10 +69,11 @@ export class AdminProjectHomeComponent {
}
_searchTerm = "";
constructor(private projectService: ProjectService, public translate: TranslateService, private tokenService: TokenService) {
constructor(private projectMemberService: ProjectMemberService,private projectService: ProjectService, public translate: TranslateService, private tokenService: TokenService) {
this.uploadConfig()
}
uploadConfig() {
this.uploaderProfile = new FileUploader({
url: environment.baseUrl + "/api/upload-image",
......@@ -120,12 +126,29 @@ export class AdminProjectHomeComponent {
}
ngOnInit(): void {
this.projectService.getLists().subscribe(result => {
this.itemsList = result
this.updatePagedItems()
})
const user = this.tokenService.getUser();
const memberId = user?.member?.memberId;
if (memberId) {
// ดึงรายการ ProjectMemberModel ที่ user นี้ถูก assign ไว้
this.projectMemberService.getCompanyAdmin(memberId).subscribe((memberProjects: ProjectMemberModel[]) => {
const allowedProjectIds = memberProjects
.filter(pm => !!pm.projectId)
.map(pm => pm.projectId);
// ดึงรายการโปรเจกต์ทั้งหมด แล้วกรองตามที่ admin assign ให้
this.projectService.getLists().subscribe((allProjects: ProjectModel[]) => {
this.itemsList = allProjects.filter(p =>
p.projectId && allowedProjectIds.includes(p.projectId)
);
this.updatePagedItems();
});
});
}
}
filter(v: string) {
return this.itemsList?.filter(
(x) =>
......@@ -295,4 +318,4 @@ export class AdminProjectHomeComponent {
// }
}
<app-page-header
[title]="'อนุมัติการคืนอุปกรณ์'"
[activeTitle]="'ผู้ดูแลระบบ'"
[title1]="'อนุมัติการคืนอุปกรณ์'"
></app-page-header>
<div class="grid grid-cols-12 gap-x-6">
<div class="xl:col-span-12 col-span-12">
<div class="box mt-6">
<div class="box-header justify-between">
<div class="box-title">
{{ "รายการทั้งหมด" | translate }}
<span
class="badge bg-light text-default rounded-full ms-1 text-[0.75rem] align-middle"
>{{ filteredList.length }}</span
>
</div>
<!-- แท็บสถานะ -->
<div class="flex flex-wrap gap-2">
<!-- สถานะทั้งหมด -->
<div>
<button
class="px-4 py-2 rounded-full border text-sm font-medium bg-blue-100 text-blue-800 border-blue-200"
(click)="selectedStatus = '!requested,!rejected'; applyFilter()"
>
ทั้งหมด
</button>
</div>
<div>
<button
class="px-4 py-2 rounded-full border text-sm font-medium bg-warning/10 text-warning border-warning/20"
(click)="selectedStatus = 'approved'; applyFilter()"
>
รอคืนอุปกรณื
</button>
</div>
<!-- <div>
<button
class="px-4 py-2 rounded-full border text-sm font-medium bg-success/10 text-success border-success/20"
(click)="filterByStatus('approved')"
>
อนุมัติแล้ว
</button>
</div> -->
<div>
<button
class="px-4 py-2 rounded-full border text-sm font-medium bg-info/10 text-info border-info/20"
(click)="selectedStatus = 'returned'; applyFilter()"
>
คืนแล้ว
</button>
</div>
<!-- <div>
<button
class="px-4 py-2 rounded-full border text-sm font-medium bg-danger/10 text-danger border-danger/20"
(click)="filterByStatus('rejected')"
>
ไม่อนุมัติ
</button>
</div> -->
</div>
<div class="flex flex-wrap gap-2">
<div>
<input
[(ngModel)]="searchTerm"
class="form-control form-control"
type="text"
placeholder="{{ 'ค้นหารายการ...' | translate }}"
aria-label=".form-control-sm example"
(input)="applyFilter()"
/>
</div>
</div>
</div>
<div class="box-body">
<div class="table-responsive">
<table class="table whitespace-nowrap table-bordered min-w-full">
<thead class="bg-light">
<tr>
<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>
@for(product of filteredList;track filterList){
<tr class="product-list">
<td>{{ product.project_equipment?.project?.project_name }}
</td>
<td>
{{ product.project_equipment?.equipment?.equipmentName }}
</td>
<td>
<div class="flex items-center">
<div class="font-semibold">
{{ product.member?.getFullname() }}
</div>
</div>
</td>
<td>{{ product.quantity_borrowed }}</td>
<td>{{ product.created_at | date : "dd/MM/yyyy HH:mm" }}</td>
<td>
@if(product.returned_date) {
{{ product.returned_date | date : "dd/MM/yyyy HH:mm" }}
}
</td>
<td>
<span
class="badge"
[ngClass]="{
'bg-warning/10 text-warning':
product.status === 'approved',
'bg-danger/10 text-danger':
product.status === 'rejected',
'bg-info/10 text-info':
product.status === 'returned',
}"
>
{{ getStatusText(product.status) }}
</span>
</td>
<td>{{ product.approved_by_member?.getFullname()}}</td>
<td>
<div class="flex gap-1">
<!-- ปุ่มอนุมัติการคืน -->
@if(product.project_equipment?.equipment?.is_returnable &&
product.status === 'approved' && !product.returned_date) {
<button
aria-label="อนุมัติการคืน"
(click)="view(product)"
class="ti-btn ti-btn-sm ti-btn-success"
data-hs-overlay="#detail-container"
>
<i class="ri-check-line"></i>
</button>
}
<!-- ปุ่มแจ้งซ่อม -->
@if(product.project_equipment?.equipment?.is_returnable &&
(product.status === 'returned' || product.status ===
'returned')) {
<button
aria-label="แจ้งซ่อม"
(click)="reportRepair(product)"
class="ti-btn ti-btn-sm ti-btn-warning"
>
<i class="ri-tools-line"></i>
</button>
} @if(product.status === 'approved' || product.status ===
'returned'){
<button
aria-label="รายละเอียด"
(click)="view(product)"
class="ti-btn ti-btn-sm ti-btn-light me-[0.375rem]"
data-hs-overlay="#detail-borrow"
>
<i class="fe fe-eye"></i>
</button>
}
</div>
</td>
</tr>
}
</tbody>
</table>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filteredList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filteredList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
id="detail-container"
class="hs-overlay hidden ti-modal"
*ngIf="selectedBorrowItem as item"
>
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
<div class="ti-modal-header">
<h6
class="modal-title text-[1rem] font-semibold text-defaulttextcolor"
id="mail-ComposeLabel"
>
รายละเอียดการยืม
</h6>
<button
type="button"
class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#detail-container"
>
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<div class="xl:col-span-12 col-span-12">
<label for="deal-name" class="form-label">ชื่ออุปกรณ์</label>
<input
readonly
type="text"
class="form-control"
id="deal-name"
placeholder="ชื่ออุปกรณ์"
[ngModel]="
selectedBorrowItem.project_equipment.equipment.equipmentName
"
/>
</div>
<div class="xl:col-span-12 col-span-12">
<label for="deal-name" class="form-label">จำนวน</label>
<input
type="number"
class="form-control"
id="quantity"
placeholder="จำนวน"
[(ngModel)]="selectedBorrowItem.quantity_borrowed"
/>
</div>
</div>
<div class="ti-modal-footer">
<button
#closeModalStock
type="button"
class="hs-dropdown-toggle ti-btn ti-btn-light align-middle"
data-hs-overlay="#detail-container"
>
ยกเลิก
</button>
<button
type="button"
class="ti-btn bg-primary text-white !font-medium"
data-hs-overlay="#detail-container"
(click)="approve(item)"
>
บันทึก
</button>
</div>
</div>
</div>
</div>
</div>
<div
id="detail-borrow"
class="hs-overlay hidden ti-modal"
*ngIf="selectedBorrowItem"
>
<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">
รายละเอียดการยืม
</h6>
<button
type="button"
class="hs-dropdown-toggle !text-[1rem] !font-semibold !text-defaulttextcolor"
data-hs-overlay="#detail-borrow"
>
<span class="sr-only">Close</span>
<i class="ri-close-line"></i>
</button>
</div>
<div class="ti-modal-body px-4">
<div class="grid grid-cols-12 gap-4">
<!-- ข้อมูลผู้ยืม -->
<div class="xl:col-span-12 col-span-12">
<label class="form-label">ชื่อผู้ยืม</label>
<input
readonly
type="text"
class="form-control"
[value]="selectedBorrowItem.member.getFullname() || '-'"
/>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">เบอร์โทรติดต่อ</label>
<input
readonly
type="text"
class="form-control"
[value]="selectedBorrowItem.member.phoneNumber || '-'"
/>
</div>
<!-- ข้อมูลอุปกรณ์ -->
<div class="xl:col-span-12 col-span-12">
<label class="form-label">ชื่ออุปกรณ์</label>
<input
readonly
type="text"
class="form-control"
[value]="
selectedBorrowItem.project_equipment.equipment.equipmentName
"
/>
</div>
<div class="xl:col-span-12 col-span-12">
<label class="form-label">ชื่อโปรเจค</label>
<input
readonly
type="text"
class="form-control"
[value]="
selectedBorrowItem.project_equipment.project.project_name
"
/>
</div>
<!-- จำนวน -->
<div class="xl:col-span-12 col-span-12">
<label class="form-label">จำนวน</label>
<input
readonly
type="number"
class="form-control"
[value]="selectedBorrowItem.quantity_borrowed || '0'"
/>
</div>
<!-- วันที่ยืม -->
<div class="xl:col-span-6 col-span-12">
<label class="form-label">วันที่ยืม</label>
<input
readonly
type="text"
class="form-control"
[value]="
selectedBorrowItem.created_at
? (selectedBorrowItem.created_at | date : 'dd/MM/yyyy HH:mm')
: '-'
"
/>
</div>
<!-- วันที่คืน -->
<div class="xl:col-span-6 col-span-12">
<label class="form-label">วันที่คืน</label>
<input
readonly
type="text"
class="form-control"
[value]="
selectedBorrowItem.returned_date
? (selectedBorrowItem.returned_date
| date : 'dd/MM/yyyy HH:mm')
: '-'
"
/>
</div>
</div>
</div>
<div class="ti-modal-footer">
<button
type="button"
class="ti-btn ti-btn-light align-middle"
data-hs-overlay="#detail-borrow"
>
ปิด
</button>
</div>
</div>
</div>
</div>
import { UserModel } from './../../../shared/user-auth.model';
import { ProjectEquipmentService } from './../../services/project-equipments.service';
import { ProjectMemberService } from './../../services/project-members.service';
import { filter } from 'rxjs/operators';
import { CommonModule } from "@angular/common";
import { Component, ViewChild, ElementRef } 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 { SharedModule } from "../../../shared/shared.module";
import { TokenService } from "../../../shared/services/token.service";
import { BorrowTransactionsService } from '../../services/borrow-transactions.service';
import { BorrowTransactionsModel } from '../../models/borrow-transactions';
import { EquipmentService } from "../../services/equirement.service";
import { EquipmentModel, EquipmentStockModel } from "../../models/equipments.model";
import { ProjectEquipmentModel } from "../../models/project-equipments";
import { ProjectMemberModel } from '../../models/project-members';
import { HttpClient } from '@angular/common/http';
import { ProjectModel } from '../../models/project.model';
import { ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UserProfileModel } from '../../models/user.model';
@Component({
selector: 'app-emp-borrow-repair',
templateUrl: './emp-borrow-repair.component.html',
styleUrls: ['./emp-borrow-repair.component.css']
})
export class EmpBorrowRepairComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
......@@ -144,7 +144,7 @@
<!-- ปุ่มแจ้งซ่อม -->
@if(product.project_equipment?.equipment?.is_returnable &&
(product.status === 'approved' || product.status ===
(product.status === 'returned' || product.status ===
'returned')) {
<button
aria-label="แจ้งซ่อม"
......@@ -170,6 +170,47 @@
}
</tbody>
</table>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filteredList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filteredList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{pageIndex +2}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+2)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filterList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
......
......@@ -21,6 +21,7 @@ import { ProjectModel } from '../../models/project.model';
import { ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UserProfileModel } from '../../models/user.model';
......@@ -47,7 +48,7 @@ export class EmpBorrowReturnStatusComponent {
Projecteqlist: ProjectEquipmentModel;
projecteMember: ProjectMemberModel;
selectedBorrowItem: BorrowTransactionsModel;
projectList:ProjectModel;
projectList: ProjectModel;
allSelected = false;
someSelected = false;
uploadErrorMsg: string = "";
......@@ -61,6 +62,7 @@ export class EmpBorrowReturnStatusComponent {
borrowSelect: BorrowTransactionsModel = new BorrowTransactionsModel()
filteredList: any[] = [];
pageIndex = 0;
member: UserProfileModel;
get searchTerm(): string {
return this._searchTerm;
}
......@@ -78,7 +80,7 @@ export class EmpBorrowReturnStatusComponent {
_searchTerm = "";
isEdit = false;
empList: ProjectMemberModel[] = []
hisList: BorrowTransactionsModel [] = [];
hisList: BorrowTransactionsModel[] = [];
selectedStatus: string = '!requested,!rejected';
isProcessing = false;
constructor(
......@@ -217,7 +219,7 @@ export class EmpBorrowReturnStatusComponent {
}
getStatusText(status: string): string {
switch(status) {
switch (status) {
case 'requested': return 'รออนุมัติ';
case 'approved': return 'รอคืนอุปกรณ์';
case 'rejected': return 'ไม่อนุมัติ';
......@@ -237,7 +239,7 @@ export class EmpBorrowReturnStatusComponent {
}
viewHisStock(item: ProjectEquipmentModel) {
this.borrowTransactionsService.search({ "peId": item.peId , "member_id" :this.tokenService.getUser().member.memberId }).subscribe(result => {
this.borrowTransactionsService.search({ "peId": item.peId, "member_id": this.tokenService.getUser().member.memberId }).subscribe(result => {
this.hisList = result
})
}
......@@ -333,57 +335,74 @@ export class EmpBorrowReturnStatusComponent {
title: "แจ้งซ่อมอุปกรณ์",
text: `คุณต้องการแจ้งซ่อมอุปกรณ์ ${item.project_equipment?.equipment?.equipmentName} หรือไม่?`,
content: {
element: "input",
attributes: {
placeholder: "ระบุอาการเสีย/ปัญหาที่พบ",
type: "text",
},
element: this.createRepairForm(item.quantity_borrowed) // << ส่งจำนวนยืมเข้าไป
},
icon: "warning",
buttons: ["ยกเลิก", "แจ้งซ่อม"],
}).then((repairDescription) => {
if (repairDescription) {
}).then((confirmed) => {
if (confirmed) {
const description = (document.getElementById("repairDescription") as HTMLInputElement).value;
const quantity = +(document.getElementById("repairQuantity") as HTMLInputElement).value;
if (!description || quantity <= 0) {
swal("กรุณากรอกข้อมูลให้ครบถ้วน", "", "error");
return;
}
const repairData = {
borrowTransactionId: item.borrowId,
equipmentId: item.project_equipment?.equipment?.equipmentId,
reportedBy: this.tokenService.getUser().member.memberId,
description: repairDescription,
description: `${description} (จำนวน ${quantity})`,
status: 'pending'
};
// อัปเดตสถานะการยืมเป็น repairing
const updateBorrowData = {
status: 'repairing'
};
// // สร้างรายการซ่อม
// this.equipmentRepairService.create(repairData).subscribe(
// (result) => {
// // อัปเดตสถานะการยืม
// this.borrowTransactionsService.update(item.borrowId, updateBorrowData).subscribe(
// (updateResult) => {
// swal("แจ้งซ่อมสำเร็จ", "ระบบได้บันทึกการแจ้งซ่อมแล้ว", "success");
// this.loadReturnList();
// },
// (updateError) => {
// swal("เกิดข้อผิดพลาด", "ไม่สามารถอัปเดตสถานะการยืมได้", "error");
// }
// );
// },
// (result) => {
// swal("เกิดข้อผิดพลาด", "ไม่สามารถแจ้งซ่อมได้", "error");
// }
// );
// เรียก service เพื่อบันทึกการแจ้งซ่อม...
}
});
}
private createRepairForm(defaultQuantity: number): HTMLElement {
const wrapper = document.createElement("div");
const qtyInput = document.createElement("input");
qtyInput.id = "repairQuantity";
qtyInput.placeholder = "จำนวนที่แจ้งซ่อม";
qtyInput.type = "number";
qtyInput.min = "1";
qtyInput.className = "swal-content__input";
qtyInput.style.marginTop = "10px";
qtyInput.value = defaultQuantity?.toString() || '1';
const descInput = document.createElement("input");
descInput.id = "repairDescription";
descInput.placeholder = "ระบุอาการเสีย/ปัญหาที่พบ";
descInput.type = "text";
descInput.className = "swal-content__input";
wrapper.appendChild(descInput);
wrapper.appendChild(qtyInput);
return wrapper;
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
// this.filterList = this.itemsList.slice(startIndex, endIndex);
this.filterList = this.itemsList
const endIndex = startIndex + 10;
const source = this.searchTerm
? this.itemsList.filter(x => x.equipment?.equipmentName?.includes(this.searchTerm))
: this.itemsList;
this.filterList = source.slice(startIndex, endIndex);
}
updatePagedItemsAll() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
......
......@@ -135,6 +135,46 @@
}
</tbody>
</table>
<div class="box-footer">
<div class="flex items-center flex-wrap overflow-auto" *ngIf="filteredList.length > 0">
<div class="mb-2 sm:mb-0">
<div>
{{'Showing' | translate}} {{filteredList.length}} {{'entries'
| translate}} <i class="bi bi-arrow-right ms-2 font-semibold"></i>
</div>
</div>
<div class="ms-auto">
<nav aria-label="Page navigation">
<ul class="ti-pagination mb-0">
<li *ngIf="pageIndex>0" class="page-item {{pageIndex==0 ? 'disabled' : ''}}"><a
class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{'Previous' | translate}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex-1>0" (click)="pageIndex = pageIndex-2;updatePagedItems()">{{pageIndex-1}}</a></li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="pageIndex>0 && ((pageIndex-1)*10 < (searchTerm == '' ? itemsList.length : filterList.length))"
(click)="pageIndex = pageIndex-1;updatePagedItems()">{{pageIndex}}</a></li>
<li class="page-item"><a class="page-link active px-3 py-[0.375rem]"
href="javascript:void(0);">{{pageIndex +1}}</a>
</li>
<li class="page-item"><a class="page-link px-3 py-[0.375rem]" href="javascript:void(0);"
*ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filteredList.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 : filteredList.length)"
(click)="pageIndex = pageIndex+2;updatePagedItems()">{{pageIndex +3}}</a></li>
<li *ngIf="(pageIndex+1)*10 < (searchTerm == '' ? itemsList.length : filteredList.length)"
class="page-item"><a class="page-link px-3 py-[0.375rem]"
(click)="pageIndex = pageIndex+1;updatePagedItems()">{{'Next' |
translate}}</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
......
......@@ -83,6 +83,8 @@ export class EmpBorrowStatusComponent {
ngOnInit(): void {
this.loadBorrowHistory();
this.filterList = [...this.itemsList];
this.updatePagedItems()
}
loadBorrowHistory(): void {
......@@ -183,12 +185,17 @@ export class EmpBorrowStatusComponent {
x.equipment.description?.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
updatePagedItems() {
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
// this.filterList = this.itemsList.slice(startIndex, endIndex);
this.filterList = this.itemsList
const endIndex = startIndex + 10;
const source = this.searchTerm
? this.itemsList.filter(x => x.peId?.includes(this.searchTerm))
: this.itemsList;
this.filterList = source.slice(startIndex, endIndex);
}
updatePagedItemsAll() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
......
<app-page-header [title]="'จัดการสมาชิก'" [activeTitle]="'ผู้ดูแลระบบ'" [title1]="'จัดการสมาชิก'"></app-page-header>
<app-page-header [title]="'ประวัติการทำรายการ'" [activeTitle]="'ผู้ใช้งานทั่วไป'" [title1]="'ประวัติการทำรายการ'"></app-page-header>
<div class="grid grid-cols-12 gap-6">
......@@ -10,11 +10,11 @@
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()"
<!-- <a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-primary-full me-2" (click)="new()"
data-hs-overlay="#modal-detail"><i class="ri-add-line font-semibold align-middle"></i>{{ 'Create' |
translate}}
</a>
<a href="javascript:void(0);" class="hs-dropdown-toggle ti-btn ti-btn-success-full me-2" *ngIf="someSelected"
</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>
......@@ -28,7 +28,7 @@
<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>
</a> -->
<div>
<input class="form-control form-control" type="text" placeholder="ค้นหาสมาชิก"
aria-label=".form-control-sm example" [(ngModel)]='searchTerm'>
......@@ -55,18 +55,16 @@
<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">{{ 'Username' | translate}}</th>
<th scope="col" class="text-start">{{ 'Fullname' | translate}}</th>
<th scope="col" class="text-start">{{ 'Email' | translate}}</th>
<th scope="col" class="text-start">{{ 'Mobile' | translate}}</th>
<th scope="col" class="text-start">{{ 'User Group' | translate}}</th>
<th scope="col" class="text-start">{{ 'Borrowed equipment' | translate}}</th>
<th scope="col" class="text-start">{{ 'Amount' | translate}}</th>
<th scope="col" class="text-start">{{ 'Borrow Date' | translate}}</th>
<th scope="col" class="text-start">{{ 'Date return' | translate}}</th>
<th scope="col" class="text-start">{{ 'Status' | translate}}</th>
<th scope="col" class="text-start">{{ 'Update Date' | translate}}</th>
<th scope="col" class="text-start"></th>
<th scope="col" class="text-start">{{ 'Approver' | translate}}</th>
</tr>
</thead>
<tbody>
@for(item of filterList;track filterList){
@for(item of filterList;track item.peId ){
<tr class="border border-defaultborder dark:border-defaultborder/10">
<td class="product-checkbox"><input class="form-check-input" type="checkbox" [checked]="selectedItems.get(item.memberId)"
......@@ -74,14 +72,14 @@
</td>
<td>
<div class="flex items-center">
<span class="avatar avatar-sm p-1 me-1 bg-light !rounded-full">
<!-- <span class="avatar avatar-sm p-1 me-1 bg-light !rounded-full">
<img
[src]="item.getPicture()"
alt="" id="profile-img">
</span>
</span> -->
<div class="ms-2">
<p class="font-semibold mb-0 flex items-center text-primary"><a (click)="view(item)">
{{item.username}}</a></p>
<p class="font-semibold mb-0 flex items-center text-primary"><a>
{{item.project_equipment.equipment.equipmentName}}</a></p>
<p class="text-[0.75rem] text-muted mb-0">{{item.memberId}}</p>
</div>
</div>
......@@ -97,30 +95,37 @@
</div>
</div>
</td> -->
<td> {{item.firstName}} {{item.lastName}}</td>
<td> {{item.quantity_borrowed}}</td>
<td>
<div>
<span class="block mb-1"><i
class="ri-mail-line me-2 align-middle text-[.875rem] text-[#8c9097] dark:text-white/50 inline-flex"></i>{{item.email}}</span>
class=" me-2 align-middle text-[.875rem] text-[#8c9097] dark:text-white/50 inline-flex"></i>{{item.created_at}}</span>
</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.phoneNumber}}</span>
class=" me-2 align-middle text-[.875rem] text-[#8c9097] dark:text-white/50 inline-flex"></i>{{item.returned_date}}</span>
</div>
</td>
<td> <span
class="badge bg-{{ item.role == 1 ? 'primary' : 'info'}} text-white">{{item.getRole()}}</span>
class="badge"
[ngClass]="{
'bg-success/10 text-success':
item.status === 'approved',
'bg-warning/10 text-warning':
item.status === 'requested',
'bg-danger/10 text-danger': item.status === 'rejected',
'bg-info/10 text-info': item.status === 'returned'
}"
>
{{ getStatusText(item.status) }}
</span>
</td>
<td> <span
class="badge bg-{{ item.status == 1 ? 'primary' : 'danger'}} text-white">{{item.getStatus()}}</span>
class="badge bg text-white">{{item.approved_by}}</span>
</td>
<td><span class="badge bg-info/10 text-primary"><i
class="bi bi-clock me-1"></i>{{item.updatedAt | date : 'medium'}}</span></td>
<td>
<td>
<!-- <td>
<div class="flex flex-row items-center !gap-2 ">
<a aria-label="anchor" (click)="view(item)" data-hs-overlay="#modal-detail"
class="ti-btn ti-btn-wave !gap-0 !m-0 bg-info/10 text-info hover:bg-info hover:text-white hover:border-info"><i
......@@ -129,7 +134,7 @@
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>
</td> -->
</tr>
}
......@@ -183,7 +188,7 @@
<!-- Start:: Create Contact -->
<!-- Start:: Create Contact
<div id="modal-detail" class="hs-overlay hidden ti-modal [--overlay-backdrop:static]">
<div class="hs-overlay-open:mt-7 ti-modal-box mt-0 ease-out">
<div class="ti-modal-content">
......@@ -304,5 +309,5 @@
</div>
</div>
</div>
</div>
</div> -->
<!-- End:: Create Contact -->
import { BorrowTransactionsService } from './../../services/borrow-transactions.service';
import { UserService } from './../../services/user.service';
import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, ViewChild } from '@angular/core';
......@@ -8,7 +9,6 @@ import { BorrowTransactionsModel } from '../../models/borrow-transactions';
import { EquipmentModel, EquipmentStockModel } from '../../models/equipments.model';
import { ProjectEquipmentModel } from '../../models/project-equipments';
import { ProjectMemberModel } from '../../models/project-members';
import { BorrowTransactionsService } from '../../services/borrow-transactions.service';
import { EquipmentService } from '../../services/equirement.service';
import { ProjectEquipmentService } from '../../services/project-equipments.service';
import { ProjectMemberService } from '../../services/project-members.service';
......@@ -46,13 +46,14 @@ export class EmpBorrowTransactionComponent {
allSelected = false;
someSelected = false;
confirmPassword = ""
itemsList: UserProfileModel[] = []
filterList: UserProfileModel[] = []
selectModel: UserProfileModel = new UserProfileModel()
itemsList: BorrowTransactionsModel[] = []
filterList: BorrowTransactionsModel[] = []
selectModel: BorrowTransactionsModel = new BorrowTransactionsModel()
selectedItems = new Map<string, boolean>();
roleList: UserRoleModel[] = []
descName = 'engName'
pageIndex = 0;
memberId = ""
uploaderProfile: FileUploader | undefined;
uploadErrorMsg: string = "";
get searchTerm(): string {
......@@ -135,7 +136,7 @@ export class EmpBorrowTransactionComponent {
if (item.isSuccess) {
const res = JSON.parse(response);
console.log("res", res);
this.selectModel.picture = res.filename
this.selectModel.member = res.filename
swal(res.message, "บันทึกสำเร็จ", "success");
} else {
......@@ -147,25 +148,41 @@ export class EmpBorrowTransactionComponent {
ngOnInit(): void {
this.userService.getLists().subscribe(result => {
this.itemsList = result
const user = this.tokenService.getUser();
const memberId = user?.member?.memberId;
this.borrowTransactionsService.getLists().subscribe(result => {
this.itemsList = result .filter(e => e.member.memberId == memberId);
this.itemsList.sort((a, b) =>
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
);
this.filterList = [...this.itemsList];
this.updatePagedItems()
})
}
filter(v: string) {
return this.itemsList?.filter(
(x) =>
x.memberId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.username?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.email?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.phoneNumber?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getRole()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getStatus()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.getFullname()?.toLowerCase().indexOf(v.toLowerCase()) !== -1
x.member.username.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.created_at?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.returned_date?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.approved_by?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.status?.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
getStatusText(status: string): string {
switch(status) {
case 'requested': return 'รออนุมัติ';
case 'approved': return 'อนุมัติแล้ว';
case 'rejected': return 'ไม่อนุมัติ';
case 'returned': return 'คืนแล้ว';
case 'repairing': return 'กำลังซ่อม';
default: return status;
}
}
delete(item: UserProfileModel) {
swal({
title: "Are you sure?",
......@@ -186,16 +203,16 @@ export class EmpBorrowTransactionComponent {
});
}
new() {
this.action = 'add'
this.selectModel = new UserProfileModel()
}
// new() {
// this.action = 'add'
// this.selectModel = new UserProfileModel()
// }
view(item: UserProfileModel) {
view(item: BorrowTransactionsModel) {
this.action = 'edit'
this.confirmPassword = ''
this.selectModel = new UserProfileModel(item)
this.selectModel = new BorrowTransactionsModel(item)
console.log(this.selectModel)
}
......@@ -208,35 +225,40 @@ export class EmpBorrowTransactionComponent {
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
if (this.action == 'add') {
this.userService.save(this.selectModel).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสมาชิก", "success");
this.ngOnInit()
this.childModal?.nativeElement.click()
})
} else if (this.action == 'edit') {
this.userService.update(this.selectModel).subscribe(result => {
console.log(result)
swal("Update Success!!", "บันทึกข้อมูลสมาชิก", "success");
this.ngOnInit()
this.childModal?.nativeElement.click()
})
}
}
});
// .then((willDelete: any) => {
// if (willDelete) {
// if (this.action == 'add') {
// this.userService.save(this.selectModel).subscribe(result => {
// console.log(result)
// swal("Save Success!!", "บันทึกข้อมูลสมาชิก", "success");
// this.ngOnInit()
// this.childModal?.nativeElement.click()
// })
// } else if (this.action == 'edit') {
// this.userService.update(this.selectModel).subscribe(result => {
// console.log(result)
// swal("Update Success!!", "บันทึกข้อมูลสมาชิก", "success");
// this.ngOnInit()
// this.childModal?.nativeElement.click()
// })
// }
// }
// });
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList = this.itemsList.slice(startIndex, endIndex);
const endIndex = startIndex + 10;
const source = this.searchTerm
? this.itemsList.filter(x => x.member?.username?.includes(this.searchTerm))
: this.itemsList;
this.filterList = source.slice(startIndex, endIndex);
}
toggleAll(event: any) {
......@@ -255,77 +277,77 @@ export class EmpBorrowTransactionComponent {
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.memberId));
}
deleteSelect() {
let employeeInfo = '';
this.selectedItems.forEach((isSelected, memberId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.memberId === memberId);
if (user) {
employeeInfo += `${this.translate.instant('Fullname')}: ${user.getFullname()}\n`;
}
}
});
swal({
title: "Are you sure?",
text: employeeInfo,
icon: "warning",
dangerMode: true,
buttons: ["Cancel", "Yes, Delete it!"],
})
.then((willDelete: any) => {
if (willDelete) {
this.selectedItems.forEach((isSelected, memberId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.memberId === memberId);
if (user) {
this.userService.delete(user).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit();
});
}
}
});
}
});
}
// deleteSelect() {
// let employeeInfo = '';
// this.selectedItems.forEach((isSelected, memberId) => {
// if (isSelected) {
// const user = this.itemsList.find(user => user.memberId === memberId);
// if (user) {
// employeeInfo += `${this.translate.instant('Fullname')}: ${user.getFullname()}\n`;
// }
// }
// });
adjustSelect(status: number) {
let title = "Are you sure?"
let employeeInfo = ''; // ตัวแปรสำหรับเก็บข้อมูลพนักงาน
this.selectedItems.forEach((isSelected, memberId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.memberId === memberId);
if (user) {
employeeInfo += `${this.translate.instant('Fullname')}: ${user.getFullname()}\n`;
}
}
});
swal({
title: title,
text: employeeInfo,
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
if (willDelete) {
this.selectedItems.forEach((isSelected, memberId) => {
if (isSelected) {
const user = this.itemsList.find(user => user.memberId === memberId);
if (user) {
user.status = status
this.userService.update(user).subscribe(result => {
swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
this.ngOnInit();
});
}
}
});
}
// swal({
// title: "Are you sure?",
// text: employeeInfo,
// icon: "warning",
// dangerMode: true,
// buttons: ["Cancel", "Yes, Delete it!"],
// })
// .then((willDelete: any) => {
// if (willDelete) {
// this.selectedItems.forEach((isSelected, memberId) => {
// if (isSelected) {
// const user = this.itemsList.find(user => user.memberId === memberId);
// if (user) {
// this.userService.delete(user).subscribe(result => {
// swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
// this.ngOnInit();
// });
// }
// }
// });
// }
// });
// }
});
}
// adjustSelect(status: number) {
// let title = "Are you sure?"
// let employeeInfo = ''; // ตัวแปรสำหรับเก็บข้อมูลพนักงาน
// this.selectedItems.forEach((isSelected, memberId) => {
// if (isSelected) {
// const user = this.itemsList.find(user => user.memberId === memberId);
// if (user) {
// employeeInfo += `${this.translate.instant('Fullname')}: ${user.getFullname()}\n`;
// }
// }
// });
// swal({
// title: title,
// text: employeeInfo,
// icon: "warning",
// dangerMode: false,
// buttons: ["Cancel", "Confirm"],
// })
// .then((willDelete: any) => {
// if (willDelete) {
// this.selectedItems.forEach((isSelected, memberId) => {
// if (isSelected) {
// const user = this.itemsList.find(user => user.memberId === memberId);
// if (user) {
// user.status = status
// this.userService.update(user).subscribe(result => {
// swal("Save Success!!", "บันทึกข้อมูลสำเร็จ", "success");
// this.ngOnInit();
// });
// }
// }
// });
// }
// });
// }
// filterEmp(empId: string) {
// this.selectModel = this.empList.filter(e => e.memberId == empId)[0]
......
......@@ -14,7 +14,7 @@ export class UserProfileModel extends BaseModel {
status: number;
picture: any;
createdAt: string;
updatedAt: string;
updatedAt: string;
constructor(
data?: Partial<UserProfileModel>,
translateService?: TranslateService
......
......@@ -43,8 +43,8 @@
<a href="javascript:void(0);" class="badge badge-md !rounded-full bg-info/10 text-info me-2"
title="No of employees"><i class="bi bi-people me-1"></i>No. of Emp : 345 //TODO</a>
</div> -->
<a routerLink="/company/company-info" class="ti-btn btn-wave ti-btn-primary-full">
View Profile <i class="ri-arrow-right-line"></i>
<a routerLink="/company/admin-home" class="ti-btn btn-wave ti-btn-primary-full">
เลือกโครงการ <i class="ri-arrow-right-line"></i>
</a>
</div>
</div>
......
......@@ -68,6 +68,7 @@ export class SidebarComponent {
isInstallerRoute: boolean = false;
previousUrl: string = '';
currentUrl: string = '';
roleInProject: string = '';
constructor(
private navServices: NavService,
private sanitizer: DomSanitizer,
......@@ -80,6 +81,8 @@ export class SidebarComponent {
this.screenWidth = window.innerWidth;
}
ngOnInit() {
let bodyElement: any = document.querySelector('.main-content');
......
......@@ -152,7 +152,7 @@ export class NavService implements OnDestroy {
{ headTitle: 'จัดการโครงการ' },
{
icon: 'home',
path: '/company/admin-home',
path: '/company/home',
title: 'หน้าแรก',
type: 'link',
selected: false,
......@@ -183,6 +183,18 @@ export class NavService implements OnDestroy {
{ path: '/company/emp-borrow-return-status', title: 'คืน', type: 'link' }
],
},
{
icon: 'ruler',
title: 'จัดการอุปกรซ่อม-ชำรุด',
type: 'sub',
selected: false,
active: false,
Menusub: true,
children: [
{ path: '/company/emp-borrow-status', title: 'อุปกรณ์ที่ซ่อม/ชำรุด', type: 'link' },
],
},
];
}
......
{
"Home": "หน้าแรก",
"Approver":"ผู้อนุมัติ",
"All List": "รายการทั้งหมด",
"Create": "สร้าง",
"Delete": "ลบ",
......@@ -29,12 +30,15 @@
"Gender": "เพศ",
"Admin-System" : "ผู้ดูแลระบบ",
"Admin-Company" : "ผู้ดูแลบริษัท",
"Amount":"จำนวน",
"User": "ผู้ใช้งาน",
"User Group": "กลุ่มผู้ใช้งาน",
"User Role": "บทบาทผู้ใช้งาน",
"User Setting": "กำหนดผู้ใช้งาน",
"User Management": "จัดการผู้ใช้งาน",
"Borrowed equipment":"อุปกรณ์ที่ยืม",
"Menu" : "เมนู",
"Member":"พนักงาน",
"Action": "รูปแบบการใช้งาน",
"Login History": "การเข้าใช้งานระบบ",
"usage History": "กิจกรรมผู้ใช้งาน",
......@@ -43,6 +47,7 @@
"Prefix": "คำนำหน้า",
"Region": "ภูมิภาค",
"Province": "จังหวัด",
"Project": "โปรเจค",
"District": "อำเภอ",
"Subdistrict": "ตำบล",
"Zipcode": "รหัสไปรษณีย์",
......@@ -50,6 +55,8 @@
"Department": "แผนก",
"Division": "ฝ่าย",
"Section": "ส่วน",
"Borrow Date":"วันที่ยืม",
"Date return":"วันที่คืน",
"Position": "ตำแหน่ง",
"Employee Management": "จัดการพนักงาน",
"Please fill in information": "*",
......
......@@ -1375,4 +1375,4 @@ const icons_l = [
$controls.querySelector("select[name='animation']").addEventListener("input", setIconAttr.bind(null, "animation"));
</script>
</body>
</html>
\ No newline at end of file
</html>
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