feature/admin #5

Merged
gh.yeom merged 6 commits from feature/admin into main 2024-05-10 17:26:59 +09:00
15 changed files with 278 additions and 313 deletions
Showing only changes of commit 93cdaec42e - Show all commits

View File

@ -1,22 +0,0 @@
package com.bpgroup.poc.admin.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
public class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrfToken != null) {
response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
}
filterChain.doFilter(request, response);
}
}

View File

@ -8,9 +8,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Objects; import java.util.Objects;
@ -25,17 +22,6 @@ public class SecurityConfig {
@Bean @Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
// CSRF 설정
CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler();
csrfTokenRequestAttributeHandler.setCsrfRequestAttributeName("_csrf");
http.csrf(c -> {
c.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler)
.ignoringRequestMatchers("/common/modal/**")
.ignoringRequestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH) // CSRF 무시 URL 설정
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); // CSRF 토큰을 쿠키에 저장 사용 가능
}).addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // 로그인이 완료된 CSRF Filter 실행
// 인증 설정 // 인증 설정
http.authorizeHttpRequests(c -> c http.authorizeHttpRequests(c -> c
.requestMatchers("/css/**", "/images/**", "/js/**").permitAll() .requestMatchers("/css/**", "/images/**", "/js/**").permitAll()

View File

@ -1,5 +1,5 @@
const EventRouter = { const EventRouter = {
_eventRouter: $({}), _eventRouter: new EventTarget(),
/** /**
* 이벤트 발생 * 이벤트 발생
@ -8,7 +8,8 @@ const EventRouter = {
* @param {*=} data * @param {*=} data
*/ */
trigger(eventName, data) { trigger(eventName, data) {
this._eventRouter.trigger(eventName, data); const event = new CustomEvent(eventName, { detail: data });
this._eventRouter.dispatchEvent(event);
}, },
/** /**
@ -23,15 +24,7 @@ const EventRouter = {
return; return;
} }
this._eventRouter.off(eventName); this._eventRouter.removeEventListener(eventName, func);
this._eventRouter.on(eventName, func); this._eventRouter.addEventListener(eventName, func);
}, },
};
/**
* 현재 등록된 이벤트 조회 (디버깅용)
*/
logCurrentRegistered() {
const events = $._data(this._eventRouter[0], "events");
console.log(events);
}
};

View File

@ -0,0 +1,26 @@
const originalFetch = window.fetch;
/**
* csrf token 없을 경우 추가하는 fetch interceptor
* @param url
* @param options
* @returns {Promise<Response>}
*/
window.fetch = function(url, options) {
// CSRF 토큰
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');
if (options.headers) {
options.headers[csrfHeader] = csrfToken;
} else {
options.headers = {
[csrfHeader]: csrfToken,
ContentType: 'application/json',
method: options.method ? options.method : 'POST'
};
}
// 원래의 fetch 함수 호출
return originalFetch.apply(this, arguments);
};

File diff suppressed because one or more lines are too long

View File

@ -1,73 +1,73 @@
// mobile 100vh err issue 대응 // mobile 100vh err issue 대응
const setVh = () => { const setVh = () => {
document.documentElement.style.setProperty('--vh', `${window.innerHeight}px`) document.documentElement.style.setProperty('--vh', `${window.innerHeight}px`)
}; };
window.addEventListener('resize', setVh); window.addEventListener('resize', setVh);
setVh(); setVh();
$(document).on('touchstart', function () { document.addEventListener('touchstart', function () {
}); });
// 콘텐츠영역 자동 높이 조절 // 콘텐츠영역 자동 높이 조절
$(document).ready(function () { document.addEventListener('DOMContentLoaded', function () {
// var headerHeight = $('header').outerHeight();
// $('.wrapper').css('padding-top', headerHeight + 'px');
});
$(document).ready(function () {
setupAccordion(); setupAccordion();
// navigation 모바일 처리 // navigation 모바일 처리
$('#toggleLink').click(function () { document.querySelector('#toggleLink').addEventListener('click', function () {
toggleMenu('.menu_wrap'); toggleMenu('.menu_wrap');
}); });
$('.closeButton').click(function () { document.querySelector('.closeButton').addEventListener('click', function () {
closeMenu('.menu_wrap'); closeMenu('.menu_wrap');
}); });
//modal document.querySelectorAll('.modal .close, .modal').forEach(function (element) {
$("#openModalBtn").click(function () { element.addEventListener('click', function (e) {
openModal(); if (e.target !== this) return; // 모달 내부를 클릭한 경우에는 닫히지 않도록 처리
}); closeModal();
});
$(".modal .close, .modal").click(function (e) {
if (e.target !== this) return; // 모달 내부를 클릭한 경우에는 닫히지 않도록 처리
closeModal();
}); });
}); });
// 아코디언 // 아코디언
function setupAccordion() { function setupAccordion() {
$('.nav_tit').on('click', function () { document.querySelectorAll('.nav_tit').forEach(function (element) {
$(this).next('.nav_section').slideToggle(150); element.addEventListener('click', function () {
$(this).toggleClass('active'); this.nextElementSibling.classList.toggle('active');
$('.nav_section').not($(this).next('.nav_section')).slideUp(150); document.querySelectorAll('.nav_section').forEach(function (section) {
$('.nav_tit').not($(this)).removeClass('active'); if (section !== this.nextElementSibling) {
section.classList.remove('active');
} else {
section.classList.toggle('active');
}
}, this);
document.querySelectorAll('.nav_tit').forEach(function (tit) {
if (tit !== this) {
tit.classList.remove('active');
} else {
tit.classList.toggle('active');
}
}, this);
});
}); });
} }
// navigation 모바일 처리 // navigation 모바일 처리
function toggleMenu(selector) { function toggleMenu(selector) {
$(selector).toggleClass('active'); document.querySelector(selector).classList.toggle('active');
} }
function closeMenu(selector) { function closeMenu(selector) {
$(selector).removeClass('active'); document.querySelector(selector).classList.remove('active');
} }
//modal 열기 닫기 //modal 열기 닫기
function openModal() { function openModal() {
$(".modal").fadeIn(100); document.querySelector('.modal').style.display = 'block';
$("body").css("overflow", "hidden"); document.body.style.overflow = 'hidden';
} }
function closeModal() { function closeModal() {
$(".modal").fadeOut(100); document.querySelector('.modal').style.display = 'none';
$("body").css("overflow", "auto"); document.body.style.overflow = 'auto';
} }

View File

@ -7,12 +7,12 @@ const PageHelper = {
* @param confirmFunction * @param confirmFunction
*/ */
showSimpleConfirmModal(message, confirmFunction) { showSimpleConfirmModal(message, confirmFunction) {
this.showConfirmModal({ this.showConfirmModal({
title: '확인', title: '확인',
message: message, message: message,
}, { }, {
confirm: confirmFunction, confirm: confirmFunction,
}) })
}, },
/** /**
@ -52,7 +52,8 @@ const PageHelper = {
*/ */
showErrorModal(message, cancelFunction) { showErrorModal(message, cancelFunction) {
if (!cancelFunction) { if (!cancelFunction) {
cancelFunction = () => {} cancelFunction = () => {
}
} }
this.showAlertModal({ this.showAlertModal({
@ -71,7 +72,8 @@ const PageHelper = {
*/ */
showFinishModal(message, cancelFunction) { showFinishModal(message, cancelFunction) {
if (!cancelFunction) { if (!cancelFunction) {
cancelFunction = () => {} cancelFunction = () => {
}
} }
this.showAlertModal({ this.showAlertModal({
@ -117,7 +119,7 @@ const PageHelper = {
* @param {string} params.contentSelector * @param {string} params.contentSelector
* @param {string} params.appendToSelector * @param {string} params.appendToSelector
* @param {Object=} callbacks * @param {Object=} callbacks
* @param {function(jQuery)=} callbacks.success - modal body의 jquery 객체가 파라미터로 오는 success 콜백 함수 * @param {function(Element)=} callbacks.success - 파라미터로 오는 success 콜백 함수
* @param {function(Object)=} callbacks.error - 에러 메세지가 파라미터로 오는 error 콜백 함수 * @param {function(Object)=} callbacks.error - 에러 메세지가 파라미터로 오는 error 콜백 함수
* @param {function()=} callbacks.complete * @param {function()=} callbacks.complete
*/ */
@ -132,11 +134,19 @@ const PageHelper = {
data: params.data, data: params.data,
}, { }, {
success: function (res) { success: function (res) {
const e = $(res); const p = new DOMParser();
const d = e.find(params.contentSelector); const e = p.parseFromString(res, 'text/html');
d.appendTo(params.appendToSelector);
const s = e.find('script'); const d = e.querySelector(params.contentSelector);
s.appendTo('body'); document.body.appendChild(d);
const sDiv = document.createElement('script');
sDiv.setAttribute('data-page-modal', 'script');
const s = e.querySelector('script');
sDiv.textContent = s.textContent;
document.body.appendChild(sDiv);
if (callbacks.success) callbacks.success(d); if (callbacks.success) callbacks.success(d);
}, },
error: function (error) { error: function (error) {
@ -163,20 +173,21 @@ const PageHelper = {
callbacks = {}; callbacks = {};
} }
$.ajax({ fetch(params.url, {
url: params.url, method: params.method,
type: params.method, body: new URLSearchParams(params.data)
data: params.data, })
success: function (res) { .then(response => response.text())
if (callbacks.success) callbacks.success(res); .then(data => {
}, if (callbacks.success) callbacks.success(data);
error: function (error) { })
.catch(error => {
console.error(error); console.error(error);
if (callbacks.error) callbacks.error(error); if (callbacks.error) callbacks.error(error);
}, })
complete: function () { .finally(() => {
if (callbacks.complete) callbacks.complete(); if (callbacks.complete) callbacks.complete();
} });
});
}, },
}; };

View File

@ -1,81 +1,79 @@
const ReqHelper = { const Reqhelper = {
/** /**
* * post json http request
* @param {jQuery} form * @param url request url
* @param {Object=} callbacks * @param data is not json stringify data
* @param {function(Object=)=} callbacks.success * @param sFunc success function
* @param {function(Object)=} callbacks.error * @param eFunc error function
* @param {function()=} callbacks.complete * @param fFunc finally function
*/ */
reqByForm(form, callbacks) { reqPostJson: function (url, data, sFunc, eFunc, fFunc) {
if (!(form instanceof jQuery)) { fetch(url, {
throw new Error('form is not jQuery instance'); method: 'POST',
} body: JSON.stringify(data),
if (!callbacks) { headers: {
callbacks = {}; 'Content-Type': 'application/json'
}
const url = form.attr('action');
const method = form.attr('method');
const dataObj = form.serializeArray().reduce((obj, item) => {
obj[item.name] = item.value;
return obj;
}, {});
$.ajax({
url: url,
type: method,
contentType: 'application/json',
data: JSON.stringify(dataObj),
success: function (res) {
if (!callbacks.success) return;
res.isSuccess = () => res.resultCode === "0000";
res.isFail = () => !res.isSuccess();
res.getMessage = () => res.resultMsg || '';
callbacks.success(res);
},
error: function (error) {
if (callbacks.error) callbacks.error(error);
},
complete: function () {
if (callbacks.complete) callbacks.complete();
} }
}); })
.then(response => {
if (!response.ok) {
throw new Error(response.statusText);
}
return response.text().then(text => text ? JSON.parse(text) : null);
})
.then((data) => {
if (sFunc) {
sFunc(data);
}
})
.catch((error) => {
if (eFunc) {
eFunc(error);
}
})
.finally(() => {
if (fFunc) {
fFunc();
}
});
}, },
/** /**
* * get json http request
* @param url * @param url request url
* @param sendData {Object=} * @param sFunc success function
* @param {Object=} callbacks * @param eFunc error function
* @param {function(Object=)=} callbacks.success * @param fFunc finally function
* @param {function(Object)=} callbacks.error
* @param {function()=} callbacks.complete
*/ */
reqByObj(url, sendData, callbacks) {
if (!url) {
throw new Error('url is empty');
}
if (!callbacks) {
callbacks = {};
}
$.ajax({ reqGetJson: function (url, sFunc, eFunc, fFunc) {
url: url, fetch(url, {
type: "post", method: 'GET',
contentType: 'application/json', headers: {
data: JSON.stringify(sendData), 'Content-Type': 'application/json'
success: function (res) {
if (callbacks.success) callbacks.success(res);
},
error: function (error) {
if (callbacks.error) callbacks.error(error);
},
complete: function () {
if (callbacks.complete) callbacks.complete();
} }
}); })
}, .then(response => {
}; if (!response.ok) {
throw new Error(response.statusText);
}
return response.text().then(text => text ? JSON.parse(text) : null);
})
.then((data) => {
if (sFunc) {
sFunc(data);
}
})
.catch((error) => {
if (eFunc) {
eFunc(error);
}
})
.finally(() => {
if (fFunc) {
fFunc();
}
});
}
}

View File

@ -4,33 +4,6 @@
<th:block th:fragment="applyCsrf"> <th:block th:fragment="applyCsrf">
<meta name="_csrf_header" th:content="${_csrf.headerName}"/> <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf" th:content="${_csrf.token}"/>
<script th:inline="javascript">
// CSRF 토큰
// fetch ajax 사용 시 헤더에 추가 필요
const csrfToken = $('meta[name="_csrf"]').attr('content');
const csrfHeader = $('meta[name="_csrf_header"]').attr('content');
function fetchPost(requestUri, body) {
return fetch(requestUri, {
method: 'POST',
headers: {
[csrfHeader]: csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify(body)
});
}
function fetchGet(requestUri) {
return fetch(requestUri, {
method: 'GET',
headers: {
[csrfHeader]: csrfToken,
'Content-Type': 'application/json',
}
});
}
</script>
</th:block> </th:block>
</body> </body>
</html> </html>

View File

@ -21,21 +21,25 @@
<script data-alert-modal="script"> <script data-alert-modal="script">
(function () { (function () {
const modalBody = $('div[data-alert-modal="body"]'); const modalBody = document.querySelector('div[data-alert-modal="body"]');
modalBody.fadeIn(100); modalBody.style.display = "block";
$("body").css("overflow", "hidden"); document.body.style.overflow = "hidden";
$('[data-alert-modal="cancelButton"]').on('click', function () { document.querySelectorAll('[data-alert-modal="cancelButton"]').forEach(button => {
close(); button.addEventListener('click', function () {
EventRouter.trigger('clickAlertModalCancelButton'); close();
EventRouter.trigger('clickAlertModalCancelButton');
});
}); });
function close() { function close() {
modalBody.fadeOut(100); modalBody.style.display = "none";
modalBody.remove(); modalBody.parentNode.removeChild(modalBody);
$("body").css("overflow", "auto"); document.body.style.overflow = "auto";
$('script[data-alert-modal="script"]').remove();
const modalScript = document.querySelector('script[data-page-modal="script"]');
modalScript.parentNode.removeChild(modalScript);
} }
})() })()
</script> </script>

View File

@ -22,12 +22,12 @@
<script data-confirm-modal="script"> <script data-confirm-modal="script">
(function () { (function () {
const modalBody = $('div[data-confirm-modal="body"]'); const modalBody = document.querySelector('div[data-confirm-modal="body"]');
modalBody.fadeIn(100); modalBody.style.display = "block";
$("body").css("overflow", "hidden"); document.body.style.overflow = "hidden";
$('button[data-confirm-modal="confirmButton"]').on('click', function () { document.querySelector('button[data-confirm-modal="confirmButton"]').addEventListener('click', function () {
let prevented = false; let prevented = false;
EventRouter.trigger('clickConfirmModalConfirmButton', {preventDefault: () => prevented = true}); EventRouter.trigger('clickConfirmModalConfirmButton', {preventDefault: () => prevented = true});
if (prevented) { if (prevented) {
@ -37,21 +37,23 @@
hideAndRemove(); hideAndRemove();
}); });
$('[data-confirm-modal="cancelButton"]').on('click', function () { document.querySelectorAll('[data-alert-modal="cancelButton"]').forEach(button => {
let prevented = false; button.addEventListener('click', function () {
EventRouter.trigger('clickConfirmModalCancelButton', {preventDefault: () => prevented = true}); let prevented = false;
if (prevented) { EventRouter.trigger('clickConfirmModalCancelButton', {preventDefault: () => prevented = true});
return; if (prevented) {
} return;
}
hideAndRemove(); hideAndRemove();
});
}); });
function hideAndRemove() { function hideAndRemove() {
modalBody.fadeOut(100); modalBody.style.display = "none";
modalBody.remove(); modalBody.remove();
$("body").css("overflow", "auto"); document.body.style.overflow = "auto";
$('script[data-confirm-modal="script"]').remove(); document.querySelector('script[data-confirm-modal="script"]').remove();
} }
})() })()
</script> </script>

View File

@ -21,10 +21,12 @@
</div> </div>
</div> </div>
<script data-page-modal="script"> <script data-page-modal="script">
$('div[data-page-modal="body"]').fadeIn(100); document.querySelector('div[data-page-modal="body"]').style.display = "block";
$("body").css("overflow", "hidden"); document.body.style.overflow = "hidden";
$('[data-page-modal="cancelButton"]').on('click', cancelModal); document.querySelectorAll('[data-page-modal="cancelButton"]').forEach(button => {
button.addEventListener('click', cancelModal);
});
function cancelModal() { function cancelModal() {
closeModal(); closeModal();
@ -32,11 +34,13 @@
} }
function closeModal() { function closeModal() {
const modalBody = $('div[data-page-modal="body"]'); const modalBody = document.querySelector('div[data-page-modal="body"]');
modalBody.fadeOut(100); modalBody.style.display = "none";
modalBody.remove(); modalBody.parentNode.removeChild(modalBody);
$("body").css("overflow", "auto"); document.body.style.overflow = "auto";
$('script[data-page-modal="script"]').remove();
const modalScript = document.querySelector('script[data-page-modal="script"]');
modalScript.parentNode.removeChild(modalScript);
} }
</script> </script>

View File

@ -6,15 +6,15 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>관리자 시스템</title> <title>관리자 시스템</title>
<script type="text/javascript" th:src="@{/js/jquery/jquery-3.7.1.min.js}"></script>
<link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<link rel="stylesheet" th:href="@{/css/style.css}"/> <link rel="stylesheet" th:href="@{/css/style.css}"/>
<link rel="stylesheet" th:href="@{/css/sub.css}"/> <link rel="stylesheet" th:href="@{/css/sub.css}"/>
<script type="text/javascript" th:src="@{/js/motion.js}"></script> <script type="text/javascript" th:src="@{/js/motion.js}"></script>
<script type="text/javascript" th:src="@{/js/pagehelper.js}"></script> <script type="text/javascript" th:src="@{/js/pagehelper.js}"></script>
<script type="text/javascript" th:src="@{/js/reqhelper.js}"></script>
<script type="text/javascript" th:src="@{/js/eventrouter.js}"></script> <script type="text/javascript" th:src="@{/js/eventrouter.js}"></script>
<script type="text/javascript" th:src="@{/js/httpinterceptor.js}"></script>
<script type="text/javascript" th:src="@{/js/reqhelper.js}"></script>
<th:block th:replace="~{fragment/csrf/csrf :: applyCsrf}"></th:block> <th:block th:replace="~{fragment/csrf/csrf :: applyCsrf}"></th:block>
</head> </head>

View File

@ -5,13 +5,14 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>시스템</title> <title>시스템</title>
<script type="text/javascript" th:src="@{/js/jquery/jquery-3.7.1.min.js}"></script>
<link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<link rel="stylesheet" th:href="@{/css/style.css}"/> <link rel="stylesheet" th:href="@{/css/style.css}"/>
<script type="text/javascript" th:src="@{/js/pagehelper.js}"></script> <script type="text/javascript" th:src="@{/js/pagehelper.js}"></script>
<script type="text/javascript" th:src="@{/js/reqhelper.js}"></script>
<script type="text/javascript" th:src="@{/js/eventrouter.js}"></script> <script type="text/javascript" th:src="@{/js/eventrouter.js}"></script>
<script type="text/javascript" th:src="@{/js/httpinterceptor.js}"></script>
<th:block th:replace="~{fragment/csrf/csrf :: applyCsrf}"></th:block>
</head> </head>
<body> <body>
<div class="login_wrap"> <div class="login_wrap">
@ -39,16 +40,16 @@
PageHelper.showErrorModal(errorMessage); PageHelper.showErrorModal(errorMessage);
} }
$('#loginButton').click(function () { document.querySelector('#loginButton').addEventListener('click', function () {
if (!validate()) { if (!validate()) {
return; return;
} }
$('#loginForm').submit(); document.querySelector('#loginForm').submit();
function validate() { function validate() {
const loginId = $('input[name="loginId"]').val(); const loginId = document.querySelector('input[name="loginId"]').value;
const password = $('input[name="password"]').val(); const password = document.querySelector('input[name="password"]').value;
if (!loginId) { if (!loginId) {
PageHelper.showErrorModal('아이디를 입력해주세요.'); PageHelper.showErrorModal('아이디를 입력해주세요.');

View File

@ -28,7 +28,7 @@
<div class="tb_wrapper"> <div class="tb_wrapper">
<input type="hidden" id="iptUpdateId" name="id"> <input type="hidden" id="iptUpdateId" name="id">
<label for="iptUpdateLoginId">아이디: </label><input type="text" id="iptUpdateLoginId" name="loginId" readonly> <label for="iptUpdateLoginId">아이디: </label><input type="text" id="iptUpdateLoginId" name="loginId" readonly>
<label for="iptUpdatePassword">비밀번호: </label ><input type="password" id="iptUpdatePassword" name="password"> <label for="iptUpdatePassword">비밀번호: </label><input type="password" id="iptUpdatePassword" name="password">
<label for="iptUpdateEmail">이메일: </label><input type="text" id="iptUpdateEmail" name="email"> <label for="iptUpdateEmail">이메일: </label><input type="text" id="iptUpdateEmail" name="email">
<label for="iptUpdateName">이름: </label><input type="text" id="iptUpdateName" name="name"> <label for="iptUpdateName">이름: </label><input type="text" id="iptUpdateName" name="name">
<button type="button" id="btnUpdate" class="btn_confirm" data-bs-dismiss="modal">수정</button> <button type="button" id="btnUpdate" class="btn_confirm" data-bs-dismiss="modal">수정</button>
@ -59,48 +59,44 @@
document.getElementById('iptDeleteLoginId').value = ''; document.getElementById('iptDeleteLoginId').value = '';
document.getElementById('selAdministrator').innerHTML = '<option value="">선택</option>'; document.getElementById('selAdministrator').innerHTML = '<option value="">선택</option>';
} }
/** /**
* 아이디 중복 체크 * 아이디 중복 체크
*/ */
document.getElementById('btnCheckDuplication').addEventListener(('click'), function () { document.getElementById('btnCheckDuplication').addEventListener(('click'), function () {
const loginId = document.getElementById('iptCreateLoginId').value; const loginId = document.getElementById('iptCreateLoginId').value;
const requestUri = /*[[@{/admin/management}]]*/ ''; const requestUri = /*[[@{/admin/management}]]*/ '';
fetchGet(requestUri.concat('/', loginId))
.then(response => { Reqhelper.reqGetJson(requestUri.concat('/', loginId), (res) => {
if (response.ok) { if (!res) {
return response.text().then(text => text ? JSON.parse(text) : null); PageHelper.showAlertModal({title: '아이디 중복', message: '사용 가능한 아이디입니다.'});
} else { } else {
PageHelper.showErrorModal('데이터 조회에 실패했습니다.'); PageHelper.showAlertModal({title: '아이디 중복', message: '사용할 수 없는 아이디입니다.'});
} }
}) }, () => {
.then((data) => { PageHelper.showErrorModal('데이터 조회에 실패했습니다.');
if (!data) { });
PageHelper.showAlertModal({title: '아이디 중복', message: '사용 가능한 아이디입니다.'}); })
} else { ;
PageHelper.showAlertModal({title: '아이디 중복', message: '사용할 수 없는 아이디입니다.'});
}
});
});
/** /**
* 관리자 등록 * 관리자 등록
*/ */
document.getElementById('btnCreate').addEventListener(('click'), function () { document.getElementById('btnCreate').addEventListener(('click'), function () {
const requestUri = /*[[@{/admin/management/create}]]*/ ''; let data = {
fetchPost(requestUri, {
loginId: document.getElementById('iptCreateLoginId').value, loginId: document.getElementById('iptCreateLoginId').value,
password: document.getElementById('iptCreatePassword').value, password: document.getElementById('iptCreatePassword').value,
email: document.getElementById('iptCreateEmail').value, email: document.getElementById('iptCreateEmail').value,
name: document.getElementById('iptCreateEmail').value name: document.getElementById('iptCreateEmail').value
}).then(response => { };
if (response.ok) { const requestUri = /*[[@{/admin/management/create}]]*/ '';
PageHelper.showAlertModal({title: '등록 완료', message: '등록이 완료되었습니다.'});
clearInput(); Reqhelper.reqPostJson(requestUri, data, () => {
} else { PageHelper.showAlertModal({title: '등록 완료', message: '등록이 완료되었습니다.'});
PageHelper.showErrorModal('등록에 실패했습니다.'); clearInput();
}
}, () => {
PageHelper.showErrorModal('등록에 실패했습니다.');
}); });
}); });
@ -110,62 +106,57 @@
*/ */
document.getElementById('btnFindAll').addEventListener(('click'), function () { document.getElementById('btnFindAll').addEventListener(('click'), function () {
const requestUri = /*[[@{/admin/management/list}]]*/ ''; const requestUri = /*[[@{/admin/management/list}]]*/ '';
fetchGet(requestUri) Reqhelper.reqGetJson(requestUri, (res) => {
.then(response => { const selAdministrator = document.getElementById('selAdministrator');
if (response.ok) { selAdministrator.innerHTML = '<option value="">선택</option>';
return response.json(); res.forEach(item => {
} else { const option = document.createElement('option');
PageHelper.showErrorModal('데이터 조회에 실패했습니다.'); option.value = item.id;
} option.text = item.loginId;
}) selAdministrator.appendChild(option);
.then((data) => {
const selAdministrator = document.getElementById('selAdministrator');
selAdministrator.innerHTML = '<option value="">선택</option>';
data.forEach(item => {
const option = document.createElement('option');
option.value = item.id;
option.text = item.loginId;
selAdministrator.appendChild(option);
});
}); });
}, () => {
PageHelper.showErrorModal('데이터 조회에 실패했습니다.');
});
}); });
document.getElementById('selAdministrator').addEventListener(('change'), function () { document.getElementById('selAdministrator').addEventListener(('change'), function () {
document.getElementById('iptUpdateId').value = this.value; document.getElementById('iptUpdateId').value = this.value;
document.getElementById('iptUpdateLoginId').value = this.options[this.selectedIndex].text; document.getElementById('iptUpdateLoginId').value = this.options[this.selectedIndex].text;
document.getElementById('iptDeleteId').value = this.value; document.getElementById('iptDeleteId').value = this.value;
document.getElementById('iptDeleteLoginId').value = this.options[this.selectedIndex].text; document.getElementById('iptDeleteLoginId').value = this.options[this.selectedIndex].text;
}); });
/**
* 관리자 수정
*/
document.getElementById('btnUpdate').addEventListener(('click'), function () { document.getElementById('btnUpdate').addEventListener(('click'), function () {
const requestUri = /*[[@{/admin/management/update}]]*/ ''; let data = {
fetchPost(requestUri, {
id: document.getElementById('iptUpdateId').value, id: document.getElementById('iptUpdateId').value,
password: document.getElementById('iptUpdatePassword').value, password: document.getElementById('iptUpdatePassword').value,
email: document.getElementById('iptUpdateEmail').value, email: document.getElementById('iptUpdateEmail').value,
name: document.getElementById('iptUpdateEmail').value name: document.getElementById('iptUpdateName').value
}).then(response => { };
if (response.ok) { const requestUri = /*[[@{/admin/management/update}]]*/ '';
PageHelper.showAlertModal({title: '수정 완료', message: '수정이 완료되었습니다.'});
} else { Reqhelper.reqPostJson(requestUri, data, () => {
PageHelper.showErrorModal('수정에 실패했습니다.'); PageHelper.showAlertModal({title: '수정 완료', message: '수정이 완료되었습니다.'});
} }, () => {
PageHelper.showErrorModal('수정에 실패했습니다.');
}); });
}); });
document.getElementById('btnDelete').addEventListener(('click'), function () { document.getElementById('btnDelete').addEventListener(('click'), function () {
const requestUri = /*[[@{/admin/management/delete}]]*/ ''; let data = {
fetchPost(requestUri, {
id: document.getElementById('iptDeleteId').value id: document.getElementById('iptDeleteId').value
}).then(response => { };
if (response.ok) { const requestUri = /*[[@{/admin/management/delete}]]*/ '';
PageHelper.showAlertModal({title: '삭제 완료', message: '삭제가 완료되었습니다.'});
clearInput(); Reqhelper.reqPostJson(requestUri, data, () => {
} else { PageHelper.showAlertModal({title: '삭제 완료', message: '삭제가 완료되었습니다.'});
PageHelper.showErrorModal('삭제에 실패했습니다.'); clearInput();
} }, () => {
PageHelper.showErrorModal('삭제에 실패했습니다.');
}); });
}); });