From a40c1e0f18b299142ee545cb9885425a8672d41b Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Wed, 25 Mar 2026 08:15:33 +0900 Subject: [PATCH] =?UTF-8?q?feat(tkuser):=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B2=80=EC=83=89=20+=20=EB=B6=80?= =?UTF-8?q?=EC=84=9C=20=ED=95=84=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- user-management/web/index.html | 13 ++++- user-management/web/static/js/tkuser-users.js | 55 +++++++++++++++++-- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/user-management/web/index.html b/user-management/web/index.html index 4b37fa1..3ba0192 100644 --- a/user-management/web/index.html +++ b/user-management/web/index.html @@ -149,7 +149,16 @@
-

사용자 목록

+

+ 사용자 목록 + +

+
+ + +

로딩 중...

@@ -2351,7 +2360,7 @@ - + diff --git a/user-management/web/static/js/tkuser-users.js b/user-management/web/static/js/tkuser-users.js index 857da72..54b5792 100644 --- a/user-management/web/static/js/tkuser-users.js +++ b/user-management/web/static/js/tkuser-users.js @@ -112,7 +112,7 @@ async function loadUsers() { try { const r = await api('/users'); users = r.data || r; displayUsers(); updatePermissionUserSelect(); - populateUserDeptSelects(); + populateUserDeptSelects(); populateUserDeptFilter(); const hireDateInput = document.getElementById('newHireDate'); if (hireDateInput && !hireDateInput.value) hireDateInput.value = getSeoulToday(); } catch (err) { @@ -129,6 +129,15 @@ function populateUserDeptSelects() { sel.value = val; }); } +// 필터용 부서 셀렉트 (populateUserDeptSelects는 모달 폼용) +function populateUserDeptFilter() { + const sel = document.getElementById('userDeptFilter'); + if (!sel) return; + const val = sel.value; + sel.innerHTML = ''; + departmentsCache.forEach(d => { const o = document.createElement('option'); o.value = d.department_id; o.textContent = d.department_name; sel.appendChild(o); }); + sel.value = val; +} function renderUserRow(u, isResigned) { return `
@@ -151,12 +160,38 @@ function renderUserRow(u, isResigned) { } function displayUsers() { - const activeUsers = users.filter(u => u.is_active !== 0 && u.is_active !== false); - const resignedUsers = users.filter(u => u.is_active === 0 || u.is_active === false); + const searchTerm = (document.getElementById('userSearchInput')?.value || '').trim().toLowerCase(); + const deptFilterVal = document.getElementById('userDeptFilter')?.value || ''; + + let filtered = users; + + if (searchTerm) { + filtered = filtered.filter(u => { + const name = (u.name || '').toLowerCase(); + const username = (u.username || '').toLowerCase(); + return name.includes(searchTerm) || username.includes(searchTerm); + }); + } + + if (deptFilterVal) { + const deptId = parseInt(deptFilterVal); + filtered = filtered.filter(u => u.department_id === deptId); + } + + const activeUsers = filtered.filter(u => u.is_active !== 0 && u.is_active !== false); + const resignedUsers = filtered.filter(u => u.is_active === 0 || u.is_active === false); const c = document.getElementById('userList'); - if (!activeUsers.length) { c.innerHTML = '

등록된 사용자가 없습니다.

'; } - else { c.innerHTML = activeUsers.map(u => renderUserRow(u, false)).join(''); } + const countEl = document.getElementById('activeUserCount'); + if (countEl) countEl.textContent = `(${activeUsers.length}명)`; + + if (!activeUsers.length) { + c.innerHTML = searchTerm || deptFilterVal + ? '

검색 결과가 없습니다.

' + : '

등록된 사용자가 없습니다.

'; + } else { + c.innerHTML = activeUsers.map(u => renderUserRow(u, false)).join(''); + } const resignedSection = document.getElementById('resignedSection'); const resignedList = document.getElementById('resignedUserList'); @@ -419,6 +454,16 @@ function populateDeptPermSelect() { } document.addEventListener('DOMContentLoaded', () => { + // 사용자 검색 + 부서 필터 + let userSearchTimeout; + const userSearchEl = document.getElementById('userSearchInput'); + if (userSearchEl) userSearchEl.addEventListener('input', () => { + clearTimeout(userSearchTimeout); + userSearchTimeout = setTimeout(displayUsers, 300); + }); + const userDeptFilterEl = document.getElementById('userDeptFilter'); + if (userDeptFilterEl) userDeptFilterEl.addEventListener('change', displayUsers); + const sel = document.getElementById('deptPermSelect'); if (sel) sel.addEventListener('change', async e => { selectedDeptId = e.target.value;