import { vars } from './variable.js'; //설정창 close 25-08-18 권한 설정 모달 사용 중에 모달이 꺼지는일이 많아 주석처리로 꺼지는 기능을 막음 // document.querySelector('.permission-modal').addEventListener('click', (e) => { // if (e.target.className.includes('permission-modal')) document.querySelector('.permission-modal').style.display = 'none'; // }) document.querySelector('.permission-modal img[alt="닫기"]').addEventListener('click', () => { // 이름 검색 결과창 초기화 searchResultInit(); document.querySelector('.permission-modal').style.display = 'none'; }) //설정창 저장 document.getElementById('permission-submit').addEventListener('click', () => { if(vars.permissionList.changed && Object.keys(vars.permissionList.changed).length > 0){ if (confirm(`${Object.keys(vars.permissionList.changed).length}개의 변경사항을 저장하시겠습니까?`)) { upsertAuthList(); } } else { searchResultInit(); document.querySelector('.permission-modal').style.display = 'none'; } }); //설정창 취소 document.getElementById('permission-cancel').addEventListener('click', () => { if (vars.permissionList.changed && Object.keys(vars.permissionList.changed).length > 0) { if (!confirm(`${Object.keys(vars.permissionList.changed).length}개의 변경사항을 저장하지 않고 취소하시겠습니까?`)) { return; } } // 이름 검색 결과창 초기화 searchResultInit(); document.querySelector('.permission-modal').style.display = 'none'; }); //tab 변경 document.querySelectorAll('.permission-modal input[type="radio"]').forEach(ele => { ele.addEventListener('click', (e) => { document.querySelector('.permission-modal .modal-wrap .left .search-result-container').style.display = 'none'; document.querySelector('.permission-modal .modal-wrap .left ul').style.display = 'block'; let id = e.target.id; let tab = document.querySelector('.permission-modal .select-list-' + id); drawUserPermissionList(vars.permissionList, id); [...tab.parentElement.children].forEach(element => { if (element.classList.contains('select-list')) element.style.display = 'none'; }); tab.style.display = 'block'; }) }); export async function getList() { let param = { // project_id : JSON.parse(localStorage.getItem('data')).id project_id: vars.project_id } let res = await axios.get('/auth/getMemberList', { params: param }); return res.data; } // 유저권한설정창 export async function drawUserPermissionList(list, rightId = 'sub-master') { document.querySelector('.permission-modal .left ul').innerHTML = ''; document.querySelectorAll(`.permission-modal .select-list ul`).forEach(ul => ul.innerHTML = ''); // DB에 저장횐 company-list가 6개 외에 더 있어서 회사 리스트 고정 const companyList = [ '한맥기술', '삼안', '장헌산업', `(주)장헌` , '피티씨' , '한라산업개발', '바론컨설턴트' , '기타']; // 기존 권한을 가진 인원(key-value)들을 배열화 const permission = Object.values(list.changePermission); try { // changePermission에는 user_id - lev만 있기때문에 배열을 DB로 보내 유저 정보를 가져온다 const usersInfoRes = await axios.post('/auth/getPermissionUserInfo', {permission : permission}); const userInfoArr = usersInfoRes.data.result; const rightUl = document.querySelector(`.permission-modal .select-list.select-list-${rightId} ul`); userInfoArr.forEach(user => { if (findPermission(user.user_id, permission) !== rightId) return; if (rightUl.querySelector(`#_${user.user_id}`)) return; // 유저 리스트 DOM 생성 const userLi = drawUserList(user, rightId, permission, true); rightUl.appendChild(userLi); // 클릭 이벤트 bindUserLiClick(userLi, user, rightId, permission, true); }); // 유저 수 체크 checkPermissionUserNum(rightId); } catch (err) { console.error('getPermissionUserInfo', err); } // 회사 리스트 생성 companyList.forEach(company => { const companyTitle = drawCompanyTitle(company); const companyToggleImg = companyTitle.querySelector('.company-toggle img'); // 회사 리스트 클릭시 부서명 생성 companyTitle.addEventListener('click', async () => { let deptTitle = companyTitle.nextElementSibling; // 부서명 최초 1회 DOM 생성 이후에 토글 if (!deptTitle || !deptTitle.classList.contains('dept-container')) { companyToggleImg.src = '/main/img/permission/down.svg'; deptTitle = document.createElement('ul'); deptTitle.className = 'dept-container _scrollbar'; deptTitle.dataset.company = company; companyTitle.insertAdjacentElement('afterend', deptTitle); try { // 회사명을 통해 부서리스트 조회 const deptRes = await axios.get('/auth/getDeptList', { params: { company } }); if (deptRes.data.message === '200') { const deptList = deptRes.data.result; // 한글 정렬 deptList.sort((a, b) => { if (!a.dept || !b.dept) return 0; return a.dept.localeCompare(b.dept, 'ko'); }); // 부서 리스트 생성 for (const depts of deptList) { if (!depts.dept) continue; const deptLi = drawDeptTitle(company, depts.dept); const deptToggleImg = deptLi.querySelector('.dept-toggle img'); const deptCheckBox = deptLi.querySelector('input[type="checkbox"]'); const deptToggelWrap = deptLi.querySelector('.toggle-wrap'); // 부서 개별 체크 박스 change 이벤트 deptCheckBox.addEventListener('change', async (e) => { const isChecked = deptCheckBox.checked; const rightUl = document.querySelector(`.permission-modal .right .select-list.select-list-${rightId} ul`); try { // 회사명 - 부서를 통해 부서의 유저를 전부 조회 const userRes = await axios.get('/auth/getUserList', { params: { company, dept: depts.dept }}); const userList = userRes.data.result; // 부서 전체 선택시에 이미 권한이 있어 권한을 부여할 수 없는 인원 수를 체크하는 배열 const diffGroupArr = []; // 유저 리스트 생성 for (let user of userList) { const existingRight = rightUl.querySelector(`#_${user.user_id}`); const existingLeft = document.querySelector(`.permission-modal .left ul li#_${user.user_id} .user-wrap`); const lev = findPermission(user.user_id, Object.values(vars.permissionList.changePermission)); // 전체 선택시 if (isChecked) { // 이미 오른쪽(권한이 있던)에 있는 인원과 권한이 다른 인원은 continue로 건너뛰기 if (existingRight) continue; if (lev && lev !== rightId){ diffGroupArr.push(user); continue; } // 부관리자 10명 제한 if (rightId === 'sub-master' && rightUl.children.length >= 10) { updateCheckboxState(user.company, user.dept); return alert('부관리자는 최대 10명까지 선택 가능합니다.'); } // 전체선택으로 권한 변경된 유저 권한 변경 changePermission(user.user_id, rightId, undefined); // 오른쪽 리스트에 생성 const copy = drawUserList(user, rightId, permission, true); rightUl.appendChild(copy); // 클릭이벤트 추카 bindUserLiClick(copy, user, rightId, permission, true); // 왼쪽 유저리스트에 권한 뱃지 생성 if (existingLeft) { const roleDiv = createRoleDiv(rightId); existingLeft.insertAdjacentHTML('beforeend', roleDiv); existingLeft.classList.add('selected_' + rightId); existingLeft.style.display = 'flex'; } } else { // 전체 선택해제 if (existingRight) { // 오른쪽 DOM 삭제와 권한 해제 existingRight.remove(); changePermission(user.user_id, undefined, rightId); } // 왼쪽 유저 리스트에 권한 뱃지 삭제 existingLeft?.querySelector(`.user-permission-${rightId}`)?.remove(); existingLeft?.classList.remove(`selected_${rightId}`); } } // 체크 박스 상태 변화 감지 updateCheckboxState(company, depts.dept); if (diffGroupArr.length > 0){ alert(`이미 권한을 가진 ${diffGroupArr.length}명은 ${{'sub-master' : '부관리자', 'security-worker' : '보안참여자', 'worker' : '일반참여자', 'viewer' : '참관자'}[rightId]} 권한을 부여할 수 없습니다.`); deptCheckBox.checked = true; } } catch (err) { console.error('getUserList Error (checkbox): ', err); } }); // 체크 박스 상태 변화 감지 updateCheckboxState(company, depts.dept); // 부서명 클릭 이벤트 deptToggelWrap.addEventListener('click', async () => { let userTitle = deptLi.nextElementSibling; // 유저 최초 1회 생성 이후에 토글 if (!userTitle || !userTitle.classList.contains('user-container')) { deptToggleImg.src = '/main/img/permission/down.svg'; userTitle = document.createElement('ul'); userTitle.className = 'user-container _scrollbar'; deptLi.insertAdjacentElement('afterend', userTitle); // 회사명,부서명으로 유저리스트 조회 const userRes = await axios.get('/auth/getUserList', { params: { company, dept: depts.dept }}); if (userRes.data.message === '200') { const userList = userRes.data.result; // 유저리스트 생성 userList.forEach(user => { // 유저 DOM 생성 const userLi = drawUserList(user, rightId, permission); userTitle.appendChild(userLi); // 유저 클릭 이벤트 bindUserLiClick(userLi, user, rightId, permission); }); } } // 유저 토글 처리 userTitle.style.display = userTitle.style.display === 'block' ? 'none' : 'block'; deptToggleImg.src = userTitle.style.display === 'block' ? '/main/img/permission/down.svg' : '/main/img/permission/up.svg'; }); } } } catch (error) { console.error('getDeptList Error', error); } } // 부서 토글처리 deptTitle.style.display = deptTitle.style.display === 'block' ? 'none' : 'block'; companyToggleImg.src = deptTitle.style.display === 'block' ? '/main/img/permission/down.svg' : '/main/img/permission/up.svg'; }); }); } // 유저 검색 기능 엔터 이벤트 document.getElementById('permission-search').addEventListener('keydown', (e) => { if (e.key === 'Enter') { // input 값 추출 이후 DOM 생성 const keyword = e.target.value.trim().toLowerCase(); searchResultUser(keyword); } }); // 유저 검색 기능 클릭 이벤트 document.querySelector('.permission-modal .modal-wrap .left .search-box .wrap .search-image').addEventListener('click', () => { const keyword = document.querySelector('.permission-modal .modal-wrap .left .search-input').value.trim().toLowerCase(); searchResultUser(keyword); }); // 유저 검색 기능(엔터, 검색바 클릭) function searchResultUser(keyword) { // 프로젝트 관리자를 제외하고 검색하기 위해 필터링 const users = vars.permissionList.all.filter(user => user.user_id !== vars.project.user_id); const searchResult = document.querySelector('.permission-modal .modal-wrap .left .search-result-container'); const permissionLeftUl = document.querySelector('.permission-modal .modal-wrap .left ul'); const permission = Object.values(vars.permissionList.changePermission); const rightId = document.querySelector('.permission-modal .modal-wrap .tab-input:checked').id; const positionMap = {'회장': 1, '부회장':2, '사장':3, '상임고문':4, '기술위원':5, '부사장':6, '고문':7, '전무이사':8, '수석연구원':9, '상무이사':10, '이사':11, '책임연구원':12, '부장':13, '차장':14, '선임연구원':15, '과장':16, '연구원':17, '대리':18, '사원':19}; if (keyword !== '') { // 이름으로 필터링 이후 직급으로 정렬 const result = users.filter(user => user.user_nm?.toLowerCase().includes(keyword)).sort((a,b) => { const aPosition = positionMap[a.position] ?? 99; const bPosition = positionMap[b.position] ?? 99; if(aPosition !== bPosition) return aPosition - bPosition; return a.user_id.localeCompare(b.user_id); }) if (result.length === 0) { searchResult.style.display = 'none'; permissionLeftUl.style.display = 'block'; return alert('일치하는 사용자가 없습니다.'); } searchResult.innerHTML = ''; // 조회된 유저 있을때 result.forEach(user => { //유저 DOM 생성 const userLi = drawUserList(user, rightId, permission); searchResult.appendChild(userLi); //클릭이벤트 bindUserLiClick(userLi, user, rightId, permission, false); }); // 검색결과창 띄우기 searchResult.style.display = 'block'; permissionLeftUl.style.display = 'none'; } else { // 빈 값 입력시 검색결과창 내리기 searchResult.style.display = 'none'; permissionLeftUl.style.display = 'block'; } } // 유저 검색결과 초기화(닫기버튼, X버튼 클릭시 실행) function searchResultInit(){ const searchResult = document.querySelector('.search-result-container'); searchResult.innerHTML = ''; searchResult.style.display = 'none'; document.querySelector('.permission-modal .modal-wrap .left ul').style.display = 'block'; document.querySelector('.permission-modal .left .search-input').value = ''; } // 회사명 DOM 생성 function drawCompanyTitle(company){ if(company == '(주)장헌') company = '장헌'; if(company == '피티씨') company = 'PTC'; const companyTitle = document.createElement('li'); companyTitle.className = 'company-title'; companyTitle.innerHTML += `
${(company == '기타')? '' :"(주)"}${company}
방향표
` document.querySelector('.permission-modal .left ul').append(companyTitle); return companyTitle; } // 부서명 DOM 생성 function drawDeptTitle(company, dept){ const deptLi = document.createElement('li'); deptLi.className = 'dept-title'; deptLi.dataset.company = company; deptLi.dataset.dept = dept; deptLi.innerHTML = `
${dept}
방향표
`; document.querySelector(`.permission-modal .left ul .dept-container[data-company="${company}"]`).appendChild(deptLi) return deptLi; } // 유저리스트 DOM 생성 function drawUserList(user, rightId, permissionList, isRightSide = false) { const li = document.createElement('li'); li.className = 'user-li'; li.id = `_${user.user_id}`; li.dataset.company = user.company; li.dataset.dept = user.dept; const userWrap = document.createElement('div'); userWrap.className = `user-wrap${isRightSide ? ` selected_${rightId} _scrollbar` : ''}`; userWrap.innerHTML += `
이미지
${user.dept}
${user.user_nm} ${user.position ?? ''}
`; // 유저 권한 확인 const lev = findPermission(user.user_id, Object.values(vars.permissionList.changePermission)); const role = isRightSide ? rightId : lev; // 유저권한 생성 if (role) { const roleDiv = createRoleDiv(role); userWrap.insertAdjacentHTML('beforeend', roleDiv); if (!isRightSide && lev !== rightId) userWrap.classList.add('disabled'); if (!isRightSide) userWrap.classList.add(`selected_${lev}`); } li.appendChild(userWrap); return li; } // 유저 권한 뱃지 생성 function createRoleDiv(role){ return `
${{'sub-master' : '부관리', 'security-worker' : '보안', 'worker' : '일반', 'viewer' : '참관'}[role]}
`; } // 유저 클릭이벤트 바인딩 function bindUserLiClick(userLi, user, rightId, permissionList, isRightSide = false) { const userWrap = userLi.querySelector('.user-wrap'); const rightUl = document.querySelector(`.permission-modal .select-list.select-list-${rightId} ul`); userLi.addEventListener('click', () => { const rightUser = rightUl.querySelector(`li#_${user.user_id}`); const leftUserWrap = document.querySelector(`.permission-modal .left ul li#_${user.user_id} .user-wrap`); const leftUserPermission = leftUserWrap?.querySelector(`.user-permission-${rightId}`); if (userWrap.classList.contains('disabled')) return alert('다른 그룹에 등록된 인원입니다.'); const alreadySelected = userWrap.classList.contains(`selected_${rightId}`); // 이미 권한을 가진 인원 클릭했을때 해제 이벤트 if (alreadySelected) { changePermission(user.user_id, undefined, rightId); // 오른쪽 유저 삭제(오른쪽 리스트 유저 삭제) rightUser.remove(); // 왼쪽 유저 삭제(뱃지삭제 -> userWrap 클래스 삭제) 리스트가 펼쳐지지않아 아직 DOM 생성전에는 삭제가 불가능하기 때문에 옵셔널체이닝 추가 leftUserPermission?.remove(); leftUserWrap?.classList.remove(`selected_${rightId}`); } else { // 권한 부여 이벤트 if (rightId === 'sub-master' && rightUl.children.length >= 10) return alert('부관리자는 최대 10명까지 선택 가능합니다.'); changePermission(user.user_id, rightId, undefined); userWrap.classList.add(`selected_${rightId}`); userWrap.insertAdjacentHTML('beforeend', createRoleDiv(rightId)); // 왼쪽 유저리스트 클릭시 오른쪽에 복사하여 생성+이벤트 추가 const copy = drawUserList(user, rightId, permissionList, true); rightUl.appendChild(copy); bindUserLiClick(copy, user, rightId, permissionList, true); } updateCheckboxState(user.company, user.dept); // 유저 수 조회 이후 갱신 checkPermissionUserNum(rightId); }); } document.querySelector('.permission-modal #help-btn').addEventListener('click', () => toggleHelp(true)); document.querySelector('.permission-modal .modal-permission-help .modal-permission-help__head img').addEventListener('click', ()=>toggleHelp(false)); function toggleHelp(show){ const helpModal = document.querySelector('.permission-modal .modal-permission-help'); const modalBack = document.querySelector('.permission-modal .modal-background'); helpModal.style.display = show ? 'block' : 'none'; modalBack.style.display = show ? 'block' : 'none'; } // 권한을 통해 유저 수 체크 function checkPermissionUserNum(rightId){ // 오른쪽 리스트에 몇명이 있는지 체크 const userNum = document.querySelector(`.permission-modal .modal-wrap .right .select-list.select-list-${rightId} ul`).children.length; // 유저 수를 나타낼 dom const headerUserNum = document.querySelector('.permission-modal .modal-header .header-user-num-wrap .user-num'); // 부관리자 10명이 넘을때 강조효과 let text = `현재 ${userNum}명`; let color = '#111'; if(rightId === 'sub-master'){ text += ' (최대 10명)'; if(userNum >= 10) color = '#F21D0D'; } headerUserNum.innerText = text; headerUserNum.style.color = color; } // 체크박스 DOM, 회사명, 부서를 통해 체크박스 상태 반영 함수 async function updateCheckboxState(company, dept){ const deptUsers = document.querySelectorAll(`.permission-modal .modal-wrap .right ul li[data-company="${company}"][data-dept="${dept}"]`); const checkbox = document.querySelector(`.permission-modal .modal-wrap .left li[data-company="${company}"][data-dept="${dept}"] input[type=checkbox]`); const checkmark = document.querySelector(`.permission-modal .modal-wrap .left li[data-company="${company}"][data-dept="${dept}"] label .checkmark`); const rightId = document.querySelector('.permission-modal .modal-wrap .tab-input:checked').id; const key = `${company}_${dept}`; //permissionList.deptUserCount와 현재 권한 부여된 유저의 길이를 비교해 체크박스 상태변화 if(vars.permissionList.deptUserCount[key]){ if(!checkbox) return; if(deptUsers.length === 0){ checkbox.checked = false; checkmark.classList.remove('indeterminate'); } else if(deptUsers.length === vars.permissionList.deptUserCount[key]) { checkbox.checked = true; checkmark.classList.remove('indeterminate'); } else { checkbox.checked = false; checkmark.classList.add('indeterminate'); } } checkPermissionUserNum(rightId); } function findPermission(id, permissionList) { for (let i = 0; i < permissionList.length; i++) { if (permissionList[i].user_id.toUpperCase() == id.toUpperCase()) return permissionList[i].lev; } return undefined; } function getUserInfo(id) { for (let i = 0; i < vars.permissionList.all.length; i++) { if (vars.permissionList.all[i].user_id == id) return vars.permissionList.all[i]; } } function changePermission(id, group, rightId) { if (!vars.permissionList.changed) vars.permissionList.changed = []; if (!vars.permissionList.changePermission) vars.permissionList.changePermission = {}; id = id.toUpperCase(); /* vars.permissionList.changed => 변화객체 vars.permissionList.permission => 원본 permission vars.permissionList.changePermission => 변화후 permission */ if (group) { //그룹에 넣을때 key, value 형태로 저장 vars.permissionList.changePermission[id] = {user_id : id, lev : group} } else { //그룹에서 제외 delete vars.permissionList.changePermission[id]; } // 기존 permission에서 이전 Lev 가져오기 const original = vars.permissionList.permission?.find(p => p.user_id.toUpperCase() === id); const originalLev = original?.lev; // 기존 permission과 새로운 permission을 비교해 변화 감지 if(group === originalLev || (!group && !originalLev)){ delete vars.permissionList.changed[id]; return; } // 변화감지하여 변화객체 저장 (중복성 피하기 위해) vars.permissionList.changed[id] = {user_id : id, lev : group, before : originalLev, user_nm : getUserInfo(id)?.user_nm} } async function upsertAuthList() { //lev undefined는 deleteAuthList let upsertArr = []; let deleteArr = []; //key-value 형태로 만들었던 changed를 배열로 전환 const changedArr = Object.values(vars.permissionList.changed); if (changedArr.length > 0) { for (let i = 0; i < changedArr.length; i++) { if (changedArr[i].lev == undefined) { deleteArr.push(changedArr[i]); } else { upsertArr.push(changedArr[i]); } } let upsertMessage = (upsertArr.length > 0) ? false : true; let deleteMessage = (deleteArr.length > 0) ? false : true; console.log(upsertArr); console.log(deleteArr); if (upsertArr.length > 0) { let upsertRes = await axios.post('/auth/upsertPermission', { project_id: vars.project_id, targetArr: upsertArr, userInfoString: vars.userInfoString }); if (upsertRes.data.message == 'upsert success'){ const logs = upsertRes.data.logs; if(logs.length > 0){ try{ // upsert 이후 로그 추가 const logRes = await axios.post(`${vars.path_name}/addPermissionLog`, {logs}); if(logRes.data.message === 'addPermissionLog success') upsertMessage = true; } catch(err) { console.error('upsert addPermissionLog Error', err); } } } } if (deleteArr.length > 0) { let deleteRes = await axios.post('/auth/deletePermission', { project_id: vars.project_id, targetArr: deleteArr, userInfoString: vars.userInfoString }); if (deleteRes.data.message == 'delete success'){ const logs = deleteRes.data.logs; if(logs.length > 0){ try{ // delete 이후 로그 추가 const logRes = await axios.post(`${vars.path_name}/addPermissionLog`, {logs}); if(logRes.data.message === 'addPermissionLog success') deleteMessage = true; } catch(err) { console.error('delete addPermssionLog Error', err); } } } } if (upsertMessage && deleteMessage) { alert(`${Object.keys(vars.permissionList.changed).length}개의 변경사항이 저장되었습니다.`); } } searchResultInit(); document.querySelector('.permission-modal').style.display = 'none'; } export class Permission { constructor(){ //permission 정의 this.permissionDef = { 1535 : '개발자', 255 : '관리자', 191 : '부관리자', 15 : '보안참여자', 7 : '일반참여자', 1 : '참관자' } //기능 정의 -'기능명': 허용 권한 code -> ex) 'headerPlusBtn' : 191 this.funcDef = { 'dev-menu' : 1535, 'permission-btn' : 191, 'overview-left-edit' : 191, 'overview-middle-edit' : 191, 'overview-task-history-add' : 191, 'overview-task-history-save' : 191, 'overview-task-history-delete' : 191, 'overview-schedule-add' : 7, 'overview-schedule-edit' : 7, 'overview-issue-edit' : 191, 'header-menu-add' : 191, 'memo-ai' : 7, 'memo-edit' : 7, 'memo-text' : 7, 'set-user-permission' : 191, 'context-menu-viewer' : 7, 'context-menu-sub-master' : 191, 'add-badge-master' : 255, 'add-badge-sub-master' : 191, 'add-badge-security-worker': 15, 'add-badge-worker': 7, 'add-badge-viewer': 1, 'convert-btn-viewer': 7, 'project-inactive-sign' : 1535, 'createFolder-gallery' : 1535, 'download-folder' : 255, 'change-project-btn' : 255, } this.user = (vars.userInfoString)?JSON.parse(vars.userInfoString) : undefined; this.permission = this.user.permission; this.permissionString = this._toPermissionString(); } _toPermissionString(){ return this.permissionDef[`${this.permission}`]; } checkPermission(func){ return this.permission >= this.funcDef[func]; } }