From 0362db6d9a5a2367e639664d92b64027b9728973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=98=84=EC=84=B1=ED=95=84?= Date: Thu, 6 Nov 2025 13:12:45 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20scss,=20=ED=8C=9D?= =?UTF-8?q?=EC=97=85=20html=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/sass/components/_badges.scss | 171 +++++ .../static/sass/pages/_user-management.scss | 656 ++++++++++++++++++ .../views/fragment/popup/changeRolePopup.html | 54 ++ .../views/fragment/popup/userInvitePopup.html | 45 ++ 4 files changed, 926 insertions(+) create mode 100644 src/main/resources/static/sass/components/_badges.scss create mode 100644 src/main/resources/static/sass/pages/_user-management.scss create mode 100644 src/main/resources/templates/views/fragment/popup/changeRolePopup.html create mode 100644 src/main/resources/templates/views/fragment/popup/userInvitePopup.html diff --git a/src/main/resources/static/sass/components/_badges.scss b/src/main/resources/static/sass/components/_badges.scss new file mode 100644 index 0000000..69e5a99 --- /dev/null +++ b/src/main/resources/static/sass/components/_badges.scss @@ -0,0 +1,171 @@ +// ----------------------------------------------------------------------------- +// Badge Component Styles - Status badges and labels +// ----------------------------------------------------------------------------- + +// Base status badge +.status-badge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: $spacing-xs $spacing-md; + border-radius: $border-radius-full; + font-size: $font-size-xs; + font-weight: $font-weight-semibold; + white-space: nowrap; + transition: $transition-base; + + // Status variants + &.status-pending { + background: rgba($accent-orange, 0.1); + color: $accent-orange; + } + + &.status-completed { + background: rgba($accent-green, 0.1); + color: $accent-green; + } + + &.status-active { + background: rgba($primary-blue, 0.1); + color: $primary-blue; + } + + &.status-inactive { + background: rgba($text-gray, 0.1); + color: $text-gray; + } + + &.status-processing { + background: rgba($accent-yellow, 0.1); + color: darken($accent-yellow, 30%); + } + + &.status-failed { + background: rgba($accent-orange, 0.1); + color: $accent-orange; + } + + &.status-success { + background: rgba($accent-green, 0.1); + color: $accent-green; + } +} + +// Header variant (larger, with margin) +.status-badge-header { + display: inline-flex; + align-items: center; + justify-content: center; + padding: $spacing-xs $spacing-md; + border-radius: $border-radius-full; + font-size: $font-size-xs; + font-weight: $font-weight-semibold; + margin-bottom: $spacing-md; + transition: $transition-base; + + &.status-pending { + background: rgba($accent-orange, 0.1); + color: $accent-orange; + } + + &.status-completed { + background: rgba($accent-green, 0.1); + color: $accent-green; + } + + &.status-active { + background: rgba($primary-blue, 0.1); + color: $primary-blue; + } + + &.status-inactive { + background: rgba($text-gray, 0.1); + color: $text-gray; + } + + &.status-processing { + background: rgba($accent-yellow, 0.1); + color: darken($accent-yellow, 30%); + } +} + +// Small badge variant +.badge-sm { + padding: 2px $spacing-sm; + font-size: 10px; + border-radius: $border-radius-sm; +} + +// Large badge variant +.badge-lg { + padding: $spacing-sm $spacing-lg; + font-size: $font-size-sm; + border-radius: $border-radius-md; +} + +// Outline badge variant +.badge-outline { + background: transparent; + border: 1px solid currentColor; + + &.status-pending { + color: $accent-orange; + border-color: $accent-orange; + } + + &.status-completed { + color: $accent-green; + border-color: $accent-green; + } + + &.status-active { + color: $primary-blue; + border-color: $primary-blue; + } + + &.status-inactive { + color: $text-gray; + border-color: $text-gray; + } +} + +// Badge with icon +.badge-icon { + i { + margin-right: $spacing-xs; + font-size: 0.9em; + } +} + +// Notification badge (for counts) +.notification-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 20px; + height: 20px; + padding: 0 $spacing-xs; + background: $accent-orange; + color: $white; + border-radius: $border-radius-circle; + font-size: 11px; + font-weight: $font-weight-bold; + line-height: 1; + + &.badge-primary { + background: $primary-blue; + } + + &.badge-success { + background: $accent-green; + } + + &.badge-warning { + background: $accent-yellow; + color: $text-dark; + } + + &.badge-danger { + background: $accent-orange; + } +} diff --git a/src/main/resources/static/sass/pages/_user-management.scss b/src/main/resources/static/sass/pages/_user-management.scss new file mode 100644 index 0000000..791ba76 --- /dev/null +++ b/src/main/resources/static/sass/pages/_user-management.scss @@ -0,0 +1,656 @@ +// ----------------------------------------------------------------------------- +// User Management Pages - Modern Card-Based Layout +// Styles for user list and user detail pages +// ----------------------------------------------------------------------------- + +// User Management Container (List Page) +.user-management-container { + max-width: 1400px; + margin: 0 auto; + padding: $spacing-3xl $spacing-2xl; + background-color: $gray-bg; + min-height: 100vh; + + @media (max-width: $breakpoint-sm) { + padding: $spacing-lg $spacing-md; + } +} + +// Header Section +.user-management-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: $spacing-2xl; + + @media (max-width: $breakpoint-sm) { + flex-direction: column; + align-items: flex-start; + gap: $spacing-lg; + } +} + +.user-management-title { + h1 { + font-size: $font-size-sm; + color: $text-gray; + font-weight: $font-weight-regular; + margin-bottom: $spacing-xs; + } + + h2 { + font-size: $font-size-2xl; + font-weight: $font-weight-bold; + color: $text-dark; + } +} + +.user-management-actions { + display: flex; + gap: $spacing-md; +} + +.btn-create-user, +.btn-create-user-large { + display: inline-flex; + align-items: center; + gap: $spacing-sm; + padding: 12px $spacing-xl; + background: $gradient-primary; + color: $white; + font-size: $font-size-base; + font-weight: $font-weight-semibold; + border: none; + border-radius: $border-radius-lg; + cursor: pointer; + transition: $transition-base; + box-shadow: $shadow-md; + + &:hover { + transform: translateY(-2px); + box-shadow: $shadow-lg; + } + + &:active { + transform: translateY(0); + } + + .btn-icon { + font-size: $font-size-lg; + } +} + +.btn-create-user-large { + padding: $spacing-md $spacing-2xl; + font-size: $font-size-md; + margin-top: $spacing-lg; +} + +// Table Controls +.table-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: $spacing-lg; + padding: 0 $spacing-sm; + + @media (max-width: $breakpoint-sm) { + flex-direction: column; + align-items: flex-start; + gap: $spacing-md; + } +} + +.total-count { + font-size: $font-size-base; + color: $text-gray; + + strong { + color: $primary-blue; + font-weight: $font-weight-semibold; + font-size: $font-size-lg; + } +} + +// User Table +.user-list-wrapper { + background: $white; + border-radius: $border-radius-lg; + box-shadow: $shadow-sm; + overflow: hidden; + margin-bottom: $spacing-2xl; +} + +.user-table { + display: flex; + flex-direction: column; + width: 100%; +} + +// Table Header +.table-header { + display: grid; + grid-template-columns: 60px 120px 1fr 150px 120px 200px; + gap: $spacing-md; + padding: $spacing-md $spacing-lg; + background: $light-bg; + border-bottom: 2px solid $border-gray; + font-size: $font-size-sm; + font-weight: $font-weight-semibold; + color: $text-dark; + + @media (max-width: $breakpoint-lg) { + grid-template-columns: 50px 100px 1fr 120px 100px 180px; + font-size: $font-size-xs; + padding: $spacing-sm $spacing-md; + } + + @media (max-width: $breakpoint-md) { + display: none; // Hide on mobile, use stacked layout + } +} + +// Table Row +.table-row { + display: grid; + grid-template-columns: 60px 120px 1fr 150px 120px 200px; + gap: $spacing-md; + padding: $spacing-lg; + border-bottom: 1px solid $border-gray; + align-items: center; + transition: $transition-base; + cursor: pointer; + + &:hover { + background-color: rgba($primary-blue, 0.02); + } + + &:last-child { + border-bottom: none; + } + + &.row-pending { + background-color: rgba($accent-yellow, 0.03); + border-left: 3px solid $accent-yellow; + } + + @media (max-width: $breakpoint-lg) { + grid-template-columns: 50px 100px 1fr 120px 100px 180px; + padding: $spacing-md; + font-size: $font-size-sm; + } + + @media (max-width: $breakpoint-md) { + grid-template-columns: 1fr; + gap: $spacing-sm; + padding: $spacing-md; + + &::before { + content: attr(data-label); + font-weight: $font-weight-semibold; + color: $text-gray; + } + } +} + +// Table Columns +.col-num { + font-size: $font-size-sm; + color: $text-gray; + text-align: center; + + @media (max-width: $breakpoint-md) { + display: none; + } +} + +.col-name { + font-weight: $font-weight-medium; + + a { + color: $text-dark; + text-decoration: none; + transition: $transition-base; + + &:hover { + color: $primary-blue; + } + } + + .pending-label { + display: inline-flex; + align-items: center; + gap: $spacing-xs; + color: $text-gray; + font-style: italic; + + i { + color: $accent-yellow; + } + } +} + +.col-email { + color: $text-gray; + font-size: $font-size-sm; + 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($primary-blue, 0.1); + color: $primary-blue; + border-radius: $border-radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + + i { + font-size: $font-size-xs; + } +} + +// Status and Actions Columns +.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: $border-radius-full; + font-size: $font-size-xs; + font-weight: $font-weight-semibold; + text-transform: uppercase; + letter-spacing: 0.5px; + white-space: nowrap; + + &.status-active { + background-color: rgba($accent-green, 0.1); + color: darken($accent-green, 10%); + } + + &.status-inactive { + background-color: rgba($text-gray, 0.1); + color: $text-gray; + } + + &.status-pending { + background-color: rgba($accent-yellow, 0.15); + color: darken($accent-yellow, 30%); + } +} + +.user-current-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background-color: rgba($accent-purple, 0.1); + color: $accent-purple; + border-radius: $border-radius-sm; + font-size: $font-size-xs; + font-weight: $font-weight-medium; +} + +.user-action-buttons { + display: flex; + flex-wrap: wrap; + gap: $spacing-xs; + align-items: center; + + .btn-action { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 6px 12px; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + border: none; + border-radius: $border-radius-md; + cursor: pointer; + transition: $transition-base; + white-space: nowrap; + + &.btn-sm { + padding: 4px 10px; + font-size: 11px; + } + + &.btn-primary { + background-color: $primary-blue; + color: $white; + + &:hover { + background-color: darken($primary-blue, 10%); + box-shadow: $shadow-sm; + } + } + + &.btn-secondary { + background-color: $text-gray; + color: $white; + + &:hover { + background-color: darken($text-gray, 10%); + box-shadow: $shadow-sm; + } + } + + &.btn-danger { + background-color: $accent-orange; + color: $white; + + &:hover { + background-color: darken($accent-orange, 10%); + box-shadow: $shadow-sm; + } + } + + &.btn-warning { + background-color: $accent-yellow; + color: $gray-800; + + &:hover { + background-color: darken($accent-yellow, 10%); + box-shadow: $shadow-sm; + } + } + + &:active { + transform: scale(0.98); + } + } +} + +// Empty State +.user-empty-state { + text-align: center; + padding: $spacing-5xl $spacing-lg; + color: $text-gray; + + .empty-icon { + font-size: 80px; + margin-bottom: $spacing-lg; + opacity: 0.5; + } + + h3 { + font-size: $font-size-xl; + font-weight: $font-weight-semibold; + color: $text-dark; + margin-bottom: $spacing-md; + } + + p { + font-size: $font-size-base; + color: $text-gray; + } +} + +// ----------------------------------------------------------------------------- +// User Detail Page +// ----------------------------------------------------------------------------- + +.user-detail-container { + max-width: 900px; + margin: 0 auto; + padding: $spacing-2xl; + + @media (max-width: $breakpoint-sm) { + padding: $spacing-lg $spacing-md; + } +} + +// Profile Card +.user-profile-card { + background: linear-gradient(135deg, $primary-blue 0%, $secondary-blue 100%); + color: $white; + margin-bottom: $spacing-xl; + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + top: -50%; + right: -20%; + width: 400px; + height: 400px; + background: rgba($white, 0.05); + border-radius: $border-radius-circle; + } + + .detail-grid { + border: none; + } +} + +.user-profile-header { + display: flex; + align-items: center; + gap: $spacing-xl; + position: relative; + z-index: 1; + + @media (max-width: $breakpoint-sm) { + flex-direction: column; + text-align: center; + } +} + +.user-profile-avatar { + flex-shrink: 0; +} + +.avatar-circle-large { + width: 100px; + height: 100px; + border-radius: $border-radius-circle; + display: flex; + align-items: center; + justify-content: center; + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + background: rgba($white, 0.2); + color: $white; + border: 4px solid rgba($white, 0.3); + box-shadow: $shadow-lg; +} + +.user-profile-info { + flex: 1; + + h2 { + font-size: $font-size-2xl; + font-weight: $font-weight-bold; + margin-bottom: $spacing-sm; + } + + .user-profile-email { + font-size: $font-size-base; + opacity: 0.9; + margin-bottom: $spacing-md; + } +} + +.user-profile-badges { + display: flex; + gap: $spacing-sm; + flex-wrap: wrap; + + @media (max-width: $breakpoint-sm) { + justify-content: center; + } + + .user-status-badge, + .user-role-badge { + background: rgba($white, 0.2); + color: $white; + backdrop-filter: blur(10px); + border: 1px solid rgba($white, 0.3); + } +} + +// Detail Section (Reuse from API Key Detail) +.detail-section { + background: $white; + border-radius: $border-radius-lg; + padding: $spacing-xl; + box-shadow: $shadow-sm; + margin-bottom: $spacing-lg; + + h2 { + font-size: $font-size-lg; + font-weight: $font-weight-semibold; + color: $text-dark; + margin-bottom: $spacing-lg; + padding-bottom: $spacing-sm; + border-bottom: 2px solid $light-bg; + } +} + +.detail-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: $spacing-lg; + + @media (max-width: $breakpoint-sm) { + grid-template-columns: 1fr; + } +} + +.detail-item { + display: flex; + flex-direction: column; + gap: $spacing-xs; + + &.full-width { + grid-column: 1 / -1; + } +} + +.detail-label { + font-size: $font-size-sm; + font-weight: $font-weight-semibold; + color: $text-gray; + display: flex; + align-items: center; + gap: $spacing-xs; + + i { + color: $primary-blue; + font-size: $font-size-sm; + } +} + +.detail-value { + font-size: $font-size-base; + color: $text-dark; + padding: $spacing-sm 0; +} + +.status-indicator { + display: inline-flex; + align-items: center; + gap: $spacing-xs; + padding: 6px 12px; + border-radius: $border-radius-sm; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + + &.status-active { + background-color: rgba($accent-green, 0.1); + color: darken($accent-green, 10%); + + .status-dot { + background-color: $accent-green; + } + } + + &.status-inactive { + background-color: rgba($text-gray, 0.1); + color: $text-gray; + + .status-dot { + background-color: $text-gray; + } + } +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: $border-radius-circle; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +// Detail Actions +.detail-actions { + display: flex; + gap: $spacing-md; + justify-content: center; + padding-top: $spacing-lg; + + @media (max-width: $breakpoint-sm) { + flex-direction: column; + } +} + +.btn-secondary { + display: inline-flex; + align-items: center; + gap: $spacing-sm; + padding: 12px $spacing-xl; + background-color: $white; + color: $text-dark; + font-size: $font-size-base; + font-weight: $font-weight-medium; + border: 2px solid $border-gray; + border-radius: $border-radius-lg; + text-decoration: none; + cursor: pointer; + transition: $transition-base; + + &:hover { + background-color: $gray-bg; + border-color: $primary-blue; + color: $primary-blue; + transform: translateY(-2px); + box-shadow: $shadow-sm; + } + + &:active { + transform: translateY(0); + } + + i { + font-size: $font-size-base; + } +} diff --git a/src/main/resources/templates/views/fragment/popup/changeRolePopup.html b/src/main/resources/templates/views/fragment/popup/changeRolePopup.html new file mode 100644 index 0000000..4a352de --- /dev/null +++ b/src/main/resources/templates/views/fragment/popup/changeRolePopup.html @@ -0,0 +1,54 @@ + + diff --git a/src/main/resources/templates/views/fragment/popup/userInvitePopup.html b/src/main/resources/templates/views/fragment/popup/userInvitePopup.html new file mode 100644 index 0000000..a53ce94 --- /dev/null +++ b/src/main/resources/templates/views/fragment/popup/userInvitePopup.html @@ -0,0 +1,45 @@ + +