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)
......
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 {
// }
}
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 -->
......@@ -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