Commit f2f0f2ae by DESKTOP-E0VCCBD\zedan

update

parent c2ed17fe
......@@ -114,17 +114,56 @@ async def get_admin_dashboard(
]
# --- 3) EQUIPMENT DISTRIBUTION ---
stmt2 = select(
stmt2 = (
select(
Equipment.equipmentName,
func.coalesce(func.sum(ProjectEquipment.quantity_in_project), 0).label('count')
).join(ProjectEquipment, Equipment.equipmentId == ProjectEquipment.equipmentId)
# จำนวนที่มีอยู่ในโครงการ
func.coalesce(func.sum(ProjectEquipment.quantity_in_project), 0).label('count'),
# ยอด 'ชิ้น' ที่ถูกยืม / คืน (quantity_borrowed)
func.coalesce(
func.sum(
case((BorrowTransaction.status != 'returned', BorrowTransaction.quantity_borrowed), else_=0)
),
0,
).label('qty_borrowed'),
func.coalesce(
func.sum(
case((BorrowTransaction.status == 'returned', BorrowTransaction.quantity_borrowed), else_=0)
),
0,
).label('qty_returned'),
# -------- ใหม่: นับ "ครั้ง" ที่ยืม/คืน --------
func.count(
case((BorrowTransaction.status != 'returned', BorrowTransaction.peId), else_=None)
).label('borrow_times'),
func.count(
case((BorrowTransaction.status == 'returned', BorrowTransaction.peId), else_=None)
).label('return_times'),
)
.join(ProjectEquipment, Equipment.equipmentId == ProjectEquipment.equipmentId)
.outerjoin(BorrowTransaction, BorrowTransaction.peId == ProjectEquipment.peId)
)
stmt2 = pe_filter(stmt2).group_by(Equipment.equipmentName)
rows2 = (await db.execute(stmt2)).all()
equipment_distribution = [
PieSlice(category=name, count=int(cnt))
for name, cnt in rows2
]
{
"category": name,
"count": int(total_in_project), # stock ในโครงการ
"qty_borrowed": int(qty_b), # ชิ้นที่ถูกยืมสะสม
"qty_returned": int(qty_r), # ชิ้นที่คืนสะสม
"borrow_times": int(bt_borrow), # ครั้งที่ยืม
"return_times": int(bt_return), # ครั้งที่คืน
"percent_returned": round((qty_r / qty_b * 100), 2) if qty_b else 0.0,
}
for name, total_in_project, qty_b, qty_r, bt_borrow, bt_return in rows2
]
# --- 4) BORROW/RETURN LINE ---
stmt3 = (
......
......@@ -8,7 +8,6 @@ from ..config.database import Base
class BorrowTransaction(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)
......
......@@ -14,8 +14,13 @@ class TrendItem(BaseModel):
returned: int
class PieSlice(BaseModel):
category: str # เช่น ชื่ออุปกรณ์ หรือ "Returned"/"Borrowed"
category: str
count: int
qty_borrowed: int # เดิมใช้ borrowed → เปลี่ยนชื่อให้ตรงกับ controller
qty_returned: int # เดิมใช้ returned → เปลี่ยนชื่อให้ตรงกับ controller
borrow_times: int # ใหม่: จำนวน "ครั้ง" ที่ยืม
return_times: int # ใหม่: จำนวน "ครั้ง" ที่คืน
percent_returned: float
class AdminDashboardResponse(BaseModel):
period: dict # {"start": date, "end": date}
......
......@@ -160,11 +160,11 @@
<div class="grid grid-cols-2 border-t border-dashed dark:border-defaultborder/10">
<div class="col p-4 border-e border-dashed dark:border-defaultborder/10 text-center">
<div class="text-[#8c9097] dark:text-white/50 text-[0.75rem] mb-1">
การยืมทั้งหมด
จำนวนยืมทั้งหมด
</div>
<div class="flex justify-center items-center">
<span class="me-3 text-[1.625rem] font-semibold">1,234</span>
<span class="text-success font-semibold"><i class="ri-arrow-up-s-fill me-1"></i>0.23%</span>
<span class="me-3 text-[1.625rem] font-semibold">{{ totalBorrowedChart }} ครั้ง</span>
</div>
</div>
<div class="col p-4 text-center">
......@@ -172,8 +172,8 @@
การคืนทั้งหมด
</div>
<div class="flex justify-center items-center">
<span class="me-3 text-[1.625rem] font-semibold">1,754</span>
<span class="text-danger font-semibold"><i class="ri-arrow-down-s-fill me-1"></i>0.11%</span>
<span class="me-3 text-[1.625rem] font-semibold">{{ totalReturnedChart }} ครั้ง</span>
</div>
</div>
</div>
......@@ -188,19 +188,6 @@
<div class="box-title">จำนวนอุปกรณ์คงเหลือ</div>
<div class="flex gap-4 flex-wrap">
<div class="inline-flex rounded-md shadow-sm" role="group" aria-label="Basic example">
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
1M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
3M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
6M
</button>
<button type="button"
class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary-full !rounded-s-none !text-white">
1Y
</button>
</div>
</div>
</div>
......@@ -222,26 +209,13 @@
</div>
</div>
<div class="xxl:col-span-6 col-span-12">
<!-- <div class="xxl:col-span-6 col-span-12">
<div class="box custom-box">
<div class="box-header justify-between items-center flex-wrap gap-4">
<div class="box-title">จำนวนอุปกรณ์ทั้งหมด</div>
<div class="flex gap-4 flex-wrap">
<div class="inline-flex rounded-md shadow-sm" role="group" aria-label="Basic example">
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
1M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
3M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
6M
</button>
<button type="button"
class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary-full !rounded-s-none !text-white">
1Y
</button>
</div>
</div>
</div>
......@@ -260,4 +234,4 @@
</div>
</div>
</div>
</div>
</div> -->
......@@ -40,6 +40,9 @@ export class HomeCommonComponent {
@ViewChild('closeModal') public childModal?: ElementRef;
@ViewChild('modalDetail') public modalDetail?: ElementRef;
totalBorrowedChart: number = 0;
totalReturnedChart: number = 0;
totalBorrowed: number = 0;
action = "new";
rawData: any[];
allSelected = false;
......@@ -104,7 +107,7 @@ export class HomeCommonComponent {
colors: ['#23b7e5'],
xaxis: {
categories: [] as string[],
title: { text: 'อุปกรณ์' }
},
yaxis: {
title: { text: 'จำนวนคงเหลือ' }
......@@ -356,15 +359,6 @@ export class HomeCommonComponent {
this.selectedProjects = this.selectedProjects.filter(p => p !== value);
}
}
toggleDropdown() {
this.isDropdownOpen = !this.isDropdownOpen;
}
toggleDropdown1() {
this.isDropdownOpen1 = !this.isDropdownOpen1;
}
getSelected(): string {
const selectedIds = Object.keys(this.itemSelection).filter(
......@@ -396,7 +390,7 @@ export class HomeCommonComponent {
this.equipmentList = result
})
const today = new Date();
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
const firstDay = new Date(today.getFullYear(), 0, 1);
this.startDate = this.formatDateToYYYYMMDD(firstDay);
this.endDate = this.formatDateToYYYYMMDD(today);
this.getDashbord()
......@@ -406,24 +400,16 @@ export class HomeCommonComponent {
this.DashboardService.getDashboard(this.startDate, this.endDate).subscribe(result => {
this.dashboardModel = result
this.updateChart(this.dashboardModel)
this.updateDonut(this.dashboardModel.equipment_distribution)
this.updateDonut(this.dashboardModel.equipment_distribution);
this.mapStockChart(this.dashboardModel.equipment_distribution);
this.mapEquipmentChart(this.dashboardModel.equipment_distribution);
console.log(this.dashboardModel)
})
}
private updateDonut(data: Equipmentdistribution[]) {
// series = array of counts
this.optionsCircle1.series = data.map(item => item.count);
// labels = array of categories
this.optionsCircle1.labels = data.map(item => item.category);
}
private updateChart(data: DashboardModel) {
// แมป monthly_trends ไปเป็น series + categories
const trends = data.monthly_trends;
this.chartOptions9.series = [
{ name: 'จำนวนการยืม', data: trends.map(t => t.borrowed) },
{ name: 'จำนวนการคืน', data: trends.map(t => t.returned) },
......@@ -432,7 +418,28 @@ export class HomeCommonComponent {
...this.chartOptions9.xaxis,
categories: trends.map(t => t.period)
};
}
// ✅ คำนวณยอดรวมทั้งหมด
this.totalBorrowedChart = trends.map(t => t.borrowed).reduce((a, b) => a + b, 0);
this.totalReturnedChart = trends.map(t => t.returned).reduce((a, b) => a + b, 0);
}
private updateDonut(data: Equipmentdistribution[]) {
const series = data.map(d => d.qty_borrowed);
const labels = data.map(d => d.category);
console.log("Donut updated:", series, labels);
this.totalBorrowed = series.reduce((a, b) => a + b, 0); // 👈 รวม total ไว้ใช้ใน HTML
this.optionsCircle1 = {
...this.optionsCircle1,
series,
labels,
};
}
private mapStockChart(dist: Equipmentdistribution[]) {
// data = quantity ของแต่ละอุปกรณ์
......
......@@ -279,8 +279,8 @@
<label class="form-label">{{'User Role' | translate}}</label>
<ng-select name="choices-multiple-remove-button1" id="choices-multiple-remove-button1" placeholder="เลือก"
[(ngModel)]="selectModel.role">
<ng-option [value]="0">{{'ผู้ใช้งานทั่วไป' | translate}}</ng-option>
<ng-option [value]="1">{{'ผู้ดูแลบริษัท' | translate}}</ng-option>
<ng-option [value]="0">{{'พนักงานบริษัท' | translate}}</ng-option>
<ng-option [value]="1">{{'ผู้จัดการโครงการ' | translate}}</ng-option>
</ng-select>
</div>
<div class="xl:col-span-12 col-span-12">
......
......@@ -36,6 +36,7 @@ export class AdminProjectEmpManageComponent {
@ViewChild('modalDetail') public modalDetail?: ElementRef;
action = "new";
allSelected = false;
projectMembers: ProjectMemberModel[] = [];
someSelected = false;
confirmPassword = ""
itemsList: ProjectMemberModel[] = []
......@@ -63,8 +64,9 @@ export class AdminProjectEmpManageComponent {
this.updatePagedItems()
}
}
projectId = ""
_searchTerm = "";
projectId = ""
hisList: BorrowTransactionsModel[] = []
constructor(private projectMemberService: ProjectMemberService, private userService: UserService, public translate: TranslateService, private tokenService: TokenService, private borrowTransactionsService: BorrowTransactionsService) {
this.projectId = this.tokenService.getSelectCompany().projectId!;
......@@ -204,35 +206,36 @@ export class AdminProjectEmpManageComponent {
icon: "warning",
dangerMode: false,
buttons: ["Cancel", "Confirm"],
})
.then((willDelete: any) => {
}).then((willDelete: any) => {
if (willDelete) {
if (this.action == 'add') {
this.projectMemberService.getLists(this.projectId).subscribe(members => {
this.projectMembers = members;
const isDuplicate = this.projectMembers.some(m => m.memberId === item.memberId);
if (this.action === 'add') {
if (isDuplicate) {
swal("เกิดข้อผิดพลาด", "สมาชิกนี้ถูกเพิ่มแล้ว", "error");
return;
}
this.projectMemberService.save({
"memberId": item.memberId,
"role_in_project": "employee",
"projectId": this.projectId
memberId: item.memberId,
role_in_project: "employee",
projectId: this.projectId
}).subscribe(result => {
console.log(result)
swal("Save Success!!", "บันทึกข้อมูลสมาชิก", "success");
this.getUserProject()
this.childModal?.nativeElement.click()
})
} else if (this.action == 'edit') {
this.getUserProject();
this.childModal?.nativeElement.click();
});
} else if (this.action === 'edit') {
this.projectMemberService.update(this.selectModel).subscribe(result => {
console.log(result)
swal("Update Success!!", "บันทึกข้อมูลสมาชิก", "success");
this.getUserProject()
this.childModal?.nativeElement.click()
})
}
this.getUserProject();
this.childModal?.nativeElement.click();
});
}
});
}
});
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
......
......@@ -131,6 +131,14 @@
>
<i class="ri-tools-line"></i>
</button>
<!-- <button
*ngIf="borrowSelect?.quantity_borrowed === 0"
type="button"
class="ti-btn ti-btn-sm ti-btn-danger me-0"
(click)="deleteEquipment()">
<i class="ri-delete-bin-line"></i>
</button> -->
</div>
</div>
</div>
......@@ -225,9 +233,8 @@
<div class="font-semibold mb-1">จำนวน :</div>
<!-- <h3 class="text-[#8c9097] dark:text-white/50">{{item.quantity}}</h3> -->
<input
type="number"
ype="number"
class="form-control"
id="deal-name"
placeholder="จำนวน"
[(ngModel)]="item.quantity"
/>
......
......@@ -49,7 +49,7 @@ export class AdminProjectEquirementComponent {
itemsListAll: EquipmentModel[] = []
filterListAll: EquipmentModel[] = []
selectModel: ProjectEquipmentModel = new ProjectEquipmentModel()
selectStock?: EquipmentStockModel
selectStock: EquipmentStockModel
selectedItems = new Map<string, boolean>();
borrowSelect: BorrowTransactionsModel = new BorrowTransactionsModel()
pageIndex = 0;
......@@ -119,6 +119,32 @@ export class AdminProjectEquirementComponent {
);
}
deleteEquipment(item?: EquipmentModel) {
if (!item) return;
swal({
title: "ลบรายการ?",
text: "คุณต้องการลบอุปกรณ์นี้หรือไม่",
icon: "warning",
dangerMode: true,
buttons: ["ยกเลิก", "ลบ"],
}).then((willDelete) => {
if (willDelete) {
this.eqService.delete(item).subscribe({
next: () => {
swal("ลบสำเร็จ", "อุปกรณ์ถูกลบแล้ว", "success");
this.ngOnInit();
},
error: (error) => {
swal("เกิดข้อผิดพลาด", error.error.detail || "ไม่สามารถลบได้", "error");
}
});
}
});
}
delete(item: EquipmentModel) {
swal({
title: "Are you sure?",
......@@ -310,6 +336,7 @@ export class AdminProjectEquirementComponent {
}
stock(item: EquipmentModel) {
if (item.quantity > 0) {
swal({
......
......@@ -290,100 +290,6 @@
</div>
</div>
</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)]="selectedBorrowItem.project_equipment.equipment.equipmentName"
/>
</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)]="selectedBorrowItem.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>
<div
id="detail-borrow"
class="hs-overlay hidden ti-modal"
......
......@@ -22,6 +22,7 @@ import { ChangeDetectorRef } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UserProfileModel } from '../../models/user.model';
import { FileUploader } from 'ng2-file-upload';
......@@ -140,6 +141,7 @@ quantityExceeded: boolean = false;
this.applyFilter();
}
applyFilter(): void {
let filtered = [...this.hisList];
......@@ -383,6 +385,4 @@ quantityExceeded: boolean = false;
this.filterListAll = this.itemsListAll
}
}
......@@ -53,7 +53,7 @@
<div class="flex-grow">
<div class="flex mb-1 items-start justify-between">
<h5 class="font-semibold mb-0 leading-none text-[1.25rem]">
{{ itemsList.length }}
{{ itemsList1.length }}
</h5>
<div class="text-danger font-semibold"></div>
</div>
......@@ -84,7 +84,7 @@
<div class="flex-grow">
<div class="flex mb-1 items-start justify-between">
<h5 class="font-semibold mb-0 leading-none text-[1.25rem]">
{{ projectList.length }}
{{ itemsList2.length }}
</h5>
<div class="text-success font-semibold"></div>
</div>
......@@ -113,7 +113,7 @@
<div class="flex-grow">
<div class="flex mb-1 items-start justify-between">
<h5 class="font-semibold mb-0 leading-none text-[1.25rem]">
{{ equipmentList.length }}
{{ itemsList.length }}
</h5>
<div class="text-success font-semibold"></div>
</div>
......@@ -167,8 +167,8 @@
การยืมทั้งหมด
</div>
<div class="flex justify-center items-center">
<span class="me-3 text-[1.625rem] font-semibold">1,234</span>
<span class="text-success font-semibold"><i class="ri-arrow-up-s-fill me-1"></i>0.23%</span>
<span class="me-3 text-[1.625rem] font-semibold">{{ totalBorrowedChart }} ครั้ง</span>
</div>
</div>
<div class="col p-4 text-center">
......@@ -176,8 +176,8 @@
การคืนทั้งหมด
</div>
<div class="flex justify-center items-center">
<span class="me-3 text-[1.625rem] font-semibold">1,754</span>
<span class="text-danger font-semibold"><i class="ri-arrow-down-s-fill me-1"></i>0.11%</span>
<span class="me-3 text-[1.625rem] font-semibold">{{ totalReturnedChart }} ครั้ง</span>
</div>
</div>
</div>
......@@ -192,19 +192,6 @@
<div class="box-title">จำนวนอุปกรณ์คงเหลือ</div>
<div class="flex gap-4 flex-wrap">
<div class="inline-flex rounded-md shadow-sm" role="group" aria-label="Basic example">
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
1M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
3M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
6M
</button>
<button type="button"
class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary-full !rounded-s-none !text-white">
1Y
</button>
</div>
</div>
</div>
......@@ -226,26 +213,13 @@
</div>
</div>
<div class="xxl:col-span-6 col-span-12">
<!-- <div class="xxl:col-span-6 col-span-12">
<div class="box custom-box">
<div class="box-header justify-between items-center flex-wrap gap-4">
<div class="box-title">จำนวนอุปกรณ์ทั้งหมด</div>
<div class="flex gap-4 flex-wrap">
<div class="inline-flex rounded-md shadow-sm" role="group" aria-label="Basic example">
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
1M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
3M
</button>
<button type="button" class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary">
6M
</button>
<button type="button"
class="ti-btn-group !border-0 !text-xs !py-2 !px-3 ti-btn-primary-full !rounded-s-none !text-white">
1Y
</button>
</div>
</div>
</div>
......@@ -264,4 +238,5 @@
</div>
</div>
</div>
</div>
</div> -->
import { NotificationModel } from './../../models/notification.model';
import { NotificationService } from './../../services/notification.service';
import { DashboardService } from './../../services/dashboard.service';
import { EquipmentService } from './../../services/equirement.service';
import { ProjectService } from './../../services/project.service';
......@@ -21,8 +23,14 @@ import { environment } from '../../../../environments/environment';
import swal from 'sweetalert';
import { ProjectModel } from '../../models/project.model';
import { EquipmentModel } from '../../models/equipments.model';
import { DashboardModel } from '../../models/dasbord.model';
import { DashboardModel, Equipmentdistribution, Summary } from '../../models/dasbord.model';
import { CompanyService } from '../../services/company.service';
import { Subscription } from 'rxjs';
import { OnInit, OnDestroy } from '@angular/core';
import { ProjectEquipmentService } from '../../services/project-equipments.service';
import { ProjectEquipmentModel } from '../../models/project-equipments';
import { ProjectMemberModel } from '../../models/project-members';
import { ProjectMemberService } from '../../services/project-members.service';
@Component({
selector: 'app-home-installer',
......@@ -42,8 +50,15 @@ export class HomeInstallerComponent {
@ViewChild('modalDetail') public modalDetail?: ElementRef;
action = "new";
rawData: any[];
summary: Summary[] = [];
totalBorrowedChart: number = 0;
totalReturnedChart: number = 0;
userCount = 0;
projectCount = 0;
equipmentCount = 0;
allSelected = false;
someSelected = false;
itemSelection: { [key: string]: boolean } = {};
startDate: string = '';
endDate: string = '';
selectedProjects: string[] = [];
......@@ -54,8 +69,12 @@ export class HomeInstallerComponent {
dashbordList: string[] = [];
equipmentList: EquipmentModel[] = []
projectList: ProjectModel[] = []
itemsList: UserProfileModel[] = []
filterList: UserProfileModel[] = []
itemsList: ProjectEquipmentModel[] = []
itemsList1: ProjectMemberModel[] = []
itemsList2: ProjectModel[] = []
filterList2: ProjectModel[] = []
filterList: ProjectEquipmentModel[] = []
filterList1: ProjectMemberModel[] = []
selectModel: UserProfileModel = new UserProfileModel()
selectedItems = new Map<string, boolean>();
roleList: UserRoleModel[] = []
......@@ -268,7 +287,7 @@ export class HomeInstallerComponent {
},
};
projectId = ""
constructor(private el: ElementRef, private renderer: Renderer2, private cdr: ChangeDetectorRef, private userService: UserService, public translate: TranslateService, private tokenService: TokenService, private projectService: ProjectService, private EquipmentService: EquipmentService, private DashboardService: DashboardService, private route: ActivatedRoute,private comService: ProjectService) {
constructor(private projectMemberService: ProjectMemberService,private projectEquipmentService: ProjectEquipmentService,private el: ElementRef, private renderer: Renderer2, private cdr: ChangeDetectorRef, private userService: UserService, public translate: TranslateService, private tokenService: TokenService, private projectService: ProjectService, private EquipmentService: EquipmentService, private DashboardService: DashboardService, private route: ActivatedRoute,private comService: ProjectService) {
this.projectId = this.route.snapshot.paramMap.get('projectId')!;
this.projectService.getById(this.projectId).subscribe(result => {
console.log("SAVEEE")
......@@ -330,6 +349,14 @@ export class HomeInstallerComponent {
};
}
formatDateToYYYYMMDD(date: Date): string {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // เพิ่ม 1 เพราะเดือนเริ่มที่ 0
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
onProjectCheckboxChange(event: any) {
const value = event.target.value;
......@@ -339,63 +366,215 @@ export class HomeInstallerComponent {
this.selectedProjects = this.selectedProjects.filter(p => p !== value);
}
}
toggleDropdown() {
this.isDropdownOpen = !this.isDropdownOpen;
}
getdasbord() {
this.DashboardService.getDashboard(this.startDate, this.endDate,)
.subscribe(data => {
console.log('Dashboard Data:', data);
});
getSelected(): string {
const selectedIds = Object.keys(this.itemSelection).filter(
id => this.itemSelection[id]
);
if (selectedIds.length === 0) return 'เลือกอุปกรณ์';
if (selectedIds.length === this.equipmentList.length) return 'อุปกรณ์ทั้งหมด';
const selectedNames = this.equipmentList
.filter(equipmentList => selectedIds.includes(equipmentList.equipmentName))
.map(equipmentList => equipmentList.equipmentName);
return selectedNames.join(', ');
}
ngOnInit(): void {
this.userService.getLists().subscribe(result => {
this.itemsList = result
this.updatePagedItems()
})
this.projectService.getLists().subscribe(result => {
this.projectList = result
})
this.EquipmentService.getLists().subscribe(result => {
this.equipmentList = result
})
const today = new Date();
const firstDay = new Date(today.getFullYear(), today.getMonth(),1); // เดือน 0 = มกราคม
const firstDay = new Date(today.getFullYear(), 0, 1);
this.startDate = this.formatDateToYYYYMMDD(firstDay);
this.endDate = this.formatDateToYYYYMMDD(today);
this.getDashbord()
this.getProjectEquirment()
this.getUserProject()
this.getProject()
}
getDashbord() {
this.DashboardService.getDashboard(this.startDate, this.endDate).subscribe(result => {
this.dashboardModel = result
console.log(this.dashboardModel)
})
this.DashboardService.getDashboard(this.startDate, this.endDate, this.projectId).subscribe(result => {
this.dashboardModel = result;
this.mapSummary(this.dashboardModel.summary);
this.updateChart(this.dashboardModel);
this.updateDonut(this.dashboardModel.equipment_distribution);
this.mapStockChart(this.dashboardModel.equipment_distribution);
this.mapEquipmentChart(this.dashboardModel.equipment_distribution);
console.log(this.dashboardModel);
});
}
private updateDonut(data: Equipmentdistribution[]) {
// series = array of counts
this.optionsCircle1.series = data.map(item => item.qty_borrowed);
// labels = array of categories
this.optionsCircle1.labels = data.map(item => item.category);
}
formatDateToYYYYMMDD(date: Date): string {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // เพิ่ม 1 เพราะเดือนเริ่มที่ 0
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
private updateChart(data: DashboardModel) {
const trends = data.monthly_trends;
this.chartOptions9.series = [
{ name: 'จำนวนการยืม', data: trends.map(t => t.borrowed) },
{ name: 'จำนวนการคืน', data: trends.map(t => t.returned) },
];
this.chartOptions9.xaxis = {
...this.chartOptions9.xaxis,
categories: trends.map(t => t.period)
};
// ✅ คำนวณยอดรวมทั้งหมด
this.totalBorrowedChart = trends.map(t => t.borrowed).reduce((a, b) => a + b, 0);
this.totalReturnedChart = trends.map(t => t.returned).reduce((a, b) => a + b, 0);
}
private mapStockChart(dist: Equipmentdistribution[]) {
// data = quantity ของแต่ละอุปกรณ์
this.chartStockOptions.series[0].data = dist.map(x => x.count);
// labels = ชื่ออุปกรณ์
this.chartStockOptions.xaxis.categories = dist.map(x => x.category);
}
private mapEquipmentChart(dist: Equipmentdistribution[]) {
this.chartEquipmentOptions.series[0].data = dist.map(x => x.count);
this.chartEquipmentOptions.xaxis.categories = dist.map(x => x.category);
}
private mapSummary (summary: Summary[]) {
this.summary = summary;
const user = summary.find(x => x.label === 'จำนวนผู้ใช้งาน');
const project = summary.find(x => x.label === 'จำนวนโครงการ');
const equipment = summary.find(x => x.label === 'จำนวนอุปกรณ์');
this.userCount = user?.value ?? 0;
this.projectCount = project?.value ?? 0;
this.equipmentCount = equipment?.value ?? 0;
}
getProjectEquirment() {
this.projectEquipmentService.getLists(this.projectId).subscribe(result => {
this.itemsList = result
this.updatePagedItems()
})
}
filter(v: string) {
this.pageIndex = 0;
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.equipment.equipmentName.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.equipment.description?.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
getProject() {
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);
this.projectService.getLists().subscribe((allProjects: ProjectModel[]) => {
this.itemsList2 = allProjects.filter(p =>
p.projectId && allowedProjectIds.includes(p.projectId)
);
this.updatePagedItems2();
});
});
}
}
getUserProject() {
this.projectMemberService.getLists(this.projectId).subscribe(result => {
this.itemsList1 = result
this.updatePagedItems1()
})
}
updatePagedItems1() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList1 = this.itemsList1.slice(startIndex, endIndex);
}
filter1(v: string) {
return this.itemsList1?.filter(
(x) =>
x.member.memberId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.username?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.email?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.phoneNumber?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.getRole()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.getStatus()?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.member.getFullname()?.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
toggleAll(event: any) {
this.allSelected = event.target.checked;
this.selectedItems.clear();
this.itemsList1.forEach(item => {
this.selectedItems.set(item.memberId, this.allSelected);
});
this.someSelected = this.itemsList1.some(item => this.selectedItems.get(item.memberId));
}
onCheckboxChange(memberId: string) {
const isSelected = this.selectedItems.get(memberId) || false;
this.selectedItems.set(memberId, !isSelected);
this.allSelected = this.itemsList1.every(item => this.selectedItems.get(item.memberId));
this.someSelected = this.itemsList1.some(item => this.selectedItems.get(item.memberId));
}
updatePagedItems2() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList2 = this.itemsList2.slice(startIndex, endIndex);
}
filter2(v: string) {
return this.itemsList2?.filter(
(x) =>
x.projectId?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.project_name?.toLowerCase().indexOf(v.toLowerCase()) !== -1 ||
x.project_desc?.toLowerCase().indexOf(v.toLowerCase()) !== -1
);
}
toggleAll1(event: any) {
this.allSelected = event.target.checked;
this.selectedItems.clear();
this.itemsList2.forEach(item => {
this.selectedItems.set(item.projectId!, this.allSelected);
});
this.someSelected = this.itemsList2.some(item => this.selectedItems.get(item.projectId!));
}
onCheckboxChange1(projectId: string) {
const isSelected = this.selectedItems.get(projectId) || false;
this.selectedItems.set(projectId, !isSelected);
this.allSelected = this.itemsList2.every(item => this.selectedItems.get(item.projectId!));
this.someSelected = this.itemsList2.some(item => this.selectedItems.get(item.projectId!));
}
delete(item: UserProfileModel) {
swal({
......@@ -465,115 +644,101 @@ export class HomeInstallerComponent {
}
applyFilters() {
const selectedProjects = Object.keys(this.projectSelection).filter(p => this.projectSelection[p]);
// Filter your full data here
const filteredData = this.rawData.filter(item => {
return (!this.startDate || item.date >= this.startDate) &&
(!this.endDate || item.date <= this.endDate) &&
(selectedProjects.length === 0 || selectedProjects.includes(item.project_name)) &&
(this.selectedEquipmentType === 'ALL' || item.equipment_type === this.selectedEquipmentType);
});
}
updatePagedItems() {
const startIndex = this.pageIndex * 10;
const endIndex = startIndex + 10;
this.filterList = this.itemsList.slice(startIndex, endIndex);
}
toggleAll(event: any) {
this.allSelected = event.target.checked;
this.selectedItems.clear();
this.itemsList.forEach(item => {
this.selectedItems.set(item.memberId, this.allSelected);
});
this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.memberId));
}
onCheckboxChange(memberId: string) {
const isSelected = this.selectedItems.get(memberId) || false;
this.selectedItems.set(memberId, !isSelected);
this.allSelected = this.itemsList.every(item => this.selectedItems.get(item.memberId));
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();
});
}
}
});
}
});
}
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();
});
}
}
});
}
});
}
// this.filterList = this.itemsList.slice(startIndex, endIndex);
this.filterList = this.itemsList
}
// toggleAll(event: any) {
// this.allSelected = event.target.checked;
// this.selectedItems.clear();
// this.itemsList.forEach(item => {
// this.selectedItems.set(item.memberId, this.allSelected);
// });
// this.someSelected = this.itemsList.some(item => this.selectedItems.get(item.memberId));
// }
// onCheckboxChange(memberId: string) {
// const isSelected = this.selectedItems.get(memberId) || false;
// this.selectedItems.set(memberId, !isSelected);
// this.allSelected = this.itemsList.every(item => this.selectedItems.get(item.memberId));
// 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();
// });
// }
// }
// });
// }
// });
// }
// 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]
......
......@@ -6,9 +6,16 @@ export interface DashboardModel {
borrow_return_line: Monthlytrend[];
}
export interface Equipmentdistribution {
category: string;
count: number;
count: number; // จำนวนสต็อกในโครงการ
qty_borrowed: number; // ยอด "ชิ้น" ที่ถูกยืมสะสม
qty_returned: number; // ยอดชิ้นที่คืนสะสม
borrow_times: number; // จำนวน "ครั้ง" ที่ยืม
return_times: number; // จำนวน "ครั้ง" ที่คืน
percent_returned: number;
}
export interface Monthlytrend {
......
import { TranslateService } from "@ngx-translate/core";
import { BaseModel } from "./base.model";
export class NotificationModel {
notifId!: string; // UUID
projectId!: string; // UUID
equipmentId!: string; // UUID
message!: string;
isRead = false;
createdAt!: string; // ISO-string
constructor(init?: Partial<NotificationModel>) {
Object.assign(this, init);
// เผื่อใช้สร้าง object ฝั่ง client
}
}
// equipment-repair.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '../../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class EquipmentRepairService {
apiBaseUrl = "/equipments";
constructor(
private http: HttpClient
) { }
create(repairData: any) {
return this.http.post<EquipmentRepairModel>(this.apiBaseUrl, repairData);
}
update(id: number, repairData: any) {
return this.http.put(`${this.apiBaseUrl}/${id}`, repairData);
}
getList() {
return this.http.get<EquipmentRepairModel[]>(this.apiBaseUrl);
}
}
import { TranslateService } from '@ngx-translate/core';
import { PageResponseModel } from './../models/base.model';
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { map, switchMap, forkJoin, Observable, catchError, of } from 'rxjs';
import { environment } from '../../../environments/environment';
import { NotificationModel } from './../models/notification.model';
import { PositionModel } from '../models/position.model';
import { ResponseModel } from './../models/base.model';
@Injectable({ providedIn: 'root' })
export class NotificationService {
apiBaseUrl = "notifications/notifications"
;
constructor(
private http: HttpClient,
private translateService: TranslateService
) { }
/** ดึงตาม id */
getById(id: string) {
return this.http
.get<NotificationModel>(`${this.apiBaseUrl}/${id}`)
.pipe(map(n => new NotificationModel(n)));
}
/** ดึงทั้งหมด หรือกรอง project_id */
getList(projectId?: string) {
// ... ใช้ this.apiBaseUrl ตรง ๆ
return this.http.get<NotificationModel[]>(this.apiBaseUrl,)
.pipe(map(arr => arr.map(n => new NotificationModel(n))));
}
/** ดึงแบบแบ่งหน้า */
getPage(
body: { page: number; size: number },
projectId?: string
): Observable<PageResponseModel<NotificationModel>> {
let params: any = { ...body };
if (projectId) params.projectId = projectId;
return this.http
.get<PageResponseModel<NotificationModel>>(this.apiBaseUrl, { params })
.pipe(
map(p => ({
...p,
content: p.content.map(n => new NotificationModel(n)),
}))
);
}
/** ดึงทุกหน้า (lazy-load ทุกหน้าเหมือนตัวอย่าง UserService) */
getAllPages(projectId?: string) {
const first$ = this.getPage({ page: 0, size: 1 }, projectId);
return first$.pipe(
switchMap(first => {
const size = 500;
const pages = Math.ceil(first.totalElements / size);
const calls: Observable<PageResponseModel<NotificationModel>>[] = [];
for (let page = 0; page < pages; page++) {
calls.push(this.getPage({ page, size }, projectId));
}
return forkJoin(calls).pipe(
map(res => res.flatMap(r => r.content))
);
})
);
}
/** ทำเครื่องหมายอ่านแล้ว */
markRead(id: string) {
return this.http.put<ResponseModel>(
`${this.apiBaseUrl}/${id}/mark-read`,
{}
);
}
/** นับจำนวนยังไม่อ่าน (optionally by project) */
getUnreadCount(projectId?: string): Observable<number> {
let params = new HttpParams();
if (projectId) {
params = params.append('projectId', projectId);
}
return this.http.get<number>(`${this.apiBaseUrl}/unread/count`, { params }).pipe(
catchError(error => {
console.error('Error fetching unread count:', error);
return of(0); // คืนค่า 0 หากเกิด error
})
);
}
/** ลบ (ถ้าจำเป็น) */
delete(id: string) {
return this.http.delete<ResponseModel>(`${this.apiBaseUrl}/${id}`);
}
}
......@@ -46,4 +46,5 @@ export class ProjectEquipmentService {
}
}
<div
style="background-color: rgb(174, 183, 165); background-repeat: no-repeat; background-size: cover; background-attachment: fixed;"
style="background-color: rgb(150, 150, 150); background-repeat: no-repeat; background-size: cover; background-attachment: fixed;"
class="flex justify-center authentication authentication-basic items-center h-full text-defaultsize text-defaulttextcolor">
<div class="grid grid-cols-12">
<div class="xxl:col-span-4 xl:col-span-4 lg:col-span-4 md:col-span-3 sm:col-span-2"></div>
......
......@@ -60,9 +60,9 @@
<span class="flex absolute h-5 w-5 -top-[0.25rem] end-0 -me-[0.6rem]">
<span
class="animate-slow-ping absolute inline-flex -top-[2px] -start-[2px] h-full w-full rounded-full bg-secondary/40 opacity-75"></span>
<span
class="relative inline-flex justify-center items-center rounded-full h-[14.7px] w-[14px] bg-secondary text-[0.625rem] text-white"
id="notification-icon-badge">{{ notificationCount }}</span>
<span *ngIf="notificationCount > 0" class="badge bg-danger rounded-pill">
{{ notificationCount }}
</span>
</span>
</button>
<div
......@@ -104,107 +104,6 @@
</div>
</div>
</li>
<li class="ti-dropdown-item dropdown-item !flex-none" id="not2">
<div class="flex items-start">
<div class="pe-2">
<span
class="inline-flex text-secondary justify-center items-center !w-[2.5rem] !h-[2.5rem] !leading-[2.5rem] !text-[0.8rem] bg-secondary/10 rounded-[50%]"><i
class="ti ti-discount-2 text-[1.125rem]"></i></span>
</div>
<div class="grow flex items-center justify-between">
<div>
<p
class="mb-0 text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50 text-[0.8125rem] font-semibold">
<a routerLink="/pages/notifications">Discount Available</a></p>
<span
class="text-[#8c9097] dark:text-white/50 font-normal text-[0.75rem] header-notification-text">Discount
Available On Selected Products</span>
</div>
<div>
<a aria-label="anchor" (click)="removeNotify('not2')"
class="min-w-fit text-[#8c9097] dark:text-white/50 me-1 dropdown-item-close1"><i
class="ti ti-x text-[1rem]"></i></a>
</div>
</div>
</div>
</li>
<li class="ti-dropdown-item dropdown-item" id="not3">
<div class="flex items-start">
<div class="pe-2">
<span
class="inline-flex text-pink justify-center items-center !w-[2.5rem] !h-[2.5rem] !leading-[2.5rem] !text-[0.8rem] bg-pink/10 rounded-[50%]"><i
class="ti ti-user-check text-[1.125rem]"></i></span>
</div>
<div class="grow flex items-center justify-between">
<div>
<p
class="mb-0 text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50 text-[0.8125rem] font-semibold">
<a routerLink="/pages/notifications">Account Has Been Verified</a></p>
<span
class="text-[#8c9097] dark:text-white/50 font-normal text-[0.75rem] header-notification-text">Your
Account Has
Been Verified Sucessfully</span>
</div>
<div>
<a aria-label="anchor" (click)="removeNotify('not3')"
class="min-w-fit text-[#8c9097] dark:text-white/50 me-1 dropdown-item-close1"><i
class="ti ti-x text-[1rem]"></i></a>
</div>
</div>
</div>
</li>
<li class="ti-dropdown-item dropdown-item" id="not4">
<div class="flex items-start">
<div class="pe-2">
<span
class="inline-flex text-warning justify-center items-center !w-[2.5rem] !h-[2.5rem] !leading-[2.5rem] !text-[0.8rem] bg-warning/10 rounded-[50%]"><i
class="ti ti-circle-check text-[1.125rem]"></i></span>
</div>
<div class="grow flex items-center justify-between">
<div>
<p
class="mb-0 text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50 text-[0.8125rem] font-semibold">
<a routerLink="/pages/notifications">Order Placed <span class="text-warning">ID:
#1116773</span></a></p>
<span
class="text-[#8c9097] dark:text-white/50 font-normal text-[0.75rem] header-notification-text">Order
Placed
Successfully</span>
</div>
<div>
<a aria-label="anchor" (click)="removeNotify('not4')"
class="min-w-fit text-[#8c9097] dark:text-white/50 me-1 dropdown-item-close1"><i
class="ti ti-x text-[1rem]"></i></a>
</div>
</div>
</div>
</li>
<li class="ti-dropdown-item dropdown-item" id="not5">
<div class="flex items-start">
<div class="pe-2">
<span
class="inline-flex text-success justify-center items-center !w-[2.5rem] !h-[2.5rem] !leading-[2.5rem] !text-[0.8rem] bg-success/10 rounded-[50%]"><i
class="ti ti-clock text-[1.125rem]"></i></span>
</div>
<div class="grow flex items-center justify-between">
<div>
<p
class="mb-0 text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50 text-[0.8125rem] font-semibold">
<a routerLink="/pages/notifications">Order Delayed <span class="text-success">ID:
7731116</span></a></p>
<span
class="text-[#8c9097] dark:text-white/50 font-normal text-[0.75rem] header-notification-text">Order
Delayed
Unfortunately</span>
</div>
<div>
<a aria-label="anchor" (click)="removeNotify('not5')"
class="min-w-fit text-[#8c9097] dark:text-white/50 me-1 dropdown-item-close1"><i
class="ti ti-x text-[1rem]"></i></a>
</div>
</div>
</div>
</li>
</ul>
@if(!isNotifyEmpty){
......
......@@ -4,6 +4,10 @@ import { TokenService } from '../../services/token.service';
import { TranslateService } from '@ngx-translate/core';
import { UserModel } from '../../user-auth.model';
import { UserProfileModel } from '../../../DPU/models/user.model';
import { NotificationService } from '../../../DPU/services/notification.service';
import { NotificationModel } from '../../../DPU/models/notification.model';
import { interval, Subject, takeUntil } from 'rxjs';
interface Item {
id: number;
name: string;
......@@ -20,14 +24,16 @@ interface Item {
export class HeaderComponent {
userModel : UserProfileModel = new UserProfileModel(this.tokenService.getUser().member,this.translate)
adminRole = false
notificationCount = 0;
private destroy$ = new Subject<void>();
cartItemCount: number = 5;
notificationCount: number = 5;
public isCollapsed = true;
public selectedLanguage = "";
constructor(public navServices: NavService,
constructor(private notificationService: NotificationService,public navServices: NavService,
private elementRef: ElementRef, private renderer: Renderer2,
private tokenService: TokenService,
private translate: TranslateService) {
private translate: TranslateService,
private notificationSvc: NotificationService ) {
this.translate.use(
sessionStorage.getItem("Lang") != null
......@@ -149,6 +155,43 @@ export class HeaderComponent {
this.navServices.items.subscribe((menuItems) => {
this.items = menuItems;
});
this.adminRole = this.tokenService.getUser().member.role === 99;
this.navServices.items.subscribe(m => (this.items = m));
this.loadUnreadCount();
this.setupAutoRefresh();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
// โหลดจำนวนแจ้งเตือนที่ยังไม่อ่าน
loadUnreadCount(): void {
this.notificationService.getUnreadCount().subscribe(
count => this.notificationCount = count
);
}
// รีเฟรชอัตโนมัติทุก 1 นาที
private setupAutoRefresh(): void {
interval(60000).pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.loadUnreadCount();
});
}
// เมื่อผู้ใช้เปิดอ่านการแจ้งเตือน
markAsRead(notificationId: string): void {
this.notificationService.markRead(notificationId).subscribe({
next: () => {
if (this.notificationCount > 0) {
this.notificationCount--;
}
},
error: (err) => console.error('Failed to mark as read', err)
});
}
Search(searchText: string) {
......
<aside class="app-sidebar" id="sidebar" [ngClass]="{ 'sticky-pin': scrolled }">
<!-- Start::main-sidebar-header -->
<div class="main-sidebar-header" style="background-color: white !important;">
<div class="main-sidebar-header " style="background-color: rgb(237, 237, 237) " >
<a class="header-logo">
<!-- <img src="./assets/images/brand-logos/logo.png" alt="logo" width="100%">test -->
......@@ -12,10 +12,10 @@
</div>
<div>
<ngx-simplebar [options]="options" class="main-sidebar" id="sidebar-scroll">
<nav class="main-menu-container nav nav-pills flex-column sub-open">
<nav class="main-menu-container nav nav-pills flex-column sub-open" >
<div class="slide-left" id="slide-left">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8191" width="24" height="24" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8" width="24" height="24" viewBox="0 0 24 24">
<path d="M13.293 6.293 7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z"></path>
</svg>
</div>
......
......@@ -29,9 +29,9 @@
"Status": "สถานะ",
"Gender": "เพศ",
"Admin-System" : "ผู้ดูแลระบบ",
"Admin-Company" : "ผู้ดูแลบริษัท",
"Admin-Company" : "ผู้จัดการโครงการ",
"Amount":"จำนวน",
"User": "ผู้ใช้งาน",
"User": "พนักงานบริษัท",
"User Group": "กลุ่มผู้ใช้งาน",
"User Role": "บทบาทผู้ใช้งาน",
"User Setting": "กำหนดผู้ใช้งาน",
......
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