아이디찾기 스타일 적용
This commit is contained in:
@@ -6571,7 +6571,6 @@ select.form-control {
|
||||
}
|
||||
|
||||
.login-page {
|
||||
min-height: calc(100vh - 140px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -6855,6 +6854,454 @@ select.form-control {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.account-recovery-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #EDF9FE;
|
||||
padding: 40px 20px;
|
||||
position: relative;
|
||||
min-height: calc(100vh - 200px);
|
||||
margin-top: 60px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.account-recovery-container {
|
||||
width: 100%;
|
||||
max-width: 540px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.account-recovery-card {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.account-recovery-card {
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-recovery-logo {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.account-recovery-logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.account-recovery-title {
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
margin: 0 0 32px 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.account-recovery-title {
|
||||
font-size: 24px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-recovery-tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
margin-bottom: 40px;
|
||||
width: 100%;
|
||||
background: #F8F9FA;
|
||||
border-radius: 12px;
|
||||
padding: 4px;
|
||||
}
|
||||
.account-recovery-tabs .tab-link {
|
||||
flex: 1;
|
||||
padding: 14px 24px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #64748B;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.account-recovery-tabs .tab-link:hover {
|
||||
color: #0049B4;
|
||||
background: rgba(0, 73, 180, 0.05);
|
||||
}
|
||||
.account-recovery-tabs .tab-link.active {
|
||||
color: #FFFFFF;
|
||||
background: #0049B4;
|
||||
box-shadow: 0 2px 4px rgba(0, 73, 180, 0.2);
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.account-recovery-tabs .tab-link {
|
||||
padding: 12px 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-alert {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
padding: 14px 18px;
|
||||
border-radius: 12px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
animation: slideDown 0.3s ease;
|
||||
}
|
||||
.account-alert i {
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.account-alert.alert-error {
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
color: #FF6B6B;
|
||||
border: 1px solid rgba(255, 107, 107, 0.3);
|
||||
}
|
||||
.account-alert.alert-success {
|
||||
background: rgba(107, 207, 127, 0.1);
|
||||
color: #6BCF7F;
|
||||
border: 1px solid rgba(107, 207, 127, 0.3);
|
||||
}
|
||||
.account-alert.alert-info {
|
||||
background: rgba(0, 73, 180, 0.1);
|
||||
color: #0049B4;
|
||||
border: 1px solid rgba(0, 73, 180, 0.3);
|
||||
}
|
||||
|
||||
.account-recovery-form {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.account-recovery-form .form-group {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.account-recovery-form .form-group:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.account-recovery-form .form-label {
|
||||
display: block;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1A1A2E;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.account-recovery-form .form-input {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
padding: 0 24px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #1A1A2E;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.account-recovery-form .form-input::placeholder {
|
||||
color: #94A3B8;
|
||||
}
|
||||
.account-recovery-form .form-input:hover {
|
||||
border-color: #CBD5E1;
|
||||
}
|
||||
.account-recovery-form .form-input:focus {
|
||||
border-color: #0049B4;
|
||||
box-shadow: 0 0 0 4px rgba(0, 73, 180, 0.08);
|
||||
}
|
||||
.account-recovery-form .form-input:disabled {
|
||||
background: #F8F9FA;
|
||||
border-color: #E2E8F0;
|
||||
color: #94A3B8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.account-recovery-form .form-input.error {
|
||||
border-color: #FF6B6B;
|
||||
}
|
||||
.account-recovery-form .form-select {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
padding: 0 24px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #1A1A2E;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%2364748B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 24px center;
|
||||
padding-right: 50px;
|
||||
}
|
||||
.account-recovery-form .form-select:hover {
|
||||
border-color: #CBD5E1;
|
||||
}
|
||||
.account-recovery-form .form-select:focus {
|
||||
border-color: #0049B4;
|
||||
box-shadow: 0 0 0 4px rgba(0, 73, 180, 0.08);
|
||||
}
|
||||
.account-recovery-form .form-select:disabled {
|
||||
background-color: #F8F9FA;
|
||||
border-color: #E2E8F0;
|
||||
color: #94A3B8;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.account-recovery-form .form-select option {
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.phone-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.phone-input-group .phone-prefix {
|
||||
flex: 0 0 140px;
|
||||
}
|
||||
.phone-input-group .phone-middle,
|
||||
.phone-input-group .phone-last {
|
||||
flex: 1;
|
||||
}
|
||||
.phone-input-group .phone-separator {
|
||||
font-size: 16px;
|
||||
color: #64748B;
|
||||
font-weight: 500;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.phone-input-group .phone-prefix {
|
||||
flex: 0 0 110px;
|
||||
}
|
||||
.phone-input-group .phone-separator {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-number-group {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.auth-input-group {
|
||||
position: relative;
|
||||
}
|
||||
.auth-input-group .auth-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.auth-input-group .auth-timer {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #FF6B6B;
|
||||
pointer-events: none;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.auth-input-group .auth-timer {
|
||||
font-size: 14px;
|
||||
right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-request-button,
|
||||
.auth-verify-button {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
padding: 0 32px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
background: #0049B4;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
.auth-request-button:hover,
|
||||
.auth-verify-button:hover {
|
||||
background: rgb(0, 62.6583333333, 154.5);
|
||||
}
|
||||
.auth-request-button:active,
|
||||
.auth-verify-button:active {
|
||||
background: rgb(0, 52.3166666667, 129);
|
||||
}
|
||||
.auth-request-button:disabled,
|
||||
.auth-verify-button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.auth-request-button,
|
||||
.auth-verify-button {
|
||||
height: 60px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.form-actions .cancel-button,
|
||||
.form-actions .submit-button {
|
||||
flex: 1;
|
||||
height: 70px;
|
||||
padding: 0 32px;
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
.form-actions .cancel-button {
|
||||
color: #64748B;
|
||||
background: #FFFFFF;
|
||||
border: 2px solid #E2E8F0;
|
||||
}
|
||||
.form-actions .cancel-button:hover {
|
||||
background: #F8F9FA;
|
||||
border-color: #CBD5E1;
|
||||
color: #475569;
|
||||
}
|
||||
.form-actions .cancel-button:active {
|
||||
background: #F1F5F9;
|
||||
border-color: #94A3B8;
|
||||
}
|
||||
.form-actions .submit-button {
|
||||
color: #FFFFFF;
|
||||
background: #0049B4;
|
||||
}
|
||||
.form-actions .submit-button:hover {
|
||||
background: rgb(0, 62.6583333333, 154.5);
|
||||
}
|
||||
.form-actions .submit-button:active {
|
||||
background: rgb(0, 52.3166666667, 129);
|
||||
}
|
||||
.form-actions .submit-button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.form-actions .cancel-button,
|
||||
.form-actions .submit-button {
|
||||
height: 60px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(237, 249, 254, 0.9);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.loading-overlay.active {
|
||||
display: flex;
|
||||
}
|
||||
.loading-overlay .spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #DDDDDD;
|
||||
border-top-color: #0049B4;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.account-recovery-page {
|
||||
padding: 20px 12px;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.account-recovery-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.account-recovery-logo {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.account-recovery-form .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.account-recovery-form .form-label {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.account-recovery-form .form-input,
|
||||
.account-recovery-form .form-select {
|
||||
height: 60px;
|
||||
padding: 0 20px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.auth-request-button,
|
||||
.auth-verify-button {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.form-actions {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
.signup-selection-page {
|
||||
min-height: calc(100vh - 140px);
|
||||
display: flex;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -41,6 +41,7 @@
|
||||
@import 'pages/api-market';
|
||||
@import 'pages/documentation';
|
||||
@import 'pages/login';
|
||||
@import 'pages/account-recovery';
|
||||
@import 'pages/signup-selection';
|
||||
@import 'pages/mypage';
|
||||
@import 'pages/apikey-register';
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
.login-page {
|
||||
min-height: calc(100vh - 140px); // Account for header and footer
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1,415 +1,341 @@
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
|
||||
<body>
|
||||
<section layout:fragment="contentFragment" class="content">
|
||||
<div class="content_wrap">
|
||||
<!-- 타이틀 -->
|
||||
<div class="sub_title2" id="title-find-id">
|
||||
<h2 class="title add5">아이디 찾기</h2>
|
||||
<th:block layout:fragment="contentFragment">
|
||||
<div class="account-recovery-page">
|
||||
<div class="account-recovery-container">
|
||||
<div class="account-recovery-card">
|
||||
<!-- Logo -->
|
||||
<div class="account-recovery-logo">
|
||||
<img th:src="@{/img/logo/logo_box.png}" alt="광주은행">
|
||||
</div>
|
||||
|
||||
<div class="inner i_cs10 h_inner2">
|
||||
<!-- 탭 메뉴 -->
|
||||
<div class="tab-wrap">
|
||||
<div class="tabs">
|
||||
<ul class="tab_nav tabm">
|
||||
<li class="active"><a href="">아이디 찾기</a></li>
|
||||
<li><a th:href="@{/account_recovery(tab='resetPassword')}">비밀번호 초기화</a></li>
|
||||
</ul>
|
||||
<!-- Header Title -->
|
||||
<h2 class="account-recovery-title">아이디 찾기</h2>
|
||||
|
||||
<!-- 아이디 찾기 폼 -->
|
||||
<div class="tab active">
|
||||
<div class="form_type">
|
||||
<form id="accountForm" role="form" name="accountForm" th:action="@{/find_id}" method="post">
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<input type="hidden" id="mobileNumber" name="mobileNumber" th:value="${mobileNumber}">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="account-recovery-tabs">
|
||||
<a href="#" class="tab-link active">아이디 찾기</a>
|
||||
<a th:href="@{/account_recovery(tab='resetPassword')}" class="tab-link">비밀번호 초기화</a>
|
||||
</div>
|
||||
|
||||
<!-- 성명 입력 -->
|
||||
<div class="info1 p_top2">
|
||||
<p class="title w_tit">
|
||||
<span class="dot">성명</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" name="userName"
|
||||
th:value="${userName}" class="common_input_type_1" placeholder="성명"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Alert Messages -->
|
||||
<div id="alertContainer"></div>
|
||||
|
||||
<!-- 휴대폰 번호 입력 -->
|
||||
<div class="info1 pt28">
|
||||
<p class="title w_tit">
|
||||
<span class="dot">휴대폰 번호</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp5">
|
||||
<span class="input">
|
||||
<div class="custom_select ct_sc w_inp10 h_inp2 select_mobile_prefix" id="prefix_select">
|
||||
<div class="select_selected active">선택</div>
|
||||
<ul class="common_selecttype select_hide select_items ct_sc2">
|
||||
<li>010</li>
|
||||
<li>011</li>
|
||||
<li>016</li>
|
||||
<li>017</li>
|
||||
<li>018</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
<span class="dash">-</span>
|
||||
<span class="input">
|
||||
<input type="tel" name="phoneMiddle" maxlength="4" class="common_input_type_1 w_inp11"
|
||||
placeholder="앞자리" required>
|
||||
</span>
|
||||
<span class="dash">-</span>
|
||||
<span class="input">
|
||||
<input type="tel" name="phoneLast" maxlength="4" class="common_input_type_1 w_inp11"
|
||||
placeholder="뒷자리" required>
|
||||
</span>
|
||||
</div>
|
||||
<div class="btn_check">
|
||||
<a class="common_btn_type_2 cbt btn_auth"
|
||||
th:hx-post="@{/auth/request_auth_number}"
|
||||
hx-include="#mobileNumber"
|
||||
hx-target="#authNumberContainer"
|
||||
hx-swap="none">
|
||||
<span>인증번호 받기</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 인증번호 입력 -->
|
||||
<div class="info1 pt28 auth-number-container" id="authNumberContainer">
|
||||
<p class="title w_tit">
|
||||
<span class="dot">인증번호 입력</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp5">
|
||||
<div class="certify_box">
|
||||
<input type="text" name="authNumber" id="authNumber" maxlength="6"
|
||||
pattern="[0-9]*"
|
||||
inputmode="numeric"
|
||||
class="common_input_type_1 w_input auth-number-input"
|
||||
placeholder="SMS 인증번호" required>
|
||||
<span class="certify_time" id="certify_time">03:00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn_check">
|
||||
<a class="common_btn_type_2 cbt btn_verify_auth"
|
||||
th:hx-post="@{/auth/verify_auth_number}"
|
||||
hx-include="#mobileNumber, #authNumber"
|
||||
hx-target="#authNumberValidation"
|
||||
hx-swap="none">
|
||||
<span>인증번호 확인</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="authNumberValidation"></div>
|
||||
<!-- 제출 버튼 -->
|
||||
<div class="btn_application m_top">
|
||||
<a type="button" class="common_btn_type_1 gray btn_cancel"><span>취소</span></a>
|
||||
<div class="btn_gap"></div>
|
||||
<button type="button" class="common_btn_type_1" onclick="validateAndSubmit()"><span>아이디 찾기</span></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Account Recovery Form -->
|
||||
<form id="accountForm" role="form" name="accountForm" th:action="@{/find_id}" method="post" class="account-recovery-form">
|
||||
<input type="hidden" id="mobileNumber" name="mobileNumber" th:value="${mobileNumber}">
|
||||
|
||||
<!-- Name Field -->
|
||||
<div class="form-group">
|
||||
<label for="userName" class="form-label">성명</label>
|
||||
<input type="text"
|
||||
id="userName"
|
||||
name="userName"
|
||||
th:value="${userName}"
|
||||
class="form-input"
|
||||
placeholder="성명"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<!-- Phone Number Fields -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">휴대폰 번호</label>
|
||||
<div class="phone-input-group">
|
||||
<select id="phonePrefix" class="form-select phone-prefix" required>
|
||||
<option value="">선택</option>
|
||||
<option value="010">010</option>
|
||||
<option value="011">011</option>
|
||||
<option value="016">016</option>
|
||||
<option value="017">017</option>
|
||||
<option value="018">018</option>
|
||||
</select>
|
||||
<span class="phone-separator">-</span>
|
||||
<input type="tel"
|
||||
id="phoneMiddle"
|
||||
name="phoneMiddle"
|
||||
maxlength="4"
|
||||
class="form-input phone-middle"
|
||||
placeholder="0000"
|
||||
pattern="[0-9]*"
|
||||
inputmode="numeric"
|
||||
required>
|
||||
<span class="phone-separator">-</span>
|
||||
<input type="tel"
|
||||
id="phoneLast"
|
||||
name="phoneLast"
|
||||
maxlength="4"
|
||||
class="form-input phone-last"
|
||||
placeholder="0000"
|
||||
pattern="[0-9]*"
|
||||
inputmode="numeric"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Number Request Button -->
|
||||
<button type="button" class="auth-request-button" id="requestAuthButton">
|
||||
인증번호 받기
|
||||
</button>
|
||||
|
||||
<!-- Auth Number Input -->
|
||||
<div class="form-group auth-number-group" id="authNumberGroup" style="display: none;">
|
||||
<label for="authNumber" class="form-label">인증번호 입력</label>
|
||||
<div class="auth-input-group">
|
||||
<input type="text"
|
||||
id="authNumber"
|
||||
name="authNumber"
|
||||
maxlength="6"
|
||||
class="form-input auth-input"
|
||||
placeholder="인증번호 6자리"
|
||||
pattern="[0-9]*"
|
||||
inputmode="numeric"
|
||||
required>
|
||||
<span class="auth-timer" id="authTimer">03:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Number Verify Button -->
|
||||
<button type="button" class="auth-verify-button" id="verifyAuthButton" style="display: none;">
|
||||
인증번호 확인
|
||||
</button>
|
||||
|
||||
<!-- Submit Buttons -->
|
||||
<div class="form-actions">
|
||||
<button type="button" class="cancel-button" id="cancelButton">취소</button>
|
||||
<button type="submit" class="submit-button" id="submitButton">아이디 찾기</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div class="loading-overlay" id="loadingOverlay">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</th:block>
|
||||
</body>
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script th:inline="javascript">
|
||||
$(document).ready(function () {
|
||||
const errorMsg = [[${error}]];
|
||||
<script th:inline="javascript">
|
||||
$(document).ready(function() {
|
||||
const errorMsg = [[${error}]];
|
||||
if (errorMsg) {
|
||||
showAlert(errorMsg, 'error');
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
customPopups.showAlert(errorMsg);
|
||||
}
|
||||
const paramError = new URL(window.location.href).searchParams.get('error');
|
||||
if (paramError) {
|
||||
showAlert(decodeURIComponent(paramError), 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// Flash Attribute로 전달되는 에러 메시지 처리
|
||||
const paramError = new URL(window.location.href).searchParams.get('error');
|
||||
if (paramError) {
|
||||
customPopups.showAlert(decodeURIComponent(paramError));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script th:inline="javascript">
|
||||
function combineMobileNumber(evt) {
|
||||
let form = $('#accountForm');
|
||||
let prefix = form.find('.select_mobile_prefix .select_selected').text().trim();
|
||||
let middle = form.find('input[name="phoneMiddle"]').val().trim();
|
||||
let last = form.find('input[name="phoneLast"]').val().trim();
|
||||
// Alert 표시 함수
|
||||
function showAlert(message, type = 'info') {
|
||||
const alertContainer = $('#alertContainer');
|
||||
const alertClass = type === 'error' ? 'alert-error' : type === 'success' ? 'alert-success' : 'alert-info';
|
||||
const iconClass = type === 'error' ? 'fa-exclamation-circle' : type === 'success' ? 'fa-check-circle' : 'fa-info-circle';
|
||||
|
||||
if (prefix === '선택' || !middle || !last) {
|
||||
customPopups.showAlert('휴대폰 번호를 올바르게 입력해주세요.');
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
const alertHtml = `
|
||||
<div class="account-alert ${alertClass}">
|
||||
<i class="fas ${iconClass}"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let combinedNumber = `${prefix}-${middle}-${last}`;
|
||||
let mobileNumberInput = document.getElementById('mobileNumber');
|
||||
if (mobileNumberInput) {
|
||||
mobileNumberInput.value = combinedNumber;
|
||||
}
|
||||
return combinedNumber;
|
||||
alertContainer.html(alertHtml);
|
||||
setTimeout(() => alertContainer.empty(), 5000);
|
||||
}
|
||||
|
||||
// 휴대폰 번호 결합 함수
|
||||
function combineMobileNumber() {
|
||||
const prefix = $('#phonePrefix').val();
|
||||
const middle = $('#phoneMiddle').val().trim();
|
||||
const last = $('#phoneLast').val().trim();
|
||||
|
||||
if (!prefix || prefix === '' || !middle || !last) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${prefix}-${middle}-${last}`;
|
||||
}
|
||||
|
||||
// 타이머 변수
|
||||
let countdownInterval;
|
||||
let isAuthNumberRequested = false;
|
||||
let isAuthVerified = false;
|
||||
|
||||
// 인증번호 타이머 시작
|
||||
function startAuthTimer() {
|
||||
clearInterval(countdownInterval);
|
||||
let remainingTime = 180; // 3분
|
||||
|
||||
function updateTimer() {
|
||||
if (remainingTime <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
$('#authTimer').text('시간 초과').css('color', '#FF6B6B');
|
||||
showAlert('입력 시간이 초과되었습니다. 인증번호를 다시 발송해주세요.', 'error');
|
||||
|
||||
// 필드 활성화
|
||||
$('#phonePrefix, #phoneMiddle, #phoneLast').prop('disabled', false);
|
||||
$('#requestAuthButton').prop('disabled', false);
|
||||
isAuthNumberRequested = false;
|
||||
} else {
|
||||
const minutes = Math.floor(remainingTime / 60);
|
||||
const seconds = remainingTime % 60;
|
||||
$('#authTimer').text(`${minutes}:${seconds.toString().padStart(2, '0')}`);
|
||||
remainingTime--;
|
||||
}
|
||||
}
|
||||
|
||||
// 폼 전체 유효성 검증 함수
|
||||
function validateAndSubmit() {
|
||||
const form = document.getElementById('accountForm');
|
||||
updateTimer();
|
||||
countdownInterval = setInterval(updateTimer, 1000);
|
||||
}
|
||||
|
||||
// 성명 검증
|
||||
const userName = form.querySelector('input[name="userName"]').value.trim();
|
||||
if (!userName) {
|
||||
customPopups.showAlert('성명을 입력해주세요.');
|
||||
return false;
|
||||
}
|
||||
// 인증번호 요청 버튼
|
||||
$('#requestAuthButton').on('click', function() {
|
||||
const mobileNumber = combineMobileNumber();
|
||||
|
||||
// 휴대폰 번호 검증
|
||||
const prefix = form.querySelector('.select_mobile_prefix .select_selected').textContent.trim();
|
||||
const phoneMiddle = form.querySelector('input[name="phoneMiddle"]').value.trim();
|
||||
const phoneLast = form.querySelector('input[name="phoneLast"]').value.trim();
|
||||
if (!mobileNumber) {
|
||||
showAlert('휴대폰 번호를 올바르게 입력해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix === '선택' || !phoneMiddle || !phoneLast) {
|
||||
customPopups.showAlert('휴대폰 번호를 올바르게 입력해주세요.');
|
||||
return false;
|
||||
}
|
||||
$('#mobileNumber').val(mobileNumber);
|
||||
$('#loadingOverlay').fadeIn(200);
|
||||
|
||||
// 인증번호 요청 여부 검증
|
||||
if (!window.isAuthNumberRequested) {
|
||||
customPopups.showAlert('휴대폰 인증을 먼저 진행해주세요.');
|
||||
return false;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/auth/request_auth_number',
|
||||
type: 'POST',
|
||||
data: {
|
||||
mobileNumber: mobileNumber,
|
||||
_csrf: $('input[name="_csrf"]').val()
|
||||
},
|
||||
success: function(response) {
|
||||
$('#loadingOverlay').fadeOut(200);
|
||||
|
||||
// 인증 완료 여부 검증
|
||||
const authNumberInput = document.getElementById('authNumber');
|
||||
if (!authNumberInput?.disabled) {
|
||||
customPopups.showAlert('휴대폰 인증을 완료해주세요.');
|
||||
return false;
|
||||
}
|
||||
if (response.valid) {
|
||||
showAlert(response.message || '인증번호가 발송되었습니다.', 'success');
|
||||
|
||||
// 모든 검증을 통과하면 휴대폰 번호 결합 후 form submit
|
||||
combineMobileNumber({
|
||||
preventDefault: () => {
|
||||
}
|
||||
});
|
||||
// 휴대폰 번호 필드 비활성화
|
||||
$('#phonePrefix, #phoneMiddle, #phoneLast').prop('disabled', true);
|
||||
$('#requestAuthButton').prop('disabled', true);
|
||||
|
||||
// 세션 검증을 위한 파라미터 추가
|
||||
const currentAction = form.getAttribute('action');
|
||||
form.setAttribute('action', currentAction + (currentAction.includes('?') ? '&' : '?') + 'showError=true');
|
||||
// 인증번호 입력 영역 표시
|
||||
$('#authNumberGroup').slideDown(300);
|
||||
$('#verifyAuthButton').slideDown(300);
|
||||
|
||||
form.submit();
|
||||
isAuthNumberRequested = true;
|
||||
startAuthTimer();
|
||||
} else {
|
||||
showAlert(response.message || '인증번호 발송에 실패했습니다.', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('#loadingOverlay').fadeOut(200);
|
||||
showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:beforeRequest', function (evt) {
|
||||
combineMobileNumber(evt);
|
||||
});
|
||||
// 인증번호 확인 버튼
|
||||
$('#verifyAuthButton').on('click', function() {
|
||||
if (!isAuthNumberRequested) {
|
||||
showAlert('휴대폰 번호 인증을 먼저 진행해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.addEventListener('htmx:configRequest', function (evt) {
|
||||
evt.detail.parameters['mobileNumber'] = combineMobileNumber(evt);
|
||||
});
|
||||
const authNumber = $('#authNumber').val().trim();
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 기존 휴대폰 번호 복원
|
||||
const mobileNumber = document.getElementById('mobileNumber')?.value;
|
||||
if (mobileNumber) {
|
||||
const mobileParts = mobileNumber.split('-');
|
||||
if (mobileParts.length === 3) {
|
||||
const PrefixSelect = document.querySelector('#prefix_select .select_selected');
|
||||
if (PrefixSelect) {
|
||||
PrefixSelect.textContent = mobileParts[0];
|
||||
}
|
||||
const orgMiddleInput = document.querySelector('[name="phoneMiddle"]');
|
||||
if (orgMiddleInput) {
|
||||
orgMiddleInput.value = mobileParts[1];
|
||||
}
|
||||
const orgLastInput = document.querySelector('[name="phoneLast"]');
|
||||
if (orgLastInput) {
|
||||
orgLastInput.value = mobileParts[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (authNumber.length !== 6) {
|
||||
showAlert('인증번호 6자리를 입력해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
activateSelect();
|
||||
let countdownInterval;
|
||||
window.isAuthNumberRequested = false;
|
||||
const mobileNumber = $('#mobileNumber').val();
|
||||
$('#loadingOverlay').fadeIn(200);
|
||||
|
||||
function startAuthNumberTimer() {
|
||||
clearInterval(countdownInterval);
|
||||
let remainingTime = 180; // 3분
|
||||
$.ajax({
|
||||
url: '/auth/verify_auth_number',
|
||||
type: 'POST',
|
||||
data: {
|
||||
mobileNumber: mobileNumber,
|
||||
authNumber: authNumber,
|
||||
_csrf: $('input[name="_csrf"]').val()
|
||||
},
|
||||
success: function(response) {
|
||||
$('#loadingOverlay').fadeOut(200);
|
||||
|
||||
function updateRemainingTime() {
|
||||
if (remainingTime <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
let timerElement = document.getElementById('certify_time');
|
||||
if (timerElement) {
|
||||
timerElement.textContent = '입력 시간이 초과되었습니다.';
|
||||
}
|
||||
customPopups.showAlert('입력 시간이 초과되었습니다. 인증번호를 다시 발송해주세요.');
|
||||
if (response.valid) {
|
||||
showAlert(response.message || '인증이 완료되었습니다.', 'success');
|
||||
|
||||
let form = $('#accountForm');
|
||||
let mobilePrefix = form.find('.select_mobile_prefix');
|
||||
let phoneMiddle = form.find('input[name="phoneMiddle"]');
|
||||
let phoneLast = form.find('input[name="phoneLast"]');
|
||||
let authButton = document.querySelector('.btn_auth');
|
||||
let verifyAuthButton = document.querySelector('.btn_verify_auth');
|
||||
let authNumberInput = document.getElementById('authNumber');
|
||||
// 인증번호 입력 필드 비활성화
|
||||
$('#authNumber').prop('disabled', true);
|
||||
$('#verifyAuthButton').prop('disabled', true);
|
||||
|
||||
// 입력 필드 활성화
|
||||
mobilePrefix.removeClass('disabled');
|
||||
phoneMiddle.prop('readonly', false);
|
||||
phoneLast.prop('readonly', false);
|
||||
if (authButton) {
|
||||
authButton.disabled = false;
|
||||
}
|
||||
if (verifyAuthButton) {
|
||||
verifyAuthButton.disabled = true;
|
||||
}
|
||||
if (authNumberInput) {
|
||||
authNumberInput.readOnly = true;
|
||||
}
|
||||
window.isAuthNumberRequested = false;
|
||||
} else {
|
||||
let minutes = Math.floor(remainingTime / 60);
|
||||
let seconds = remainingTime % 60;
|
||||
let timerElement = document.getElementById('certify_time');
|
||||
if (timerElement) {
|
||||
timerElement.textContent = `남은 시간: ${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
}
|
||||
remainingTime--;
|
||||
}
|
||||
}
|
||||
clearInterval(countdownInterval);
|
||||
$('#authTimer').text('인증 완료').css('color', '#6BCF7F');
|
||||
|
||||
updateRemainingTime();
|
||||
countdownInterval = setInterval(updateRemainingTime, 1000);
|
||||
}
|
||||
isAuthVerified = true;
|
||||
} else {
|
||||
showAlert(response.message || '인증번호가 일치하지 않습니다.', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('#loadingOverlay').fadeOut(200);
|
||||
showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function handleAuthNumberRequest(response) {
|
||||
if (response.valid) {
|
||||
let form = $('#accountForm');
|
||||
let mobilePrefix = form.find('.select_mobile_prefix');
|
||||
let phoneMiddle = form.find('input[name="phoneMiddle"]');
|
||||
let phoneLast = form.find('input[name="phoneLast"]');
|
||||
let authButton = document.querySelector('.btn_auth');
|
||||
// 폼 제출
|
||||
$('#accountForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
mobilePrefix.addClass('disabled');
|
||||
phoneMiddle.prop('disabled', true);
|
||||
phoneLast.prop('disabled', true);
|
||||
if (authButton) {
|
||||
authButton.disabled = true;
|
||||
}
|
||||
const userName = $('#userName').val().trim();
|
||||
if (!userName) {
|
||||
showAlert('성명을 입력해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
window.isAuthNumberRequested = true;
|
||||
startAuthNumberTimer();
|
||||
customPopups.showAlert(response.message || '인증번호가 발송되었습니다.');
|
||||
} else {
|
||||
customPopups.showAlert(response.message || '알 수 없는 오류가 발생했습니다.');
|
||||
let authButton = document.querySelector('.btn_auth');
|
||||
if (authButton) {
|
||||
authButton.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const mobileNumber = combineMobileNumber();
|
||||
if (!mobileNumber) {
|
||||
showAlert('휴대폰 번호를 올바르게 입력해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
function handleAuthNumberVerification(response) {
|
||||
customPopups.showAlert(response.message);
|
||||
if (response.valid) {
|
||||
let authNumberInput = document.getElementById('authNumber');
|
||||
let verifyAuthButton = document.querySelector('.btn_verify_auth');
|
||||
let timerElement = document.getElementById('certify_time');
|
||||
if (!isAuthNumberRequested) {
|
||||
showAlert('휴대폰 인증을 먼저 진행해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (authNumberInput) {
|
||||
authNumberInput.readOnly = true;
|
||||
authNumberInput.disabled = true;
|
||||
}
|
||||
if (verifyAuthButton) {
|
||||
verifyAuthButton.disabled = true;
|
||||
}
|
||||
if (!isAuthVerified) {
|
||||
showAlert('휴대폰 인증을 완료해주세요.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(countdownInterval);
|
||||
if (timerElement) {
|
||||
timerElement.textContent = '인증 완료';
|
||||
}
|
||||
window.isAuthVerified = true;
|
||||
document.dispatchEvent(new Event('authVerified'));
|
||||
}
|
||||
}
|
||||
$('#mobileNumber').val(mobileNumber);
|
||||
this.submit();
|
||||
});
|
||||
|
||||
// HTMX 응답 처리
|
||||
document.body.addEventListener('htmx:afterSwap', function (event) {
|
||||
let targetId = event.detail.target ? event.detail.target.id : null;
|
||||
if (targetId && ['authNumberContainer', 'authNumberValidation'].includes(targetId)) {
|
||||
let response;
|
||||
try {
|
||||
response = typeof event.detail.xhr.response === 'string'
|
||||
? JSON.parse(event.detail.xhr.response)
|
||||
: event.detail.xhr.response;
|
||||
} catch (e) {
|
||||
console.error('Failed to parse response as JSON:', event.detail.xhr.response);
|
||||
customPopups.showAlert('서버에서 예상치 못한 응답을 받았습니다.');
|
||||
return;
|
||||
}
|
||||
// 취소 버튼
|
||||
$('#cancelButton').on('click', function() {
|
||||
window.location.href = '/login';
|
||||
});
|
||||
|
||||
switch (targetId) {
|
||||
case 'authNumberContainer':
|
||||
handleAuthNumberRequest(response);
|
||||
break;
|
||||
case 'authNumberValidation':
|
||||
handleAuthNumberVerification(response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:responseError', function (event) {
|
||||
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
});
|
||||
|
||||
// 인증번호 확인 버튼 이벤트
|
||||
let verifyAuthButton = document.querySelector('.btn_verify_auth');
|
||||
if (verifyAuthButton) {
|
||||
verifyAuthButton.addEventListener('click', function (e) {
|
||||
if (!window.isAuthNumberRequested) {
|
||||
e.preventDefault();
|
||||
customPopups.showAlert('휴대폰 번호 인증을 먼저 진행해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
let form = $('#accountForm');
|
||||
let authNumberInput = form.find('.auth-number-input');
|
||||
let authNumber = authNumberInput.val().trim();
|
||||
|
||||
if (authNumber.length !== 6) {
|
||||
e.preventDefault();
|
||||
customPopups.showAlert('입력된 인증번호가 유효하지 않습니다.');
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 취소 버튼 이벤트
|
||||
let cancelButton = document.querySelector('.btn_cancel');
|
||||
if (cancelButton) {
|
||||
cancelButton.addEventListener('click', function () {
|
||||
location.href = '/login';
|
||||
});
|
||||
}
|
||||
|
||||
// 폼 제출 이벤트 처리
|
||||
const accountForm = document.getElementById('accountForm');
|
||||
if (accountForm) {
|
||||
accountForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
validateAndSubmit();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
// 기존 휴대폰 번호 복원
|
||||
$(document).ready(function() {
|
||||
const savedMobileNumber = $('#mobileNumber').val();
|
||||
if (savedMobileNumber) {
|
||||
const parts = savedMobileNumber.split('-');
|
||||
if (parts.length === 3) {
|
||||
$('#phonePrefix').val(parts[0]);
|
||||
$('#phoneMiddle').val(parts[1]);
|
||||
$('#phoneLast').val(parts[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user