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