diff --git a/public/js/app.js b/public/js/app.js index dba7a07..45b581c 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -26,6 +26,8 @@ class AccountManager { this.assetPageSize = 10; this.assetBorrowPage = 1; this.assetBorrowPageSize = 10; + this.myBorrowedAssetPage = 1; + this.myBorrowedAssetPageSize = 10; this.apiBase = '/api'; this.currentPage = 'dashboard'; this.accountSearchTerm = ''; @@ -37,6 +39,7 @@ class AccountManager { this.assetStatusFilter = ''; this.assetBorrows = []; this.assetBorrowSearchTerm = ''; + this.myBorrowedAssetSearchTerm = ''; this.assetBorrowProductSearchTimer = undefined; this.assetBorrowProductItems = []; this.assetBorrowProductQuery = ''; @@ -234,6 +237,9 @@ class AccountManager { mainContent.innerHTML = this.getAssetBorrowsContent(); this.setupAssetBorrowListeners(); this.setupAddButtonListeners(); + } else if (page === 'my-borrowed-assets') { + mainContent.innerHTML = this.getMyBorrowedAssetsContent(); + this.setupMyBorrowedAssetsListeners(); } else if (page === 'asset-departments') { mainContent.innerHTML = this.getAssetDepartmentsContent(); this.setupAssetDepartmentListeners(); @@ -1081,6 +1087,7 @@ class AccountManager { const appSearch = document.getElementById('appSearch'); const assetSearch = document.getElementById('assetSearch'); const assetBorrowSearch = document.getElementById('assetBorrowSearch'); + const myBorrowedAssetSearch = document.getElementById('myBorrowedAssetSearch'); const assetDepartmentSearch = document.getElementById('assetDepartmentSearch'); const assetProjectSearch = document.getElementById('assetProjectSearch'); @@ -1108,6 +1115,12 @@ class AccountManager { assetBorrowSearch.setSelectionRange(pos, pos); } + if (myBorrowedAssetSearch && myBorrowedAssetSearch.dataset.focused === 'true') { + const pos = myBorrowedAssetSearch.selectionStart || myBorrowedAssetSearch.value.length; + myBorrowedAssetSearch.focus(); + myBorrowedAssetSearch.setSelectionRange(pos, pos); + } + if (assetDepartmentSearch && assetDepartmentSearch.dataset.focused === 'true') { const pos = assetDepartmentSearch.selectionStart || assetDepartmentSearch.value.length; assetDepartmentSearch.focus(); @@ -1358,6 +1371,104 @@ class AccountManager { }); } + normalizeNameForMatching(value) { + const normalized = String(value || '').trim().toLowerCase(); + if (!normalized) { + return ''; + } + + return normalized + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/\s+/g, ' '); + } + + getCurrentUserBorrowerNameKeys() { + const candidates = [ + this.getCurrentUserDisplayName(), + this.currentUser?.FullName, + this.currentUser?.fullname, + this.currentUser?.user?.FullName, + this.currentUser?.user?.fullname, + this.currentUser?.Username, + this.currentUser?.username, + this.currentUser?.user?.Username, + this.currentUser?.user?.username + ]; + + const keys = new Set(); + candidates.forEach(item => { + const key = this.normalizeNameForMatching(item); + if (key) { + keys.add(key); + } + }); + + return [...keys]; + } + + getCurrentUserBorrowedAssets() { + const borrowerKeys = new Set(this.getCurrentUserBorrowerNameKeys()); + if (!borrowerKeys.size) { + return []; + } + + return this.assets + .map(asset => { + const borrowerEntries = this.parseBorrowerEntries(asset?.Borrower); + const matchedEntries = borrowerEntries.filter(entry => { + const key = this.normalizeNameForMatching(entry?.name); + return key && borrowerKeys.has(key); + }); + + const borrowedQuantity = matchedEntries.reduce((sum, entry) => ( + sum + this.parseNonNegativeInteger(entry?.quantity, 0) + ), 0); + + if (borrowedQuantity <= 0) { + return null; + } + + return { + ...asset, + BorrowedQuantityByCurrentUser: borrowedQuantity, + BorrowedNamesByCurrentUser: matchedEntries + .map(entry => this.formatBorrowerDisplay(entry?.name, entry?.quantity)) + .filter(Boolean) + .join('; ') + }; + }) + .filter(Boolean); + } + + getFilteredMyBorrowedAssets() { + const search = String(this.myBorrowedAssetSearchTerm || '').toLowerCase(); + const rows = this.getCurrentUserBorrowedAssets(); + + if (!search) { + return rows; + } + + return rows.filter(item => { + const haystack = [ + item.AssetCode, + item.AssetName, + item.Model, + item.SerialNumber, + item.Project, + item.Department, + item.Location, + item.Unit, + item.Status, + item.BorrowedQuantityByCurrentUser, + item.BorrowedNamesByCurrentUser, + item.Notes + ].map(value => String(value || '').toLowerCase()); + + return haystack.some(value => value.includes(search)); + }); + } + getFilteredAssetBorrows() { const search = String(this.assetBorrowSearchTerm || '').toLowerCase(); const rows = Array.isArray(this.assetBorrows) ? this.assetBorrows : []; @@ -2895,6 +3006,177 @@ class AccountManager { `; } + renderMyBorrowedAssetsPager(pageInfo) { + const pager = document.getElementById('myBorrowedAssetsPager'); + if (!pager) { + return; + } + + pager.innerHTML = ` + Hiển thị ${pageInfo.start}-${pageInfo.end} / ${pageInfo.total} +
Danh sách tài sản bạn đang mượn theo tài khoản đăng nhập.
+| STT | +Mã tài sản | +Tên tài sản | +Số lượng đang mượn | +Đơn vị | +Dự án | +Vị trí | +Trạng thái | +Ghi chú | +
|---|---|---|---|---|---|---|---|---|
| ${pageInfo.start + index} | +${this.escapeHtml(asset.AssetCode || '-')} | +${this.escapeHtml(asset.AssetName || '-')} | +${Number(asset.BorrowedQuantityByCurrentUser) || 0} | +${this.escapeHtml(asset.Unit || '-')} | +${this.escapeHtml(asset.Project || '-')} | +${this.escapeHtml(asset.Location || '-')} | ++ ${statusMeta.label} + | +${this.escapeHtml(asset.Notes || '-')} | +
| Hiện tại bạn chưa mượn tài sản nào. | +||||||||