Merge branch 'design' into feats/api-status
This commit is contained in:
@@ -246,6 +246,110 @@ const customPopups = {
|
||||
$('#passwordPopupError').removeClass('show');
|
||||
$('#passwordPopupInput').removeClass('error');
|
||||
},
|
||||
/**
|
||||
* API 이용 해지 신청 팝업 표시 (경고문 + 사유 필수 — 본인 확인은 step-up 2FA가 담당)
|
||||
* @param {Object} options - 팝업 옵션
|
||||
* @param {Function} options.onConfirm - 해지 신청 버튼 클릭 시 호출되는 콜백 (파라미터: reason)
|
||||
* @param {Function} options.onCancel - 취소 버튼 클릭 시 호출되는 콜백 (선택사항)
|
||||
*/
|
||||
showTerminateRequest: function (options) {
|
||||
options = options || {};
|
||||
|
||||
const onConfirm = options.onConfirm;
|
||||
const onCancel = options.onCancel;
|
||||
|
||||
// 입력 필드 및 에러 초기화
|
||||
$('#terminateReasonInput').val('').removeClass('error');
|
||||
$('#terminatePopupError').removeClass('show').text('');
|
||||
|
||||
// 팝업 표시 (modal 구조 사용)
|
||||
$('#terminateRequestPopup').show();
|
||||
setTimeout(function() {
|
||||
$('#terminateModalBackdrop').addClass('show');
|
||||
$('#terminateModal').addClass('show');
|
||||
}, 10);
|
||||
|
||||
// Body 스크롤 방지
|
||||
$('body').css('overflow', 'hidden');
|
||||
|
||||
// 사유 입력 필드에 포커스
|
||||
setTimeout(function() {
|
||||
$('#terminateReasonInput').focus();
|
||||
}, 350);
|
||||
|
||||
// 확인 버튼 이벤트 (기존 이벤트 제거 후 재등록)
|
||||
$('#terminatePopupConfirmButton').off('click').on('click', function () {
|
||||
const reason = $('#terminateReasonInput').val().trim();
|
||||
|
||||
if (!reason) {
|
||||
customPopups.showTerminateError('해지 사유를 입력해주세요.');
|
||||
$('#terminateReasonInput').addClass('error').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm(reason);
|
||||
}
|
||||
});
|
||||
|
||||
// 취소 버튼 이벤트
|
||||
$('#terminatePopupCancelButton').off('click').on('click', function () {
|
||||
customPopups.hideTerminateRequest();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// 닫기 버튼 이벤트
|
||||
$('#terminatePopupCloseButton').off('click').on('click', function () {
|
||||
customPopups.hideTerminateRequest();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// 입력 시 에러 초기화
|
||||
$('#terminateReasonInput').off('input').on('input', function () {
|
||||
$(this).removeClass('error');
|
||||
$('#terminatePopupError').removeClass('show').text('');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 해지 신청 팝업 숨기기
|
||||
*/
|
||||
hideTerminateRequest: function () {
|
||||
// Modal 숨김 애니메이션
|
||||
$('#terminateModalBackdrop').removeClass('show');
|
||||
$('#terminateModal').removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 숨김
|
||||
setTimeout(function() {
|
||||
$('#terminateRequestPopup').hide();
|
||||
}, 300);
|
||||
|
||||
// Body 스크롤 복원
|
||||
$('body').css('overflow', '');
|
||||
|
||||
// 입력 필드 초기화
|
||||
$('#terminateReasonInput').val('').removeClass('error');
|
||||
$('#terminatePopupError').removeClass('show').text('');
|
||||
|
||||
// 이벤트 리스너 제거
|
||||
$('#terminatePopupConfirmButton').off('click');
|
||||
$('#terminatePopupCancelButton').off('click');
|
||||
$('#terminatePopupCloseButton').off('click');
|
||||
$('#terminateReasonInput').off('input');
|
||||
},
|
||||
|
||||
/**
|
||||
* 해지 신청 팝업 에러 메시지 표시
|
||||
* @param {string} message - 에러 메시지
|
||||
*/
|
||||
showTerminateError: function (message) {
|
||||
$('#terminatePopupError').text(message).addClass('show');
|
||||
},
|
||||
|
||||
/**
|
||||
* 확인 팝업 표시
|
||||
* @param {string} message - 확인 메시지
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<!-- Form Content -->
|
||||
<form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
|
||||
th:object="${inquiry}" method="post" enctype="multipart/form-data" class="djb-board-form">
|
||||
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
|
||||
<input type="hidden" th:field="*{id}" th:unless="${isNew}">
|
||||
|
||||
<!-- Subject Field -->
|
||||
<div class="form-group">
|
||||
@@ -85,7 +85,7 @@
|
||||
<input type="file" id="inquiryImage" name="image" class="djb-input"
|
||||
accept="image/png,image/jpeg,image/gif">
|
||||
<small class="form-help-text">jpg, jpeg, png, gif 이미지 1개만 첨부할 수 있습니다.</small>
|
||||
<small th:if="${!isNew and inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}"
|
||||
<small th:if="${isNew != true and inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}"
|
||||
class="form-help-text">현재 첨부된 이미지가 있습니다. 새 파일을 선택하면 교체됩니다.</small>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
const file = fileInput.files[0];
|
||||
|
||||
if (file) {
|
||||
/*[# th:if="${!isInternalUser}"]*/
|
||||
/*[# th:unless="${isInternalUser}"]*/
|
||||
const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png'];
|
||||
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
|
||||
|
||||
|
||||
@@ -32,57 +32,7 @@
|
||||
<!-- App List Container -->
|
||||
<div class="app-list-container-figma">
|
||||
|
||||
<!-- App Requests (Pending) -->
|
||||
<th:block th:if="${appRequests != null and !appRequests.isEmpty()}">
|
||||
<a class="app-card-figma" th:each="request : ${appRequests}"
|
||||
th:href="@{/clients/app_request_detail(id=${request.id})}">
|
||||
|
||||
<!-- App Icon -->
|
||||
<div class="app-card-icon-box">
|
||||
<img th:if="${request.appIconFileId != null}"
|
||||
th:src="@{/file/download(fileSn=1,fileId=${request.appIconFileId})}" alt="App Icon">
|
||||
<svg th:unless="${request.appIconFileId != null}" width="46" height="46" viewBox="0 0 24 24"
|
||||
fill="none" stroke="#64748b" stroke-width="1.5">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- App Info -->
|
||||
<div class="app-card-info">
|
||||
<div class="app-card-header">
|
||||
<!-- App Name -->
|
||||
<h3 class="app-card-title" th:text="${request.clientName}">앱 이름</h3>
|
||||
<!-- Status Badge -->
|
||||
<span class="app-card-badge"
|
||||
th:classappend="${request.approval != null and request.approval.approvalStatus != null and request.approval.approvalStatus.toString() == 'REQUESTED' ? 'badge-requested' : 'badge-pending'}"
|
||||
th:text="${request.approval != null and request.approval.approvalStatus != null ? request.approval.approvalStatus.description : '승인정보 없음'}">
|
||||
승인정보 없음
|
||||
</span>
|
||||
</div>
|
||||
<!-- App Description & Expected Completion Date -->
|
||||
<div class="app-card-footer-row">
|
||||
<p class="app-card-desc"
|
||||
th:text="${request.appDescription != null ? request.appDescription : '설명 없음'}">
|
||||
앱 설명이 여기에 표시됩니다.
|
||||
</p>
|
||||
<!-- Expected date or placeholder to keep structure aligned -->
|
||||
<span class="app-card-expected-date"
|
||||
th:if="${request.approval != null and request.approval.expectEndDate != null and #strings.length(request.approval.expectEndDate) >= 8}"
|
||||
th:text="|예상 완료일 : ${#strings.substring(request.approval.expectEndDate,4,6)}월 ${#strings.substring(request.approval.expectEndDate,6,8)}일 (${request.approval.expectEndDateDayOfWeek})|">
|
||||
예상 완료일 : 05월 30일 (토)
|
||||
</span>
|
||||
<span class="app-card-expected-date"
|
||||
th:unless="${request.approval != null and request.approval.expectEndDate != null and #strings.length(request.approval.expectEndDate) >= 8}">
|
||||
예상 완료일 : -
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<!-- API Keys (Approved/Inactive) -->
|
||||
<!-- API Keys (Approved/Inactive) — 승인완료 우선 표시 -->
|
||||
<th:block th:if="${apiKeys != null and !apiKeys.isEmpty()}">
|
||||
<a class="app-card-figma" th:each="apikey : ${apiKeys}"
|
||||
th:href="@{/clients/credential_detail(id=${apikey.clientid})}">
|
||||
@@ -119,6 +69,59 @@
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<!-- App Requests (Pending) — 진행중 → 요청됨, 최근 신청 순 -->
|
||||
<th:block th:if="${appRequests != null and !appRequests.isEmpty()}">
|
||||
<a class="app-card-figma" th:each="request : ${appRequests}"
|
||||
th:href="@{/clients/app_request_detail(id=${request.id})}">
|
||||
|
||||
<!-- App Icon -->
|
||||
<div class="app-card-icon-box">
|
||||
<img th:if="${request.appIconFileId != null}"
|
||||
th:src="@{/file/download(fileSn=1,fileId=${request.appIconFileId})}" alt="App Icon">
|
||||
<svg th:unless="${request.appIconFileId != null}" width="46" height="46" viewBox="0 0 24 24"
|
||||
fill="none" stroke="#64748b" stroke-width="1.5">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- App Info -->
|
||||
<div class="app-card-info">
|
||||
<div class="app-card-header">
|
||||
<!-- App Name -->
|
||||
<h3 class="app-card-title" th:text="${request.clientName}">앱 이름</h3>
|
||||
<!-- Request Type Badge (해지 신청 구분) -->
|
||||
<span class="app-card-badge badge-pending"
|
||||
th:if="${request.type != null and request.type.name() == 'DELETE'}">해지 신청</span>
|
||||
<!-- Status Badge -->
|
||||
<span class="app-card-badge"
|
||||
th:classappend="${request.approval != null and request.approval.approvalStatus != null and request.approval.approvalStatus.toString() == 'REQUESTED' ? 'badge-requested' : 'badge-pending'}"
|
||||
th:text="${request.approval != null and request.approval.approvalStatus != null ? request.approval.approvalStatus.description : '승인정보 없음'}">
|
||||
승인정보 없음
|
||||
</span>
|
||||
</div>
|
||||
<!-- App Description & Expected Completion Date -->
|
||||
<div class="app-card-footer-row">
|
||||
<p class="app-card-desc"
|
||||
th:text="${request.appDescription != null ? request.appDescription : '설명 없음'}">
|
||||
앱 설명이 여기에 표시됩니다.
|
||||
</p>
|
||||
<!-- Expected date or placeholder to keep structure aligned -->
|
||||
<span class="app-card-expected-date"
|
||||
th:if="${request.approval != null and request.approval.expectEndDate != null and #strings.length(request.approval.expectEndDate) >= 8}"
|
||||
th:text="|예상 완료일 : ${#strings.substring(request.approval.expectEndDate,4,6)}월 ${#strings.substring(request.approval.expectEndDate,6,8)}일 (${request.approval.expectEndDateDayOfWeek})|">
|
||||
예상 완료일 : 05월 30일 (토)
|
||||
</span>
|
||||
<span class="app-card-expected-date"
|
||||
th:unless="${request.approval != null and request.approval.expectEndDate != null and #strings.length(request.approval.expectEndDate) >= 8}">
|
||||
예상 완료일 : -
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div class="app-list-empty-figma"
|
||||
th:if="${(appRequests == null or appRequests.isEmpty()) and (apiKeys == null or apiKeys.isEmpty())}">
|
||||
|
||||
@@ -190,8 +190,10 @@
|
||||
<div class="dt-actions" style="justify-content: flex-end; gap: 11px; margin-top: 30px;">
|
||||
<a th:href="@{/clients}" class="dt-btn-gray">목록</a>
|
||||
<button type="button" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" class="dt-btn-red"
|
||||
th:data-client-id="${apiKey.clientid}" onclick="deleteApiKeyFromButton(this)">API 이용 해지</button>
|
||||
<a sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" class="dt-btn-blue"
|
||||
th:data-client-id="${apiKey.clientid}" onclick="deleteApiKeyFromButton(this)"
|
||||
th:disabled="${pendingDeleteRequest}"
|
||||
th:text="${pendingDeleteRequest} ? '해지 승인 대기중' : 'API 이용 해지'">API 이용 해지</button>
|
||||
<a sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" th:unless="${pendingDeleteRequest}" class="dt-btn-blue"
|
||||
th:href="@{/clients/modify/step1(clientId=${apiKey.clientid})}">변경 신청</a>
|
||||
</div>
|
||||
|
||||
@@ -295,44 +297,44 @@
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// Delete API Key function - called from button with data-client-id attribute
|
||||
// API 이용 해지 신청 진입 - 경고 + 사유 모달 (본인 확인은 step-up 2FA가 담당)
|
||||
function deleteApiKeyFromButton(button) {
|
||||
var clientId = $(button).data('client-id');
|
||||
|
||||
customPopups.showConfirm('정말로 이 인증키를 삭제하시겠습니까?', function (confirmed) {
|
||||
if (!confirmed) {
|
||||
return;
|
||||
customPopups.showTerminateRequest({
|
||||
onConfirm: function (reason) {
|
||||
doDeleteApiKey(clientId, reason);
|
||||
}
|
||||
doDeleteApiKey(clientId);
|
||||
});
|
||||
}
|
||||
|
||||
// 인증키 삭제 요청 (step-up 필요 시 2FA 후 동일 요청 재시도)
|
||||
function doDeleteApiKey(clientId) {
|
||||
// 해지 신청 요청 (step-up 필요 시 2FA 후 동일 요청 재시도)
|
||||
function doDeleteApiKey(clientId, reason) {
|
||||
$('.loading-overlay').show();
|
||||
|
||||
$.ajax({
|
||||
url: /*[[@{/clients/api_key_delete}]]*/ '/clients/api_key_delete',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ clientId: clientId }),
|
||||
data: JSON.stringify({ clientId: clientId, reason: reason }),
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': /*[[${_csrf.token}]]*/ 'token'
|
||||
}
|
||||
}).done(function (response) {
|
||||
if (response && response.success === false) {
|
||||
customPopups.showAlert(response.msg || 'API 삭제에 실패했습니다.');
|
||||
customPopups.showTerminateError(response.msg || '해지 신청에 실패했습니다.');
|
||||
return;
|
||||
}
|
||||
customPopups.showAlert(response.msg || 'API Key가 삭제되었습니다.', function () {
|
||||
customPopups.hideTerminateRequest();
|
||||
customPopups.showAlert(response.msg || '해지 신청이 접수되었습니다. 관리자 승인 후 인증키가 삭제됩니다.', function () {
|
||||
window.location.href = /*[[@{/clients}]]*/ '/clients';
|
||||
});
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
if (isStepUpRequired(jqXHR)) {
|
||||
requireStepUp('/clients/api_key_delete', function () { doDeleteApiKey(clientId); });
|
||||
requireStepUp('/clients/api_key_delete', function () { doDeleteApiKey(clientId, reason); });
|
||||
return;
|
||||
}
|
||||
customPopups.showAlert('API 삭제 요청 중 오류가 발생했습니다: ' + errorThrown);
|
||||
customPopups.showTerminateError('해지 신청 요청 중 오류가 발생했습니다: ' + errorThrown);
|
||||
}).always(function () {
|
||||
$('.loading-overlay').hide();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<section layout:fragment="title">
|
||||
<div class="page-title-banner">
|
||||
<img th:src="@{/img/img_title_bg.png}" alt="개인회원가입" class="title-image">
|
||||
<h1 th:text="${!isInvited ? '개인회원가입' : '법인회원가입'}">개인회원가입</h1>
|
||||
<h1 th:text="${isInvited != true ? '개인회원가입' : '법인회원가입'}">개인회원가입</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<!-- Registration Card Wrapper -->
|
||||
<div class="register-card-wrapper">
|
||||
<!-- Info Notice -->
|
||||
<div class="org-info-notice" th:if="${!isInvited}">
|
||||
<div class="org-info-notice" th:unless="${isInvited}">
|
||||
<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" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div th:fragment="authNumberValidation">
|
||||
<div th:if="${!isValid}" class="invalid-feedback d-block">
|
||||
<div th:unless="${isValid}" class="invalid-feedback d-block">
|
||||
올바른 인증번호를 입력해주세요.
|
||||
</div>
|
||||
<div th:if="${isValid}" class="valid-feedback d-block">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div th:fragment="result">
|
||||
<span id="validationResult"
|
||||
th:text="${!isValid} ? '올바른 법인 등록번호 형식이 아닙니다. 앞 6자리, 뒤 7자리의 숫자를 입력해주세요.' : '유효한 법인 등록번호입니다.'"
|
||||
th:class="${!isValid} ? 'invalid-feedback d-block' : 'valid-feedback d-block'">
|
||||
th:text="${isValid != true} ? '올바른 법인 등록번호 형식이 아닙니다. 앞 6자리, 뒤 7자리의 숫자를 입력해주세요.' : '유효한 법인 등록번호입니다.'"
|
||||
th:class="${isValid != true} ? 'invalid-feedback d-block' : 'valid-feedback d-block'">
|
||||
</span>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<div th:fragment="result">
|
||||
<div th:if="${!isValidFormat}" class="invalid-feedback d-block">
|
||||
<div th:unless="${isValidFormat}" class="invalid-feedback d-block">
|
||||
올바른 이메일 형식이 아닙니다.
|
||||
</div>
|
||||
<div th:if="${isValidFormat}">
|
||||
@@ -11,6 +11,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<span id="validationResult" style="display:none;"
|
||||
th:text="${!isValidFormat} ? '올바른 이메일 형식이 아닙니다.' : (${isDuplicate} ? '이미 등록된 이메일입니다.' : '사용 가능한 이메일입니다.')">
|
||||
th:text="${isValidFormat != true} ? '올바른 이메일 형식이 아닙니다.' : (${isDuplicate} ? '이미 등록된 이메일입니다.' : '사용 가능한 이메일입니다.')">
|
||||
</span>
|
||||
</div>
|
||||
@@ -4,4 +4,5 @@
|
||||
<div th:replace="fragment/popup/emailValidationPopup :: #emailValidationPopup"></div>
|
||||
<div th:replace="fragment/popup/customPopup2 :: #customConfirm"></div>
|
||||
<div th:replace="fragment/popup/passwordInputPopup :: passwordInputPopup"></div>
|
||||
<div th:replace="fragment/popup/terminateRequestPopup :: terminateRequestPopup"></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<!-- views/fragment/popup/terminateRequestPopup.html -->
|
||||
<div th:fragment="terminateRequestPopup" id="terminateRequestPopup" style="display: none;">
|
||||
<!-- Modal Backdrop -->
|
||||
<div class="modal-backdrop" id="terminateModalBackdrop"></div>
|
||||
|
||||
<!-- Modal Wrapper -->
|
||||
<div class="modal" id="terminateModal">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="terminatePopupTitle">API 이용 해지 신청</h3>
|
||||
<button type="button" class="modal-close" id="terminatePopupCloseButton">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Modal Body -->
|
||||
<div class="modal-body">
|
||||
<p id="terminatePopupMessage" style="margin-bottom: 16px; color: #64748B; word-break: keep-all;">
|
||||
해지 신청 후 <strong>관리자 승인 전까지 API는 정상 동작</strong>하며,<br>
|
||||
승인이 완료되면 <strong>인증키가 삭제되고 API 호출이 차단</strong>됩니다.<br>
|
||||
삭제된 인증키는 <strong>복구할 수 없습니다.</strong>
|
||||
</p>
|
||||
|
||||
<!-- Reason Input Field (본인 확인은 step-up 2FA가 담당하므로 비밀번호 입력 없음) -->
|
||||
<div class="pop_input_group">
|
||||
<textarea id="terminateReasonInput"
|
||||
class="pop_input_field"
|
||||
rows="3"
|
||||
maxlength="1000"
|
||||
placeholder="해지 사유를 입력해주세요 (필수)"
|
||||
style="resize: vertical; min-height: 72px;"></textarea>
|
||||
<div id="terminatePopupError" class="error-message"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Footer -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="terminatePopupCancelButton">취소</button>
|
||||
<button type="button" class="btn btn-primary" id="terminatePopupConfirmButton">해지 신청</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div th:if="${success}" class="valid-feedback d-block">
|
||||
<span th:text="${message}">인증번호를 발송하였습니다.</span>
|
||||
</div>
|
||||
<div th:if="${!success}" class="invalid-feedback d-block">
|
||||
<div th:unless="${success}" class="invalid-feedback d-block">
|
||||
<span th:text="${message}">인증번호 발송 중 오류가 발생했습니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user