이용자 목록, 이용자 상세 스타일 정리
This commit is contained in:
@@ -9857,6 +9857,609 @@ select.form-control {
|
||||
}
|
||||
}
|
||||
|
||||
.user-management-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 40px;
|
||||
background-color: #F8FAFC;
|
||||
min-height: 100vh;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-management-container {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-management-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-management-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-management-title h1 {
|
||||
font-size: 14px;
|
||||
color: #64748B;
|
||||
font-weight: 400;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.user-management-title h2 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #1A1A2E;
|
||||
}
|
||||
|
||||
.user-management-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.btn-create-user,
|
||||
.btn-create-user-large {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 32px;
|
||||
background: linear-gradient(135deg, #4B9BFF 0%, #2E7FF7 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(75, 155, 255, 0.1);
|
||||
}
|
||||
.btn-create-user:hover,
|
||||
.btn-create-user-large:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(75, 155, 255, 0.15);
|
||||
}
|
||||
.btn-create-user:active,
|
||||
.btn-create-user-large:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.btn-create-user .btn-icon,
|
||||
.btn-create-user-large .btn-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.btn-create-user-large {
|
||||
padding: 16px 40px;
|
||||
font-size: 18px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.table-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.table-controls {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.total-count {
|
||||
font-size: 16px;
|
||||
color: #64748B;
|
||||
}
|
||||
.total-count strong {
|
||||
color: #4B9BFF;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.user-list-wrapper {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.user-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 60px 120px 1fr 150px 120px 200px;
|
||||
gap: 16px;
|
||||
padding: 16px 24px;
|
||||
background: #EFF6FF;
|
||||
border-bottom: 2px solid #E2E8F0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1A1A2E;
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.table-header {
|
||||
grid-template-columns: 50px 100px 1fr 120px 100px 180px;
|
||||
font-size: 12px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.table-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 60px 120px 1fr 150px 120px 200px;
|
||||
gap: 16px;
|
||||
padding: 24px;
|
||||
border-bottom: 1px solid #E2E8F0;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.table-row:hover {
|
||||
background-color: rgba(75, 155, 255, 0.02);
|
||||
}
|
||||
.table-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.table-row.row-pending {
|
||||
background-color: rgba(255, 217, 61, 0.03);
|
||||
border-left: 3px solid #FFD93D;
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.table-row {
|
||||
grid-template-columns: 50px 100px 1fr 120px 100px 180px;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.table-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
.table-row::before {
|
||||
content: attr(data-label);
|
||||
font-weight: 600;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.col-num {
|
||||
font-size: 14px;
|
||||
color: #64748B;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.col-num {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.col-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
.col-name a {
|
||||
color: #1A1A2E;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.col-name a:hover {
|
||||
color: #4B9BFF;
|
||||
}
|
||||
.col-name .pending-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #64748B;
|
||||
font-style: italic;
|
||||
}
|
||||
.col-name .pending-label i {
|
||||
color: #FFD93D;
|
||||
}
|
||||
|
||||
.col-email {
|
||||
color: #64748B;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.col-role {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-role-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background-color: rgba(75, 155, 255, 0.1);
|
||||
color: #4B9BFF;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.user-role-badge i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.col-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.col-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.user-status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 12px;
|
||||
border-radius: 50px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.user-status-badge.status-active {
|
||||
background-color: rgba(107, 207, 127, 0.1);
|
||||
color: rgb(68.4897959184, 194.5102040816, 93.693877551);
|
||||
}
|
||||
.user-status-badge.status-inactive {
|
||||
background-color: rgba(100, 116, 139, 0.1);
|
||||
color: #64748B;
|
||||
}
|
||||
.user-status-badge.status-pending {
|
||||
background-color: rgba(255, 217, 61, 0.15);
|
||||
color: rgb(163, 131.0721649485, 0);
|
||||
}
|
||||
|
||||
.user-current-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background-color: rgba(167, 139, 250, 0.1);
|
||||
color: #A78BFA;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.user-action-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
.user-action-buttons .btn-action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-sm {
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-primary {
|
||||
background-color: #4B9BFF;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-primary:hover {
|
||||
background-color: rgb(24, 126.6666666667, 255);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-secondary {
|
||||
background-color: #64748B;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-secondary:hover {
|
||||
background-color: rgb(78.6610878661, 91.2468619247, 109.3389121339);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-danger {
|
||||
background-color: #FF6B6B;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-danger:hover {
|
||||
background-color: #ff3838;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-warning {
|
||||
background-color: #FFD93D;
|
||||
color: #1F2937;
|
||||
}
|
||||
.user-action-buttons .btn-action.btn-warning:hover {
|
||||
background-color: rgb(255, 207.0103092784, 10);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.user-action-buttons .btn-action:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.user-empty-state {
|
||||
text-align: center;
|
||||
padding: 80px 24px;
|
||||
color: #64748B;
|
||||
}
|
||||
.user-empty-state .empty-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 24px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.user-empty-state h3 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1A1A2E;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.user-empty-state p {
|
||||
font-size: 16px;
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
.user-detail-container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 40px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-detail-container {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-card {
|
||||
background: linear-gradient(135deg, #4B9BFF 0%, #2E7FF7 100%);
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 32px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.user-profile-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-profile-card .detail-grid {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.user-profile-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-profile-header {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-circle-large {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #FFFFFF;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 8px 24px rgba(75, 155, 255, 0.15);
|
||||
}
|
||||
|
||||
.user-profile-info {
|
||||
flex: 1;
|
||||
}
|
||||
.user-profile-info h2 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.user-profile-info .user-profile-email {
|
||||
font-size: 16px;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.user-profile-badges {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-profile-badges {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.user-profile-badges .user-status-badge,
|
||||
.user-profile-badges .user-role-badge {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #FFFFFF;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.detail-section h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1A1A2E;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #EFF6FF;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.detail-item.full-width {
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #64748B;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.detail-label i {
|
||||
color: #4B9BFF;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 16px;
|
||||
color: #1A1A2E;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.status-indicator.status-active {
|
||||
background-color: rgba(107, 207, 127, 0.1);
|
||||
color: rgb(68.4897959184, 194.5102040816, 93.693877551);
|
||||
}
|
||||
.status-indicator.status-active .status-dot {
|
||||
background-color: #6BCF7F;
|
||||
}
|
||||
.status-indicator.status-inactive {
|
||||
background-color: rgba(100, 116, 139, 0.1);
|
||||
color: #64748B;
|
||||
}
|
||||
.status-indicator.status-inactive .status-dot {
|
||||
background-color: #64748B;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.detail-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
padding-top: 24px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.detail-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 32px;
|
||||
background-color: #FFFFFF;
|
||||
color: #1A1A2E;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border: 2px solid #E2E8F0;
|
||||
border-radius: 12px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background-color: #F8FAFC;
|
||||
border-color: #4B9BFF;
|
||||
color: #4B9BFF;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.btn-secondary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.btn-secondary i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,30 +1,86 @@
|
||||
const customPopups = {
|
||||
/**
|
||||
* 알림 팝업 표시
|
||||
* @param {string} message - 알림 메시지
|
||||
* @param {Function} callback - 확인 버튼 클릭 시 호출되는 콜백 (선택사항)
|
||||
*/
|
||||
showAlert: function (message, callback) {
|
||||
// 메시지 설정
|
||||
$('#customAlertMessage').text(message);
|
||||
$('#customAlert').css('display', 'flex');
|
||||
|
||||
// Enter 키 이벤트 리스너 추가
|
||||
$(document).on('keydown.customAlert', function (e) {
|
||||
// 팝업 표시 (modal 구조 사용)
|
||||
$('#customAlert').show();
|
||||
setTimeout(function() {
|
||||
$('#customAlertBackdrop').addClass('show');
|
||||
$('#customAlertModal').addClass('show');
|
||||
}, 10);
|
||||
|
||||
// Body 스크롤 방지
|
||||
$('body').css('overflow', 'hidden');
|
||||
|
||||
// 확인 버튼에 포커스
|
||||
setTimeout(function() {
|
||||
$('#customAlertOkButton').focus();
|
||||
}, 350);
|
||||
|
||||
// 확인 버튼 클릭 이벤트 (기존 이벤트 제거 후 재등록)
|
||||
$('#customAlertOkButton').off('click').on('click', function () {
|
||||
customPopups.hideAlert();
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
// 닫기 버튼 클릭 이벤트
|
||||
$('#customAlertCloseButton').off('click').on('click', function () {
|
||||
customPopups.hideAlert();
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
// Enter 키 이벤트
|
||||
$(document).off('keydown.customAlert').on('keydown.customAlert', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
customPopups.hideAlert('#customAlert');
|
||||
customPopups.hideAlert();
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 확인 버튼 클릭 시 콜백 호출
|
||||
$('#customAlertOkButton').off('click').on('click', function () {
|
||||
customPopups.hideAlert('#customAlert');
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
// Escape 키 이벤트
|
||||
$(document).off('keydown.customAlertEsc').on('keydown.customAlertEsc', function (e) {
|
||||
if (e.key === 'Escape') {
|
||||
customPopups.hideAlert();
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
hideAlert: function (id) {
|
||||
$(id).hide();
|
||||
|
||||
/**
|
||||
* 알림 팝업 숨기기
|
||||
*/
|
||||
hideAlert: function () {
|
||||
// Modal 숨김 애니메이션
|
||||
$('#customAlertBackdrop').removeClass('show');
|
||||
$('#customAlertModal').removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 숨김
|
||||
setTimeout(function() {
|
||||
$('#customAlert').hide();
|
||||
}, 300);
|
||||
|
||||
// Body 스크롤 복원
|
||||
$('body').css('overflow', '');
|
||||
|
||||
// 이벤트 리스너 제거
|
||||
$('#customAlertOkButton').off('click');
|
||||
$('#customAlertCloseButton').off('click');
|
||||
$(document).off('keydown.customAlert');
|
||||
$(document).off('keydown.customAlertEsc');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -153,17 +209,97 @@ const customPopups = {
|
||||
$('#passwordPopupError').removeClass('show');
|
||||
$('#passwordPopupInput').removeClass('error');
|
||||
},
|
||||
/**
|
||||
* 확인 팝업 표시
|
||||
* @param {string} message - 확인 메시지
|
||||
* @param {Function} callback - 버튼 클릭 시 호출되는 콜백 (파라미터: boolean - true는 확인, false는 취소)
|
||||
*/
|
||||
showConfirm: function (message, callback) {
|
||||
// 메시지 설정
|
||||
$('#customConfirmMessage').text(message);
|
||||
$('#customConfirm').css('display', 'flex');
|
||||
$('#customConfirmYesButton').one('click', function () {
|
||||
$('#customConfirm').hide();
|
||||
if (callback) callback(true);
|
||||
|
||||
// 팝업 표시 (modal 구조 사용)
|
||||
$('#customConfirm').show();
|
||||
setTimeout(function() {
|
||||
$('#customConfirmBackdrop').addClass('show');
|
||||
$('#customConfirmModal').addClass('show');
|
||||
}, 10);
|
||||
|
||||
// Body 스크롤 방지
|
||||
$('body').css('overflow', 'hidden');
|
||||
|
||||
// 확인 버튼에 포커스
|
||||
setTimeout(function() {
|
||||
$('#customConfirmYesButton').focus();
|
||||
}, 350);
|
||||
|
||||
// 확인 버튼 클릭 이벤트 (기존 이벤트 제거 후 재등록)
|
||||
$('#customConfirmYesButton').off('click').on('click', function () {
|
||||
customPopups.hideConfirm();
|
||||
if (typeof callback === 'function') {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
$('#customConfirmNoButton').one('click', function () {
|
||||
$('#customConfirm').hide();
|
||||
if (callback) callback(false);
|
||||
|
||||
// 취소 버튼 클릭 이벤트
|
||||
$('#customConfirmNoButton').off('click').on('click', function () {
|
||||
customPopups.hideConfirm();
|
||||
if (typeof callback === 'function') {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
|
||||
// 닫기 버튼 클릭 이벤트
|
||||
$('#customConfirmCloseButton').off('click').on('click', function () {
|
||||
customPopups.hideConfirm();
|
||||
if (typeof callback === 'function') {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Enter 키 이벤트 (확인 버튼 실행)
|
||||
$(document).off('keydown.customConfirm').on('keydown.customConfirm', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
customPopups.hideConfirm();
|
||||
if (typeof callback === 'function') {
|
||||
callback(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Escape 키 이벤트 (취소)
|
||||
$(document).off('keydown.customConfirmEsc').on('keydown.customConfirmEsc', function (e) {
|
||||
if (e.key === 'Escape') {
|
||||
customPopups.hideConfirm();
|
||||
if (typeof callback === 'function') {
|
||||
callback(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 확인 팝업 숨기기
|
||||
*/
|
||||
hideConfirm: function () {
|
||||
// Modal 숨김 애니메이션
|
||||
$('#customConfirmBackdrop').removeClass('show');
|
||||
$('#customConfirmModal').removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 숨김
|
||||
setTimeout(function() {
|
||||
$('#customConfirm').hide();
|
||||
}, 300);
|
||||
|
||||
// Body 스크롤 복원
|
||||
$('body').css('overflow', '');
|
||||
|
||||
// 이벤트 리스너 제거
|
||||
$('#customConfirmYesButton').off('click');
|
||||
$('#customConfirmNoButton').off('click');
|
||||
$('#customConfirmCloseButton').off('click');
|
||||
$(document).off('keydown.customConfirm');
|
||||
$(document).off('keydown.customConfirmEsc');
|
||||
},
|
||||
showEmailValidationPopup: function (message, onConvertCallback, onChangeEmailCallback) {
|
||||
$('#emailValidationPopupMessage').text(message);
|
||||
@@ -188,17 +324,223 @@ const customPopups = {
|
||||
$('#userLoginId').val(loginId);
|
||||
$('#emailResend').show();
|
||||
},
|
||||
|
||||
/**
|
||||
* 사용자 초대 팝업 표시
|
||||
* @param {Object} options - 팝업 옵션
|
||||
* @param {Function} options.onConfirm - 확인 버튼 클릭 시 호출되는 콜백 (파라미터: email)
|
||||
* @param {Function} options.onCancel - 취소 버튼 클릭 시 호출되는 콜백 (선택사항)
|
||||
*/
|
||||
showUserInvite: function (options) {
|
||||
options = options || {};
|
||||
|
||||
const onConfirm = options.onConfirm;
|
||||
const onCancel = options.onCancel;
|
||||
|
||||
// 입력 필드 및 에러 초기화
|
||||
$('#userInviteEmailInput').val('').removeClass('error');
|
||||
$('#userInvitePopupError').removeClass('show').text('');
|
||||
|
||||
// 팝업 표시 (modal 구조 사용)
|
||||
$('#userInvitePopup').show();
|
||||
setTimeout(function() {
|
||||
$('#userInviteModalBackdrop').addClass('show');
|
||||
$('#userInviteModal').addClass('show');
|
||||
}, 10);
|
||||
|
||||
// Body 스크롤 방지
|
||||
$('body').css('overflow', 'hidden');
|
||||
|
||||
// 입력 필드에 포커스
|
||||
setTimeout(function() {
|
||||
$('#userInviteEmailInput').focus();
|
||||
}, 350);
|
||||
|
||||
// 확인 버튼 이벤트 (기존 이벤트 제거 후 재등록)
|
||||
$('#userInvitePopupConfirmButton').off('click').on('click', function () {
|
||||
const email = $('#userInviteEmailInput').val().trim();
|
||||
|
||||
// 이메일 유효성 검사
|
||||
if (!email) {
|
||||
customPopups.showUserInviteError('이메일 주소를 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 이메일 형식 검증
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailPattern.test(email)) {
|
||||
customPopups.showUserInviteError('올바른 이메일 형식이 아닙니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm(email);
|
||||
}
|
||||
});
|
||||
|
||||
// 취소 버튼 이벤트
|
||||
$('#userInvitePopupCancelButton').off('click').on('click', function () {
|
||||
customPopups.hideUserInvite();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// 닫기 버튼 이벤트
|
||||
$('#userInvitePopupCloseButton').off('click').on('click', function () {
|
||||
customPopups.hideUserInvite();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// Enter 키 이벤트
|
||||
$('#userInviteEmailInput').off('keypress').on('keypress', function (e) {
|
||||
if (e.which === 13) {
|
||||
$('#userInvitePopupConfirmButton').click();
|
||||
}
|
||||
});
|
||||
|
||||
// 입력 시 에러 초기화
|
||||
$('#userInviteEmailInput').off('input').on('input', function () {
|
||||
customPopups.clearUserInviteError();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 사용자 초대 팝업 숨기기
|
||||
*/
|
||||
hideUserInvite: function () {
|
||||
// Modal 숨김 애니메이션
|
||||
$('#userInviteModalBackdrop').removeClass('show');
|
||||
$('#userInviteModal').removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 숨김
|
||||
setTimeout(function() {
|
||||
$('#userInvitePopup').hide();
|
||||
}, 300);
|
||||
|
||||
// Body 스크롤 복원
|
||||
$('body').css('overflow', '');
|
||||
|
||||
// 입력 필드 초기화
|
||||
$('#userInviteEmailInput').val('').removeClass('error');
|
||||
$('#userInvitePopupError').removeClass('show').text('');
|
||||
|
||||
// 이벤트 리스너 제거
|
||||
$('#userInvitePopupConfirmButton').off('click');
|
||||
$('#userInvitePopupCancelButton').off('click');
|
||||
$('#userInvitePopupCloseButton').off('click');
|
||||
$('#userInviteEmailInput').off('keypress').off('input');
|
||||
},
|
||||
|
||||
/**
|
||||
* 사용자 초대 에러 메시지 표시
|
||||
* @param {string} message - 에러 메시지
|
||||
*/
|
||||
showUserInviteError: function (message) {
|
||||
$('#userInvitePopupError').text(message).addClass('show');
|
||||
$('#userInviteEmailInput').addClass('error');
|
||||
},
|
||||
|
||||
/**
|
||||
* 사용자 초대 에러 메시지 제거
|
||||
*/
|
||||
clearUserInviteError: function () {
|
||||
$('#userInvitePopupError').removeClass('show');
|
||||
$('#userInviteEmailInput').removeClass('error');
|
||||
},
|
||||
|
||||
/**
|
||||
* 권한 변경 팝업 표시
|
||||
* @param {Object} options - 팝업 옵션
|
||||
* @param {string} options.userName - 사용자 이름
|
||||
* @param {string} options.userEmail - 사용자 이메일
|
||||
* @param {string} options.userId - 사용자 ID
|
||||
* @param {Function} options.onConfirm - 확인 버튼 클릭 시 호출되는 콜백 (파라미터: userId)
|
||||
* @param {Function} options.onCancel - 취소 버튼 클릭 시 호출되는 콜백 (선택사항)
|
||||
*/
|
||||
showChangeRole: function (options) {
|
||||
options = options || {};
|
||||
|
||||
const userName = options.userName || '';
|
||||
const userEmail = options.userEmail || '';
|
||||
const userId = options.userId;
|
||||
const onConfirm = options.onConfirm;
|
||||
const onCancel = options.onCancel;
|
||||
|
||||
// 사용자 정보 설정
|
||||
$('#changeRoleUserName').text(userName);
|
||||
$('#changeRoleUserEmail').text(userEmail ? `(${userEmail})` : '');
|
||||
|
||||
// 팝업 표시 (modal 구조 사용)
|
||||
$('#changeRolePopup').show();
|
||||
setTimeout(function() {
|
||||
$('#changeRoleModalBackdrop').addClass('show');
|
||||
$('#changeRoleModal').addClass('show');
|
||||
}, 10);
|
||||
|
||||
// Body 스크롤 방지
|
||||
$('body').css('overflow', 'hidden');
|
||||
|
||||
// 확인 버튼 이벤트 (기존 이벤트 제거 후 재등록)
|
||||
$('#changeRolePopupConfirmButton').off('click').on('click', function () {
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm(userId);
|
||||
}
|
||||
});
|
||||
|
||||
// 취소 버튼 이벤트
|
||||
$('#changeRolePopupCancelButton').off('click').on('click', function () {
|
||||
customPopups.hideChangeRole();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// 닫기 버튼 이벤트
|
||||
$('#changeRolePopupCloseButton').off('click').on('click', function () {
|
||||
customPopups.hideChangeRole();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
|
||||
// Escape 키 이벤트
|
||||
$(document).off('keydown.changeRole').on('keydown.changeRole', function (e) {
|
||||
if (e.key === 'Escape') {
|
||||
customPopups.hideChangeRole();
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 권한 변경 팝업 숨기기
|
||||
*/
|
||||
hideChangeRole: function () {
|
||||
// Modal 숨김 애니메이션
|
||||
$('#changeRoleModalBackdrop').removeClass('show');
|
||||
$('#changeRoleModal').removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 숨김
|
||||
setTimeout(function() {
|
||||
$('#changeRolePopup').hide();
|
||||
}, 300);
|
||||
|
||||
// Body 스크롤 복원
|
||||
$('body').css('overflow', '');
|
||||
|
||||
// 이벤트 리스너 제거
|
||||
$('#changeRolePopupConfirmButton').off('click');
|
||||
$('#changeRolePopupCancelButton').off('click');
|
||||
$('#changeRolePopupCloseButton').off('click');
|
||||
$(document).off('keydown.changeRole');
|
||||
},
|
||||
|
||||
init: function () {
|
||||
// customAlert 팝업의 닫기 버튼에 이벤트 리스너 추가
|
||||
$('#customAlertOkButton, #customAlert .btn_close').on('click', function () {
|
||||
customPopups.hideAlert('#customAlert');
|
||||
});
|
||||
|
||||
// customConfirm 팝업의 닫기 버튼에 이벤트 리스너 추가
|
||||
$('#customConfirmNoButton').on('click', function () {
|
||||
customPopups.hideAlert('#customConfirm');
|
||||
});
|
||||
|
||||
// emailValidationPopup 팝업의 닫기 버튼에 이벤트 리스너 추가
|
||||
$('#emailValidationPopupCloseButton').on('click', function () {
|
||||
$('#emailValidationPopup').hide();
|
||||
@@ -208,11 +550,13 @@ const customPopups = {
|
||||
$('#emailResend').hide();
|
||||
});
|
||||
|
||||
// 다른 팝업의 닫기 버튼에 이벤트 리스너 추가
|
||||
$('.btn_close:not(#customAlert .btn_close)').on('click', function () {
|
||||
$(this).closest('.popup_total').hide();
|
||||
// 다른 팝업의 닫기 버튼에 이벤트 리스너 추가 (modernized popups 제외)
|
||||
$('.btn_close').on('click', function () {
|
||||
const $popup = $(this).closest('.popup_total');
|
||||
if ($popup.length) {
|
||||
$popup.hide();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
@import 'pages/apikey-detail';
|
||||
@import 'pages/notice';
|
||||
@import 'pages/inquiry';
|
||||
@import 'pages/user-management';
|
||||
|
||||
// 6. Themes
|
||||
@import 'themes/dark';
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
|
||||
layout:decorate="~{layout/kbank_apikey_layout}">
|
||||
<head>
|
||||
<title>API 키 상세</title>
|
||||
</head>
|
||||
<body>
|
||||
<section layout:fragment="contentFragment" class="content">
|
||||
<div class="content_wrap">
|
||||
<div class="sub_title3">
|
||||
<h2 class="title">인증키 정보</h2>
|
||||
</div>
|
||||
|
||||
<div class="inner i_cs h_inner11">
|
||||
|
||||
<div class="form_type">
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>인증키 이름</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp6">
|
||||
<input type="text" id="clientName" th:value="${apiKey.clientname}"
|
||||
class="common_input_type_1" disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>Client Id</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp6">
|
||||
<input type="text" id="clientId" th:value="${apiKey.clientid}" class="common_input_type_1"
|
||||
disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 p_top1">
|
||||
<p class="title">
|
||||
<span>Client Secret</span>
|
||||
</p>
|
||||
<div class="info_line flex_w">
|
||||
<div class="info_box1 w_inp6">
|
||||
<div class="key_box">
|
||||
<p th:text="${apiKey.clientsecret}">
|
||||
client_key
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 p_top5">
|
||||
<p class="title"></p>
|
||||
<div class="info_line line_add">
|
||||
<div class="info_box1 m_top0 w_inp6">
|
||||
<div class="btn_keydeltype">
|
||||
<button sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" type="button"
|
||||
class="btn_keydel">인증키 삭제
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>변경일</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp6">
|
||||
<input type="text" th:value="${#temporals.format(apiKey.modifiedon, 'yyyy.MM.dd')}"
|
||||
name="text" class="common_input_type_1" placeholder="2024.06.01" disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>인증키 상태</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp6">
|
||||
<input type="text" th:value="${apiKey.appstatus == '1' ? '활성화' : '비활성화'}" name="text"
|
||||
class="common_input_type_1" placeholder="활성화" disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="terms_box title">
|
||||
<p>API 정보</p>
|
||||
</div>
|
||||
<div class="table_box">
|
||||
|
||||
<div class="pc-only">
|
||||
<table class="table tb_key m_top" cellspacing="0" cellpadding="0" width="100%" id="apiList">
|
||||
<colgroup>
|
||||
<col width="60px">
|
||||
<col width="166px">
|
||||
<col width="230px">
|
||||
<col width="120px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>서비스</th>
|
||||
<th>API명</th>
|
||||
<th>비고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="api, apiStat : ${apiKey.apiList}">
|
||||
<td th:text="${apiStat.count}">14</td>
|
||||
<td th:text="${api.service}">공통</td>
|
||||
<td th:text="${api.apiDesc}">회원여부조회</td>
|
||||
<td><input type="hidden" name="apiId" th:value="${api.apiId}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="m-only">
|
||||
<table class="table tb_key m_top" cellspacing="0" cellpadding="0" width="100%">
|
||||
<colgroup>
|
||||
<col width="46px">
|
||||
<col width="90px">
|
||||
<col width="140px">
|
||||
<col width="60px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>서비스</th>
|
||||
<th>API명</th>
|
||||
<th>비고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="api, apiStat : ${apiKey.apiList}">
|
||||
<td th:text="${apiStat.count}">14</td>
|
||||
<td th:text="${api.apiGroupId}">공통</td>
|
||||
<td th:text="${api.apiDesc}">회원여부조회</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="terms_box title">
|
||||
<p>운영 전환 신청 내역</p>
|
||||
</div>
|
||||
<div class="table_box">
|
||||
<div class="pc-only">
|
||||
<table class="table tb_key m_top" cellspacing="0" cellpadding="0" width="100%">
|
||||
<colgroup>
|
||||
<col width="180px">
|
||||
<col width="180px">
|
||||
<col width="556px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>상태</th>
|
||||
<th>일시</th>
|
||||
<th>상세내용</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="history : ${prodhistory}">
|
||||
<td th:text="${history.approval.approvalStatus.description}">요청</td>
|
||||
<td th:text="${#temporals.format(history.createdDate, 'yyyy.MM.dd HH:mm:ss')}">2024.06.01
|
||||
12:12:12
|
||||
</td>
|
||||
<td th:text="${history.approval.approvalSubject}+ '(' + ${history.reason} + ')'">운영 전환 요청</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="m-only">
|
||||
<table class="table tb_key m_top" cellspacing="0" cellpadding="0" width="100%">
|
||||
<colgroup>
|
||||
<col width="80px">
|
||||
<col width="180px">
|
||||
<col width="240px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>상태</th>
|
||||
<th>일시</th>
|
||||
<th>상세내용</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="history : ${prodhistory}">
|
||||
<td th:text="${history.approval.approvalStatus.description}">요청</td>
|
||||
<td th:text="${#temporals.format(history.createdDate, 'yyyy.MM.dd HH:mm:ss')}">2024.06.01
|
||||
12:12:12
|
||||
</td>
|
||||
<td th:text="${history.approval.approvalSubject}+ '(' + ${history.reason} + ')'">운영 전환 요청</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn_wrap bt_location">
|
||||
<button type="button" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" class="btn_add btn_request_api"
|
||||
id="requestApiKey">API 변경
|
||||
</button>
|
||||
<button type="button" sec:authorize="hasRole('ROLE_API_KEY_REQUEST')"
|
||||
class="btn_refresh btn_request_prod_api">운영 전환 신청
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn_inventory btn_mtop">
|
||||
<a th:href="@{/myapikey}" class="common_btn_type_1">목록</a>
|
||||
</div>
|
||||
<!-- // 기본 정보 -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section sec:authorize="hasRole('ROLE_API_KEY_REQUEST')" layout:fragment="pagePopups" class="content pop">
|
||||
<div th:insert="~{fragment/apikey-request :: apiKeyRequest}" class="popup_total" id="apiKeyRequestPopup"
|
||||
style="display:none; z-index: 1000;"></div>
|
||||
<th:block th:replace="~{fragment/apikey-request :: apiKeyRequestScript}"></th:block>
|
||||
<div th:insert="~{fragment/apikey-request-prod :: prodApiKeyRequest}" class="popup_total"
|
||||
id="apiKeyProdRequestPopup" style="display:none; z-index: 1000;"></div>
|
||||
<th:block th:replace="~{fragment/apikey-request-prod :: prodApiKeyRequestScript}"></th:block>
|
||||
</section>
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
activateSelect();
|
||||
$('.btn_keydel').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
$('.loading-overlay').show();
|
||||
const requestData = {
|
||||
clientId: document.getElementById('clientId').value,
|
||||
type: 'DELETE'
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/myapikey/api_key_request',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(requestData)
|
||||
}).done(function (response) {
|
||||
customPopups.showAlert(response.msg, function () {
|
||||
window.location = '[[@{/myapikey/api_key_request/history}]]';
|
||||
});
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
customPopups.showAlert('API 삭제 요청 중 오류가 발생했습니다: ' + errorThrown);
|
||||
}).always(function () {
|
||||
$('.loading-overlay').hide();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,173 +0,0 @@
|
||||
<!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}">
|
||||
|
||||
<body>
|
||||
<section class="content" layout:fragment="contentFragment">
|
||||
<div class="content_wrap">
|
||||
<div class="dashboard_content h_inner2">
|
||||
<p class="month_date"><span th:text="${#temporals.format(#temporals.createToday().minusDays(1), 'yyyy.MM.dd')}"></span> 기준</p>
|
||||
|
||||
<div class="dashboard_box bd_box">
|
||||
<div class="icon1">
|
||||
<img th:src="@{/img/icon/icon_board04.png}" alt="API키">
|
||||
<p class="tit">API 키 현황</p>
|
||||
</div>
|
||||
<div class="api_use">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<p class="tit_m">테스트 키 승인 대기</p>
|
||||
<p class="num_m"><a th:href="@{/myapikey/api_key_request/history}"><span th:text="${pendingKeyCount}">5</span>건</a></p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<p class="tit_m">테스트 키 보유 건수</p>
|
||||
<p class="num_m"><a th:href="@{/myapikey/}"><span th:text="${testKeyCount}">1</span>건</a></p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<p class="tit_m">운영 승인 대기</p>
|
||||
<p class="num_m"><a th:href="@{/myapikey/api_key_request/prod_history}"><span th:text="${pendingProdCount}">1</span>건</a></p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<p class="tit_m">운영 키 보유 건수</p>
|
||||
<p class="num_m"><a th:href="@{/myapikey/api_key_prod_page}"><span th:text="${prodKeyCount}">1</span>건</a></p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard_box bd_box1">
|
||||
<div class="icon1">
|
||||
<img th:src="@{/img/icon/icon_board01.png}" alt="이용API">
|
||||
<p class="tit">이용 API</p>
|
||||
</div>
|
||||
<p class="num"><span th:text="${totalApi}">1000</span> 개</p>
|
||||
</div>
|
||||
|
||||
<div class="dashboard_box bd_box2">
|
||||
<div class="icon1">
|
||||
<img th:src="@{/img/icon/icon_board02.png}" alt="API사용량">
|
||||
<p class="tit">API 사용량</p>
|
||||
</div>
|
||||
<div class="api_use">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<img th:src="@{/img/icon/icon_calendar_month.png}" alt="월간 사용량">
|
||||
<p class="tit_m">월간 사용량</p>
|
||||
<p class="num_m"><span th:text="${monthlyTotal}">999,999,999</span>건</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<img th:src="@{/img/icon/icon_calendar_week.png}" alt="주간 사용량">
|
||||
<p class="tit_m">주간 사용량</p>
|
||||
<p class="num_m"><span th:text="${weeklyTotal}">999,999</span>건</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="use_box">
|
||||
<img th:src="@{/img/icon/icon_calendar_day.png}" alt="일간 사용량">
|
||||
<p class="tit_m">일간 사용량</p>
|
||||
<p class="num_m"><span th:text="${dailyTotal}">999,999</span>건</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard_box bd_box3">
|
||||
<div class="icon1">
|
||||
<img th:src="@{/img/icon/icon_board03.png}" alt="이용API">
|
||||
<p class="tit">주 사용 API</p>
|
||||
</div>
|
||||
<div class="api_use_num">
|
||||
<ul>
|
||||
<li>
|
||||
<img th:src="@{/img/icon/icon_circle_num01_1.png}" alt="1번">
|
||||
<span th:if="${topApi != null && topApi.size() > 0}" th:text="${topApi[0].apiName}">API1</span>
|
||||
</li>
|
||||
<li>
|
||||
<img th:src="@{/img/icon/icon_circle_num02_2.png}" alt="2번">
|
||||
<span th:if="${topApi != null && topApi.size() > 0}" th:text="${topApi[1].apiName}">API2</span>
|
||||
</li>
|
||||
<li>
|
||||
<img th:src="@{/img/icon/icon_circle_num03_3.png}" alt="3번">
|
||||
<span th:if="${topApi != null && topApi.size() > 0}" th:text="${topApi[2].apiName}">API3</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard_box2 i_cs">
|
||||
<h3>일별 API 이용현황</h3>
|
||||
<div class="form_type" style="display: block; height: 64px;">
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 달력 -->
|
||||
<div class="datepicker_wrapper">
|
||||
<input type="text" class="datepicker_input" name="daterange" id="daily_usage_from_to"
|
||||
th:value="${#temporals.format(#temporals.createToday().withDayOfMonth(1), 'yyyy.MM.dd')} + ' - ' + ${#temporals.format(#temporals.createToday(), 'yyyy.MM.dd')}"/>
|
||||
<img th:src="@{/img/icon/icon_datepicker.png}" class="datepicker_icon" alt="달력">
|
||||
</div>
|
||||
<!-- // 달력 -->
|
||||
<div class="table_type api_tb" id="dailyUsage">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script th:src="@{/js/moment.min.js}"></script>
|
||||
<script th:src="@{/js/daterangepicker.js}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
let dateRange = $('#daily_usage_from_to').val().split(' - ');
|
||||
let fromDate = dateRange[0];
|
||||
let toDate = dateRange[1];
|
||||
|
||||
// Initial load with date range
|
||||
loadDailyUsage(fromDate, toDate);
|
||||
|
||||
$('#daily_usage_from_to').on('apply.daterangepicker', function(ev, picker) {
|
||||
loadDailyUsage(
|
||||
picker.startDate.format('YYYY.MM.DD'),
|
||||
picker.endDate.format('YYYY.MM.DD')
|
||||
);
|
||||
});
|
||||
// Function to load daily usage data
|
||||
function loadDailyUsage(fromDate, toDate) {
|
||||
fromDate = fromDate.replace(/\./g, '');
|
||||
toDate = toDate.replace(/\./g, '');
|
||||
|
||||
$.ajax({
|
||||
url: '/dashboard/daily_usage',
|
||||
method: 'GET',
|
||||
data: {
|
||||
fromDate: fromDate,
|
||||
toDate: toDate
|
||||
},
|
||||
success: function(response) {
|
||||
$('#dailyUsage').html(response);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$('#dailyUsage').html('<p class="error">Failed to load daily usage data. Please try again later.</p>');
|
||||
console.error('Error loading daily usage:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</th:block>
|
||||
@@ -1,284 +0,0 @@
|
||||
<!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/base_layout}">
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<section layout:fragment="contentFragment">
|
||||
<div class="container mt-2">
|
||||
<div class="text-center">
|
||||
<h3>API 키 관리</h3>
|
||||
</div>
|
||||
<div class="card mt-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">API 키 정보 등록</h5>
|
||||
<form name="apiKey" id="apiKey" th:object="${apiKey}" th:action="${#strings.isEmpty(apiKey.id) == true}?@{/myapikey/register}:@{/myapikey/update}" method="post">
|
||||
<th:block th:if="${#strings.isEmpty(apiKey.id) == false}">
|
||||
<input type="hidden" name="id" id="apiKeyId" th:value="${apiKey.id}"/>
|
||||
<input type="hidden" name="owner" id="owner" th:value="${apiKey.owner}"/>
|
||||
</th:block>
|
||||
<div class="form-floating">
|
||||
<input type="text" name="name" class="form-control mb-2" id="name" th:value="${apiKey.name}" maxlength="80" required>
|
||||
<label for="name">이용 용도</label>
|
||||
<th:block th:each="err : ${#fields.errors('name')}">
|
||||
<div th:text="${err}" class="invalid-feedback"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="text" name="description" class="form-control mb-2" id="description" maxlength="600" th:value="${apiKey.description}"/>
|
||||
<label for="description">이용 목적</label>
|
||||
</div>
|
||||
<th:block th:if="${#strings.isEmpty(apiKey.id) == false}">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="form-floating col-10 mt-2 me-2">
|
||||
<input type="text" name="clientId" class="form-control" id="clientId" th:value="${apiKey.clientId}" readonly>
|
||||
<label for="clientId">Client ID</label>
|
||||
</div>
|
||||
<button class="btn btn-outline-secondary col-2" type="button" id="button-copy">Copy</button>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="form-floating col-10 me-2">
|
||||
<input type="text" name="clientSecret" class="form-control" id="clientSecret" th:value="${apiKey.clientSecret}" readonly>
|
||||
<label for="clientSecret">Client Secret</label>
|
||||
</div>
|
||||
<button class="btn btn-outline-secondary col-2" type="button" id="button-copy2">Copy</button>
|
||||
</div>
|
||||
</th:block>
|
||||
<input type="hidden" name="apiIds" id="apiIds"/>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
전체 API
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table" id="sourceTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>이름</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
선택한 API
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table" id="targetTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">이름</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="api, status: ${apiKey.apiList}">
|
||||
<td>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span th:text="${api.name}" th:data-id="${api.id}" th:data-name="${api.name}"></span>
|
||||
<button type="button" class="btn btn-primary btn-sm">제거</button>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-primary btn_list" id="back"><i class="fa-solid fa-list"></i> 목록</button>
|
||||
<button type="button" class="btn btn-primary btn_save" id="save"><i class="fa-sharp fa-solid fa-floppy-disk"></i> 저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script>
|
||||
$(function () {
|
||||
let url = '[[@{/api/routes/list}]]';
|
||||
let model = {
|
||||
sourceItems: [],
|
||||
targetItems: []
|
||||
};
|
||||
|
||||
let view = {
|
||||
init: function () {
|
||||
this.apiIds = $('#apiIds');
|
||||
this.sourceTable = $('#sourceTable').DataTable({
|
||||
data: model.sourceItems,
|
||||
fixedColumns: true,
|
||||
autoWidth: false,
|
||||
columns: [
|
||||
{data: 'name'},
|
||||
{
|
||||
data: null, width: '60px',
|
||||
className : 'text-center',
|
||||
render: function (data, type, row) {
|
||||
let disabled = row.selected === true ? 'disabled="true" ' : '';
|
||||
return '<button type="button" class="btn btn-primary btn-sm" data-id="' + row.id + '"' + disabled + '>추가</button>';
|
||||
}
|
||||
}
|
||||
],
|
||||
columnDefs: [
|
||||
{ targets: 1, orderable: false }
|
||||
],
|
||||
language : {
|
||||
paginate: {
|
||||
first: '«',
|
||||
previous: '‹',
|
||||
next: '›',
|
||||
last: '»'
|
||||
},
|
||||
lengthMenu : "페이지 당 _MENU_ 개씩 보기",
|
||||
info: '[총 _TOTAL_ 건 중 _START_ ~ _END_ 표시]'
|
||||
}
|
||||
});
|
||||
|
||||
this.targetTable = $('#targetTable').DataTable({
|
||||
data: model.targetItems,
|
||||
paging:false,
|
||||
searching:false,
|
||||
columns: [
|
||||
{data: 'name'},
|
||||
{
|
||||
data: null, width: '60px',
|
||||
className : 'text-center',
|
||||
render: function (data, type, row) {
|
||||
return '<button type="button" class="btn btn-primary btn-sm" data-id="' + row.id + '">제거</button>';
|
||||
}
|
||||
}
|
||||
],
|
||||
columnDefs: [
|
||||
{ targets: 1, orderable: false }
|
||||
],
|
||||
language : {
|
||||
info: '',
|
||||
infoEmpty: '',
|
||||
emptyTable : '선택된 API가 없습니다.'
|
||||
}
|
||||
});
|
||||
|
||||
$('#sourceTable').on('click', 'button', function () {
|
||||
let id = $(this).data('id');
|
||||
controller.addToSelected(id);
|
||||
});
|
||||
|
||||
$('#targetTable').on('click', 'button', function () {
|
||||
let id = $(this).data('id');
|
||||
controller.removeFromSelected(id);
|
||||
});
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.sourceTable.rows().invalidate();
|
||||
this.targetTable.clear();
|
||||
this.targetTable.rows.add(model.targetItems);
|
||||
this.targetTable.draw();
|
||||
// this.targetTable.rows().invalidate();
|
||||
|
||||
let idString = model.targetItems.map(function (obj) {
|
||||
return obj.id;
|
||||
}).join(',');
|
||||
|
||||
view.apiIds.val(idString);
|
||||
}
|
||||
};
|
||||
|
||||
let controller = {
|
||||
init: function () {
|
||||
let data = {};
|
||||
|
||||
$('#targetTable tbody tr').each(function () {
|
||||
let row = $(this);
|
||||
let rowData = {
|
||||
id: row.find('span').data('id'),
|
||||
name: row.find('span').data('name')
|
||||
};
|
||||
model.targetItems.push(rowData);
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(data),
|
||||
success: function (data) {
|
||||
model.sourceItems = data;
|
||||
model.sourceItems.forEach(function (item) {
|
||||
let foundItem = model.targetItems.find(function (obj) {
|
||||
if (obj.id === item.id) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
item.selected = (foundItem !== undefined);
|
||||
});
|
||||
view.init();
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
console.error('POST request failed: ' + textStatus + ', ' + errorThrown);
|
||||
}
|
||||
});
|
||||
},
|
||||
addToSelected: function (id) {
|
||||
let foundObject = model.sourceItems.find(function (obj) {
|
||||
return obj.id === id;
|
||||
});
|
||||
foundObject.selected = true;
|
||||
model.targetItems.push(foundObject);
|
||||
view.render();
|
||||
},
|
||||
removeFromSelected: function (id) {
|
||||
let sourceFound = model.sourceItems.find(function (obj) {
|
||||
return obj.id === id;
|
||||
});
|
||||
sourceFound.selected = false;
|
||||
let foundObject = model.targetItems.find(function (obj) {
|
||||
return obj.id === id;
|
||||
});
|
||||
let index = model.targetItems.indexOf(foundObject);
|
||||
model.targetItems.splice(index, 1);
|
||||
view.render();
|
||||
}
|
||||
};
|
||||
|
||||
$(".btn_save").on("click", function (event) {
|
||||
let form = document.forms.apiKey;
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
} else {
|
||||
form.submit();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
});
|
||||
|
||||
$(".btn_list").on("click", function (e) {
|
||||
window.location.href = '[[@{/myapikey/list_view}]]';
|
||||
});
|
||||
|
||||
controller.init();
|
||||
})
|
||||
</script>
|
||||
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -1,132 +0,0 @@
|
||||
<!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/base_layout}">
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<section layout:fragment="contentFragment">
|
||||
<div class="container mt-2">
|
||||
<div class="text-center">
|
||||
<h3>API 키 관리</h3>
|
||||
</div>
|
||||
<div class="card mt-2">
|
||||
<div class="card-body" th:if="${apiKeys.size() > 0}">
|
||||
<form name="deleteForm" th:action="@{/myapikey/delete}" method="POST">
|
||||
<input type="hidden" value="" name="id" />
|
||||
<input id="csrf" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
|
||||
</form>
|
||||
<th:block th:each="apikey, status : ${apiKeys}">
|
||||
<div class="card p-0 me-2 mt-2">
|
||||
<div class="card-header">
|
||||
<span>API 키 정보</span>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item" th:text="'이름: ' + ${apikey.clientname}"></li>
|
||||
</ul>
|
||||
|
||||
<div class="accordion accordion-flush" id="apiKeySecret" th:if="${apikey.state.toString() =='ACTIVE'}" >
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="flush-headingOne">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseOne" aria-expanded="false" aria-controls="flush-collapseOne">
|
||||
Client ID
|
||||
</button>
|
||||
</h2>
|
||||
<div id="flush-collapseOne" class="accordion-collapse collapse" aria-labelledby="flush-headingOne" data-bs-parent="#apiKeySecret">
|
||||
<div class="accordion-body">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<div class="form-floating" style="flex-grow: 1;">
|
||||
<input type="text" name="clientid" class="form-control" id="clientid" th:value="${apikey.clientid}" readonly>
|
||||
<label for="clientid">Client ID</label>
|
||||
</div>
|
||||
<button class="btn btn-primary ms-2" type="button" id="button-copy" data-target="#clientid">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="flush-headingTwo">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTwo" aria-expanded="false" aria-controls="flush-collapseTwo">
|
||||
Client Secret
|
||||
</button>
|
||||
</h2>
|
||||
<div id="flush-collapseTwo" class="accordion-collapse collapse" aria-labelledby="flush-headingTwo" data-bs-parent="#apiKeySecret">
|
||||
<div class="accordion-body">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<div class="form-floating" style="flex-grow: 1;">
|
||||
<input type="text" name="clientsecret" class="form-control" id="clientsecret" th:value="${apikey.clientsecret}" readonly>
|
||||
<label for="clientsecret">Client Secret</label>
|
||||
</div>
|
||||
<button class="btn btn-primary ms-2" type="button" data-target="#clientsecret">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button class="btn btn-primary btn-sm" th:onclick="fnDeleteItem([[${apikey.clientid}]])">삭제</button>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
<div class="toast" id="copyToast" style="position: absolute; top: 0; right: 0;">
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto">Notification</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
복사되었습니다
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body" th:if="${apiKeys.size() == 0}">
|
||||
<h5 class="card-title">개발 키 추가</h5>
|
||||
<p class="card-text">승인 후 개발환경에서 사용가능한 키가 발급됩니다.</p>
|
||||
<h3><a th:href="@{/myapikey/create_view}" class="btn btn-primary">발급하기</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script>
|
||||
function fnDeleteItem(id) {
|
||||
if (confirm("[[#{common.delete.msg}]]")) {
|
||||
document.deleteForm.id.value = id;
|
||||
document.deleteForm.submit();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
// Listen for click events on buttons with data-target attribute
|
||||
$("button[data-target]").click(function(){
|
||||
// Get the value of the data-target attribute
|
||||
var targetId = $(this).attr("data-target");
|
||||
|
||||
// Select the input element by its ID and get its value
|
||||
var textToCopy = $(targetId).val();
|
||||
|
||||
// Create a temporary textarea to hold the text to be copied
|
||||
var $temp = $("<textarea>");
|
||||
$("body").append($temp);
|
||||
$temp.val(textToCopy).select();
|
||||
|
||||
// Execute the copy command
|
||||
document.execCommand("copy");
|
||||
|
||||
// Remove the temporary textarea
|
||||
$temp.remove();
|
||||
|
||||
// Show the Bootstrap Toast
|
||||
var toast = new bootstrap.Toast(document.getElementById("copyToast"));
|
||||
toast.show();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -1,108 +1,114 @@
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
|
||||
|
||||
<body>
|
||||
<section class="content" layout:fragment="contentFragment">
|
||||
<div class="content_wrap">
|
||||
<div class="sub_title3">
|
||||
<h2 class="title">이용자 정보</h2>
|
||||
<th:block layout:fragment="contentFragment">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="register-header">
|
||||
<h1>이용자 상세</h1>
|
||||
<p class="header-description">
|
||||
이용자의 상세 정보를 확인할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="inner i_cs h_inner8 h_inner12">
|
||||
<section class="user-detail-container">
|
||||
|
||||
<!-- 기본 정보 -->
|
||||
<div class="terms_box user_top">
|
||||
<p>기본정보</p>
|
||||
</div>
|
||||
<!-- User Information -->
|
||||
<div class="detail-section">
|
||||
<h2 class="section-title">기본 정보</h2>
|
||||
<div class="detail-grid">
|
||||
|
||||
<div class="form_type">
|
||||
<!-- Email -->
|
||||
<div class="detail-item full-width">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-envelope"></i> 이메일 아이디
|
||||
</label>
|
||||
<div class="detail-value" th:text="${user.maskedEmailAddr}">
|
||||
user@example.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Name -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-user"></i> 성명
|
||||
</label>
|
||||
<div class="detail-value" th:text="${user.maskedUserName}">
|
||||
홍길동
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-mobile-alt"></i> 휴대폰 번호
|
||||
</label>
|
||||
<div class="detail-value" th:text="${user.maskedMobileNumber}">
|
||||
010-****-****
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Role -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-shield-alt"></i> 권한
|
||||
</label>
|
||||
<div class="detail-value">
|
||||
<span class="user-role-badge" th:if="${user.roleCode != null}">
|
||||
<span th:text="#{${user.roleCode}}">권한</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-info-circle"></i> 계정상태
|
||||
</label>
|
||||
<div class="detail-value">
|
||||
<span class="status-indicator"
|
||||
th:classappend="${user.userStatus.toString() == 'ACTIVE'} ? 'status-active' : 'status-inactive'">
|
||||
<span class="status-dot"></span>
|
||||
<span th:text="${user.userStatus.getDescription()}">활성화</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Created Date -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-calendar-plus"></i> 가입일
|
||||
</label>
|
||||
<div class="detail-value" th:text="${#temporals.format(user.createdDate, 'yyyy.MM.dd')}">
|
||||
2024.01.01
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last Login -->
|
||||
<div class="detail-item">
|
||||
<label class="detail-label">
|
||||
<i class="fas fa-clock"></i> 마지막 로그인
|
||||
</label>
|
||||
<div class="detail-value" th:text="${#temporals.format(user.lastLoginDate, 'yyyy.MM.dd HH:mm:ss')}">
|
||||
2024.01.01 12:00:00
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1">
|
||||
<p class="title">
|
||||
<span>이메일 아이디</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" class="common_input_type_1" th:value="${#strings.substringBefore(user.maskedEmailAddr, '@')}" disabled="disabled">
|
||||
<span class="special_t">@</span>
|
||||
<input type="text" class="common_input_type_1" th:value="${#strings.substringAfter(user.maskedEmailAddr, '@')}" disabled="disabled">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>성명</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="${user.maskedUserName}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Action Buttons -->
|
||||
<div class="detail-actions">
|
||||
<a th:href="@{/users}" class="btn-secondary">
|
||||
<i class="fas fa-list"></i> 목록으로
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>휴대폰 번호</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="${user.maskedMobileNumber}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>권한</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="#{${user.roleCode}}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>계정상태</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="${user.userStatus.getDescription()}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>가입일</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="${#temporals.format(user.createdDate, 'yyyy.MM.dd')}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info1 info_mypage">
|
||||
<p class="title">
|
||||
<span>마지막 로그인</span>
|
||||
</p>
|
||||
<div class="info_line">
|
||||
<div class="info_box1 w_inp4">
|
||||
<input type="text" th:value="${#temporals.format(user.lastLoginDate, 'yyyy.MM.dd HH:mm:ss')}" name="text" class="common_input_type_1" disabled="disabled" style="background:#F7F9FD;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn_inventory btn_mtop">
|
||||
<a th:href="@{/users}" class="common_btn_type_1">목록</a>
|
||||
</div>
|
||||
<!-- // 기본 정보 -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,158 +1,159 @@
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
|
||||
|
||||
<body>
|
||||
<section class="content" layout:fragment="contentFragment">
|
||||
<div class="content_wrap">
|
||||
<div class="sub_title3">
|
||||
<h2 class="title">이용자 목록</h2>
|
||||
<th:block layout:fragment="contentFragment">
|
||||
<section class="user-management-container">
|
||||
|
||||
<!-- Header Section -->
|
||||
<div class="user-management-header">
|
||||
<div class="user-management-title">
|
||||
<h1>사용자 관리</h1>
|
||||
<h2>이용자 목록</h2>
|
||||
</div>
|
||||
<div class="user-management-actions">
|
||||
<button type="button" class="btn-create-user" id="addUserBtn">
|
||||
<span class="btn-icon">➕</span>
|
||||
이용자 추가
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inner i_cs h_inner8 h_inner12">
|
||||
<!-- Table Controls -->
|
||||
<div class="table-controls">
|
||||
<div class="total-count">
|
||||
총 <strong th:text="${(users != null ? users.size() : 0) + (pendingUsers != null ? pendingUsers.size() : 0)}">0</strong>명
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tb_board_list">
|
||||
<div class="table_box">
|
||||
<table class="table table_min bdt2" cellspacing="0" cellpadding="0" width="100%">
|
||||
<colgroup>
|
||||
<col width="60px">
|
||||
<col width="86px">
|
||||
<col width="210px">
|
||||
<col width="80px">
|
||||
<col width="180px">
|
||||
<col>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>성명</th>
|
||||
<th>이메일 아이디</th>
|
||||
<th>권한</th>
|
||||
<th>계정상태</th>
|
||||
<th>설정</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user, status : ${users}">
|
||||
<td th:text="${status.index + 1}">No</td>
|
||||
<td>
|
||||
<a th:href="@{/users/detail(id=${user.id})}" th:text="${user.maskedUserName}">성명</a>
|
||||
</td>
|
||||
<td th:text="${user.maskedEmailAddr}">이메일 아이디</td>
|
||||
<td>
|
||||
<span th:if="${user.roleCode != null}" th:text="#{${user.roleCode}}">권한</span>
|
||||
</td>
|
||||
<td th:text="${user.userStatus.getDescription()}">계정상태</td>
|
||||
<td>
|
||||
<div th:if="${user.id == currentUserId}">
|
||||
현재 사용자
|
||||
</div>
|
||||
<div class="btn_wrap btn_set"
|
||||
th:if="${user.roleCode != null and user.id != currentUserId}">
|
||||
<a href="#" class="common_btn_type_4 inactivate_user"
|
||||
th:if="${user.userStatus.toString() == 'ACTIVE'}"><span>비활성화</span></a>
|
||||
<a href="#" class="common_btn_type_4 activate_user"
|
||||
th:if="${user.userStatus.toString() != 'ACTIVE'}"><span>활성화</span></a>
|
||||
<a href="#" class="common_btn_type_4 cancel_invitation"
|
||||
th:if="${user.userStatus.toString() == 'PENDING'}"><span>초대취소</span></a>
|
||||
<a href="#" class="common_btn_type_4 assign_manager"
|
||||
th:if="${user.userStatus.toString() == 'ACTIVE' && user.roleCode.toString() == 'ROLE_CORP_USER'}"><span>관리자 권한부여</span></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:each="user : ${pendingUsers}">
|
||||
<td th:data-id="${user.id}"></td>
|
||||
<td>
|
||||
</td>
|
||||
<td th:text="${user.maskedEmailAddr}">이메일 아이디</td>
|
||||
<td>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<div class="btn_wrap btn_set">
|
||||
<a href="#none" class="common_btn_type_4 cancel_invitation"><span>초대취소</span></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- User Table -->
|
||||
<div class="user-list-wrapper" th:if="${(users != null and !users.isEmpty()) or (pendingUsers != null and !pendingUsers.isEmpty())}">
|
||||
<div class="user-table">
|
||||
<!-- Table Header -->
|
||||
<div class="table-header">
|
||||
<div class="col-num">NO</div>
|
||||
<div class="col-name">성명</div>
|
||||
<div class="col-email">이메일 아이디</div>
|
||||
<div class="col-role">권한</div>
|
||||
<div class="col-status">계정상태</div>
|
||||
<div class="col-actions">설정</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Active/Inactive Users -->
|
||||
<div class="table-row" th:each="user, status : ${users}">
|
||||
<div class="col-num" th:text="${status.index + 1}">1</div>
|
||||
<div class="col-name">
|
||||
<a th:href="@{/users/detail(id=${user.id})}" th:text="${user.maskedUserName}">
|
||||
사용자 이름
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-email" th:text="${user.maskedEmailAddr}">
|
||||
user@example.com
|
||||
</div>
|
||||
<div class="col-role">
|
||||
<span class="user-role-badge" th:if="${user.roleCode != null}">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
<span th:text="#{${user.roleCode}}">권한</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-status">
|
||||
<span class="user-status-badge"
|
||||
th:classappend="${user.userStatus.toString() == 'ACTIVE'} ? 'status-active' : (${user.userStatus.toString() == 'PENDING'} ? 'status-pending' : 'status-inactive')"
|
||||
th:text="${user.userStatus.getDescription()}">
|
||||
활성
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-actions">
|
||||
<!-- Current User -->
|
||||
<div class="user-current-badge" th:if="${user.id == currentUserId}">
|
||||
<i class="fas fa-user-check"></i> 현재 사용자
|
||||
</div>
|
||||
|
||||
<div class="btn_wrap bt_location">
|
||||
<button type="button" class="btn_add">이용자 추가</button>
|
||||
<!-- Action Buttons -->
|
||||
<div class="user-action-buttons" th:if="${user.roleCode != null and user.id != currentUserId}">
|
||||
<button type="button" class="btn-action btn-sm btn-secondary inactivate_user"
|
||||
th:if="${user.userStatus.toString() == 'ACTIVE'}"
|
||||
th:data-user-id="${user.id}"
|
||||
th:data-user-name="${user.maskedUserName}">
|
||||
비활성화
|
||||
</button>
|
||||
<button type="button" class="btn-action btn-sm btn-primary activate_user"
|
||||
th:if="${user.userStatus.toString() != 'ACTIVE'}"
|
||||
th:data-user-id="${user.id}"
|
||||
th:data-user-name="${user.maskedUserName}">
|
||||
활성화
|
||||
</button>
|
||||
<button type="button" class="btn-action btn-sm btn-danger cancel_invitation"
|
||||
th:if="${user.userStatus.toString() == 'PENDING'}"
|
||||
th:data-user-id="${user.id}"
|
||||
th:data-user-email="${user.maskedEmailAddr}">
|
||||
초대취소
|
||||
</button>
|
||||
<button type="button" class="btn-action btn-sm btn-warning assign_manager"
|
||||
th:if="${user.userStatus.toString() == 'ACTIVE' && user.roleCode.toString() == 'ROLE_CORP_USER'}"
|
||||
th:data-user-id="${user.id}"
|
||||
th:data-user-name="${user.maskedUserName}"
|
||||
th:data-user-email="${user.maskedEmailAddr}">
|
||||
관리자 권한
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pending Invitation Users -->
|
||||
<div class="table-row row-pending" th:each="user : ${pendingUsers}">
|
||||
<div class="col-num"></div>
|
||||
<div class="col-name">
|
||||
<span class="pending-label">
|
||||
<i class="fas fa-envelope"></i> 초대 대기중
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-email" th:text="${user.maskedEmailAddr}">
|
||||
pending@example.com
|
||||
</div>
|
||||
<div class="col-role">-</div>
|
||||
<div class="col-status">
|
||||
<span class="user-status-badge status-pending">
|
||||
초대중
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-actions">
|
||||
<div class="user-action-buttons">
|
||||
<button type="button" class="btn-action btn-sm btn-danger cancel_invitation"
|
||||
th:data-user-id="${user.id}"
|
||||
th:data-user-email="${user.maskedEmailAddr}">
|
||||
초대취소
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagination page_top"
|
||||
th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div class="user-empty-state" th:if="${(users == null or users.isEmpty()) and (pendingUsers == null or pendingUsers.isEmpty())}">
|
||||
<div class="empty-icon">👥</div>
|
||||
<h3>등록된 이용자가 없습니다</h3>
|
||||
<p>새로운 이용자를 추가하여 시작하세요.</p>
|
||||
<button type="button" class="btn-create-user-large" id="addUserBtnEmpty">
|
||||
<span class="btn-icon">➕</span>
|
||||
첫 이용자 추가하기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="pagination page_top"
|
||||
th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
|
||||
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<section layout:fragment="pagePopups" class="content pop">
|
||||
<div class="popup_wrap pop01" id="user_register" style="display: none; z-index: 1000">
|
||||
<div class="pop_dim">
|
||||
<div class="inner">
|
||||
<div class="pop_body">
|
||||
<div class="box-txt">
|
||||
<div class="inner_txt">
|
||||
<div class="title">
|
||||
<h4>이용자 추가</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<p class="con_txt2">추가할 이용자 e-mail 주소를 입력해 주세요.</p>
|
||||
<div class="inp_box">
|
||||
<input type="text" class="common_input_type_1 w_input" id="new_user_email"
|
||||
placeholder="이용자 e-mail 주소">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pop_btn p_btn">
|
||||
<a href="#" class="common_btn_type_1 gray btn_cancel"><span>취소</span></a>
|
||||
<a href="#" class="common_btn_type_1 blue btn_register"><span>확인</span></a>
|
||||
</div>
|
||||
<button class="btn_close" style="margin-top:0">
|
||||
<img th:src="@{/img/icon/icon_close.png}">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="popup_wrap pop01" id="change_role" style="display: none; z-index: 1000">
|
||||
<div class="pop_dim">
|
||||
<div class="inner inner2">
|
||||
<div class="pop_body">
|
||||
<div class="box-txt">
|
||||
<div class="inner_txt">
|
||||
<div class="title">
|
||||
<h4>권한변경</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contents">
|
||||
<p class="con_txt">
|
||||
<span class="txt_bold" id="change_role_name"></span><span class="txt_mail"
|
||||
id="change_role_email"></span>님
|
||||
에게<br>
|
||||
법인 관리자 권한을 부여 하시겠습니까?
|
||||
</p>
|
||||
<div class="con_info_box">
|
||||
<p>법인 관리자는 1개의 기관에 1명만 지정 가능 합니다.</p>
|
||||
<p>본 계정의 관리자 권한은 해제 됩니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pop_btn">
|
||||
<a href="#" class="common_btn_type_1 gray btn_cancel_role"><span>취소</span></a>
|
||||
<a href="#" class="common_btn_type_1 blue btn_confirm_role"><span>확인</span></a>
|
||||
</div>
|
||||
<button class="btn_close" style="margin-top:0">
|
||||
<img th:src="@{/img/icon/icon_close.png}" alt="">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- User Invite Modal -->
|
||||
<div th:replace="~{fragment/popup/userInvitePopup :: userInvitePopup}"></div>
|
||||
<!-- Change Role Modal -->
|
||||
<div th:replace="~{fragment/popup/changeRolePopup :: changeRolePopup}"></div>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
@@ -165,129 +166,80 @@
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Function to open the popup
|
||||
function openPopup() {
|
||||
document.getElementById('user_register').style.display = '';
|
||||
}
|
||||
|
||||
// Function to close the popup
|
||||
function closePopup() {
|
||||
document.getElementById('user_register').style.display = 'none';
|
||||
}
|
||||
|
||||
// Add event listener to the "이용자 추가" button to open the popup
|
||||
const addUserBtn = document.querySelector('.btn_add');
|
||||
if (addUserBtn) {
|
||||
addUserBtn.addEventListener('click', openPopup);
|
||||
}
|
||||
|
||||
// Add event listener to the close button
|
||||
const closeBtn = document.querySelector('button.btn_close');
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', closePopup);
|
||||
}
|
||||
|
||||
// Add event listener to the cancel button
|
||||
const cancelBtn = document.querySelector('.btn_cancel');
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', closePopup);
|
||||
}
|
||||
|
||||
// Add event listener to the confirm button
|
||||
const confirmBtn = document.querySelector('.btn_register');
|
||||
if (confirmBtn) {
|
||||
confirmBtn.addEventListener('click', sendInvitation);
|
||||
}
|
||||
|
||||
function sendInvitation() {
|
||||
const emailInput = document.getElementById('new_user_email');
|
||||
const email = emailInput.value.trim();
|
||||
|
||||
if (!email) {
|
||||
customPopups.showAlert('이메일 주소를 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/users/invite',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({emailAddr: email})
|
||||
}).done(function (response) {
|
||||
closePopup();
|
||||
emailInput.value = '';
|
||||
customPopups.showAlert(response.msg, function () {
|
||||
location.reload(); // Reload the page to reflect changes
|
||||
});
|
||||
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
let errorMessage;
|
||||
|
||||
try {
|
||||
// Parse the error response body
|
||||
const errorResponse = JSON.parse(jqXHR.responseText);
|
||||
errorMessage = errorResponse.msg || '이용자 초대 중 오류가 발생했습니다';
|
||||
} catch (e) {
|
||||
// Fallback if response isn't valid JSON
|
||||
errorMessage = '이용자 초대 중 오류가 발생했습니다: ' + errorThrown;
|
||||
// Function to open the user invite popup
|
||||
function openUserInvitePopup() {
|
||||
customPopups.showUserInvite({
|
||||
onConfirm: function(email) {
|
||||
$.ajax({
|
||||
url: '/users/invite',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({emailAddr: email})
|
||||
}).done(function (response) {
|
||||
customPopups.hideUserInvite();
|
||||
customPopups.showAlert(response.msg, function () {
|
||||
location.reload();
|
||||
});
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
let errorMessage;
|
||||
try {
|
||||
const errorResponse = JSON.parse(jqXHR.responseText);
|
||||
errorMessage = errorResponse.msg || '이용자 초대 중 오류가 발생했습니다';
|
||||
} catch (e) {
|
||||
errorMessage = '이용자 초대 중 오류가 발생했습니다: ' + errorThrown;
|
||||
}
|
||||
customPopups.showUserInviteError(errorMessage);
|
||||
});
|
||||
},
|
||||
onCancel: function() {
|
||||
// Optional: handle cancel action
|
||||
}
|
||||
customPopups.showAlert(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
// Optional: Close the popup when clicking outside of it
|
||||
window.addEventListener('click', function (event) {
|
||||
const popup = document.getElementById('user_register');
|
||||
if (event.target === popup) {
|
||||
closePopup();
|
||||
}
|
||||
});
|
||||
// Add event listeners to the "이용자 추가" buttons
|
||||
const addUserBtn = document.getElementById('addUserBtn');
|
||||
const addUserBtnEmpty = document.getElementById('addUserBtnEmpty');
|
||||
|
||||
if (addUserBtn) {
|
||||
addUserBtn.addEventListener('click', openUserInvitePopup);
|
||||
}
|
||||
if (addUserBtnEmpty) {
|
||||
addUserBtnEmpty.addEventListener('click', openUserInvitePopup);
|
||||
}
|
||||
|
||||
// Change role button handlers
|
||||
const adminPrivilegeButtons = document.querySelectorAll('.assign_manager');
|
||||
adminPrivilegeButtons.forEach(button => {
|
||||
button.addEventListener('click', openChangeRolePopup);
|
||||
});
|
||||
button.addEventListener('click', function(event) {
|
||||
const button = event.currentTarget;
|
||||
const userId = button.dataset.userId;
|
||||
const userName = button.dataset.userName;
|
||||
const userEmail = button.dataset.userEmail;
|
||||
|
||||
function openChangeRolePopup(event) {
|
||||
const row = event.target.closest('tr');
|
||||
const name = row.querySelector('td:nth-child(2)').textContent;
|
||||
const email = row.querySelector('td:nth-child(3)').textContent;
|
||||
const userId = row.querySelector('a').getAttribute('href').split('=')[1];
|
||||
|
||||
document.getElementById('change_role_name').textContent = name;
|
||||
document.getElementById('change_role_email').textContent = `(${email})`;
|
||||
document.getElementById('change_role').style.display = 'block';
|
||||
|
||||
// Store userId for later use
|
||||
document.getElementById('change_role').dataset.userId = userId;
|
||||
}
|
||||
|
||||
function closeChangeRolePopup() {
|
||||
document.getElementById('change_role').style.display = 'none';
|
||||
}
|
||||
|
||||
document.querySelector('#change_role .btn_close').addEventListener('click', closeChangeRolePopup);
|
||||
document.querySelector('#change_role .btn_cancel_role').addEventListener('click', closeChangeRolePopup);
|
||||
document.querySelector('#change_role .btn_confirm_role').addEventListener('click', confirmRoleChange);
|
||||
|
||||
function confirmRoleChange() {
|
||||
const userId = document.getElementById('change_role').dataset.userId;
|
||||
|
||||
$.ajax({
|
||||
url: '/users/change-role',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({id: userId})
|
||||
}).done(function (response) {
|
||||
closeChangeRolePopup();
|
||||
customPopups.showAlert(response.msg, function () {
|
||||
window.location.href = '/actionLogout.do';
|
||||
customPopups.showChangeRole({
|
||||
userName: userName,
|
||||
userEmail: userEmail,
|
||||
userId: userId,
|
||||
onConfirm: function(userId) {
|
||||
$.ajax({
|
||||
url: '/users/change-role',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({id: userId})
|
||||
}).done(function (response) {
|
||||
customPopups.hideChangeRole();
|
||||
customPopups.showAlert(response.msg, function () {
|
||||
window.location.href = '/actionLogout.do';
|
||||
});
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
customPopups.hideChangeRole();
|
||||
customPopups.showAlert('권한 변경 중 오류가 발생했습니다: ' + errorThrown);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
customPopups.showAlert('권한 변경 중 오류가 발생했습니다: ' + errorThrown);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const activateButtons = document.querySelectorAll('.activate_user');
|
||||
const inactivateButtons = document.querySelectorAll('.inactivate_user');
|
||||
@@ -306,17 +258,14 @@
|
||||
});
|
||||
|
||||
function handleUserStatusChange(event) {
|
||||
const row = event.target.closest('tr');
|
||||
const userId = row.querySelector('a').getAttribute('href').split('=')[1];
|
||||
const userName = row.querySelector('td:nth-child(2)').textContent;
|
||||
console.log(event.target.classList);
|
||||
const isActivating = event.target.classList.contains('activate_user');
|
||||
console.log({isActivating});
|
||||
const button = event.currentTarget;
|
||||
const userId = button.dataset.userId;
|
||||
const userName = button.dataset.userName;
|
||||
const isActivating = button.classList.contains('activate_user');
|
||||
const actionText = isActivating ? '활성화' : '비활성화';
|
||||
|
||||
customPopups.showConfirm(`${userName} 사용자를 ${actionText}하시겠습니까?`, function (selection) {
|
||||
if (selection) {
|
||||
console.log({isActivating});
|
||||
$.ajax({
|
||||
url: `/users/${isActivating ? 'activate' : 'inactivate'}`,
|
||||
type: 'POST',
|
||||
@@ -336,12 +285,11 @@
|
||||
}
|
||||
|
||||
function handleCancelInvitation(event) {
|
||||
const row = event.target.closest('tr');
|
||||
const userId = row.querySelector('td[data-id]').getAttribute('data-id');
|
||||
console.log(userId);
|
||||
const userName = row.querySelector('td:nth-child(3)').textContent;
|
||||
const button = event.currentTarget;
|
||||
const userId = button.dataset.userId;
|
||||
const userEmail = button.dataset.userEmail;
|
||||
|
||||
customPopups.showConfirm(`${userName}에 대한 초대를 취소하시겠습니까?`, function (selection) {
|
||||
customPopups.showConfirm(`${userEmail}에 대한 초대를 취소하시겠습니까?`, function (selection) {
|
||||
if (selection) {
|
||||
$.ajax({
|
||||
url: '/users/cancel-invitation',
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
<!-- views/fragment/custom-popup.html -->
|
||||
<div th:fragment="customAlert" class="popup_total" id="customAlert" style="display:none; z-index:1001;">
|
||||
<div class="pop_dim">
|
||||
<div class="inner">
|
||||
<div class="popup-content">
|
||||
<div class="inner_txt">
|
||||
<div class="title">
|
||||
<h2>알림</h2>
|
||||
</div>
|
||||
</div>
|
||||
<p class="pop_text mo_text" id="customAlertMessage"></p>
|
||||
<div class="pop_btnbox">
|
||||
<button class="popup_button_blue inner_button" id="customAlertOkButton">확인</button>
|
||||
</div>
|
||||
<a class="btn_close" style="margin-top:0">
|
||||
<img th:src="@{/img/icon/icon_close.png}" alt="닫기">
|
||||
</a>
|
||||
<div th:fragment="customAlert" id="customAlert" style="display: none;">
|
||||
<!-- Modal Backdrop -->
|
||||
<div class="modal-backdrop" id="customAlertBackdrop"></div>
|
||||
|
||||
<!-- Modal Wrapper -->
|
||||
<div class="modal" id="customAlertModal">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="customAlertTitle">알림</h3>
|
||||
<button type="button" class="modal-close" id="customAlertCloseButton">
|
||||
<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="customAlertMessage" style="text-align: center; margin-bottom: 24px; color: #64748B;">
|
||||
<!-- 메시지가 여기에 표시됩니다 -->
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Modal Footer -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="customAlertOkButton">확인</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
<!-- views/fragment/custom-popup2.html -->
|
||||
<div th:fragment="customAlert2" class="popup_total" id="customConfirm" style="display:none; z-index:1001;">
|
||||
<div class="pop_dim">
|
||||
<div class="popup-content">
|
||||
<div class="inner_txt">
|
||||
<div class="title">
|
||||
<h2 id="customAlertTitle2">알림</h2>
|
||||
</div>
|
||||
<div th:fragment="customConfirm" id="customConfirm" style="display: none;">
|
||||
<!-- Modal Backdrop -->
|
||||
<div class="modal-backdrop" id="customConfirmBackdrop"></div>
|
||||
|
||||
<!-- Modal Wrapper -->
|
||||
<div class="modal" id="customConfirmModal">
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="customConfirmTitle">확인</h3>
|
||||
<button type="button" class="modal-close" id="customConfirmCloseButton">
|
||||
<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>
|
||||
<p class="pop_text" id="customConfirmMessage"></p>
|
||||
<div class="pop_btnbox">
|
||||
<button class="popup_button_gray" id="customConfirmNoButton">취소</button>
|
||||
<button class="popup_button_blue" id="customConfirmYesButton">확인</button>
|
||||
|
||||
<!-- Modal Body -->
|
||||
<div class="modal-body">
|
||||
<p id="customConfirmMessage" style="text-align: center; margin-bottom: 24px; color: #64748B;">
|
||||
<!-- 메시지가 여기에 표시됩니다 -->
|
||||
</p>
|
||||
</div>
|
||||
<a class="btn_close" style="margin-top:0" id="customAlertCloseButton2">
|
||||
<img th:src="@{/img/icon/icon_close.png}" alt="닫기">
|
||||
</a>
|
||||
|
||||
<!-- Modal Footer -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="customConfirmNoButton">취소</button>
|
||||
<button type="button" class="btn btn-primary" id="customConfirmYesButton">확인</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user