회원가입 디자인 적용
This commit is contained in:
@@ -1,65 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{layout/kbank_base_layout}">
|
||||
layout:decorate="~{layout/kjbank_signup_layout}">
|
||||
<body>
|
||||
<section layout:fragment="contentFragment" class="content">
|
||||
<script th:src="@{/js/htmx.min.js}"></script>
|
||||
<div class="content_wrap">
|
||||
<div class="sub_title2">
|
||||
<h2 class="title add" th:if="${!isInvited}">개인회원가입</h2>
|
||||
<h2 class="title add" th:if="${isInvited}">법인회원가입</h2>
|
||||
</div>
|
||||
<div class="inner i_cs h_inner2">
|
||||
<div class="top_info" th:if="${!isInvited}">
|
||||
<ul>
|
||||
<li>서비스 또는 API 사용을 원하실 경우 법인회원 승인 후 이용하실 수 있습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 약관동의 섹션 -->
|
||||
<input type="hidden" id="csrf" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<th:block th:replace="~{apps/register/userAgreementContent :: agreementContent(${termsOfUse}, ${privacyCollect})}"></th:block>
|
||||
<!-- 기본 정보 입력 섹션 -->
|
||||
<form name="portalUser" id="registerForm" role="form" th:action="@{/signup/portalUser}"
|
||||
th:object="${portalUser}" method="post">
|
||||
<!-- 법인 정보 섹션, 초대받은 경우에만 -->
|
||||
<div class="terms_box" th:if="${isInvited}">
|
||||
<p>법인 정보</p>
|
||||
</div>
|
||||
<section layout:fragment="title">
|
||||
<div class="org-page-title-banner">
|
||||
<img th:src="@{/img/user_register_title.png}" alt="개인회원가입" class="title-image">
|
||||
<h1 th:text="${!isInvited ? '개인회원가입' : '법인회원가입'}">개인회원가입</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="form_type" th:if="${isInvited}">
|
||||
<div class="info1">
|
||||
<p class="title w_tit">
|
||||
<span>법인명</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" name="companyName" class="common_input_type_1"
|
||||
th:classappend="${isInvited ? 'input-readonly' : ''}" th:value="${orgName}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end 법인 정보 섹션, 초대받은 경우에만 -->
|
||||
<div class="terms_box">
|
||||
<p>기본 정보</p>
|
||||
<span class="dot">필수 입력란입니다</span>
|
||||
</div>
|
||||
<div class="form_type" th:if="${registrationType == isInvited ? 'corporate' :'personal'}">
|
||||
<th:block th:replace="~{apps/register/components/commonUserInfoForm :: commonUserInfo}"></th:block>
|
||||
<th:block th:replace="~{apps/register/components/newUserInfoForm :: newUserForm}"></th:block>
|
||||
</div>
|
||||
<div class="form_type">
|
||||
<div class="btn_application">
|
||||
<a type="button" class="common_btn_type_1 gray" id="cancelButton"><span>취소</span></a>
|
||||
<div class="btn_gap"></div>
|
||||
<a type="button" class="common_btn_type_1 btn_register"><span>가입신청</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<section layout:fragment="contentFragment">
|
||||
<div class="org-register-page">
|
||||
<div class="org-register-container">
|
||||
<!-- Hidden Fields -->
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<input type="hidden" name="registrationType" th:value="${registrationType}"/>
|
||||
|
||||
<!-- Alert Container -->
|
||||
<div id="alertContainer"></div>
|
||||
|
||||
<!-- Info Notice -->
|
||||
<div class="org-info-notice" th:if="${!isInvited}">
|
||||
<ul>
|
||||
<li>서비스 또는 API 사용을 원하실 경우 법인회원 승인 후 이용하실 수 있습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Agreement Section -->
|
||||
<th:block th:replace="~{apps/register/userAgreementContent :: agreementContent(${termsOfUse}, ${privacyCollect})}"></th:block>
|
||||
|
||||
<form name="portalUser" id="registerForm" role="form" th:action="@{/signup/portalUser}"
|
||||
th:object="${portalUser}" method="post">
|
||||
|
||||
<!-- Corporate Information Section (invited users only) -->
|
||||
<div th:if="${isInvited}">
|
||||
<div class="org-section-header org-section-header--agreement">
|
||||
<h3>법인 정보</h3>
|
||||
</div>
|
||||
|
||||
<div class="org-form-group">
|
||||
<label class="org-form-label">법인명</label>
|
||||
<div class="org-form-input-wrapper">
|
||||
<input type="text" name="companyName" class="org-form-input" th:value="${orgName}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Basic Information Section -->
|
||||
<div class="org-section-header org-section-header--agreement" th:style="${isInvited ? 'margin-top: 48px;' : ''}">
|
||||
<h3>기본 정보</h3>
|
||||
<span class="required-badge">필수 입력</span>
|
||||
</div>
|
||||
|
||||
<div th:if="${registrationType == (isInvited ? 'corporate' : 'personal')}">
|
||||
<th:block th:replace="~{apps/register/components/commonUserInfoForm :: commonUserInfo}"></th:block>
|
||||
<th:block th:replace="~{apps/register/components/newUserInfoForm :: newUserForm}"></th:block>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="org-action-buttons">
|
||||
<button type="button" class="org-btn org-btn-secondary" id="cancelButton">취소</button>
|
||||
<button type="button" class="org-btn org-btn-primary btn_register">가입신청</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div class="org-loading-overlay" id="loadingOverlay">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script th:if="${error}" th:inline="javascript">
|
||||
@@ -70,128 +81,119 @@
|
||||
<script th:replace="~{apps/register/userAgreementContent :: agreementScript}"></script>
|
||||
<script th:replace="~{apps/register/components/commonUserInfoForm :: commonUserInfoScript}"></script>
|
||||
<script th:replace="~{apps/register/components/newUserInfoForm :: newUserScript}"></script>
|
||||
<script>
|
||||
let form = document.getElementById('registerForm');
|
||||
let agreementForm = document.getElementById('agreementForm');
|
||||
let btn_register = document.querySelector('.btn_register');
|
||||
let isAuthVerified = false;
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
let form = document.getElementById('registerForm');
|
||||
let agreementForm = document.getElementById('agreementForm');
|
||||
let btn_register = document.querySelector('.btn_register');
|
||||
let isAuthVerified = false;
|
||||
|
||||
// 인증 완료 이벤트 리스너
|
||||
document.addEventListener('authVerified', function() {
|
||||
isAuthVerified = true;
|
||||
});
|
||||
// 인증 완료 이벤트 리스너
|
||||
document.addEventListener('authVerified', function() {
|
||||
isAuthVerified = true;
|
||||
});
|
||||
|
||||
// 이메일 유효성 검사 함수
|
||||
function isValidEmail(email) {
|
||||
const safeEmailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
return safeEmailRegex.test(email);
|
||||
}
|
||||
|
||||
// 폼 유효성 검사 함수
|
||||
function checkFormValidity() {
|
||||
return isValidEmail($('#loginId').val()) &&
|
||||
($('#password').val() === $('#password2').val()) &&
|
||||
($('#userName').val().trim() !== '') &&
|
||||
$('#termsOfUse').prop('checked') &&
|
||||
$('#privacyCollect').prop('checked') &&
|
||||
(combineMobileNumber('#mobileNumber') !== null) &&
|
||||
isAuthVerified; // 인증 여부 확인
|
||||
}
|
||||
|
||||
let isSubmitting = false;
|
||||
|
||||
// 등록 버튼 클릭 이벤트
|
||||
btn_register.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Prevent resubmission
|
||||
if (isSubmitting) {
|
||||
console.log('Form is already being submitted...');
|
||||
return;
|
||||
// 이메일 유효성 검사 함수
|
||||
function isValidEmail(email) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
isSubmitting = true;
|
||||
btn_register.disabled = true;
|
||||
// 폼 유효성 검사 함수
|
||||
function checkFormValidity() {
|
||||
const loginIdElement = $('#loginId');
|
||||
const userNameElement = $('#userName');
|
||||
const termsOfUseElement = $('#termsOfUse');
|
||||
const privacyCollectElement = $('#privacyCollect');
|
||||
const mobileNumberElement = $('#mobileNumber');
|
||||
const isPasswordValid = $('#isPasswordValid').val() === 'true';
|
||||
const isPasswordMatch = $('#isPasswordMatch').val() === 'true';
|
||||
|
||||
form.classList.add('was-validated');
|
||||
agreementForm.classList.add('was-validated');
|
||||
return isValidEmail(loginIdElement.val()) &&
|
||||
isPasswordValid &&
|
||||
isPasswordMatch &&
|
||||
userNameElement.val().trim() !== '' &&
|
||||
termsOfUseElement.prop('checked') &&
|
||||
privacyCollectElement.prop('checked') &&
|
||||
mobileNumberElement.val().trim() !== '' &&
|
||||
isAuthVerified;
|
||||
}
|
||||
|
||||
if (checkFormValidity() && form.checkValidity() && agreementForm.checkValidity()) {
|
||||
// 약관 동의 폼의 데이터를 hidden input으로 추가
|
||||
let agreementFormData = new FormData(agreementForm);
|
||||
for (let pair of agreementFormData.entries()) {
|
||||
if (!form.querySelector('input[name="' + pair[0] + '"]')) {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = pair[0];
|
||||
input.value = pair[1];
|
||||
form.appendChild(input);
|
||||
}
|
||||
let isSubmitting = false;
|
||||
|
||||
// 등록 버튼 클릭 이벤트
|
||||
btn_register.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Prevent resubmission
|
||||
if (isSubmitting) {
|
||||
console.log('Form is already being submitted...');
|
||||
return;
|
||||
}
|
||||
|
||||
// 불필요한 hidden input 제거
|
||||
const unnecessaryFields = ['isPasswordValid', 'isPasswordMatch'];
|
||||
unnecessaryFields.forEach(fieldName => {
|
||||
const field = form.querySelector(`input[name="${fieldName}"]`);
|
||||
if (field) {
|
||||
field.remove();
|
||||
isSubmitting = true;
|
||||
btn_register.disabled = true;
|
||||
|
||||
// Show loading overlay
|
||||
document.getElementById('loadingOverlay').classList.add('active');
|
||||
|
||||
try {
|
||||
form.classList.add('was-validated');
|
||||
if (agreementForm) {
|
||||
agreementForm.classList.add('was-validated');
|
||||
}
|
||||
});
|
||||
|
||||
// authCompletedYn 값 설정
|
||||
const authCompletedField = document.createElement('input');
|
||||
authCompletedField.type = 'hidden';
|
||||
authCompletedField.name = 'authCompletedYn';
|
||||
authCompletedField.value = isAuthVerified ? 'Y' : 'N';
|
||||
form.appendChild(authCompletedField);
|
||||
if (checkFormValidity() && form.checkValidity() && (agreementForm ? agreementForm.checkValidity() : true)) {
|
||||
// 약관 동의 폼의 데이터를 hidden input으로 추가
|
||||
if (agreementForm) {
|
||||
let agreementFormData = new FormData(agreementForm);
|
||||
for (let pair of agreementFormData.entries()) {
|
||||
if (!form.querySelector('input[name="' + pair[0] + '"]')) {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = pair[0];
|
||||
input.value = pair[1];
|
||||
form.appendChild(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.submit();
|
||||
} else {
|
||||
customPopups.showAlert('모든 필수 항목을 입력하고 약관에 동의해주세요.');
|
||||
isSubmitting = false;
|
||||
btn_register.disabled = false;
|
||||
}
|
||||
});
|
||||
// 불필요한 hidden input 제거
|
||||
const unnecessaryFields = ['isPasswordValid', 'isPasswordMatch'];
|
||||
unnecessaryFields.forEach(fieldName => {
|
||||
const field = form.querySelector(`input[name="${fieldName}"]`);
|
||||
if (field) {
|
||||
field.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// 취소버튼 클릭시
|
||||
document.getElementById('cancelButton').addEventListener('click', function() {
|
||||
location.href = '/login';
|
||||
});
|
||||
// authCompletedYn 값 설정
|
||||
const authCompletedField = document.createElement('input');
|
||||
authCompletedField.type = 'hidden';
|
||||
authCompletedField.name = 'authCompletedYn';
|
||||
authCompletedField.value = isAuthVerified ? 'Y' : 'N';
|
||||
form.appendChild(authCompletedField);
|
||||
|
||||
// HTMX 이벤트 리스너 - 모든 HTMX 응답을 중앙에서 처리
|
||||
document.body.addEventListener('htmx:afterRequest', function(event) {
|
||||
const targetId = event.detail.target.id;
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = JSON.parse(event.detail.xhr.response);
|
||||
} catch (e) {
|
||||
console.error('응답 JSON 파싱 실패:', e);
|
||||
customPopups.showAlert('서버 응답을 처리할 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 커스텀 헤더를 체크하여 등록 검증 요청 처리
|
||||
if (event.detail.xhr.getResponseHeader('X-Custom-Handler') === 'registration-validation') {
|
||||
if (response && response.message) {
|
||||
if (response.status === 'SUCCESS') {
|
||||
customPopups.showSuccess(response.message);
|
||||
form.submit();
|
||||
} else {
|
||||
customPopups.showAlert(response.message);
|
||||
customPopups.showAlert('모든 필수 항목을 입력하고 약관에 동의해주세요.');
|
||||
isSubmitting = false;
|
||||
btn_register.disabled = false;
|
||||
document.getElementById('loadingOverlay').classList.remove('active');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
isSubmitting = false;
|
||||
btn_register.disabled = false;
|
||||
document.getElementById('loadingOverlay').classList.remove('active');
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// 응답 처리 로직
|
||||
if (targetId === 'email-validation') {
|
||||
// commonUserInfoForm의 함수 호출
|
||||
handleEmailValidationResponse(response);
|
||||
} else if (targetId === 'password-validation' || targetId === 'password-match-validation') {
|
||||
// 비밀번호 검증은 컴포넌트에서 처리하도록 함
|
||||
return;
|
||||
}
|
||||
// 취소 버튼 클릭 시
|
||||
document.getElementById('cancelButton').addEventListener('click', function() {
|
||||
location.href = '/login';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
Reference in New Issue
Block a user