From f49d5a427c198d4f2a5c0289bb4021cb5018c0d0 Mon Sep 17 00:00:00 2001 From: DungTT Date: Thu, 7 May 2026 09:44:39 +0700 Subject: [PATCH] =?UTF-8?q?t=C3=A0i=20s=E1=BA=A3n=20m=C6=B0=E1=BB=A3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/app.js | 286 +++++++++++++++++++++++++++++++++++++++- public/pages/index.html | 4 + 2 files changed, 288 insertions(+), 2 deletions(-) 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} +
+ + Trang ${pageInfo.current} / ${pageInfo.totalPages} + +
+ `; + } + + getMyBorrowedAssetsContent() { + const filteredAssets = this.getFilteredMyBorrowedAssets(); + const pageInfo = this.getPaged(filteredAssets, this.myBorrowedAssetPage, this.myBorrowedAssetPageSize); + this.myBorrowedAssetPage = pageInfo.current; + + return ` +
+ + +
+
+ Tìm kiếm + +
+
+ +
+
+ + + + + + + + + + + + + + + + ${pageInfo.data.length > 0 ? pageInfo.data.map((asset, index) => { + const statusMeta = this.getAssetStatusMeta(asset.Status); + return ` + + + + + + + + + + + + `; + }).join('') : ` + + + + `} + +
STTMã tài sảnTên tài sảnSố lượng đang mượnĐơn vịDự ánVị tríTrạng tháiGhi 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.
+
+
+ Hiển thị ${pageInfo.start}-${pageInfo.end} / ${pageInfo.total} +
+ + Trang ${pageInfo.current} / ${pageInfo.totalPages} + +
+
+
+
+ `; + } + + renderMyBorrowedAssetsTableBody() { + const tbody = document.querySelector('.my-borrowed-assets-table-body'); + if (!tbody) { + return; + } + + const pageInfo = this.getPaged(this.getFilteredMyBorrowedAssets(), this.myBorrowedAssetPage, this.myBorrowedAssetPageSize); + this.myBorrowedAssetPage = pageInfo.current; + + if (!pageInfo.data.length) { + tbody.innerHTML = ` + + Hiện tại bạn chưa mượn tài sản nào. + + `; + } else { + tbody.innerHTML = pageInfo.data.map((asset, index) => { + const statusMeta = this.getAssetStatusMeta(asset.Status); + return ` + + ${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 || '-')} + + `; + }).join(''); + } + + this.renderMyBorrowedAssetsPager(pageInfo); + this.setupMyBorrowedAssetsPagerListeners(); + } + + setupMyBorrowedAssetsPagerListeners() { + document.querySelectorAll('.my-borrowed-asset-page-btn').forEach(btn => { + btn.addEventListener('click', () => { + const targetPage = Number(btn.dataset.page); + if (!targetPage || targetPage < 1) { + return; + } + + this.myBorrowedAssetPage = targetPage; + this.renderMyBorrowedAssetsTableBody(); + }); + }); + } + + setupMyBorrowedAssetsListeners() { + const searchInput = document.getElementById('myBorrowedAssetSearch'); + if (searchInput && searchInput.dataset.boundInput !== 'true') { + searchInput.addEventListener('input', event => { + this.myBorrowedAssetSearchTerm = String(event.target.value || '').trim(); + this.myBorrowedAssetPage = 1; + this.renderMyBorrowedAssetsTableBody(); + }); + searchInput.addEventListener('focus', () => { + searchInput.dataset.focused = 'true'; + }); + searchInput.addEventListener('blur', () => { + searchInput.dataset.focused = 'false'; + }); + searchInput.dataset.boundInput = 'true'; + } + + this.setupMyBorrowedAssetsPagerListeners(); + } + getAssetBorrowsContent() { const canManageAssets = this.canCurrentUserManageAssets(); const filteredBorrows = this.getFilteredAssetBorrows(); @@ -4592,8 +4874,8 @@ class AccountManager { await this.fetchAssets(); await this.fetchAssetDepartments(); await this.fetchAssetProjects(); - if (this.currentPage === 'assets') { - this.renderView('assets'); + if (this.currentPage === 'assets' || this.currentPage === 'my-borrowed-assets') { + this.renderView(this.currentPage); } } diff --git a/public/pages/index.html b/public/pages/index.html index 713d17d..4541e73 100644 --- a/public/pages/index.html +++ b/public/pages/index.html @@ -244,6 +244,10 @@ assignment_returned Mượn/Trả tài sản + + inventory + Tài sản đang mượn + apartment Phòng Ban