feat: 로그인 퍼블리싱

This commit is contained in:
hong
2026-07-30 10:01:38 +09:00
parent 4181d114c1
commit faaf9e8cd6
20 changed files with 1120 additions and 1238 deletions
@@ -73,7 +73,7 @@ public class AppServiceFacade {
List<AppRequest> appRequests = appRequestRepository.findAllByOrgAndTypeIsInAndApproval_ApprovalStatusIn(portalOrg, types, List<AppRequest> appRequests = appRequestRepository.findAllByOrgAndTypeIsInAndApproval_ApprovalStatusIn(portalOrg, types,
Arrays.asList(new ProcessingState(), new RequestedState())); Arrays.asList(new ProcessingState(), new RequestedState()));
// 승인정보(approval) 없는 신청도 목록에 노출한다. (사용자가 직접 삭제 가능) // 승인정보(approval) 없는 신청도 목록에 노출`한다. (사용자가 직접 삭제 가능)
appRequests.addAll(appRequestRepository .findAllByOrgAndTypeIsInAndApprovalIsNull(portalOrg, types)); appRequests.addAll(appRequestRepository .findAllByOrgAndTypeIsInAndApprovalIsNull(portalOrg, types));
return appRequests; return appRequests;
+182 -237
View File
@@ -1421,7 +1421,7 @@ hr {
.nav-menu { .nav-menu {
display: flex; display: flex;
list-style: none; list-style: none;
gap: 28px; gap: 24px;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@@ -1431,6 +1431,7 @@ hr {
.nav-menu > li.submenu-open > .nav-link { .nav-menu > li.submenu-open > .nav-link {
background: var(--light-bg); background: var(--light-bg);
color: var(--primary-blue); color: var(--primary-blue);
border-radius: 9px;
} }
.nav-menu > li.submenu-open .sub-menu { .nav-menu > li.submenu-open .sub-menu {
visibility: visible; visibility: visible;
@@ -1476,7 +1477,7 @@ hr {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: var(--text-gray); color: var(--text-gray);
padding: 8px; padding: 12px;
position: relative; position: relative;
transition: var(--transition-smooth); transition: var(--transition-smooth);
} }
@@ -4319,7 +4320,7 @@ select.form-control {
box-shadow: 0 0 0 3px rgba(0, 73, 180, 0.1); box-shadow: 0 0 0 3px rgba(0, 73, 180, 0.1);
} }
.form-input:disabled, .form-input.input-readonly { .form-input:disabled, .form-input.input-readonly {
background: #F8FAFC; background: #e9ecef !important;
color: #64748B; color: #64748B;
cursor: not-allowed; cursor: not-allowed;
} }
@@ -12133,26 +12134,27 @@ body.index-page-body {
} }
.account-recovery-page { .account-recovery-page {
display: flex; display: flex;
align-items: flex-start; align-items: center;
justify-content: center; justify-content: center;
background: transparent; padding: 100px 20px;
padding: 0;
position: relative; position: relative;
min-height: auto; border-radius: 12px;
margin: 0; margin-bottom: 50px;
} }
.account-recovery-container { .account-recovery-container {
width: 100%; width: 100%;
max-width: 800px; max-width: 650px;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;
padding: 24px; padding: 24px;
} }
.account-recovery-card { .account-recovery-card {
background: transparent; background: #FFFFFF;
padding: 0; border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
padding: 36px 36px 28px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
@@ -12160,7 +12162,8 @@ body.index-page-body {
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.account-recovery-card { .account-recovery-card {
padding: 0 20px; padding: 24px 20px 20px;
border-radius: 16px;
} }
} }
@@ -12175,10 +12178,9 @@ body.index-page-body {
.account-recovery-tabs { .account-recovery-tabs {
display: flex; display: flex;
gap: 0; gap: 0;
margin-bottom: 0; margin-bottom: 28px;
width: 100%; width: 100%;
background: transparent; background: transparent;
border-radius: 0;
padding: 0; padding: 0;
} }
.account-recovery-tabs .tab-link { .account-recovery-tabs .tab-link {
@@ -12186,36 +12188,37 @@ body.index-page-body {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 14px 24px; padding: 14px 20px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 16px; font-size: 15px;
font-weight: 700; font-weight: 400;
color: #8c959f; color: #adb5bd;
text-decoration: none; text-decoration: none;
text-align: center; text-align: center;
background: #eceff4; background: #FFFFFF;
border: 1.5px solid transparent;
border-bottom: 1.5px solid #dee2e6;
border-radius: 0; border-radius: 0;
transition: all 0.3s ease; transition: all 0.2s ease;
} cursor: pointer;
.account-recovery-tabs .tab-link:first-child { position: relative;
border-radius: 30px 0 0 0;
}
.account-recovery-tabs .tab-link:last-child {
border-radius: 0 30px 0 0;
} }
.account-recovery-tabs .tab-link:hover { .account-recovery-tabs .tab-link:hover {
color: #3ba4ed; color: #0049b4;
background: #e4e8ed;
} }
.account-recovery-tabs .tab-link.active { .account-recovery-tabs .tab-link.active {
color: #FFFFFF; color: #0049b4;
background: #3ba4ed;
font-weight: 700; font-weight: 700;
border-top: 1.5px solid #0049b4;
border-left: 1.5px solid #0049b4;
border-right: 1.5px solid #0049b4;
border-bottom: 2px solid #FFFFFF;
border-radius: 8px 8px 0px 0px;
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.account-recovery-tabs .tab-link { .account-recovery-tabs .tab-link {
padding: 12px 16px; padding: 12px 10px;
font-size: 14px; font-size: 13px;
} }
} }
@@ -12254,75 +12257,44 @@ body.index-page-body {
.account-recovery-form { .account-recovery-form {
width: 100%; width: 100%;
margin-bottom: 0; margin-bottom: 0;
background: #F6F9FB; background: transparent;
padding: 40px; padding: 0;
border-radius: 0 0 12px 12px;
}
@media (max-width: 576px) {
.account-recovery-form {
padding: 24px 16px;
}
} }
.account-recovery-form .form-group { .account-recovery-form .form-group {
display: flex; display: flex;
align-items: center; flex-direction: column;
gap: 20px; gap: 0;
margin-bottom: 20px; margin-bottom: 12px;
} }
.account-recovery-form .form-group:last-of-type { .account-recovery-form .form-group:last-of-type {
margin-bottom: 0; margin-bottom: 0;
} }
@media (max-width: 768px) {
.account-recovery-form .form-group {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
}
.account-recovery-form .form-label { .account-recovery-form .form-label {
display: flex; display: none;
align-items: center;
flex-shrink: 0;
width: 170px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 15px;
font-weight: 400;
color: #212529;
margin-bottom: 0;
}
.account-recovery-form .form-label .required {
color: #ed5b5b;
margin-left: 2px;
}
@media (max-width: 768px) {
.account-recovery-form .form-label {
width: 100%;
font-size: 14px;
}
} }
.account-recovery-form .form-input { .account-recovery-form .form-input {
flex: 1; width: 100%;
height: 40px; height: 48px;
padding: 0 16px; padding: 0 16px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
color: #212529; color: #212529;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #dadada; border: 1px solid #E2E8F0;
border-radius: 8px; border-radius: 10px;
outline: none; outline: none;
transition: all 0.3s ease; transition: all 0.25s ease;
} }
.account-recovery-form .form-input::placeholder { .account-recovery-form .form-input::placeholder {
color: #dadada; color: #B0B8C1;
} }
.account-recovery-form .form-input:hover { .account-recovery-form .form-input:hover {
border-color: #3ba4ed; border-color: #3ba4ed;
} }
.account-recovery-form .form-input:focus { .account-recovery-form .form-input:focus {
border-color: #3ba4ed; border-color: #3ba4ed;
box-shadow: 0 0 0 2px rgba(59, 164, 237, 0.1); box-shadow: 0 0 0 3px rgba(59, 164, 237, 0.12);
} }
.account-recovery-form .form-input:disabled { .account-recovery-form .form-input:disabled {
background: #F8F9FA; background: #F8F9FA;
@@ -12334,24 +12306,24 @@ body.index-page-body {
border-color: #ed5b5b; border-color: #ed5b5b;
} }
.account-recovery-form .form-select { .account-recovery-form .form-select {
height: 40px; height: 48px;
padding: 0 32px 0 16px; padding: 0 24px 0 12px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
color: #515151; color: #515151;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #dadada; border: 1px solid #E2E8F0;
border-radius: 8px; border-radius: 10px;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
appearance: none; appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath fill='%231D1B20' d='M5 5L0 0h10z'/%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath fill='%231D1B20' d='M5 5L0 0h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right 12px center; background-position: right 8px center;
background-size: 10px 5px; background-size: 10px 5px;
} }
.account-recovery-form .form-select:hover { .account-recovery-form .form-select:hover {
@@ -12359,7 +12331,7 @@ body.index-page-body {
} }
.account-recovery-form .form-select:focus { .account-recovery-form .form-select:focus {
border-color: #3ba4ed; border-color: #3ba4ed;
box-shadow: 0 0 0 2px rgba(59, 164, 237, 0.1); box-shadow: 0 0 0 3px rgba(59, 164, 237, 0.12);
} }
.account-recovery-form .form-select:disabled { .account-recovery-form .form-select:disabled {
background-color: #F8F9FA; background-color: #F8F9FA;
@@ -12373,50 +12345,89 @@ body.index-page-body {
font-size: 14px; font-size: 14px;
} }
.reset-method-group {
margin-bottom: 16px !important;
}
.reset-method-tabs {
display: flex;
gap: 8px;
background: #f0f2f5;
border-radius: 10px;
padding: 4px;
}
.reset-method-tabs .reset-method-tab {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin: 0;
}
.reset-method-tabs .reset-method-tab input[type=radio] {
display: none;
}
.reset-method-tabs .reset-method-tab span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 9px 12px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 13px;
font-weight: 600;
color: #8c959f;
border-radius: 7px;
transition: all 0.2s ease;
text-align: center;
}
.reset-method-tabs .reset-method-tab input[type=radio]:checked + span {
background: #FFFFFF;
color: #212529;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.09);
}
.reset-method-tabs .reset-method-tab:hover span {
color: #3ba4ed;
}
.phone-input-group { .phone-input-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
flex: 1; width: 100%;
} }
.phone-input-group .phone-prefix { .phone-input-group .phone-prefix {
width: 100px; width: 90px;
min-width: 90px;
flex-shrink: 0; flex-shrink: 0;
} }
.phone-input-group .phone-middle, .phone-input-group .phone-middle,
.phone-input-group .phone-last { .phone-input-group .phone-last {
width: 100px; flex: 1;
flex-shrink: 0; min-width: 0;
} }
.phone-input-group .phone-separator { .phone-input-group .phone-separator {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 8px; color: #B0B8C1;
height: 1px; font-size: 14px;
background: #515151;
flex-shrink: 0; flex-shrink: 0;
width: 10px;
height: auto;
background: transparent;
} }
@media (max-width: 768px) { .phone-input-group .phone-separator::before {
content: "-";
}
@media (max-width: 576px) {
.phone-input-group { .phone-input-group {
flex-wrap: wrap; flex-wrap: wrap;
gap: 6px; gap: 6px;
} }
.phone-input-group .phone-prefix, .phone-input-group .phone-prefix {
.phone-input-group .phone-middle,
.phone-input-group .phone-last {
flex: 1; flex: 1;
min-width: 60px; width: auto;
}
.phone-input-group .phone-separator {
width: 6px;
}
}
@media (max-width: 576px) {
.phone-input-group .phone-prefix,
.phone-input-group .phone-middle,
.phone-input-group .phone-last {
width: 100%;
} }
.phone-input-group .phone-separator { .phone-input-group .phone-separator {
display: none; display: none;
@@ -12431,92 +12442,97 @@ body.index-page-body {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1; width: 100%;
gap: 8px;
}
.auth-input-group.mg-y {
margin-top: 20px;
margin-bottom: 12px;
} }
.auth-input-group .auth-input { .auth-input-group .auth-input {
flex: 1; flex: 1;
padding-right: 60px; min-width: 0;
} }
.auth-input-group .auth-timer { .auth-input-group .auth-timer {
position: absolute; position: absolute;
right: 16px; right: 20px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 13px;
font-weight: 400; font-weight: 500;
color: #ed5b5b; color: #ed5b5b;
pointer-events: none; pointer-events: none;
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.auth-input-group .auth-timer { .auth-input-group .auth-timer {
font-size: 12px; font-size: 12px;
right: 12px; right: 100px;
} }
} }
.auth-request-button, .auth-request-button,
.auth-verify-button { .auth-verify-button {
width: 140px; height: 48px;
height: 40px; padding: 0 16px;
padding: 8px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 13px;
font-weight: 700; font-weight: 700;
color: #FFFFFF; color: #FFFFFF;
background: #a4d6ea; background: #3ba4ed;
border: none; border: none;
border-radius: 8px; border-radius: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
margin: 0;
flex-shrink: 0; flex-shrink: 0;
white-space: nowrap;
line-height: 1; line-height: 1;
} }
.auth-request-button:hover, .auth-request-button:hover,
.auth-verify-button:hover { .auth-verify-button:hover {
background: rgb(147.83125, 206.7151785714, 230.26875); background: rgb(37.3117757009, 153.9304672897, 235.0082242991);
transform: none !important;
} }
.auth-request-button:active, .auth-request-button:active,
.auth-verify-button:active { .auth-verify-button:active {
background: rgb(131.6625, 199.4303571429, 226.5375); background: rgb(21.411588785, 146.3125233645, 233.148411215);
} }
.auth-request-button:disabled, .auth-request-button:disabled,
.auth-verify-button:disabled { .auth-verify-button:disabled {
opacity: 0.6; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.auth-request-button, .auth-request-button,
.auth-verify-button { .auth-verify-button {
width: 100%; width: 100%;
height: 40px; height: 44px;
font-size: 13px; font-size: 13px;
margin-top: 8px; margin-top: 4px;
} }
} }
.account-recovery-card .form-actions { .account-recovery-card .form-actions {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 20px; gap: 12px;
margin-top: 32px; margin-top: 28px;
padding: 32px 0; padding: 0;
border-top: none; border-top: none;
background: transparent; background: transparent;
} }
.account-recovery-card .form-actions .cancel-button, .account-recovery-card .form-actions .cancel-button,
.account-recovery-card .form-actions .submit-button { .account-recovery-card .form-actions .submit-button {
width: 160px; flex: 1;
height: 40px; height: 48px;
padding: 8px; padding: 0 8px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 15px;
font-weight: 700; font-weight: 700;
border: none; border: none;
border-radius: 8px; border-radius: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
line-height: 1; line-height: 1;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -12525,20 +12541,20 @@ body.index-page-body {
} }
.account-recovery-card .form-actions .cancel-button { .account-recovery-card .form-actions .cancel-button {
color: #5f666c; color: #5f666c;
background: #e5e7eb; background: #f0f2f5;
} }
.account-recovery-card .form-actions .cancel-button:hover { .account-recovery-card .form-actions .cancel-button:hover {
background: rgb(215.8869565217, 218.8956521739, 224.9130434783); background: rgb(225.45, 229.39, 235.3);
} }
.account-recovery-card .form-actions .cancel-button:active { .account-recovery-card .form-actions .cancel-button:active {
background: rgb(202.7739130435, 206.7913043478, 214.8260869565); background: rgb(210.9, 216.78, 225.6);
} }
.account-recovery-card .form-actions .submit-button { .account-recovery-card .form-actions .submit-button {
color: #FFFFFF; color: #FFFFFF;
background: #0049B4; background: #0049B4;
} }
.account-recovery-card .form-actions .submit-button:hover { .account-recovery-card .form-actions .submit-button:hover {
background: rgb(0, 69.35, 171); background: rgb(6, 54, 125);
} }
.account-recovery-card .form-actions .submit-button:active { .account-recovery-card .form-actions .submit-button:active {
background: rgb(0, 65.7, 162); background: rgb(0, 65.7, 162);
@@ -12549,15 +12565,12 @@ body.index-page-body {
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.account-recovery-card .form-actions { .account-recovery-card .form-actions {
flex-direction: column;
gap: 10px; gap: 10px;
padding: 20px 0;
} }
.account-recovery-card .form-actions .cancel-button, .account-recovery-card .form-actions .cancel-button,
.account-recovery-card .form-actions .submit-button { .account-recovery-card .form-actions .submit-button {
width: 100%; height: 44px;
height: 40px; font-size: 14px;
font-size: 13px;
} }
} }
@@ -12613,7 +12626,6 @@ body.index-page-body {
} }
.account-recovery-result { .account-recovery-result {
width: 100%; width: 100%;
background: #F6F9FB;
padding: 60px 40px; padding: 60px 40px;
border-radius: 0 0 12px 12px; border-radius: 0 0 12px 12px;
text-align: center; text-align: center;
@@ -12765,128 +12777,62 @@ body.index-page-body {
margin: 0; margin: 0;
} }
.account-recovery-container { .account-recovery-container {
padding: 20px; padding: 16px;
} }
.account-recovery-card { .account-recovery-card {
padding: 0; padding: 24px 16px 20px;
border-radius: 16px;
}
.account-recovery-tabs {
margin-bottom: 20px;
} }
.account-recovery-tabs .tab-link { .account-recovery-tabs .tab-link {
height: 40px; padding: 9px 12px;
padding: 10px 16px; font-size: 13px;
font-size: 14px;
font-weight: 700;
}
.account-recovery-tabs .tab-link:first-child {
border-radius: 8px 0 0 0;
}
.account-recovery-tabs .tab-link:last-child {
border-radius: 0 8px 0 0;
}
.account-recovery-form {
padding: 20px;
border-radius: 0 0 8px 8px;
} }
.account-recovery-form .form-group { .account-recovery-form .form-group {
flex-direction: column; margin-bottom: 10px;
align-items: stretch;
gap: 8px;
margin-bottom: 16px;
}
.account-recovery-form .form-label {
width: 100%;
font-size: 14px;
font-weight: 500;
margin-bottom: 0;
} }
.account-recovery-form .form-input { .account-recovery-form .form-input {
flex: none; height: 44px;
width: 100%; font-size: 13px;
height: 40px;
padding: 0 16px;
font-size: 12px;
border-radius: 8px;
}
.account-recovery-form .form-input::placeholder {
color: #8c959f;
} }
.account-recovery-form .form-select { .account-recovery-form .form-select {
height: 40px; height: 44px;
padding: 0 32px 0 12px; font-size: 13px;
font-size: 12px;
border-radius: 8px;
background-position: right 10px center;
} }
.phone-input-group { .phone-input-group {
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 6px;
align-items: center;
width: 100%;
} }
.phone-input-group .phone-prefix { .phone-input-group .phone-prefix {
flex: 1; flex: 1;
min-width: 0;
width: auto;
}
.phone-input-group .phone-middle,
.phone-input-group .phone-last {
flex: 1;
min-width: 0;
width: auto; width: auto;
} }
.phone-input-group .phone-separator { .phone-input-group .phone-separator {
display: flex; display: none;
width: auto;
height: auto;
background: transparent;
font-size: 12px;
color: #000;
}
.phone-input-group .phone-separator::before {
content: "-";
}
.auth-input-group {
width: 100%;
}
.auth-input-group .auth-input {
width: 100%;
padding-right: 60px;
} }
.auth-input-group .auth-timer { .auth-input-group .auth-timer {
font-size: 12px; font-size: 12px;
right: 12px; right: 100px;
} }
.auth-request-button, .auth-request-button,
.auth-verify-button { .auth-verify-button {
width: 100%; width: 100%;
height: 40px; height: 44px;
padding: 10px; font-size: 13px;
font-size: 14px; margin-top: 4px;
font-weight: 700;
border-radius: 8px;
margin-top: 8px;
} }
.account-recovery-card .form-actions { .account-recovery-card .form-actions {
flex-direction: row; flex-direction: row;
gap: 21px; gap: 10px;
margin-top: 24px; margin-top: 20px;
padding: 24px 0;
} }
.account-recovery-card .form-actions .cancel-button, .account-recovery-card .form-actions .cancel-button,
.account-recovery-card .form-actions .submit-button { .account-recovery-card .form-actions .submit-button {
flex: 1; flex: 1;
min-width: 144px; height: 44px;
height: 40px;
font-size: 14px; font-size: 14px;
font-weight: 700;
border-radius: 8px;
}
.account-recovery-card .form-actions .cancel-button {
background: #e5e7eb;
color: #5f666c;
}
.account-recovery-card .form-actions .submit-button {
background: #0049b4;
color: #ffffff;
} }
.account-recovery-result { .account-recovery-result {
padding: 30px 20px; padding: 30px 20px;
@@ -13376,7 +13322,7 @@ body.index-page-body {
.corp-manager-wrapper .input-readonly, .corp-manager-wrapper .input-readonly,
.password-change-wrapper .form-input:disabled, .password-change-wrapper .form-input:disabled,
.password-change-wrapper .input-readonly { .password-change-wrapper .input-readonly {
background: #ededed; background: #e9ecef !important;
} }
.corp-manager-wrapper .compound-input, .corp-manager-wrapper .compound-input,
.password-change-wrapper .compound-input { .password-change-wrapper .compound-input {
@@ -21429,7 +21375,6 @@ input[type=checkbox]:checked + .custom-checkbox {
} }
.org-compound-input .org-form-input, .org-compound-input .org-form-input,
.org-compound-input .org-form-select { .org-compound-input .org-form-select {
flex: 1;
min-width: 0; min-width: 0;
} }
.org-compound-input .org-form-input.phone-number { .org-compound-input .org-form-input.phone-number {
@@ -21571,16 +21516,15 @@ input[type=checkbox]:checked + .custom-checkbox {
padding: 10px 20px; padding: 10px 20px;
width: auto; width: auto;
min-width: 100px; min-width: 100px;
background: #00A1D7;
color: #FFFFFF; color: #FFFFFF;
border: 1px solid #00A1D7;
border-radius: 8px; border-radius: 8px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
transition: all 0.2s ease; background: #3BA4ED;
border: 1px solid #3BA4ED;
} }
.org-btn-check:not(:disabled):hover, .org-file-label:not(:disabled):hover { .org-btn-check:not(:disabled):hover, .org-file-label:not(:disabled):hover {
background: #008BC0; background: #008BC0;
@@ -21806,6 +21750,7 @@ input[type=checkbox]:checked + .custom-checkbox {
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
margin-bottom: 90px; margin-bottom: 90px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
} }
.agreement-all-section { .agreement-all-section {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -478,7 +478,7 @@ select.form-control {
&:disabled, &:disabled,
&.input-readonly { &.input-readonly {
background: $gray-bg; background: #e9ecef !important;
color: $text-gray; color: $text-gray;
cursor: not-allowed; cursor: not-allowed;
} }
@@ -913,7 +913,7 @@
.nav-menu { .nav-menu {
display: flex; display: flex;
list-style: none; list-style: none;
gap: 28px; gap: 24px;
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -925,6 +925,7 @@
>.nav-link { >.nav-link {
background: var(--light-bg); background: var(--light-bg);
color: var(--primary-blue); color: var(--primary-blue);
border-radius: 9px;
} }
.sub-menu { .sub-menu {
@@ -978,7 +979,7 @@
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: var(--text-gray); color: var(--text-gray);
padding: 8px; padding: 12px;
position: relative; position: relative;
transition: var(--transition-smooth); transition: var(--transition-smooth);
@@ -9,33 +9,35 @@
.account-recovery-page { .account-recovery-page {
display: flex; display: flex;
align-items: flex-start; align-items: center;
justify-content: center; justify-content: center;
background: transparent; padding: 100px 20px;
padding: 0;
position: relative; position: relative;
min-height: auto; border-radius: 12px;
margin: 0; margin-bottom: 50px;
} }
.account-recovery-container { .account-recovery-container {
width: 100%; width: 100%;
max-width: 800px; max-width: 650px;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;
padding: $spacing-lg; padding: $spacing-lg;
} }
.account-recovery-card { .account-recovery-card {
background: transparent; background: #FFFFFF;
padding: 0; border-radius: 20px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.10);
padding: 36px 36px 28px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
position: relative; position: relative;
@media (max-width: 576px) { @media (max-width: 576px) {
padding: 0 20px; padding: 24px 20px 20px;
border-radius: 16px;
} }
} }
@@ -49,14 +51,13 @@
display: none; display: none;
} }
// Tab Navigation (Figma 디자인: 약관 페이지와 동일) // Tab Navigation (Design 2: 클래식 폴더 탭)
.account-recovery-tabs { .account-recovery-tabs {
display: flex; display: flex;
gap: 0; gap: 0;
margin-bottom: 0; margin-bottom: 28px;
width: 100%; width: 100%;
background: transparent; background: transparent;
border-radius: 0;
padding: 0; padding: 0;
.tab-link { .tab-link {
@@ -64,45 +65,46 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 14px 24px; padding: 14px 20px;
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 16px; font-size: 15px;
font-weight: 700; font-weight: 400;
color: #8c959f; color: #adb5bd;
text-decoration: none; text-decoration: none;
text-align: center; text-align: center;
background: #eceff4; background: #FFFFFF;
// 비활성 탭: 하단 border만 (바닥선 역할)
border: 1.5px solid transparent;
border-bottom: 1.5px solid #dee2e6;
border-radius: 0; border-radius: 0;
transition: all 0.3s ease; transition: all 0.2s ease;
cursor: pointer;
// 왼쪽 탭 둥근 모서리 position: relative;
&:first-child {
border-radius: 30px 0 0 0;
}
// 오른쪽 탭 둥근 모서리
&:last-child {
border-radius: 0 30px 0 0;
}
&:hover { &:hover {
color: #3ba4ed; color: $primary-blue;
background: #e4e8ed;
} }
&.active { &.active {
color: #FFFFFF; color: $primary-blue;
background: #3ba4ed;
font-weight: 700; font-weight: 700;
// 활성 탭: 상/좌/우 파란 테두리 + 하단 흰색으로 바닥선 가려 콘텐츠와 연결
border-top: 1.5px solid $primary-blue;
border-left: 1.5px solid $primary-blue;
border-right: 1.5px solid $primary-blue;
border-bottom: 2px solid #FFFFFF;
border-radius: 8px 8px 0px 0px;
} }
@media (max-width: 576px) { @media (max-width: 576px) {
padding: 12px 16px; padding: 12px 10px;
font-size: 14px; font-size: 13px;
} }
} }
} }
// Alert Messages // Alert Messages
.account-alert { .account-alert {
width: 100%; width: 100%;
@@ -140,73 +142,45 @@
} }
} }
// Form Content Area (Figma: 아이디찾기BG_box) // Form Content Area (세로 스택형, 라벨 없는 심플 스타일)
.account-recovery-form { .account-recovery-form {
width: 100%; width: 100%;
margin-bottom: 0; margin-bottom: 0;
background: #F6F9FB; background: transparent;
padding: 40px; padding: 0;
border-radius: 0 0 12px 12px;
@media (max-width: 576px) {
padding: 24px 16px;
}
.form-group { .form-group {
display: flex; display: flex;
align-items: center; flex-direction: column;
gap: 20px; gap: 0;
margin-bottom: 20px; margin-bottom: 12px;
&:last-of-type { &:last-of-type {
margin-bottom: 0; margin-bottom: 0;
} }
@media (max-width: 768px) {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
} }
// 라벨은 숨김 (placeholder로 대체)
.form-label { .form-label {
display: flex; display: none;
align-items: center;
flex-shrink: 0;
width: 170px;
font-family: $font-family-primary;
font-size: 15px;
font-weight: 400;
color: #212529;
margin-bottom: 0;
.required {
color: #ed5b5b;
margin-left: 2px;
}
@media (max-width: 768px) {
width: 100%;
font-size: 14px;
}
} }
.form-input { .form-input {
flex: 1; width: 100%;
height: 40px; height: 48px;
padding: 0 16px; padding: 0 16px;
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
color: #212529; color: #212529;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #dadada; border: 1px solid #E2E8F0;
border-radius: 8px; border-radius: 10px;
outline: none; outline: none;
transition: all 0.3s ease; transition: all 0.25s ease;
&::placeholder { &::placeholder {
color: #dadada; color: #B0B8C1;
} }
&:hover { &:hover {
@@ -215,7 +189,7 @@
&:focus { &:focus {
border-color: #3ba4ed; border-color: #3ba4ed;
box-shadow: 0 0 0 2px rgba(59, 164, 237, 0.1); box-shadow: 0 0 0 3px rgba(59, 164, 237, 0.12);
} }
&:disabled { &:disabled {
@@ -231,24 +205,24 @@
} }
.form-select { .form-select {
height: 40px; height: 48px;
padding: 0 32px 0 16px; padding: 0 24px 0 12px; // padding을 좀 더 좁혀서 내용이 더 잘 보이도록 수정
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
color: #515151; color: #515151;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #dadada; border: 1px solid #E2E8F0;
border-radius: 8px; border-radius: 10px;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
appearance: none; appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath fill='%231D1B20' d='M5 5L0 0h10z'/%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath fill='%231D1B20' d='M5 5L0 0h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right 12px center; background-position: right 8px center; // 화살표 아이콘 위치도 조정
background-size: 10px 5px; background-size: 10px 5px;
&:hover { &:hover {
@@ -257,7 +231,7 @@
&:focus { &:focus {
border-color: #3ba4ed; border-color: #3ba4ed;
box-shadow: 0 0 0 2px rgba(59, 164, 237, 0.1); box-shadow: 0 0 0 3px rgba(59, 164, 237, 0.12);
} }
&:disabled { &:disabled {
@@ -275,55 +249,99 @@
} }
} }
// Phone Input Group (Figma: 휴대폰번호+텍스트필드) // 인증 방식 선택 pill 탭 (비밀번호 초기화 탭 내부)
.reset-method-group {
margin-bottom: 16px !important;
}
.reset-method-tabs {
display: flex;
gap: 8px;
background: #f0f2f5;
border-radius: 10px;
padding: 4px;
.reset-method-tab {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin: 0;
input[type="radio"] {
display: none;
}
span {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 9px 12px;
font-family: $font-family-primary;
font-size: 13px;
font-weight: 600;
color: #8c959f;
border-radius: 7px;
transition: all 0.2s ease;
text-align: center;
}
input[type="radio"]:checked+span {
background: #FFFFFF;
color: #212529;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.09);
}
&:hover span {
color: #3ba4ed;
}
}
}
// Phone Input Group
.phone-input-group { .phone-input-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
flex: 1; width: 100%;
.phone-prefix { .phone-prefix {
width: 100px; width: 90px;
min-width: 90px; // select가 줄어들지 않도록 고정
flex-shrink: 0; flex-shrink: 0;
} }
.phone-middle, .phone-middle,
.phone-last { .phone-last {
width: 100px; flex: 1;
flex-shrink: 0; min-width: 0;
} }
.phone-separator { .phone-separator {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 8px; color: #B0B8C1;
height: 1px; font-size: 14px;
background: #515151;
flex-shrink: 0; flex-shrink: 0;
} width: 10px;
height: auto;
background: transparent;
@media (max-width: 768px) { &::before {
flex-wrap: wrap; content: '-';
gap: 6px;
.phone-prefix,
.phone-middle,
.phone-last {
flex: 1;
min-width: 60px;
}
.phone-separator {
width: 6px;
} }
} }
@media (max-width: 576px) { @media (max-width: 576px) {
.phone-prefix, flex-wrap: wrap;
.phone-middle, gap: 6px;
.phone-last {
width: 100%; .phone-prefix {
flex: 1;
width: auto;
} }
.phone-separator { .phone-separator {
@@ -332,7 +350,7 @@
} }
} }
// Auth Number Group (Figma: 인증번호+텍스트필드) // Auth Number Group
.auth-number-group { .auth-number-group {
margin-top: 0; margin-top: 0;
} }
@@ -341,95 +359,103 @@
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1; width: 100%;
gap: 8px;
&.mg-y {
margin-top: 20px;
margin-bottom: 12px;
}
.auth-input { .auth-input {
flex: 1; flex: 1;
padding-right: 60px; min-width: 0;
} }
.auth-timer { .auth-timer {
position: absolute; position: absolute;
right: 16px; right: 20px; // verify button width + gap
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 13px;
font-weight: 400; font-weight: 500;
color: #ed5b5b; color: #ed5b5b;
pointer-events: none; pointer-events: none;
@media (max-width: 576px) { @media (max-width: 576px) {
font-size: 12px; font-size: 12px;
right: 12px; right: 100px;
} }
} }
} }
// Buttons (Figma: 인증번호 받기/확인) // Buttons: 인증번호 받기 / 확인
.auth-request-button, .auth-request-button,
.auth-verify-button { .auth-verify-button {
width: 140px; height: 48px;
height: 40px; padding: 0 16px;
padding: 8px;
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 13px;
font-weight: 700; font-weight: 700;
color: #FFFFFF; color: #FFFFFF;
background: #a4d6ea; background: #3ba4ed;
border: none; border: none;
border-radius: 8px; border-radius: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
margin: 0;
flex-shrink: 0; flex-shrink: 0;
white-space: nowrap;
line-height: 1; line-height: 1;
&:hover { &:hover {
background: darken(#a4d6ea, 5%); background: darken(#3ba4ed, 8%);
transform: none !important;
} }
&:active { &:active {
background: darken(#a4d6ea, 10%); background: darken(#3ba4ed, 14%);
} }
&:disabled { &:disabled {
opacity: 0.6; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
@media (max-width: 576px) { @media (max-width: 576px) {
width: 100%; width: 100%;
height: 40px; height: 44px;
font-size: 13px; font-size: 13px;
margin-top: 8px; margin-top: 4px;
} }
} }
// Form Actions (Figma: btn_취소, btn_신청) // Form Actions
.account-recovery-card .form-actions { .account-recovery-card .form-actions {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 20px; gap: 12px;
margin-top: 32px; margin-top: 28px;
padding: 32px 0; padding: 0;
border-top: none; border-top: none;
background: transparent; background: transparent;
.cancel-button, .cancel-button,
.submit-button { .submit-button {
width: 160px; flex: 1;
height: 40px; height: 48px;
padding: 8px; padding: 0 8px;
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 15px;
font-weight: 700; font-weight: 700;
border: none; border: none;
border-radius: 8px; border-radius: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.25s ease;
line-height: 1; line-height: 1;
// <a> 태그 지원
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -438,14 +464,14 @@
.cancel-button { .cancel-button {
color: #5f666c; color: #5f666c;
background: #e5e7eb; background: #f0f2f5;
&:hover { &:hover {
background: darken(#e5e7eb, 5%); background: darken(#f0f2f5, 5%);
} }
&:active { &:active {
background: darken(#e5e7eb, 10%); background: darken(#f0f2f5, 10%);
} }
} }
@@ -454,7 +480,9 @@
background: #0049B4; background: #0049B4;
&:hover { &:hover {
background: darken(#0049B4, 5%); // background: darken(#0049B4, 5%);
background: rgb(6 54 125);
} }
&:active { &:active {
@@ -468,15 +496,12 @@
} }
@media (max-width: 576px) { @media (max-width: 576px) {
flex-direction: column;
gap: 10px; gap: 10px;
padding: 20px 0;
.cancel-button, .cancel-button,
.submit-button { .submit-button {
width: 100%; height: 44px;
height: 40px; font-size: 14px;
font-size: 13px;
} }
} }
} }
@@ -521,6 +546,7 @@
opacity: 0; opacity: 0;
transform: translateY(-10px); transform: translateY(-10px);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
@@ -545,7 +571,6 @@
// Result Page Styles (아이디 찾기 결과 페이지) // Result Page Styles (아이디 찾기 결과 페이지)
.account-recovery-result { .account-recovery-result {
width: 100%; width: 100%;
background: #F6F9FB;
padding: 60px 40px; padding: 60px 40px;
border-radius: 0 0 12px 12px; border-radius: 0 0 12px 12px;
text-align: center; text-align: center;
@@ -701,7 +726,7 @@
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Figma Mobile Design - node 1152-7047 (sm: 768px breakpoint) // Mobile Design (sm: 768px breakpoint)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@include respond-to('sm') { @include respond-to('sm') {
.account-recovery-page { .account-recovery-page {
@@ -710,164 +735,78 @@
} }
.account-recovery-container { .account-recovery-container {
padding: 20px; padding: 16px;
} }
.account-recovery-card { .account-recovery-card {
padding: 0; padding: 24px 16px 20px;
border-radius: 16px;
} }
// Figma: 탭 - 167px × 40px, 14px Bold
.account-recovery-tabs { .account-recovery-tabs {
margin-bottom: 20px;
.tab-link { .tab-link {
height: 40px; padding: 9px 12px;
padding: 10px 16px; font-size: 13px;
font-size: 14px;
font-weight: 700;
&:first-child {
border-radius: 8px 0 0 0;
}
&:last-child {
border-radius: 0 8px 0 0;
}
} }
} }
// Figma: 폼 영역
.account-recovery-form { .account-recovery-form {
padding: 20px;
border-radius: 0 0 8px 8px;
.form-group { .form-group {
flex-direction: column; margin-bottom: 10px;
align-items: stretch;
gap: 8px;
margin-bottom: 16px;
} }
// Figma: 라벨 - 14px Medium
.form-label {
width: 100%;
font-size: 14px;
font-weight: 500;
margin-bottom: 0;
}
// Figma: 입력 필드 - 335px × 40px, border-radius 8px
.form-input { .form-input {
flex: none; height: 44px;
width: 100%; font-size: 13px;
height: 40px;
padding: 0 16px;
font-size: 12px;
border-radius: 8px;
&::placeholder {
color: #8c959f;
}
} }
// Figma: 셀렉트 - 40px 높이, 8px radius
.form-select { .form-select {
height: 40px; height: 44px;
padding: 0 32px 0 12px; font-size: 13px;
font-size: 12px;
border-radius: 8px;
background-position: right 10px center;
} }
} }
// 모바일: 휴대폰 번호 입력 - 화면 폭 채우기
.phone-input-group { .phone-input-group {
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 6px;
align-items: center;
width: 100%;
.phone-prefix { .phone-prefix {
flex: 1; flex: 1;
min-width: 0;
width: auto;
}
.phone-middle,
.phone-last {
flex: 1;
min-width: 0;
width: auto; width: auto;
} }
.phone-separator { .phone-separator {
display: flex; display: none;
width: auto;
height: auto;
background: transparent;
font-size: 12px;
color: #000;
&::before {
content: '-';
}
} }
} }
// 모바일: 인증번호 입력 그룹 - 화면 폭 채우기
.auth-input-group { .auth-input-group {
width: 100%;
.auth-input {
width: 100%;
padding-right: 60px;
}
// 타이머 - 12px, #ed5b5b
.auth-timer { .auth-timer {
font-size: 12px; font-size: 12px;
right: 12px; right: 100px;
} }
} }
// 모바일: 인증번호 받기/확인 버튼 - 화면 폭 채우기, 다음 줄에 배치
.auth-request-button, .auth-request-button,
.auth-verify-button { .auth-verify-button {
width: 100%; width: 100%;
height: 40px; height: 44px;
padding: 10px; font-size: 13px;
font-size: 14px; margin-top: 4px;
font-weight: 700;
border-radius: 8px;
margin-top: 8px;
} }
// Figma: 하단 버튼 - 144px × 40px, gap 21px, 가로 배치
.account-recovery-card .form-actions { .account-recovery-card .form-actions {
flex-direction: row; flex-direction: row;
gap: 21px; gap: 10px;
margin-top: 24px; margin-top: 20px;
padding: 24px 0;
.cancel-button, .cancel-button,
.submit-button { .submit-button {
flex: 1; flex: 1;
min-width: 144px; height: 44px;
height: 40px;
font-size: 14px; font-size: 14px;
font-weight: 700;
border-radius: 8px;
}
// Figma: 취소 버튼 - #e5e7eb 배경, #5f666c 텍스트
.cancel-button {
background: #e5e7eb;
color: #5f666c;
}
// Figma: 제출 버튼 - #0049b4 배경, 흰색 텍스트
.submit-button {
background: #0049b4;
color: #ffffff;
} }
} }
@@ -923,4 +862,4 @@
font-size: 12px; font-size: 12px;
} }
} }
} }
@@ -159,7 +159,7 @@
.form-input:disabled, .form-input:disabled,
.input-readonly { .input-readonly {
background: #ededed; background: #e9ecef !important;
} }
.compound-input { .compound-input {
@@ -295,7 +295,6 @@
.org-form-input, .org-form-input,
.org-form-select { .org-form-select {
flex: 1;
min-width: 0; min-width: 0;
} }
@@ -455,16 +454,16 @@
padding: 10px 20px; padding: 10px 20px;
width: auto; width: auto;
min-width: 100px; min-width: 100px;
background: #00A1D7;
color: $white; color: $white;
border: 1px solid #00A1D7;
border-radius: 8px; border-radius: 8px;
font-family: $font-family-primary; font-family: $font-family-primary;
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
transition: all 0.2s ease; // transition: all 0.2s ease;
background: #3BA4ED;
border: 1px solid #3BA4ED;
&:not(:disabled):hover { &:not(:disabled):hover {
background: #008BC0; background: #008BC0;
@@ -721,6 +720,7 @@
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
margin-bottom: 90px; margin-bottom: 90px;
font-family: $font-family-primary;
} }
// Agree All Section // Agree All Section
@@ -10,7 +10,8 @@
</div> </div>
</section> </section>
<section layout:fragment="contentFragment"> <section layout:fragment="contentFragment">
<div class="service-main"> <div class="service-main container" style="padding-top: 70px; padding-bottom:100px;">
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('profile')}"></th:block>
<div class="app-management-content"> <div class="app-management-content">
<div class="password-change-wrapper"> <div class="password-change-wrapper">
<h2 class="page-outer-title">본인 확인</h2> <h2 class="page-outer-title">본인 확인</h2>
@@ -1,148 +1,145 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}">
<body> <body>
<section layout:fragment="contentFragment" class="content"> <section layout:fragment="contentFragment" class="content">
<div class="content_wrap"> <div class="content_wrap">
<div class="account-recovery-container"> <div class="account-recovery-container">
<div class="account-recovery-card"> <div class="account-recovery-card">
<!-- 탭 메뉴 -->
<div class="account-recovery-tabs">
<a href="#tab1" class="tab-link active" data-tab="tab1">아이디 찾기</a>
<a href="#tab2" class="tab-link" data-tab="tab2">비밀번호 초기화</a>
</div>
<!-- 아이디 찾기 폼 --> <!-- 탭 메뉴 (pill 스타일) -->
<div id="tab1" class="account-recovery-form"> <div class="account-recovery-tabs">
<form id="findIdForm" role="form" name="findIdForm" th:action="@{/find_id}" method="post" data-form-type="findId"> <a href="#tab1" class="tab-link active" data-tab="tab1">아이디 찾기</a>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> <a href="#tab2" class="tab-link" data-tab="tab2">비밀번호 초기화</a>
<input type="hidden" id="findId-phoneNumber" name="phoneNumber"> </div>
<!-- 성명 입력 --> <!-- 아이디 찾기 폼 -->
<div class="form-group"> <div id="tab1" class="account-recovery-form">
<label class="form-label">성명</label> <form id="findIdForm" role="form" name="findIdForm" th:action="@{/find_id}" method="post"
<input type="text" name="name" class="form-input" placeholder="이름" required> data-form-type="findId">
</div> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="hidden" id="findId-phoneNumber" name="phoneNumber">
<!-- 휴대폰 번호 입력 --> <!-- 성명 입력 -->
<div class="form-group"> <div class="form-group">
<label class="form-label">휴대폰 번호</label> <input type="text" name="name" class="form-input" placeholder="성명" required>
<div class="phone-input-group">
<select class="form-select phone-prefix select_mobile_prefix">
<option>선택</option>
<option>010</option>
<option>011</option>
<option>016</option>
<option>017</option>
<option>018</option>
</select>
<span class="phone-separator">-</span>
<input type="number" name="phoneMiddle" class="form-input" placeholder="앞자리" required>
<span class="phone-separator">-</span>
<input type="number" name="phoneLast" class="form-input" placeholder="뒷자리" required>
<button type="button" class="auth-request-button btn_auth">인증번호 받기</button>
</div> </div>
</div>
<!-- 인증번호 입력 --> <!-- 휴대폰 번호 입력 -->
<div class="form-group auth-number-container" id="findId-authNumberContainer" style="display: none;"> <div class="form-group">
<label class="form-label">인증번호 입력</label> <div class="phone-input-group">
<div class="auth-input-group"> <select class="form-select phone-prefix select_mobile_prefix">
<input type="number" class="form-input auth-input auth-number-input" placeholder="SMS 인증번호" required> <option>선택</option>
<span class="auth-timer" id="findId-certify_time">03:00</span> <option>010</option>
<button type="button" class="auth-verify-button btn_verify_auth">확인</button> <option>011</option>
<option>016</option>
<option>017</option>
<option>018</option>
</select>
<span class="phone-separator">-</span>
<input type="number" name="phoneMiddle" class="form-input" placeholder="앞자리" required>
<span class="phone-separator">-</span>
<input type="number" name="phoneLast" class="form-input" placeholder="뒷자리" required>
<button type="button" class="auth-request-button btn_auth">인증번호 받기</button>
</div>
</div> </div>
</div>
<!-- 제출 버튼 --> <!-- 인증번호 입력 -->
<div class="form-actions"> <div class="form-group auth-number-container" id="findId-authNumberContainer" style="display: none;">
<a class="cancel-button btn_cancel">취소</a> <div class="auth-input-group">
<button type="submit" class="submit-button">아이디 찾기</button> <input type="number" class="form-input auth-input auth-number-input" placeholder="SMS 인증번호" required>
</div> <span class="auth-timer" id="findId-certify_time">03:00</span>
</form> <button type="button" class="auth-verify-button btn_verify_auth">확인</button>
</div> </div>
<!-- 비밀번호 초기화 폼 -->
<div id="tab2" class="account-recovery-form">
<form id="resetPasswordForm" role="form" name="resetPasswordForm" th:action="@{/reset_password}" method="post" data-form-type="resetPassword">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="hidden" id="resetPassword-phoneNumber" name="phoneNumber">
<!-- 인증 방식 선택 -->
<div class="form-group">
<label class="form-label">초기화 방법</label>
<div style="display: flex; gap: 20px; align-items: center;">
<label style="display: flex; align-items: center; gap: 6px; font-size: 14px; margin: 0;">
<input type="radio" id="radio_hp" name="resetMethod" value="hp" checked>
휴대폰 번호
</label>
<label style="display: flex; align-items: center; gap: 6px; font-size: 14px; margin: 0;">
<input type="radio" id="radio_email" name="resetMethod" value="email_id">
이메일 아이디
</label>
</div> </div>
</div>
<!-- 성명 입력 --> <!-- 제출 버튼 -->
<div class="form-group"> <div class="form-actions">
<label class="form-label">성명</label> <a class="cancel-button btn_cancel">취소</a>
<input type="text" name="name" class="form-input" placeholder="성명" required> <button type="submit" class="submit-button">아이디 찾기</button>
</div>
<!-- 아이디 입력 (이메일) -->
<div class="form-group" id="emailInputContainer" style="display: none;">
<label class="form-label">이메일 아이디</label>
<input type="text" name="emailId" class="form-input" placeholder="이메일 아이디">
</div>
<!-- 휴대폰 번호 입력 -->
<div class="form-group" id="phoneInputContainer">
<label class="form-label">휴대폰 번호</label>
<div class="phone-input-group">
<select class="form-select phone-prefix select_mobile_prefix">
<option>선택</option>
<option>010</option>
<option>011</option>
<option>016</option>
<option>017</option>
<option>018</option>
</select>
<span class="phone-separator">-</span>
<input type="number" name="phoneMiddle" class="form-input" placeholder="앞자리" required>
<span class="phone-separator">-</span>
<input type="number" name="phoneLast" class="form-input" placeholder="뒷자리" required>
<button type="button" class="auth-request-button btn_auth">인증번호 받기</button>
</div> </div>
</div> </form>
</div>
<!-- 인증번호 입력 --> <!-- 비밀번호 초기화 폼 -->
<div class="form-group auth-number-container" id="authNumberContainer" style="display: none;"> <div id="tab2" class="account-recovery-form" style="display: none;">
<label class="form-label">인증번호 입력</label> <form id="resetPasswordForm" role="form" name="resetPasswordForm" th:action="@{/reset_password}"
<div class="auth-input-group"> method="post" data-form-type="resetPassword">
<input type="number" class="form-input auth-input auth-number-input" placeholder="SMS 인증번호" required> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<span class="auth-timer" id="certify_time">03:00</span> <input type="hidden" id="resetPassword-phoneNumber" name="phoneNumber">
<button type="button" class="auth-verify-button btn_verify_auth">확인</button>
<!-- 인증 방식 선택 (pill 버튼 스타일) -->
<div class="form-group reset-method-group">
<div class="reset-method-tabs">
<label class="reset-method-tab">
<input type="radio" id="radio_hp" name="resetMethod" value="hp" checked>
<span>휴대폰 번호</span>
</label>
<label class="reset-method-tab">
<input type="radio" id="radio_email" name="resetMethod" value="email_id">
<span>이메일 아이디</span>
</label>
</div>
</div> </div>
</div>
<!-- 제출 버튼 --> <!-- 성명 입력 -->
<div class="form-actions"> <div class="form-group">
<a class="cancel-button btn_cancel">취소</a> <input type="text" name="name" class="form-input" placeholder="성명" required>
<button type="submit" class="submit-button">비밀번호 초기화</button> </div>
</div>
</form> <!-- 아이디 입력 (이메일) -->
<div class="form-group" id="emailInputContainer" style="display: none;">
<input type="text" name="emailId" class="form-input" placeholder="이메일 아이디">
</div>
<!-- 휴대폰 번호 입력 -->
<div class="form-group" id="phoneInputContainer">
<div class="phone-input-group">
<select class="form-select phone-prefix select_mobile_prefix">
<option>선택</option>
<option>010</option>
<option>011</option>
<option>016</option>
<option>017</option>
<option>018</option>
</select>
<span class="phone-separator">-</span>
<input type="number" name="phoneMiddle" class="form-input" placeholder="앞자리" required>
<span class="phone-separator">-</span>
<input type="number" name="phoneLast" class="form-input" placeholder="뒷자리" required>
<button type="button" class="auth-request-button btn_auth">인증번호 받기</button>
</div>
</div>
<!-- 인증번호 입력 -->
<div class="form-group auth-number-container" id="authNumberContainer" style="display: none;">
<div class="auth-input-group mg-y">
<input type="number" class="form-input auth-input auth-number-input" placeholder="SMS 인증번호" required>
<span class="auth-timer" id="certify_time">03:00</span>
<button type="button" class="auth-verify-button btn_verify_auth">확인</button>
</div>
</div>
<!-- 제출 버튼 -->
<div class="form-actions">
<a class="cancel-button btn_cancel">취소</a>
<button type="submit" class="submit-button">비밀번호 초기화</button>
</div>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </section>
</section>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function() { $(document).ready(function () {
// 탭 전환 기능 // 탭 전환 기능
$('.tab-link').on('click', function(e) { $('.tab-link').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const tabId = $(this).data('tab'); const tabId = $(this).data('tab');
@@ -158,7 +155,7 @@
}); });
// 초기화 방법 선택에 따라 폼 표시 변경 // 초기화 방법 선택에 따라 폼 표시 변경
$('input[name="resetMethod"]').change(function() { $('input[name="resetMethod"]').change(function () {
if (this.value === 'hp') { if (this.value === 'hp') {
$('#phoneInputContainer').show(); $('#phoneInputContainer').show();
$('#emailInputContainer').hide(); $('#emailInputContainer').hide();
@@ -169,7 +166,7 @@
}); });
// 취소 버튼 클릭 시 로그인 페이지로 이동 // 취소 버튼 클릭 시 로그인 페이지로 이동
$('.btn_cancel').on('click', function() { $('.btn_cancel').on('click', function () {
location.href = '/login'; location.href = '/login';
}); });
@@ -189,7 +186,7 @@
} }
// 인증번호 요청 버튼 이벤트 리스너 // 인증번호 요청 버튼 이벤트 리스너
$(".btn_auth").on("click", function(e) { $(".btn_auth").on("click", function (e) {
e.preventDefault(); e.preventDefault();
let form = $(this).closest("form"); let form = $(this).closest("form");
let mobileNumber = combineMobileNumber(form); let mobileNumber = combineMobileNumber(form);
@@ -201,7 +198,7 @@
}); });
// 인증번호 확인 버튼 이벤트 리스너 // 인증번호 확인 버튼 이벤트 리스너
$(".btn_verify_auth").on("click", function(e) { $(".btn_verify_auth").on("click", function (e) {
e.preventDefault(); e.preventDefault();
let form = $(this).closest("form"); let form = $(this).closest("form");
let authNumberInput = form.find(".auth-number-input"); let authNumberInput = form.find(".auth-number-input");
@@ -216,4 +213,5 @@
}); });
</script> </script>
</th:block> </th:block>
</html>
</html>
@@ -1,116 +1,98 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/djbank_title_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image"> <img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>아이디 찾기</h1> <h1>아이디 찾기</h1>
</div> </div>
</section> </section>
<th:block layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="account-recovery-page"> <div class="account-recovery-page">
<div class="account-recovery-container"> <div class="account-recovery-container">
<div class="account-recovery-card"> <div class="account-recovery-card">
<!-- Tab Navigation --> <!-- Tab Navigation -->
<div class="account-recovery-tabs"> <div class="account-recovery-tabs">
<a href="#" class="tab-link active">아이디찾기</a> <a href="#" class="tab-link active">아이디 찾기</a>
<a th:href="@{/account_recovery(tab='resetPassword')}" class="tab-link">비밀번호 초기화</a> <a th:href="@{/account_recovery(tab='resetPassword')}" class="tab-link">비밀번호 초기화</a>
</div>
<!-- Alert Messages -->
<div id="alertContainer"></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">성명<span class="required-badge">필수</span></label>
<input type="text" id="userName" name="userName" th:value="${userName}" class="form-input" placeholder="성명" required>
</div> </div>
<!-- Phone Number Fields --> <!-- Alert Messages -->
<div class="form-group"> <div id="alertContainer"></div>
<label class="form-label">휴대폰 번호<span class="required-badge">필수</span></label>
<div class="org-input-row"> <!-- Account Recovery Form -->
<div class="org-compound-input"> <form id="accountForm" role="form" name="accountForm" th:action="@{/find_id}" method="post"
<select id="phonePrefix" class="org-form-select phone-prefix" required> class="account-recovery-form">
<option value="">선택</option> <input type="hidden" id="mobileNumber" name="mobileNumber" th:value="${mobileNumber}">
<option value="010">010</option>
<option value="011">011</option> <!-- Name Field -->
<option value="016">016</option> <div class="form-group">
<option value="017">017</option> <label for="userName" class="form-label">성명<span class="required-badge">필수</span></label>
<option value="018">018</option> <input type="text" id="userName" name="userName" th:value="${userName}" class="form-input"
</select> placeholder="성명" required>
<span class="separator">-</span> </div>
<input type="tel"
id="phoneMiddle" <!-- Phone Number Fields -->
name="phoneMiddle" <div class="form-group">
maxlength="4" <label class="form-label">휴대폰 번호<span class="required-badge">필수</span></label>
class="form-input phone-number" <div class="org-input-row">
placeholder="1234" <div class="org-compound-input">
pattern="[0-9]*" <select id="phonePrefix" class="org-form-select phone-prefix" required>
inputmode="numeric" <option value="">선택</option>
required> <option value="010">010</option>
<span class="separator">-</span> <option value="011">011</option>
<input type="tel" <option value="016">016</option>
id="phoneLast" <option value="017">017</option>
name="phoneLast" <option value="018">018</option>
maxlength="4" </select>
class="form-input phone-number" <span class="separator">-</span>
placeholder="1234" <input type="tel" id="phoneMiddle" name="phoneMiddle" maxlength="4" class="form-input phone-number"
pattern="[0-9]*" placeholder="1234" pattern="[0-9]*" inputmode="numeric" required>
inputmode="numeric" <span class="separator">-</span>
required> <input type="tel" id="phoneLast" name="phoneLast" maxlength="4" class="form-input phone-number"
placeholder="1234" pattern="[0-9]*" inputmode="numeric" required>
</div>
<button type="button" class="org-btn-check btn_auth" id="requestAuthButton">
인증번호 받기
</button>
</div> </div>
<button type="button" class="btn org-btn-check btn_auth" id="requestAuthButton"> </div>
인증번호 받기
<!-- Auth Number Input -->
<div class="form-group auth-number-group" id="authNumberGroup" style="display: none;">
<label for="authNumber" class="form-label">인증번호 입력<span class="required-badge">필수</span></label>
<div class="auth-input-group mg-y">
<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>
<button type="button" class="auth-verify-button" id="verifyAuthButton">
인증번호 확인
</button> </button>
</div> </div>
</form>
<!-- Submit Buttons -->
<div class="form-actions">
<button type="button" class="cancel-button" id="cancelButton">취소</button>
<button type="submit" class="submit-button" id="submitButton" form="accountForm">아이디 찾기</button>
</div> </div>
<!-- Auth Number Input --> <!-- Loading State -->
<div class="form-group auth-number-group" id="authNumberGroup" style="display: none;"> <div class="loading-overlay" id="loadingOverlay">
<label for="authNumber" class="form-label">인증번호 입력<span class="required-badge">필수</span></label> <div class="spinner"></div>
<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>
<button type="button" class="auth-verify-button" id="verifyAuthButton">
인증번호 확인
</button>
</div> </div>
</form>
<!-- Submit Buttons -->
<div class="form-actions">
<button type="button" class="cancel-button" id="cancelButton">취소</button>
<button type="submit" class="submit-button" id="submitButton" form="accountForm">아이디 찾기</button>
</div>
<!-- Loading State -->
<div class="loading-overlay" id="loadingOverlay">
<div class="spinner"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </th:block>
</th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function() { $(document).ready(function () {
const errorMsg = [[${error}]]; const errorMsg = [[${ error }]];
if (errorMsg) { if (errorMsg) {
customPopups.showAlert(errorMsg, 'error'); customPopups.showAlert(errorMsg, 'error');
} }
@@ -167,7 +149,7 @@
} }
// 인증번호 요청 버튼 // 인증번호 요청 버튼
$('#requestAuthButton').on('click', function() { $('#requestAuthButton').on('click', function () {
const mobileNumber = combineMobileNumber(); const mobileNumber = combineMobileNumber();
if (!mobileNumber) { if (!mobileNumber) {
@@ -185,7 +167,7 @@
mobileNumber: mobileNumber, mobileNumber: mobileNumber,
_csrf: $('input[name="_csrf"]').val() _csrf: $('input[name="_csrf"]').val()
}, },
success: function(response) { success: function (response) {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
if (response.valid) { if (response.valid) {
@@ -210,7 +192,7 @@
customPopups.showAlert(response.message || '인증번호 발송에 실패했습니다.', 'error'); customPopups.showAlert(response.message || '인증번호 발송에 실패했습니다.', 'error');
} }
}, },
error: function() { error: function () {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error'); customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
} }
@@ -218,7 +200,7 @@
}); });
// 인증번호 확인 버튼 // 인증번호 확인 버튼
$('#verifyAuthButton').on('click', function() { $('#verifyAuthButton').on('click', function () {
if (!isAuthNumberRequested) { if (!isAuthNumberRequested) {
customPopups.showAlert('휴대폰 번호 인증을 먼저 진행해주세요.', 'error'); customPopups.showAlert('휴대폰 번호 인증을 먼저 진행해주세요.', 'error');
return; return;
@@ -242,7 +224,7 @@
authNumber: authNumber, authNumber: authNumber,
_csrf: $('input[name="_csrf"]').val() _csrf: $('input[name="_csrf"]').val()
}, },
success: function(response) { success: function (response) {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
if (response.valid) { if (response.valid) {
@@ -260,7 +242,7 @@
customPopups.showAlert(response.message || '인증번호가 일치하지 않습니다.', 'error'); customPopups.showAlert(response.message || '인증번호가 일치하지 않습니다.', 'error');
} }
}, },
error: function() { error: function () {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error'); customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
} }
@@ -268,7 +250,7 @@
}); });
// 폼 제출 // 폼 제출
$('#accountForm').on('submit', function(e) { $('#accountForm').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
const userName = $('#userName').val().trim(); const userName = $('#userName').val().trim();
@@ -298,7 +280,7 @@
}); });
// 취소 버튼 // 취소 버튼
$('#cancelButton').on('click', function() { $('#cancelButton').on('click', function () {
window.location.href = '/login'; window.location.href = '/login';
}); });
@@ -330,7 +312,7 @@
} }
// 기존 휴대폰 번호 복원 // 기존 휴대폰 번호 복원
$(document).ready(function() { $(document).ready(function () {
const savedMobileNumber = $('#mobileNumber').val(); const savedMobileNumber = $('#mobileNumber').val();
if (savedMobileNumber) { if (savedMobileNumber) {
const parts = savedMobileNumber.split('-'); const parts = savedMobileNumber.split('-');
@@ -343,4 +325,5 @@
}); });
</script> </script>
</th:block> </th:block>
</html>
</html>
@@ -1,140 +1,111 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/djbank_title_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image"> <img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>비밀번호 초기화</h1> <h1>비밀번호 초기화</h1>
</div> </div>
</section> </section>
<th:block layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="account-recovery-page"> <div class="account-recovery-page">
<div class="account-recovery-container"> <div class="account-recovery-container">
<div class="account-recovery-card"> <div class="account-recovery-card">
<!-- Tab Navigation --> <!-- Tab Navigation -->
<div class="account-recovery-tabs"> <div class="account-recovery-tabs">
<a th:href="@{/account_recovery(tab='findId')}" class="tab-link">아이디찾기</a> <a th:href="@{/account_recovery(tab='findId')}" class="tab-link">아이디찾기</a>
<a href="#" class="tab-link active">비밀번호 초기화</a> <a href="#" class="tab-link active">비밀번호 초기화</a>
</div>
<!-- Alert Messages -->
<div id="alertContainer"></div>
<!-- Account Recovery Form -->
<form id="accountForm" role="form" name="accountForm" th:action="@{/reset_password}" method="post" class="account-recovery-form">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="hidden" id="mobileNumber" name="mobileNumber" th:value="${mobileNumber}">
<!-- Name Field -->
<div class="form-group">
<label for="userName" class="form-label">성명<span class="required-badge">필수</span></label>
<input type="text"
id="userName"
name="userName"
th:value="${userName}"
class="form-input"
placeholder="성명"
required>
</div> </div>
<!-- Email ID Field --> <!-- Alert Messages -->
<div class="form-group"> <div id="alertContainer"></div>
<label for="loginId" class="form-label">이메일 아이디<span class="required-badge">필수</span></label>
<input type="email"
id="loginId"
name="loginId"
th:value="${loginId}"
class="form-input"
placeholder="이메일 아이디"
required>
</div>
<!-- Phone Number Fields --> <!-- Account Recovery Form -->
<div class="form-group"> <form id="accountForm" role="form" name="accountForm" th:action="@{/reset_password}" method="post"
<label class="form-label">휴대폰 번호<span class="required-badge">필수</span></label> class="account-recovery-form">
<div class="org-input-row"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="org-compound-input"> <input type="hidden" id="mobileNumber" name="mobileNumber" th:value="${mobileNumber}">
<select id="phonePrefix" class="org-form-select phone-prefix" required>
<option value="">선택</option> <!-- Name Field -->
<option value="010">010</option> <div class="form-group">
<option value="011">011</option> <label for="userName" class="form-label">성명<span class="required-badge">필수</span></label>
<option value="016">016</option> <input type="text" id="userName" name="userName" th:value="${userName}" class="form-input"
<option value="017">017</option> placeholder="성명" required>
<option value="018">018</option> </div>
</select>
<span class="separator">-</span> <!-- Email ID Field -->
<input type="tel" <div class="form-group">
id="phoneMiddle" <label for="loginId" class="form-label">이메일 아이디<span class="required-badge">필수</span></label>
name="phoneMiddle" <input type="email" id="loginId" name="loginId" th:value="${loginId}" class="form-input"
maxlength="4" placeholder="이메일 아이디" required>
class="form-input phone-number" </div>
placeholder="1234"
pattern="[0-9]*" <!-- Phone Number Fields -->
inputmode="numeric" <div class="form-group">
required> <label class="form-label">휴대폰 번호<span class="required-badge">필수</span></label>
<span class="separator">-</span> <div class="org-input-row">
<input type="tel" <div class="org-compound-input">
id="phoneLast" <select id="phonePrefix" class="org-form-select phone-prefix" required>
name="phoneLast" <option value="">선택</option>
maxlength="4" <option value="010">010</option>
class="form-input phone-number" <option value="011">011</option>
placeholder="1234" <option value="016">016</option>
pattern="[0-9]*" <option value="017">017</option>
inputmode="numeric" <option value="018">018</option>
required> </select>
<span class="separator">-</span>
<input type="tel" id="phoneMiddle" name="phoneMiddle" maxlength="4" class="form-input phone-number"
placeholder="1234" pattern="[0-9]*" inputmode="numeric" required>
<span class="separator">-</span>
<input type="tel" id="phoneLast" name="phoneLast" maxlength="4" class="form-input phone-number"
placeholder="1234" pattern="[0-9]*" inputmode="numeric" required>
</div>
<button type="button" class="org-btn-check btn_auth" id="requestAuthButton">
인증번호 받기
</button>
</div> </div>
<button type="button" class="btn org-btn-check btn_auth" id="requestAuthButton"> </div>
인증번호 받기
<!-- Auth Number Input -->
<div class="form-group auth-number-group" id="authNumberGroup" style="display: none;">
<label for="authNumber" class="form-label">인증번호 입력<span class="required-badge">필수</span></label>
<div class="auth-input-group mg-y">
<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>
<button type="button" class="auth-verify-button" id="verifyAuthButton">
인증번호 확인
</button> </button>
</div> </div>
</form>
<!-- Submit Buttons -->
<div class="form-actions">
<button type="button" class="cancel-button" id="cancelButton">취소</button>
<button type="submit" class="submit-button" id="submitButton" form="accountForm">비밀번호 초기화</button>
</div> </div>
<!-- Auth Number Input --> <!-- Loading State -->
<div class="form-group auth-number-group" id="authNumberGroup" style="display: none;"> <div class="loading-overlay" id="loadingOverlay">
<label for="authNumber" class="form-label">인증번호 입력<span class="required-badge">필수</span></label> <div class="spinner"></div>
<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>
<button type="button" class="auth-verify-button" id="verifyAuthButton">
인증번호 확인
</button>
</div> </div>
</form>
<!-- Submit Buttons -->
<div class="form-actions">
<button type="button" class="cancel-button" id="cancelButton">취소</button>
<button type="submit" class="submit-button" id="submitButton" form="accountForm">비밀번호 초기화</button>
</div>
<!-- Loading State -->
<div class="loading-overlay" id="loadingOverlay">
<div class="spinner"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </th:block>
</th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function() { $(document).ready(function () {
const errorMsg = [[${error}]]; const errorMsg = [[${ error }]];
if (errorMsg) { if (errorMsg) {
customPopups.showAlert(errorMsg, 'error'); customPopups.showAlert(errorMsg, 'error');
} }
const successMsg = [[${success}]]; const successMsg = [[${ success }]];
if (successMsg) { if (successMsg) {
customPopups.showAlert(successMsg, 'success'); customPopups.showAlert(successMsg, 'success');
} }
@@ -191,7 +162,7 @@
} }
// 인증번호 요청 버튼 // 인증번호 요청 버튼
$('#requestAuthButton').on('click', function() { $('#requestAuthButton').on('click', function () {
const userName = $('#userName').val().trim(); const userName = $('#userName').val().trim();
if (!userName) { if (!userName) {
customPopups.showAlert('성명을 입력해주세요.', 'error'); customPopups.showAlert('성명을 입력해주세요.', 'error');
@@ -226,7 +197,7 @@
mobileNumber: mobileNumber, mobileNumber: mobileNumber,
_csrf: $('input[name="_csrf"]').val() _csrf: $('input[name="_csrf"]').val()
}, },
success: function(response) { success: function (response) {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
if (response.valid) { if (response.valid) {
@@ -251,7 +222,7 @@
customPopups.showAlert(response.message || '인증번호 발송에 실패했습니다.', 'error'); customPopups.showAlert(response.message || '인증번호 발송에 실패했습니다.', 'error');
} }
}, },
error: function() { error: function () {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error'); customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
} }
@@ -259,7 +230,7 @@
}); });
// 인증번호 확인 버튼 // 인증번호 확인 버튼
$('#verifyAuthButton').on('click', function() { $('#verifyAuthButton').on('click', function () {
if (!isAuthNumberRequested) { if (!isAuthNumberRequested) {
customPopups.showAlert('휴대폰 번호 인증을 먼저 진행해주세요.', 'error'); customPopups.showAlert('휴대폰 번호 인증을 먼저 진행해주세요.', 'error');
return; return;
@@ -283,7 +254,7 @@
authNumber: authNumber, authNumber: authNumber,
_csrf: $('input[name="_csrf"]').val() _csrf: $('input[name="_csrf"]').val()
}, },
success: function(response) { success: function (response) {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
if (response.valid) { if (response.valid) {
@@ -301,7 +272,7 @@
customPopups.showAlert(response.message || '인증번호가 일치하지 않습니다.', 'error'); customPopups.showAlert(response.message || '인증번호가 일치하지 않습니다.', 'error');
} }
}, },
error: function() { error: function () {
$('#loadingOverlay').fadeOut(200); $('#loadingOverlay').fadeOut(200);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error'); customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해주세요.', 'error');
} }
@@ -309,7 +280,7 @@
}); });
// 폼 제출 // 폼 제출
$('#accountForm').on('submit', function(e) { $('#accountForm').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
const userName = $('#userName').val().trim(); const userName = $('#userName').val().trim();
@@ -351,7 +322,7 @@
}); });
// 취소 버튼 // 취소 버튼
$('#cancelButton').on('click', function() { $('#cancelButton').on('click', function () {
window.location.href = '/login'; window.location.href = '/login';
}); });
@@ -383,7 +354,7 @@
} }
// 기존 휴대폰 번호 복원 // 기존 휴대폰 번호 복원
$(document).ready(function() { $(document).ready(function () {
const savedMobileNumber = $('#mobileNumber').val(); const savedMobileNumber = $('#mobileNumber').val();
if (savedMobileNumber) { if (savedMobileNumber) {
const parts = savedMobileNumber.split('-'); const parts = savedMobileNumber.split('-');
@@ -396,4 +367,5 @@
}); });
</script> </script>
</th:block> </th:block>
</html>
</html>
@@ -9,7 +9,7 @@
</div> </div>
<div class="form-field-wrapper"> <div class="form-field-wrapper">
<input type="text" th:value="${loginId}" name="loginId" class="form-input input-readonly" <input type="text" th:value="${loginId}" name="loginId" class="form-input input-readonly"
placeholder="이메일" disabled="disabled"> placeholder="이메일" disabled="disabled" style="background: #ededed !important;">
<input type="hidden" th:value="${loginId}" name="loginId"> <input type="hidden" th:value="${loginId}" name="loginId">
</div> </div>
</div> </div>
@@ -19,8 +19,7 @@
<span class="form-label-text">성명</span> <span class="required-badge">필수</span> <span class="form-label-text">성명</span> <span class="required-badge">필수</span>
</div> </div>
<div class="form-field-wrapper"> <div class="form-field-wrapper">
<input type="text" th:value="${userName}" id="userName" name="userName" class="form-input" <input type="text" th:value="${userName}" id="userName" name="userName" class="form-input">
placeholder="성명">
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
@@ -36,13 +35,13 @@
mobileLast=${hasHyphen ? (parts != null and #arrays.length(parts) > 2 ? parts[2] : '') : (len == 11 ? #strings.substring(mobileNumber, 7, 11) : (len == 10 ? #strings.substring(mobileNumber, 6, 10) : ''))}"> mobileLast=${hasHyphen ? (parts != null and #arrays.length(parts) > 2 ? parts[2] : '') : (len == 11 ? #strings.substring(mobileNumber, 7, 11) : (len == 10 ? #strings.substring(mobileNumber, 6, 10) : ''))}">
<div class="compound-input"> <div class="compound-input">
<input type="text" th:value="${mobilePrefix}" name="mobilePrefix" class="form-input input-readonly" <input type="text" th:value="${mobilePrefix}" name="mobilePrefix" class="form-input input-readonly"
maxlength="3" disabled="disabled"> maxlength="3" disabled="disabled" style="background: #ededed !important;">
<span class="separator">-</span> <span class="separator">-</span>
<input type="text" th:value="${mobileMiddle}" name="mobileMiddle" class="form-input input-readonly" <input type="text" th:value="${mobileMiddle}" name="mobileMiddle" class="form-input input-readonly"
maxlength="4" disabled="disabled"> maxlength="4" disabled="disabled" style="background: #ededed !important;">
<span class="separator">-</span> <span class="separator">-</span>
<input type="text" th:value="${mobileLast}" name="mobileLast" class="form-input input-readonly" <input type="text" th:value="${mobileLast}" name="mobileLast" class="form-input input-readonly"
maxlength="4" disabled="disabled"> maxlength="4" disabled="disabled" style="background: #ededed !important;">
</div> </div>
<button type="button" class="btn-input-action btn-change change_mobile_phone" <button type="button" class="btn-input-action btn-change change_mobile_phone"
id="changePhoneBtn">변경</button> id="changePhoneBtn">변경</button>
@@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
layout:decorate="~{layout/djbank_base_layout}"> layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
@@ -10,59 +10,78 @@
</div> </div>
</section> </section>
<section layout:fragment="contentFragment" class="content"> <section layout:fragment="contentFragment" class="content">
<div class="org-register-page corporate-register">
<div class="org-register-container"> <div class="org-register-container">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="hidden" name="registrationType" th:value="${registrationType}"/> <input type="hidden" name="registrationType" th:value="${registrationType}"/>
<div class="register-card-wrapper">
<!-- Info Notice -->
<div class="org-info-notice">
<div class="notice-icon-wrapper">
<svg class="notice-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
</div>
<p class="notice-text">법인회원 전환 신청 완료 후 서비스 또는 API 이용을 하실 수 있습니다.</p>
</div>
<!-- 약관동의 섹션 --> <!-- 약관동의 섹션 -->
<th:block <th:block th:replace="~{apps/register/userAgreementContent :: agreementContent(${termsOfUse}, ${privacyCollect})}"></th:block>
th:replace="apps/register/userAgreementContent :: agreementContent(${termsOfUse}, ${privacyCollect})"></th:block>
<!-- 법인 기본 정보 폼 --> <!-- 법인 기본 정보 폼 -->
<form name="portalOrg" id="businessTransferForm" method="post" th:action="@{/mypage/org-transfer}" <form name="portalOrg" id="businessTransferForm" method="post" th:action="@{/mypage/org-transfer}"
th:object="${portalOrg}" enctype="multipart/form-data"> th:object="${portalOrg}" enctype="multipart/form-data">
<div class="org-section-header"> <div class="org-section-header org-section-header--agreement">
<h3>법인 기본 정보</h3> <h3>법인 기본 정보</h3>
<span class="required-badge">필수 입력</span> <span class="required-badge">필수 입력</span>
</div> </div>
<th:block th:replace="~{apps/register/components/orgBasicInfoForm :: orgInfoForm}"></th:block> <th:block th:replace="~{apps/register/components/orgBasicInfoForm :: orgInfoForm}"></th:block>
<!-- 법인 관리자 정보 --> <!-- 법인 관리자 정보 -->
<div class="org-section-header"> <div class="org-section-header org-section-header--agreement" style="margin-top: 48px;">
<h3>법인 관리자 정보</h3> <h3>법인 관리자 정보</h3>
<span class="required-badge">필수 입력</span> <span class="required-badge">필수 입력</span>
</div> </div>
<div class="org-info-notice"> <div class="org-info-notice">
<ul> <div class="notice-icon-wrapper">
<li>법인 관리자는 API 신청 및 개발자 초대 등 모든 권한을 보유합니다.</li> <svg class="notice-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<li>법인회원 전환은 반드시 법인이메일 주소로 입력하시기 바랍니다. 개인이메일로 전환 신청시, 법인 승인이 거절 될 수 있습니다</li> <circle cx="12" cy="12" r="10" />
</ul> <line x1="12" y1="8" x2="12" y2="12" />
<line x1="12" y1="16" x2="12.01" y2="16" />
</svg>
</div> </div>
<p class="notice-text">법인 관리자는 API 신청 및 개발자 초대 등 모든 권한을 보유합니다. 법인회원 전환은 반드시 법인이메일 주소로 입력하시기 바랍니다.</p>
</div>
<div class="org-form-container"> <div class="org-form-container">
<div class="org-form-group"> <div class="org-form-group">
<label class="org-form-label"> <label class="org-form-label">
<span>이메일 아이디</span> <span>이메일 아이디</span>
</label> </label>
<div class="org-form-input-wrapper"> <div class="org-form-input-wrapper">
<input type="text" th:value="${user.loginId}" name="loginId" <input type="text" th:value="${user.loginId}" name="loginId"
class="org-form-input input-readonly" class="org-form-input input-readonly"
placeholder="이메일" disabled="disabled"> placeholder="이메일" disabled="disabled">
<input type="hidden" th:value="${user.loginId}" name="loginId"> <input type="hidden" th:value="${user.loginId}" name="loginId">
</div> </div>
</div>
</div> </div>
</div>
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="org-action-buttons"> <div class="org-action-buttons">
<button type="button" class="btn btn-secondary" id="cancelButton" th:onclick="|location.href='@{/mypage}'|">취소</button> <button type="button" class="btn btn-secondary" id="cancelButton" th:onclick="|location.href='@{/mypage}'|">취소</button>
<button type="button" class="btn btn-primary" id="submitButton">법인회원 전환</button> <button type="button" class="btn btn-primary" id="submitButton">법인회원 전환</button>
</div> </div>
</form> </form>
</div>
</div> </div>
</div>
</section> </section>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript"> <script th:if="${error}" th:inline="javascript">
@@ -1,199 +1,220 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image"> <img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>내 정보 관리</h1> <h1>내 정보 관리</h1>
</div>
</section>
<section layout:fragment="contentFragment">
<div class="org-register-container">
<div class="corp-manager-wrapper">
<!-- 초대 알림 배너 -->
<div th:if="${hasPendingInvitation}" class="invitation_alert_banner" style="background-color: #f0f8ff; border: 1px solid #2196F3; border-radius: 4px; padding: 15px 20px; margin: 20px 0;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>
<strong style="color: #1976D2;">법인 회원 초대가 대기 중입니다</strong>
<p style="margin: 5px 0 0 0; color: #666;">기관에서 귀하를 법인 회원으로 초대했습니다. 초대를 수락하면 해당 기관의 법인 회원으로 전환됩니다.</p>
</div>
<div>
<a th:href="@{/signup/decision(invitation=${invitationToken})}" class="common_btn_type_1 blue" style="white-space: nowrap;">
<span>초대 확인</span>
</a>
</div>
</div>
</div>
<div class="common-title-bar">
<h2 class="common-title">기본 정보</h2>
</div>
<div class="register-form-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="register-form">
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
<!-- Corporate Transfer Button -->
<div class="corporate-transfer-section">
<button type="button" class="btn btn-secondary btn_business btn-block">
법인회원 전환
</button>
</div>
</form>
</div>
<input type="hidden" name="finalMobileNumber"/>
<!-- Action Buttons -->
<div class="form-actions form-actions--with-withdrawal">
<a class="withdrawal-link"><img th:src="@{/img/btn_withdrawal.png}" alt="회원탈퇴">회원탈퇴</a>
<div class="form-actions-buttons">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary submit-btn">수정</button>
</div> </div>
</div> </section>
</div> <section layout:fragment="contentFragment">
</div> <div class="service-main container" style="padding-top: 70px; padding-bottom:100px;">
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('profile')}"></th:block>
<div class="app-management-content">
<div class="corp-manager-wrapper">
<!-- 초대 알림 배너 -->
<div th:if="${hasPendingInvitation}" class="invitation_alert_banner"
style="background-color: #f0f8ff; border: 1px solid #2196F3; border-radius: 4px; padding: 15px 20px; margin: 20px 0;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>
<strong style="color: #1976D2;">법인 회원 초대가 대기 중입니다</strong>
<p style="margin: 5px 0 0 0; color: #666;">기관에서 귀하를 법인 회원으로 초대했습니다. 초대를 수락하면 해당 기관의 법인
회원으로 전환됩니다.</p>
</div>
<div>
<a th:href="@{/signup/decision(invitation=${invitationToken})}"
class="common_btn_type_1 blue" style="white-space: nowrap;">
<span>초대 확인</span>
</a>
</div>
</div>
</div>
</section> <h2 class="page-outer-title">정보 관리</h2>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
$(document).ready(function () {
customPopups.showAlert([[${error}]]);
});
</script>
<script th:inline="javascript">
$(document).ready(function() {
// 기존 success 메시지 처리
var successMsg = [[${success}]];
console.log("Success message:", successMsg);
if (successMsg) {
console.log("Showing alert");
customPopups.showAlert(successMsg);
}
});
</script>
<script th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfoScript}"></script>
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('updateForm');
// 수정 버튼 이벤트 <form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
const submitButton = document.querySelector('.btn-primary.submit-btn'); <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
if (submitButton) {
submitButton.addEventListener('click', function (e) {
e.preventDefault();
try { <div class="register-form-container">
// 원래 값들 가져오기 <!-- Section Title -->
const originalName = document.querySelector('input[name="userName"]').defaultValue; <div class="inner-section-title">
const originalPrefix = document.querySelector('input[name="mobilePrefix"]').value; <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#000"
const originalMiddle = document.querySelector('input[name="mobileMiddle"]').value; stroke-width="2">
const originalLast = document.querySelector('input[name="mobileLast"]').value; <rect x="3" y="3" width="7" height="7"></rect>
const originalMobileNumber = `${originalPrefix}-${originalMiddle}-${originalLast}`; <rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<h3>기본 정보</h3>
</div>
// 현재 값들 가져오기 <div class="register-form">
const currentName = document.querySelector('input[name="userName"]').value.trim(); <th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}">
const newPhoneContainer = document.getElementById('newPhoneNumberContainer'); </th:block>
const isNewPhoneVisible = newPhoneContainer && newPhoneContainer.style.display !== 'none'; </div>
const mobileNumberInput = document.getElementById('mobileNumber');
// 변경사항 체크 <!-- Corporate Transfer Button -->
let hasChanges = false; <div class="corporate-transfer-section" style="margin-top: 24px;">
<button type="button" class="btn btn_business"
style="background: #FFFFFF; border: 1.5px solid #2a69de; color: #2a69de; font-weight: 700; border-radius: 12px; padding: 12px 24px; font-size: 15px; cursor: pointer; transition: all 0.2s ease; display: inline-flex; align-items: center; justify-content: center; width: 100%; box-shadow: none;">
법인회원 전환
</button>
</div>
</div>
</form>
// 이름 변경 체크 <input type="hidden" name="finalMobileNumber" />
if (currentName !== originalName) {
hasChanges = true;
}
// 휴대폰 번호 변경 체크 <!-- Action Buttons -->
if (isNewPhoneVisible) { <div class="form-actions">
if (window.isAuthVerified) { <a class="withdrawal-link btn-withdrawal">회원탈퇴</a>
const prefix = $('#phoneMobile').val(); <div class="right-buttons">
const middle = document.getElementById('newMobileMiddle').value; <button type="button" class="btn-cancel" th:onclick="|location.href='@{/}'|">취소</button>
const last = document.getElementById('newMobileLast').value; <button type="button" class="btn-apply btn-primary submit-btn">수정</button>
const newMobileNumber = `${prefix}-${middle}-${last}`; </div>
</div>
if (newMobileNumber !== originalMobileNumber) { </div>
hasChanges = true; </div>
} </div>
</section>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
$(document).ready(function () {
customPopups.showAlert([[${ error }]]);
});
</script>
<script th:inline="javascript">
$(document).ready(function () {
// 기존 success 메시지 처리
var successMsg = [[${ success }]];
console.log("Success message:", successMsg);
if (successMsg) {
console.log("Showing alert");
customPopups.showAlert(successMsg);
}
});
</script>
<script th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfoScript}"></script>
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('updateForm');
if (prefix && prefix !== '선택' && middle && last) { // 수정 버튼 이벤트
mobileNumberInput.value = newMobileNumber; const submitButton = document.querySelector('.btn-primary.submit-btn');
if (submitButton) {
submitButton.addEventListener('click', function (e) {
e.preventDefault();
try {
// 원래 값들 가져오기
const originalName = document.querySelector('input[name="userName"]').defaultValue;
const originalPrefix = document.querySelector('input[name="mobilePrefix"]').value;
const originalMiddle = document.querySelector('input[name="mobileMiddle"]').value;
const originalLast = document.querySelector('input[name="mobileLast"]').value;
const originalMobileNumber = `${originalPrefix}-${originalMiddle}-${originalLast}`;
// 현재 값들 가져오기
const currentName = document.querySelector('input[name="userName"]').value.trim();
const newPhoneContainer = document.getElementById('newPhoneNumberContainer');
const isNewPhoneVisible = newPhoneContainer && newPhoneContainer.style.display !== 'none';
const mobileNumberInput = document.getElementById('mobileNumber');
// 변경사항 체크
let hasChanges = false;
// 이름 변경 체크
if (currentName !== originalName) {
hasChanges = true;
}
// 휴대폰 번호 변경 체크
if (isNewPhoneVisible) {
if (window.isAuthVerified) {
const prefix = $('#phoneMobile').val();
const middle = document.getElementById('newMobileMiddle').value;
const last = document.getElementById('newMobileLast').value;
const newMobileNumber = `${prefix}-${middle}-${last}`;
if (newMobileNumber !== originalMobileNumber) {
hasChanges = true;
}
if (prefix && prefix !== '선택' && middle && last) {
mobileNumberInput.value = newMobileNumber;
} else {
customPopups.showAlert('휴대폰 번호를 올바르게 입력해주세요.');
return;
}
} else { } else {
customPopups.showAlert('휴대폰 번호를 올바르게 입력해주세요.'); customPopups.showAlert('인증번호확인을 완료해주세요.');
return; return;
} }
} else { } else {
customPopups.showAlert('인증번호확인을 완료해주세요.'); mobileNumberInput.value = originalMobileNumber;
}
// 변경사항이 없는 경우
if (!hasChanges) {
customPopups.showAlert('변경된 내용이 없습니다.');
return; return;
} }
} else {
mobileNumberInput.value = originalMobileNumber; // 불필요한 필드 제거
const fieldsToRemove = [
'mobilePrefix', 'mobileMiddle', 'mobileLast',
'newMobilePrefix', 'newMobileMiddle', 'newMobileLast',
'authNumber', 'finalMobileNumber'
];
fieldsToRemove.forEach(fieldName => {
const elements = form.querySelectorAll(`[name="${fieldName}"]`);
elements.forEach(element => element.remove());
});
form.submit();
} catch (error) {
console.error('Error during form submission:', error);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해 주세요.');
} }
});
}
// 변경사항이 없는 경우 // 법인전환 버튼 이벤트
if (!hasChanges) { const businessTransferBtn = document.querySelector('.btn_business');
customPopups.showAlert('변경된 내용이 없습니다.'); if (businessTransferBtn) {
return; businessTransferBtn.addEventListener('click', () => {
} customPopups.showConfirm(
'법인 회원으로 전환하시겠습니까?',
// 불필요한 필드 제거 (confirmed) => {
const fieldsToRemove = [ if (confirmed) {
'mobilePrefix', 'mobileMiddle', 'mobileLast', const transferForm = document.createElement('form');
'newMobilePrefix', 'newMobileMiddle', 'newMobileLast', transferForm.method = 'GET';
'authNumber', 'finalMobileNumber' transferForm.action = '/mypage/org-transfer';
]; document.body.appendChild(transferForm);
transferForm.submit();
fieldsToRemove.forEach(fieldName => { }
const elements = form.querySelectorAll(`[name="${fieldName}"]`);
elements.forEach(element => element.remove());
});
form.submit();
} catch (error) {
console.error('Error during form submission:', error);
customPopups.showAlert('처리 중 오류가 발생했습니다. 다시 시도해 주세요.');
}
});
}
// 법인전환 버튼 이벤트
const businessTransferBtn = document.querySelector('.btn_business');
if (businessTransferBtn) {
businessTransferBtn.addEventListener('click', () => {
customPopups.showConfirm(
'법인 회원으로 전환하시겠습니까?',
(confirmed) => {
if (confirmed) {
const transferForm = document.createElement('form');
transferForm.method = 'GET';
transferForm.action = '/mypage/org-transfer';
document.body.appendChild(transferForm);
transferForm.submit();
} }
} );
); });
}); }
}
// 회원탈퇴 버튼 이벤트 // 회원탈퇴 버튼 이벤트
const withdrawalBtn = document.querySelector('.withdrawal-link'); const withdrawalBtn = document.querySelector('.withdrawal-link');
if (withdrawalBtn) { if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => { withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
customPopups.showWithdrawal(); customPopups.showWithdrawal();
}); });
} }
}); });
</script> </script>
</th:block> </th:block>
<section layout:fragment="pagePopups"> <section layout:fragment="pagePopups">
<th:block th:replace="~{fragment/popup/withdrawalPopup :: withdrawalPopup}"></th:block> <th:block th:replace="~{fragment/popup/withdrawalPopup :: withdrawalPopup}"></th:block>
</section> </section>
</body> </body>
</html>
</html>
@@ -1,76 +1,78 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image"> <img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>이메일 인증</h1> <h1>이메일 인증</h1>
</div>
</section>
<section layout:fragment="contentFragment">
<div class="org-register-container">
<div class="common-title-bar">
<h2 class="common-title">이메일 인증</h2>
</div> </div>
</section>
<section layout:fragment="contentFragment">
<div class="service-main">
<div class="app-management-content">
<div class="password-change-wrapper">
<h2 class="page-outer-title">이메일 인증</h2>
<div class="register-form-container"> <form id="verificationEmailForm" role="form" name="verificationEmailForm" method="post">
<form id="verificationEmailForm" role="form" name="verificationEmailForm" method="post"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> <input type="hidden" id="userId" name="userId" th:value="${userId}">
<input type="hidden" id="userId" name="userId" th:value="${userId}">
<div class="register-form"> <div class="register-form-container">
<!-- 이메일 주소 + 인증코드 받기 버튼 (한 줄) --> <!-- 안내 Notice Box -->
<div class="form-row" style="margin-bottom: 0"> <div class="info-notice-box" style="margin-bottom: 24px;">
<div class="form-label-wrapper label-offset"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4685ef" stroke-width="2">
<span class="form-label-text">이메일 주소</span> <circle cx="12" cy="12" r="10"></circle>
</div> <path d="M12 16v-4"></path>
<div class="form-field-wrapper input-with-button"> <path d="M12 8h.01"></path>
<input type="email" id="email" name="email" class="form-input input-readonly" </svg>
th:value="${email}" readonly disabled="disabled"> <p>본인 확인을 위해 이메일 인증을 진행해 주세요. 아래 이메일 주소로 인증코드가 발송됩니다.</p>
<button type="button" class="btn-input-action btn-auth btn_send_code">인증코드 받기</button>
</div>
</div>
<div class="form-row" style="margin-bottom: 0">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">&nbsp;</span>
</div>
<div class="form-field-wrapper">
<p class="form-hint" style="margin-top: 0;">위 이메일 주소로 인증코드가 발송됩니다.</p>
</div>
</div>
<!-- 인증번호 입력 -->
<div class="form-row" id="authCodeContainer" style="display: none;">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">인증코드 입력</span> <span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="auth-input-group">
<input type="text" id="authCode" name="authCode" maxlength="6" pattern="[0-9]*"
inputmode="numeric" class="form-input"
placeholder="이메일로 받은 인증코드 6자리">
<span class="auth-timer" id="certify_time">05:00</span>
</div> </div>
<button type="button" class="btn-input-action btn-auth btn_verify_code">인증코드 확인</button>
</div>
</div>
</div>
</form>
</div>
<!-- Action Buttons --> <!-- 이메일 주소 행 -->
<div class="form-actions"> <div class="form-row">
<button type="button" class="btn-submit btn-secondary">취소</button> <div class="form-label-wrapper">
<span class="form-label-text">이메일 주소</span>
</div>
<div class="form-field-wrapper input-with-button">
<input type="email" id="email" name="email" class="form-input input-readonly" th:value="${email}"
readonly disabled="disabled" style="flex: 1;">
<button type="button" class="btn-input-action btn-auth btn_send_code">인증코드 받기</button>
</div>
</div>
<!-- 인증번호 입력 행 (초기 숨김) -->
<div class="form-row" id="authCodeContainer" style="display: none;">
<div class="form-label-wrapper">
<span class="form-label-text">인증코드 입력</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="auth-input-group" style="flex: 1; position: relative;">
<input type="text" id="authCode" name="authCode" maxlength="6" pattern="[0-9]*" inputmode="numeric"
class="form-input" placeholder="이메일로 받은 인증코드 6자리" style="width: 100%; padding-right: 70px;">
<span class="auth-timer" id="certify_time"
style="position: absolute; right: 16px; top: 50%; transform: translateY(-50%); font-weight: 500; font-size: 13px;">05:00</span>
</div>
<button type="button" class="btn-input-action btn-auth btn_verify_code">인증코드 확인</button>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="form-actions" style="justify-content: flex-end; margin-top: 24px;">
<div class="right-buttons">
<button type="button" class="btn-cancel btn_cancel">취소</button>
</div>
</div>
</form>
</div>
</div>
</div> </div>
</div> </section>
</section>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function() { $(document).ready(function () {
let timerInterval; let timerInterval;
let resendTimerInterval; let resendTimerInterval;
let isVerified = false; let isVerified = false;
@@ -83,10 +85,10 @@
const resendLimitSeconds = /*[[${@environment.getProperty('portal.auth.resend_limit_seconds', '60')}]]*/ 60; const resendLimitSeconds = /*[[${@environment.getProperty('portal.auth.resend_limit_seconds', '60')}]]*/ 60;
// 취소 버튼 클릭 시 메인 페이지로 이동 // 취소 버튼 클릭 시 메인 페이지로 이동
$('.btn_cancel').on('click', function() { $('.btn_cancel').on('click', function () {
customPopups.showConfirm( customPopups.showConfirm(
'이메일 인증을 취소하고 메인 페이지로 이동하시겠습니까?', '이메일 인증을 취소하고 메인 페이지로 이동하시겠습니까?',
function() { function () {
location.href = '/'; location.href = '/';
} }
); );
@@ -170,7 +172,7 @@
} }
// 인증코드 받기 버튼 클릭 // 인증코드 받기 버튼 클릭
$('.btn_send_code').on('click', function(e) { $('.btn_send_code').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const email = $('#email').val(); const email = $('#email').val();
@@ -186,7 +188,7 @@
headers: { headers: {
[csrfHeaderName]: csrfToken [csrfHeaderName]: csrfToken
}, },
success: function(response) { success: function (response) {
// 테스트 환경 인증번호 표시 // 테스트 환경 인증번호 표시
if (response.authNumber) { if (response.authNumber) {
displayTestAuthNotice(response.authNumber); displayTestAuthNotice(response.authNumber);
@@ -199,7 +201,7 @@
startTimer(300); // 5분 타이머 startTimer(300); // 5분 타이머
startResendTimer(); // 재발송 타이머 시작 (1분) startResendTimer(); // 재발송 타이머 시작 (1분)
}, },
error: function(xhr) { error: function (xhr) {
const errorMsg = xhr.responseJSON?.error || '인증코드 발송에 실패했습니다.'; const errorMsg = xhr.responseJSON?.error || '인증코드 발송에 실패했습니다.';
customPopups.showAlert(errorMsg); customPopups.showAlert(errorMsg);
$button.prop('disabled', false).text('인증코드 받기'); $button.prop('disabled', false).text('인증코드 받기');
@@ -208,7 +210,7 @@
}); });
// 인증코드 확인 버튼 클릭 // 인증코드 확인 버튼 클릭
$('.btn_verify_code').on('click', function(e) { $('.btn_verify_code').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const authCode = $('#authCode').val().trim(); const authCode = $('#authCode').val().trim();
@@ -235,7 +237,7 @@
headers: { headers: {
[csrfHeaderName]: csrfToken [csrfHeaderName]: csrfToken
}, },
success: function(response) { success: function (response) {
clearInterval(timerInterval); clearInterval(timerInterval);
clearInterval(resendTimerInterval); clearInterval(resendTimerInterval);
isVerified = true; isVerified = true;
@@ -249,11 +251,11 @@
customPopups.showAlert('이메일 인증이 완료되었습니다!'); customPopups.showAlert('이메일 인증이 완료되었습니다!');
// 알림 확인 후 메인 페이지로 이동 // 알림 확인 후 메인 페이지로 이동
setTimeout(function() { setTimeout(function () {
location.href = '/'; location.href = '/';
}, 2000); }, 2000);
}, },
error: function(xhr) { error: function (xhr) {
const errorMsg = xhr.responseJSON?.error || '인증코드가 일치하지 않습니다.'; const errorMsg = xhr.responseJSON?.error || '인증코드가 일치하지 않습니다.';
customPopups.showAlert(errorMsg); customPopups.showAlert(errorMsg);
} }
@@ -261,11 +263,12 @@
}); });
// 인증코드 입력 필드 숫자만 허용 // 인증코드 입력 필드 숫자만 허용
$('#authCode').on('input', function() { $('#authCode').on('input', function () {
$(this).val($(this).val().replace(/[^0-9]/g, '')); $(this).val($(this).val().replace(/[^0-9]/g, ''));
}); });
}); });
</script> </script>
</th:block> </th:block>
</html>
</html>
@@ -1,79 +1,78 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
layout:decorate="~{layout/djbank_title_layout}">
<body> <body>
<section layout:fragment="title"> <section layout:fragment="title">
<div class="page-title-banner"> <div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image"> <img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>회원가입</h1> <h1>회원가입</h1>
</div>
</section>
<section layout:fragment="contentFragment">
<div class="org-register-container">
<div class="common-title-bar">
<h2 class="common-title">이메일 인증</h2>
</div> </div>
</section>
<section layout:fragment="contentFragment">
<div class="service-main">
<div class="app-management-content">
<div class="password-change-wrapper">
<h2 class="page-outer-title">이메일 인증</h2>
<div class="register-form-container"> <form id="signupVerificationEmailForm" role="form" name="signupVerificationEmailForm" method="post">
<p class="form-hint" style="margin-bottom: 16px;"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
회원가입을 완료하려면 가입하신 이메일 주소로 인증을 진행해 주세요.
</p>
<form id="signupVerificationEmailForm" role="form" name="signupVerificationEmailForm" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="register-form"> <div class="register-form-container">
<!-- 이메일 주소 + 인증코드 받기 버튼 (한 줄) --> <!-- 안내 Notice Box -->
<div class="form-row" style="margin-bottom: 0"> <div class="info-notice-box" style="margin-bottom: 24px;">
<div class="form-label-wrapper label-offset"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4685ef" stroke-width="2">
<span class="form-label-text">이메일 주소</span> <circle cx="12" cy="12" r="10"></circle>
</div> <path d="M12 16v-4"></path>
<div class="form-field-wrapper input-with-button"> <path d="M12 8h.01"></path>
<input type="email" id="email" name="email" class="form-input input-readonly" </svg>
th:value="${email}" readonly disabled="disabled"> <p>회원가입을 완료하려면 가입하신 이메일 주소로 인증을 진행해 주세요.</p>
<button type="button" class="btn-input-action btn-auth btn_send_code">인증코드 받기</button>
</div>
</div>
<div class="form-row" style="margin-bottom: 0">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">&nbsp;</span>
</div>
<div class="form-field-wrapper">
<p class="form-hint" style="margin-top: 0;">위 이메일 주소로 인증코드가 발송됩니다.</p>
</div>
</div>
<!-- 인증번호 입력 -->
<div class="form-row" id="authCodeContainer" style="display: none;">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">인증코드 입력</span> <span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="auth-input-group">
<input type="text" id="authCode" name="authCode" maxlength="6" pattern="[0-9]*"
inputmode="numeric" class="form-input"
placeholder="이메일로 받은 인증코드 6자리">
<span class="auth-timer" id="certify_time">05:00</span>
</div> </div>
<button type="button" class="btn-input-action btn-auth btn_verify_code">인증코드 확인</button>
</div>
</div>
</div>
</form>
</div>
<!-- Action Buttons --> <!-- 이메일 주소 행 -->
<div class="form-actions"> <div class="form-row">
<button type="button" class="btn-submit btn-secondary btn_cancel">나중에 인증</button> <div class="form-label-wrapper">
<span class="form-label-text">이메일 주소</span>
</div>
<div class="form-field-wrapper input-with-button">
<input type="email" id="email" name="email" class="form-input input-readonly" th:value="${email}"
readonly disabled="disabled" style="flex: 1;">
<button type="button" class="btn-input-action btn-auth btn_send_code">인증코드 받기</button>
</div>
</div>
<!-- 인증번호 입력 행 (초기 숨김) -->
<div class="form-row" id="authCodeContainer" style="display: none;">
<div class="form-label-wrapper">
<span class="form-label-text">인증코드 입력</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="auth-input-group" style="flex: 1; position: relative;">
<input type="text" id="authCode" name="authCode" maxlength="6" pattern="[0-9]*" inputmode="numeric"
class="form-input" placeholder="이메일로 받은 인증코드 6자리" style="width: 100%; padding-right: 70px;">
<span class="auth-timer" id="certify_time"
style="position: absolute; right: 16px; top: 50%; transform: translateY(-50%); font-weight: 500; font-size: 13px;">05:00</span>
</div>
<button type="button" class="btn-input-action btn-auth btn_verify_code">인증코드 확인</button>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="form-actions" style="justify-content: flex-end; margin-top: 24px;">
<div class="right-buttons">
<button type="button" class="btn-cancel btn_cancel">나중에 인증</button>
</div>
</div>
</form>
</div>
</div>
</div> </div>
</div> </section>
</section>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function() { $(document).ready(function () {
let timerInterval; let timerInterval;
let resendTimerInterval; let resendTimerInterval;
let isVerified = false; let isVerified = false;
@@ -86,10 +85,10 @@
const resendLimitSeconds = /*[[${@environment.getProperty('portal.auth.resend_limit_seconds', '60')}]]*/ 60; const resendLimitSeconds = /*[[${@environment.getProperty('portal.auth.resend_limit_seconds', '60')}]]*/ 60;
// "나중에 인증" 클릭 시: 가입은 완료된 상태이므로 로그인 페이지로 이동 (로그인 후 인증 유도) // "나중에 인증" 클릭 시: 가입은 완료된 상태이므로 로그인 페이지로 이동 (로그인 후 인증 유도)
$('.btn_cancel').on('click', function() { $('.btn_cancel').on('click', function () {
customPopups.showConfirm( customPopups.showConfirm(
'이메일 인증을 나중에 하시겠습니까? 로그인 후에도 인증을 완료할 수 있습니다.', '이메일 인증을 나중에 하시겠습니까? 로그인 후에도 인증을 완료할 수 있습니다.',
function() { function () {
location.href = /*[[@{/login}]]*/ '/login'; location.href = /*[[@{/login}]]*/ '/login';
} }
); );
@@ -173,7 +172,7 @@
} }
// 인증코드 받기 버튼 클릭 // 인증코드 받기 버튼 클릭
$('.btn_send_code').on('click', function(e) { $('.btn_send_code').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const $button = $(this); const $button = $(this);
@@ -187,7 +186,7 @@
headers: { headers: {
[csrfHeaderName]: csrfToken [csrfHeaderName]: csrfToken
}, },
success: function(response) { success: function (response) {
if (!response.valid) { if (!response.valid) {
customPopups.showAlert(response.message || '인증코드 발송에 실패했습니다.'); customPopups.showAlert(response.message || '인증코드 발송에 실패했습니다.');
$button.prop('disabled', false).text('인증코드 받기'); $button.prop('disabled', false).text('인증코드 받기');
@@ -206,7 +205,7 @@
startTimer(300); // 5분 타이머 startTimer(300); // 5분 타이머
startResendTimer(); // 재발송 타이머 시작 startResendTimer(); // 재발송 타이머 시작
}, },
error: function() { error: function () {
customPopups.showAlert('인증코드 발송에 실패했습니다.'); customPopups.showAlert('인증코드 발송에 실패했습니다.');
$button.prop('disabled', false).text('인증코드 받기'); $button.prop('disabled', false).text('인증코드 받기');
} }
@@ -214,7 +213,7 @@
}); });
// 인증코드 확인 버튼 클릭 // 인증코드 확인 버튼 클릭
$('.btn_verify_code').on('click', function(e) { $('.btn_verify_code').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const authCode = $('#authCode').val().trim(); const authCode = $('#authCode').val().trim();
@@ -237,7 +236,7 @@
headers: { headers: {
[csrfHeaderName]: csrfToken [csrfHeaderName]: csrfToken
}, },
success: function(response) { success: function (response) {
if (!response.valid) { if (!response.valid) {
customPopups.showAlert(response.message || '인증코드가 일치하지 않습니다.'); customPopups.showAlert(response.message || '인증코드가 일치하지 않습니다.');
return; return;
@@ -256,22 +255,23 @@
customPopups.showAlert('이메일 인증이 완료되었습니다! 로그인 후 이용해 주세요.'); customPopups.showAlert('이메일 인증이 완료되었습니다! 로그인 후 이용해 주세요.');
// 알림 확인 후 로그인 페이지로 이동 // 알림 확인 후 로그인 페이지로 이동
setTimeout(function() { setTimeout(function () {
location.href = /*[[@{/login}]]*/ '/login'; location.href = /*[[@{/login}]]*/ '/login';
}, 2000); }, 2000);
}, },
error: function() { error: function () {
customPopups.showAlert('인증코드 확인에 실패했습니다.'); customPopups.showAlert('인증코드 확인에 실패했습니다.');
} }
}); });
}); });
// 인증코드 입력 필드 숫자만 허용 // 인증코드 입력 필드 숫자만 허용
$('#authCode').on('input', function() { $('#authCode').on('input', function () {
$(this).val($(this).val().replace(/[^0-9]/g, '')); $(this).val($(this).val().replace(/[^0-9]/g, ''));
}); });
}); });
</script> </script>
</th:block> </th:block>
</html>
</html>
@@ -57,7 +57,8 @@
<a th:href="@{/myapikey}" <a th:href="@{/myapikey}"
th:classappend="${activeMenu == 'apiKey'} ? 'service-nav__item--active' : ''" th:classappend="${activeMenu == 'apiKey'} ? 'service-nav__item--active' : ''"
class="service-nav__item">앱 관리</a> class="service-nav__item"
sec:authorize="hasRole('ROLE_APP')">앱 관리</a>
<a th:href="@{/webhook}" <a th:href="@{/webhook}"
th:classappend="${activeMenu == 'webhook'} ? 'service-nav__item--active' : ''" th:classappend="${activeMenu == 'webhook'} ? 'service-nav__item--active' : ''"