fix mượn trả
This commit is contained in:
@@ -3230,42 +3230,67 @@ app.post('/api/asset-borrows/:id/process', requireAssetOrAdmin, async (req, res)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/asset-borrows/:id', requireAssetOrAdmin, async (req, res) => {
|
app.delete('/api/asset-borrows/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
const requesterRole = normalizeRole(req.headers['x-user-role'] || req.query.userRole);
|
||||||
|
const requesterId = getUserIdFromRequest(req);
|
||||||
|
const canManageRequests = requesterRole === 'admin' || requesterRole === 'asset';
|
||||||
const borrowId = Number(req.params.id);
|
const borrowId = Number(req.params.id);
|
||||||
|
|
||||||
if (!Number.isInteger(borrowId) || borrowId <= 0) {
|
if (!Number.isInteger(borrowId) || borrowId <= 0) {
|
||||||
return res.status(400).json({ success: false, message: 'Mã đơn không hợp lệ' });
|
return res.status(400).json({ success: false, message: 'Ma don khong hop le' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canManageRequests && (!Number.isInteger(requesterId) || requesterId <= 0)) {
|
||||||
|
return res.status(401).json({ success: false, message: 'Yeu cau xac thuc nguoi dung' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteResult = await pool.request()
|
const deleteResult = await pool.request()
|
||||||
.input('borrowId', sql.Int, borrowId)
|
.input('borrowId', sql.Int, borrowId)
|
||||||
|
.input('requesterId', sql.Int, requesterId || -1)
|
||||||
.query(`
|
.query(`
|
||||||
DELETE FROM AssetBorrowRequests
|
DELETE FROM AssetBorrowRequests
|
||||||
OUTPUT DELETED.BorrowId
|
OUTPUT DELETED.BorrowId
|
||||||
WHERE BorrowId = @borrowId
|
WHERE BorrowId = @borrowId
|
||||||
AND LOWER(LTRIM(RTRIM(ISNULL(RequestStatus, '')))) = 'pending'
|
AND LOWER(LTRIM(RTRIM(ISNULL(RequestStatus, '')))) = 'pending'
|
||||||
|
${canManageRequests ? '' : 'AND CreatedBy = @requesterId'}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
if (Array.isArray(deleteResult.recordset) && deleteResult.recordset.length > 0) {
|
if (Array.isArray(deleteResult.recordset) && deleteResult.recordset.length > 0) {
|
||||||
return res.json({ success: true, message: 'Đã xóa đơn chờ' });
|
return res.json({ success: true, message: 'Da huy don cho' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const existed = await pool.request()
|
const existed = await pool.request()
|
||||||
.input('borrowId', sql.Int, borrowId)
|
.input('borrowId', sql.Int, borrowId)
|
||||||
.query(`
|
.query(`
|
||||||
SELECT TOP 1 BorrowId, RequestStatus
|
SELECT TOP 1 BorrowId, RequestStatus, CreatedBy
|
||||||
FROM AssetBorrowRequests
|
FROM AssetBorrowRequests
|
||||||
WHERE BorrowId = @borrowId
|
WHERE BorrowId = @borrowId
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const row = existed.recordset?.[0];
|
const row = existed.recordset?.[0];
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return res.status(404).json({ success: false, message: 'Không tìm thấy đơn cần xóa' });
|
return res.status(404).json({ success: false, message: 'Khong tim thay don can xoa' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canManageRequests && Number(row.CreatedBy) !== requesterId) {
|
||||||
|
return res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Ban chi duoc huy don do chinh minh tao'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentStatus = normalizeAssetRequestStatus(row.RequestStatus);
|
||||||
|
if (currentStatus !== 'pending') {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Chi duoc huy don o trang thai cho xu ly'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Chỉ được xóa đơn ở trạng thái chờ xử lý'
|
message: 'Khong the huy don vao luc nay'
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res.status(500).json({ success: false, message: err.message });
|
return res.status(500).json({ success: false, message: err.message });
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
165
public/js/app.js
165
public/js/app.js
@@ -59,6 +59,7 @@ class AccountManager {
|
|||||||
this.assetBorrowRequestType = 'borrow';
|
this.assetBorrowRequestType = 'borrow';
|
||||||
this.pendingAssetRequestRejectId = undefined;
|
this.pendingAssetRequestRejectId = undefined;
|
||||||
this.assetBorrowAutoRefreshTimer = undefined;
|
this.assetBorrowAutoRefreshTimer = undefined;
|
||||||
|
this.pendingAssetRequestDeleteConfirmResolver = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
configureNotifications() {
|
configureNotifications() {
|
||||||
@@ -1008,6 +1009,21 @@ class AccountManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const confirmAssetRequestDeleteBtn = document.getElementById('confirmAssetRequestDeleteBtn');
|
||||||
|
if (confirmAssetRequestDeleteBtn && confirmAssetRequestDeleteBtn.dataset.boundClick !== 'true') {
|
||||||
|
confirmAssetRequestDeleteBtn.addEventListener('click', () => this.resolveAssetRequestDeleteConfirm(true));
|
||||||
|
confirmAssetRequestDeleteBtn.dataset.boundClick = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.cancel-asset-request-delete-confirm').forEach(btn => {
|
||||||
|
if (btn.dataset.boundClick === 'true') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener('click', () => this.resolveAssetRequestDeleteConfirm(false));
|
||||||
|
btn.dataset.boundClick = 'true';
|
||||||
|
});
|
||||||
|
|
||||||
this.setupAssetBorrowRequestModalListeners();
|
this.setupAssetBorrowRequestModalListeners();
|
||||||
|
|
||||||
const assetDepartmentForm = document.getElementById('assetDepartmentForm');
|
const assetDepartmentForm = document.getElementById('assetDepartmentForm');
|
||||||
@@ -2151,6 +2167,11 @@ class AccountManager {
|
|||||||
const statusMeta = this.getAssetRequestStatusMeta(item.RequestStatus);
|
const statusMeta = this.getAssetRequestStatusMeta(item.RequestStatus);
|
||||||
const note = String(item?.RequestNote || '').trim();
|
const note = String(item?.RequestNote || '').trim();
|
||||||
const rejectReason = String(item?.RejectReason || '').trim();
|
const rejectReason = String(item?.RejectReason || '').trim();
|
||||||
|
const canCancel = this.canCurrentUserCancelAssetRequest(item);
|
||||||
|
const requestId = Number(item?.BorrowId) || 0;
|
||||||
|
const cancelActionHtml = canCancel
|
||||||
|
? `<button class="asset-borrow-cancel-btn px-3 py-1.5 rounded-md bg-red-600 hover:bg-red-700 text-white text-xs font-bold whitespace-nowrap" data-request-id="${requestId}">Hủy đơn</button>`
|
||||||
|
: `<span class="text-xs text-slate-400">-</span>`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr class="hover:bg-slate-50/80 transition-colors">
|
<tr class="hover:bg-slate-50/80 transition-colors">
|
||||||
@@ -2171,14 +2192,33 @@ class AccountManager {
|
|||||||
<td class="px-4 py-3 text-sm text-slate-600 whitespace-nowrap">${this.formatDateOnly(item.BorrowDate)}</td>
|
<td class="px-4 py-3 text-sm text-slate-600 whitespace-nowrap">${this.formatDateOnly(item.BorrowDate)}</td>
|
||||||
<td class="px-4 py-3 text-sm text-slate-600 max-w-xs">${this.escapeHtml(note || '-')}</td>
|
<td class="px-4 py-3 text-sm text-slate-600 max-w-xs">${this.escapeHtml(note || '-')}</td>
|
||||||
<td class="px-4 py-3 text-sm text-slate-600 max-w-xs">${this.escapeHtml(rejectReason || '-')}</td>
|
<td class="px-4 py-3 text-sm text-slate-600 max-w-xs">${this.escapeHtml(rejectReason || '-')}</td>
|
||||||
|
<td class="px-4 py-3 text-sm text-slate-600">${cancelActionHtml}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canCurrentUserCancelAssetRequest(item) {
|
||||||
|
const status = this.normalizeAssetRequestStatus(item?.RequestStatus);
|
||||||
|
if (status !== 'pending') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.canCurrentUserManageAssets()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUserId = Number(this.getUserId());
|
||||||
|
const createdBy = Number(item?.CreatedBy);
|
||||||
|
return Number.isFinite(currentUserId)
|
||||||
|
&& currentUserId > 0
|
||||||
|
&& Number.isFinite(createdBy)
|
||||||
|
&& createdBy === currentUserId;
|
||||||
|
}
|
||||||
|
|
||||||
buildAssetBorrowEmptyRowHtml() {
|
buildAssetBorrowEmptyRowHtml() {
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="10" class="px-4 py-8 text-sm text-center text-slate-500">Chưa có đơn mượn/trả tài sản nào.</td>
|
<td colspan="11" class="px-4 py-8 text-sm text-center text-slate-500">Chưa có đơn mượn/trả tài sản nào.</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -2235,7 +2275,7 @@ class AccountManager {
|
|||||||
>
|
>
|
||||||
<span class="material-symbols-outlined text-base">notifications_active</span>
|
<span class="material-symbols-outlined text-base">notifications_active</span>
|
||||||
Đơn chờ
|
Đơn chờ
|
||||||
<span id="pendingAssetBorrowsCountBadge" class="${pendingCount > 0 ? '' : 'hidden'} absolute -top-2.5 -right-2.5 min-w-[22px] h-[22px] px-1.5 rounded-full bg-red-600 text-white text-xs font-extrabold leading-[22px] text-center ring-2 ring-white">${pendingCount > 99 ? '99+' : pendingCount}</span>
|
<span id="pendingAssetBorrowsCountBadge" class="${pendingCount > 0 ? '' : 'hidden'} absolute -top-2.5 -right-2.5 min-w-[22px] h-[22px] px-1.5 rounded-[999px] bg-red-600 text-white text-xs font-extrabold leading-[22px] text-center ring-2 ring-white">${pendingCount > 99 ? '99+' : pendingCount}</span>
|
||||||
</button>
|
</button>
|
||||||
` : ''}
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
@@ -2268,6 +2308,7 @@ class AccountManager {
|
|||||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ngày</th>
|
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ngày</th>
|
||||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ghi chú</th>
|
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Ghi chú</th>
|
||||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Lý do</th>
|
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Lý do</th>
|
||||||
|
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500">Hành động</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-100 asset-borrows-table-body">
|
<tbody class="divide-y divide-slate-100 asset-borrows-table-body">
|
||||||
@@ -2339,6 +2380,30 @@ class AccountManager {
|
|||||||
searchInput.dataset.boundInput = 'true';
|
searchInput.dataset.boundInput = 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableBody = document.querySelector('.asset-borrows-table-body');
|
||||||
|
if (tableBody && tableBody.dataset.boundActions !== 'true') {
|
||||||
|
tableBody.addEventListener('click', (event) => {
|
||||||
|
const cancelButton = event.target.closest('.asset-borrow-cancel-btn');
|
||||||
|
if (!cancelButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = Number(cancelButton.dataset.requestId);
|
||||||
|
if (!Number.isFinite(requestId) || requestId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deletePendingAssetBorrowRequest(requestId, {
|
||||||
|
confirmMessage: `Bạn có chắc muốn hủy đơn #${requestId}?`,
|
||||||
|
confirmButtonText: 'Hủy đơn',
|
||||||
|
successMessage: 'Đã hủy đơn thành công',
|
||||||
|
failureMessage: 'Hủy đơn thất bại'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tableBody.dataset.boundActions = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
this.setupAssetBorrowPagerListeners();
|
this.setupAssetBorrowPagerListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2489,7 +2554,8 @@ class AccountManager {
|
|||||||
this.notifyFailure('Tạo đơn thất bại');
|
this.notifyFailure('Tạo đơn thất bại');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildPendingAssetRequestCardHtml(item) {
|
|
||||||
|
buildPendingAssetRequestCardHtml(item) {
|
||||||
const typeMeta = this.getAssetRequestTypeMeta(item?.RequestType);
|
const typeMeta = this.getAssetRequestTypeMeta(item?.RequestType);
|
||||||
const dateLabel = typeMeta.value === 'return' ? 'Ngày trả' : 'Ngày mượn';
|
const dateLabel = typeMeta.value === 'return' ? 'Ngày trả' : 'Ngày mượn';
|
||||||
const note = String(item?.RequestNote || '').trim();
|
const note = String(item?.RequestNote || '').trim();
|
||||||
@@ -2508,10 +2574,10 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
<div><span class="font-bold">Ghi chú:</span> ${this.escapeHtml(note || '-')}</div>
|
<div><span class="font-bold">Ghi chú:</span> ${this.escapeHtml(note || '-')}</div>
|
||||||
<div><span class="font-bold">${dateLabel}:</span> ${this.formatDateOnly(item?.BorrowDate)}</div>
|
<div><span class="font-bold">${dateLabel}:</span> ${this.formatDateOnly(item?.BorrowDate)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 pt-1">
|
<div class="flex items-center gap-2 pt-1">
|
||||||
<button class="asset-request-approve-btn w-full px-3 py-1.5 rounded-md bg-primary hover:bg-primary-dim text-on-primary text-xs font-bold" data-request-id="${requestId}">Chấp nhận</button>
|
<button class="asset-request-approve-btn flex-1 min-w-0 px-3 py-1.5 rounded-md bg-primary hover:bg-primary-dim text-on-primary text-xs font-bold whitespace-nowrap" data-request-id="${requestId}">Chấp nhận</button>
|
||||||
<button class="asset-request-reject-btn w-full px-3 py-1.5 rounded-md bg-red-600 hover:bg-red-700 text-white text-xs font-bold" data-request-id="${requestId}">Từ chối</button>
|
<button class="asset-request-reject-btn flex-1 min-w-0 px-3 py-1.5 rounded-md bg-red-600 hover:bg-red-700 text-white text-xs font-bold whitespace-nowrap" data-request-id="${requestId}">Từ chối</button>
|
||||||
<button class="asset-request-delete-btn w-full px-3 py-1.5 rounded-md bg-slate-700 hover:bg-slate-800 text-white text-xs font-bold" data-request-id="${requestId}">Xóa đơn</button>
|
<button class="asset-request-delete-btn flex-1 min-w-0 px-3 py-1.5 rounded-md bg-slate-700 hover:bg-slate-800 text-white text-xs font-bold whitespace-nowrap" data-request-id="${requestId}">Xóa đơn</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -2639,6 +2705,43 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
reasonInput.focus();
|
reasonInput.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveAssetRequestDeleteConfirm(confirmed) {
|
||||||
|
const modal = document.getElementById('assetRequestDeleteConfirmModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolver = this.pendingAssetRequestDeleteConfirmResolver;
|
||||||
|
this.pendingAssetRequestDeleteConfirmResolver = undefined;
|
||||||
|
if (typeof resolver === 'function') {
|
||||||
|
resolver(Boolean(confirmed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmAssetRequestDelete(message, confirmButtonText = 'Xóa đơn') {
|
||||||
|
const modal = document.getElementById('assetRequestDeleteConfirmModal');
|
||||||
|
const messageNode = document.getElementById('assetRequestDeleteConfirmMessage');
|
||||||
|
const confirmButton = document.getElementById('confirmAssetRequestDeleteBtn');
|
||||||
|
|
||||||
|
if (!modal || !messageNode || !confirmButton) {
|
||||||
|
return window.confirm(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageNode.textContent = String(message || 'Bạn có chắc muốn thực hiện thao tác này?');
|
||||||
|
confirmButton.textContent = String(confirmButtonText || 'Xóa đơn');
|
||||||
|
|
||||||
|
if (this.pendingAssetRequestDeleteConfirmResolver) {
|
||||||
|
this.resolveAssetRequestDeleteConfirm(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.style.zIndex = '140';
|
||||||
|
modal.classList.add('open');
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.pendingAssetRequestDeleteConfirmResolver = resolve;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async handleAssetRequestRejectSubmit(event) {
|
async handleAssetRequestRejectSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -2686,7 +2789,10 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
&& failureMessage.toLowerCase().includes('xóa đơn chờ');
|
&& failureMessage.toLowerCase().includes('xóa đơn chờ');
|
||||||
|
|
||||||
if (canAutoSuggestDelete) {
|
if (canAutoSuggestDelete) {
|
||||||
const shouldDelete = window.confirm(`Đơn #${requestId} không còn hợp lệ. Bạn có muốn xóa đơn chờ này không?`);
|
const shouldDelete = await this.confirmAssetRequestDelete(
|
||||||
|
`Đơn #${requestId} không còn hợp lệ. Bạn có muốn xóa đơn chờ này không?`,
|
||||||
|
'Xóa đơn'
|
||||||
|
);
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
await this.deletePendingAssetBorrowRequest(requestId);
|
await this.deletePendingAssetBorrowRequest(requestId);
|
||||||
}
|
}
|
||||||
@@ -2722,19 +2828,18 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePendingAssetBorrowRequest(requestId) {
|
async deletePendingAssetBorrowRequest(requestId, options = {}) {
|
||||||
if (!this.canCurrentUserManageAssets()) {
|
|
||||||
this.notifyWarning('Chỉ role Asset/Admin mới được xóa đơn chờ.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetId = Number(requestId);
|
const targetId = Number(requestId);
|
||||||
if (!Number.isFinite(targetId) || targetId <= 0) {
|
if (!Number.isFinite(targetId) || targetId <= 0) {
|
||||||
this.notifyWarning('Không xác định được đơn cần xóa.');
|
this.notifyWarning('Không xác định được đơn cần xóa.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = window.confirm(`Bạn có chắc muốn xóa đơn chờ #${targetId}?`);
|
const confirmMessage = String(options?.confirmMessage || `Bạn có chắc muốn xóa đơn chờ #${targetId}?`);
|
||||||
|
const confirmButtonText = String(options?.confirmButtonText || 'Xóa đơn');
|
||||||
|
const successMessage = String(options?.successMessage || 'Đã xóa đơn chờ');
|
||||||
|
const failureMessage = String(options?.failureMessage || 'Xóa đơn chờ thất bại');
|
||||||
|
const confirmed = await this.confirmAssetRequestDelete(confirmMessage, confirmButtonText);
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2747,11 +2852,16 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response.ok || !data.success) {
|
if (!response.ok || !data.success) {
|
||||||
this.notifyFailure(data.message || 'Xóa đơn chờ thất bại');
|
const resolvedFailureMessage = data.message || failureMessage;
|
||||||
|
if (response.status === 403) {
|
||||||
|
this.notifyWarning(resolvedFailureMessage);
|
||||||
|
} else {
|
||||||
|
this.notifyFailure(resolvedFailureMessage);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notifySuccess(data.message || 'Đã xóa đơn chờ');
|
this.notifySuccess(data.message || successMessage);
|
||||||
await this.fetchAssetBorrows();
|
await this.fetchAssetBorrows();
|
||||||
|
|
||||||
if (this.currentPage === 'asset-borrows') {
|
if (this.currentPage === 'asset-borrows') {
|
||||||
@@ -2766,7 +2876,7 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
this.updatePendingAssetRequestsBadge();
|
this.updatePendingAssetRequestsBadge();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.notifyFailure('Xóa đơn chờ thất bại');
|
this.notifyFailure(failureMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getAssetsContent() {
|
getAssetsContent() {
|
||||||
@@ -5279,6 +5389,9 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
closeModals() {
|
closeModals() {
|
||||||
|
if (this.pendingAssetRequestDeleteConfirmResolver) {
|
||||||
|
this.resolveAssetRequestDeleteConfirm(false);
|
||||||
|
}
|
||||||
document.querySelectorAll('.modal-backdrop').forEach(modal => {
|
document.querySelectorAll('.modal-backdrop').forEach(modal => {
|
||||||
modal.classList.remove('open');
|
modal.classList.remove('open');
|
||||||
});
|
});
|
||||||
@@ -6186,6 +6299,9 @@ buildPendingAssetRequestCardHtml(item) {
|
|||||||
|
|
||||||
// Global modal close functions
|
// Global modal close functions
|
||||||
function closeAllModals() {
|
function closeAllModals() {
|
||||||
|
if (app?.pendingAssetRequestDeleteConfirmResolver) {
|
||||||
|
app.resolveAssetRequestDeleteConfirm(false);
|
||||||
|
}
|
||||||
document.querySelectorAll('.modal-backdrop').forEach(modal => {
|
document.querySelectorAll('.modal-backdrop').forEach(modal => {
|
||||||
modal.classList.remove('open');
|
modal.classList.remove('open');
|
||||||
});
|
});
|
||||||
@@ -6250,6 +6366,7 @@ function closeAssetPendingRequestsModal() {
|
|||||||
if (modal) {
|
if (modal) {
|
||||||
modal.classList.remove('open');
|
modal.classList.remove('open');
|
||||||
}
|
}
|
||||||
|
closeAssetRequestDeleteConfirmModal();
|
||||||
const rejectModal = document.getElementById('assetRequestRejectModal');
|
const rejectModal = document.getElementById('assetRequestRejectModal');
|
||||||
if (rejectModal) {
|
if (rejectModal) {
|
||||||
rejectModal.classList.remove('open');
|
rejectModal.classList.remove('open');
|
||||||
@@ -6263,6 +6380,18 @@ function closeAssetRequestRejectModal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeAssetRequestDeleteConfirmModal() {
|
||||||
|
if (app?.pendingAssetRequestDeleteConfirmResolver) {
|
||||||
|
app.resolveAssetRequestDeleteConfirm(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = document.getElementById('assetRequestDeleteConfirmModal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('open');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function closeAssetDepartmentModal() {
|
function closeAssetDepartmentModal() {
|
||||||
const modal = document.getElementById('assetDepartmentModal');
|
const modal = document.getElementById('assetDepartmentModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
|
|||||||
@@ -454,6 +454,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Confirm Delete/Cancel Asset Request Modal -->
|
||||||
|
<div class="modal-backdrop fixed inset-0 z-[120] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetRequestDeleteConfirmModal" style="z-index: 140;">
|
||||||
|
<div class="modal-content w-full max-w-md bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
||||||
|
<div class="px-6 py-4 border-b border-slate-100 bg-red-50 flex items-center gap-3">
|
||||||
|
<span class="material-symbols-outlined text-red-600 text-2xl">warning</span>
|
||||||
|
<h3 class="text-base font-extrabold text-red-700">Xác nhận thao tác</h3>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<p id="assetRequestDeleteConfirmMessage" class="text-sm text-slate-600 mb-6">Bạn có chắc muốn thực hiện thao tác này?</p>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" class="cancel-asset-request-delete-confirm flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg">Đóng</button>
|
||||||
|
<button type="button" id="confirmAssetRequestDeleteBtn" class="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-xs font-bold">Xóa đơn</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Reject Asset Request Modal -->
|
<!-- Reject Asset Request Modal -->
|
||||||
<div class="modal-backdrop fixed inset-0 z-[110] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetRequestRejectModal" style="z-index: 130;">
|
<div class="modal-backdrop fixed inset-0 z-[110] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetRequestRejectModal" style="z-index: 130;">
|
||||||
<div class="modal-content w-full max-w-lg bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
<div class="modal-content w-full max-w-lg bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
||||||
|
|||||||
@@ -280,7 +280,7 @@
|
|||||||
<button id="pendingAssetRequestsBtn" type="button" class="hidden relative flex items-center gap-2 px-3 py-2 rounded-lg border border-amber-200 bg-amber-50 hover:bg-amber-100 text-amber-800 transition-colors" title="Đơn chờ xử lý">
|
<button id="pendingAssetRequestsBtn" type="button" class="hidden relative flex items-center gap-2 px-3 py-2 rounded-lg border border-amber-200 bg-amber-50 hover:bg-amber-100 text-amber-800 transition-colors" title="Đơn chờ xử lý">
|
||||||
<span class="material-symbols-outlined text-base">notifications_active</span>
|
<span class="material-symbols-outlined text-base">notifications_active</span>
|
||||||
<span class="text-xs font-bold">Đơn chờ</span>
|
<span class="text-xs font-bold">Đơn chờ</span>
|
||||||
<span id="pendingAssetRequestsBadge" class="hidden absolute -top-2.5 -right-2.5 min-w-[22px] h-[22px] px-1.5 rounded-full bg-red-600 text-white text-xs font-extrabold leading-[22px] text-center ring-2 ring-white">0</span>
|
<span id="pendingAssetRequestsBadge" class="hidden absolute -top-2.5 -right-2.5 min-w-[22px] h-[22px] px-1.5 rounded-[999px] bg-red-600 text-white text-xs font-extrabold leading-[22px] text-center ring-2 ring-white">0</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="profileBtn" type="button" class="profile-btn flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors" title="Sửa hồ sơ">
|
<button id="profileBtn" type="button" class="profile-btn flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors" title="Sửa hồ sơ">
|
||||||
<span class="material-symbols-outlined text-slate-600 dark:text-slate-400">account_circle</span>
|
<span class="material-symbols-outlined text-slate-600 dark:text-slate-400">account_circle</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user