내 정보 관리 - 스타일 적용

This commit is contained in:
현성필
2025-12-01 16:48:20 +09:00
parent f3f313a105
commit b4f9555cb8
21 changed files with 1152 additions and 1304 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -488,3 +488,20 @@
.transition-none {
transition: none;
}
// Accessibility utilities
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.sr-only {
@extend .visually-hidden;
}
@@ -0,0 +1,66 @@
// -----------------------------------------------------------------------------
// Alert Components
// Used in: Form validation, system messages, notifications
// -----------------------------------------------------------------------------
.alert {
display: flex;
align-items: flex-start;
gap: $spacing-md;
padding: $spacing-md $spacing-lg;
border-radius: $border-radius-md;
margin-bottom: $spacing-xl;
.alert-icon {
font-size: 20px;
flex-shrink: 0;
}
.alert-content {
flex: 1;
text-align: left;
strong {
font-weight: $font-weight-semibold;
}
}
// Warning alert - yellow/amber style
&.alert-warning {
background: rgba($accent-yellow, 0.1);
border: 1px solid rgba($accent-yellow, 0.3);
color: #B89900;
}
// Error alert - red/orange style
&.alert-error {
background: rgba($accent-orange, 0.1);
border: 1px solid rgba($accent-orange, 0.3);
color: darken($accent-orange, 10%);
align-items: center;
svg {
flex-shrink: 0;
}
span {
flex: 1;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
}
}
// Success alert - green style
&.alert-success {
background: rgba($accent-green, 0.1);
border: 1px solid rgba($accent-green, 0.3);
color: darken($accent-green, 20%);
}
// Info alert - blue style
&.alert-info {
background: rgba($primary-blue, 0.1);
border: 1px solid rgba($primary-blue, 0.3);
color: $secondary-blue;
}
}
@@ -18,7 +18,7 @@ $apikey-border-color: #dadada;
// -----------------------------------------------------------------------------
// Common Container
// -----------------------------------------------------------------------------
.apikey-container {
.common-container {
max-width: $container-max-width;
margin: 0 auto;
padding: 60px $spacing-lg 80px;
@@ -31,7 +31,7 @@ $apikey-border-color: #dadada;
// -----------------------------------------------------------------------------
// Common Section Box (Background container)
// -----------------------------------------------------------------------------
.apikey-section-box {
.common-section-box {
background-color: $apikey-bg-color;
border-radius: $border-radius-lg;
padding: 30px 45px;
@@ -44,7 +44,7 @@ $apikey-border-color: #dadada;
// -----------------------------------------------------------------------------
// Common Title Bar
// -----------------------------------------------------------------------------
.apikey-title-bar {
.common-title-bar {
background-color: $apikey-bg-color;
border-radius: $border-radius-lg;
padding: 14px 45px;
@@ -54,7 +54,7 @@ $apikey-border-color: #dadada;
padding: 12px $spacing-lg;
}
.apikey-title {
.common-title {
font-family: $font-family-primary;
font-size: 28px;
font-weight: $font-weight-bold;
@@ -66,7 +66,7 @@ $apikey-border-color: #dadada;
}
}
.apikey-subtitle {
.common-subtitle {
font-size: 16px;
font-weight: $font-weight-regular;
color: #515151;
@@ -473,3 +473,99 @@
font-size: 14px;
}
}
// -----------------------------------------------------------------------------
// Input Action Button - Figma Design (node 985:1649)
// Button at the end of input field (변경, 인증번호 받기, etc.)
// -----------------------------------------------------------------------------
.btn-input-action {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 172px;
height: 60px;
padding: 10px $spacing-lg;
border: none;
border-radius: $border-radius-lg;
font-size: $font-size-lg;
font-weight: $font-weight-bold;
color: $white;
cursor: pointer;
transition: $transition-base;
white-space: nowrap;
// "변경" button style - light blue (#a4d6ea)
&.btn-change {
background: #a4d6ea;
&:hover {
background: darken(#a4d6ea, 10%);
transform: translateY(-2px);
box-shadow: $shadow-md;
}
&:active {
transform: translateY(0);
}
}
// "인증번호 받기/확인" button style - primary blue
&.btn-auth {
background: #A4D6EA;
&:hover {
background: $secondary-blue;
transform: translateY(-2px);
box-shadow: $shadow-md;
}
&:active {
transform: translateY(0);
}
&:disabled {
background: $border-gray;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
}
@media (max-width: $breakpoint-sm) {
width: 100%;
min-width: auto;
height: 52px;
font-size: $font-size-base;
}
}
// -----------------------------------------------------------------------------
// Upload Button - File upload trigger button
// Used in: Icon upload, file attachment areas
// -----------------------------------------------------------------------------
.btn-upload {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 60px;
padding: 10px 24px;
background-color: #3ba4ed;
color: $white;
border: none;
border-radius: $border-radius-lg;
font-size: 18px;
font-weight: $font-weight-bold;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background-color: #2b94dd;
}
@include respond-to('md') {
font-size: 16px;
height: 50px;
}
}
@@ -376,3 +376,255 @@ select.form-control {
}
}
}
// -----------------------------------------------------------------------------
// Form Row Layout - Horizontal Label-Field Layout
// Used in: API Key registration, User profile forms
// -----------------------------------------------------------------------------
.form-row {
display: flex;
align-items: flex-start;
gap: 40px;
margin-bottom: 50px;
//&:last-child {
// margin-bottom: 0;
//}
@include respond-to('md') {
flex-direction: column;
gap: 12px;
margin-bottom: 30px;
}
}
.form-label-wrapper {
display: flex;
align-items: center;
gap: 5px;
min-width: 214px;
width: 214px;
flex-shrink: 0;
@include respond-to('md') {
padding-top: 0;
width: auto;
}
&.label-offset {
margin-top: 15px;
@include respond-to('md') {
margin-top: 0;
}
}
}
.form-label-text {
font-size: 20px;
font-weight: $font-weight-bold;
color: #000000;
@include respond-to('md') {
font-size: 16px;
}
// Required indicator (asterisk style)
&.required::after {
content: '*';
color: $accent-orange;
margin-left: 4px;
}
}
.required-badge {
background-color: #3ba4ed;
color: $white;
font-size: 11px;
font-weight: $font-weight-regular;
padding: 3px 4px 4px;
border-radius: 4px;
line-height: 1;
}
.form-field-wrapper {
flex: 1;
@include respond-to('sm') {
width: 100%;
}
}
// -----------------------------------------------------------------------------
// Character Counter - Text input character count display
// -----------------------------------------------------------------------------
.char-counter-wrapper {
display: block;
text-align: right;
margin-top: 8px;
.char-counter {
font-size: 14px;
color: #94A3B8;
}
}
// -----------------------------------------------------------------------------
// Section Divider - Section separator with border
// Used for separating form sections (e.g., "법인 관리자 정보")
// -----------------------------------------------------------------------------
.section-divider {
margin-top: $spacing-3xl;
padding-top: $spacing-xl;
border-top: 1px solid $border-gray;
}
// -----------------------------------------------------------------------------
// Compound Input - Multi-part input fields (phone numbers, registration numbers)
// Used in: updateCorporateManager.html, updateCorporateUser.html, etc.
// -----------------------------------------------------------------------------
.compound-input {
display: flex;
align-items: center;
gap: $spacing-sm;
width: 100%;
.form-input,
.form-select {
flex: 1;
min-width: 0;
}
.separator {
color: $text-gray;
font-weight: $font-weight-semibold;
user-select: none;
flex-shrink: 0;
}
// Phone number specific
.phone-number {
flex: 1;
width: 100%;
}
// Business registration number specific
.biz-reg-number {
flex: 1;
width: 100%;
}
@media (max-width: $breakpoint-sm) {
gap: $spacing-xs;
.separator {
font-size: $font-size-sm;
}
}
}
// -----------------------------------------------------------------------------
// Input with Button Pattern - Figma Design (node 985:1546)
// Input field with action button at the end (e.g., phone number + "변경" button)
// -----------------------------------------------------------------------------
.input-with-button {
display: flex;
align-items: center;
gap: $spacing-md;
.compound-input,
.auth-input-group {
flex: 1;
}
@media (max-width: $breakpoint-sm) {
flex-direction: column;
gap: $spacing-sm;
.compound-input,
.auth-input-group {
width: 100%;
}
}
}
// -----------------------------------------------------------------------------
// Auth Input Group - Input with timer for verification codes
// -----------------------------------------------------------------------------
.auth-input-group {
display: flex;
align-items: center;
position: relative;
.form-input {
flex: 1;
padding-right: 100px; // Space for timer
}
.auth-timer {
position: absolute;
right: $spacing-md;
top: 50%;
transform: translateY(-50%);
font-size: $font-size-sm;
font-weight: $font-weight-semibold;
color: $accent-orange;
white-space: nowrap;
}
}
// -----------------------------------------------------------------------------
// Form Actions - Button container for form submissions
// Used in: Partnership, API Key registration, User profile forms
// -----------------------------------------------------------------------------
.form-actions {
display: flex;
justify-content: center;
gap: $spacing-md;
flex-wrap: wrap;
margin-top: $spacing-4xl;
padding-top: $spacing-2xl;
border-top: 1px solid $border-gray;
@include respond-to('sm') {
flex-direction: column-reverse;
align-items: stretch;
gap: $spacing-sm;
margin-top: $spacing-3xl;
padding-top: $spacing-xl;
}
// Button base styles
.btn,
.action-btn-primary,
.action-btn-secondary {
min-width: 160px;
padding: $spacing-md $spacing-2xl;
@include respond-to('sm') {
width: 100%;
min-width: auto;
}
}
// Modifier: space-between layout
&--between {
justify-content: space-between;
}
// Modifier: no border top
&--no-border {
border-top: none;
padding-top: 0;
}
// Modifier: compact spacing
&--compact {
margin-top: $spacing-2xl;
padding-top: $spacing-lg;
}
}
// Centered form actions (alias)
.form-actions-center {
@extend .form-actions;
}
+1
View File
@@ -35,6 +35,7 @@
@import 'components/tables';
@import 'components/accordion';
@import 'components/page-title-banner';
@import 'components/alerts';
// 5. Page-specific styles
@import 'pages/index';
@@ -347,11 +347,14 @@
}
}
// Form Actions
.form-actions {
// Form Actions - Account Recovery specific styles
// Scoped to avoid conflict with common .form-actions in _forms.scss
.account-recovery-card .form-actions {
display: flex;
gap: 12px;
margin-top: 32px;
padding-top: 0;
border-top: none;
.cancel-button,
.submit-button {
@@ -504,7 +507,7 @@
margin-top: 12px;
}
.form-actions {
.account-recovery-card .form-actions {
margin-top: 24px;
}
}
@@ -8,7 +8,7 @@
// Container Styles - extends common
.apikey-detail-container {
@extend .apikey-container;
@extend .common-container;
padding: $spacing-2xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
@@ -600,20 +600,4 @@
}
// ====================================
// Form Actions Center
// ====================================
.form-actions-center {
display: flex;
justify-content: center;
gap: $spacing-md;
margin-top: $spacing-2xl;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
.btn {
width: 100%;
}
}
}
// .form-actions-center moved to components/_forms.scss
@@ -8,12 +8,12 @@
// App Management Container - extends common
.app-management-container {
@extend .apikey-container;
@extend .common-container;
}
// App List Title Section - extends common
.app-list-title-section {
@extend .apikey-title-bar;
@extend .common-title-bar;
}
.app-list-title {
@@ -30,7 +30,7 @@
// App List Container - extends common
.app-list-container {
@extend .apikey-section-box;
@extend .common-section-box;
min-height: 400px;
}
@@ -10,7 +10,7 @@
// Title Bar - extends common with flex layout
.register-title-bar {
@extend .apikey-title-bar;
@extend .common-title-bar;
padding: 18px 45px;
display: flex;
align-items: baseline;
@@ -117,7 +117,7 @@
// Form Container (Step 1) - extends common
.register-form-container {
@extend .apikey-section-box;
@extend .common-section-box;
padding: 50px 45px;
@include respond-to('md') {
@@ -137,69 +137,7 @@
}
}
// Form Row - Horizontal Label-Field Layout
.form-row {
display: flex;
align-items: flex-start;
gap: 40px;
margin-bottom: 50px;
&:last-child {
margin-bottom: 0;
}
@include respond-to('md') {
flex-direction: column;
gap: 12px;
margin-bottom: 30px;
}
}
.form-label-wrapper {
display: flex;
align-items: center;
gap: 5px;
min-width: 214px;
width: 214px;
flex-shrink: 0;
@include respond-to('md') {
padding-top: 0;
width: auto;
}
&.label-offset {
margin-top: 15px;
@include respond-to('md') {
margin-top: 0;
}
}
}
.form-label-text {
font-size: 20px;
font-weight: $font-weight-bold;
color: #000000;
@include respond-to('md') {
font-size: 16px;
}
}
.required-badge {
background-color: #3ba4ed;
color: $white;
font-size: 11px;
font-weight: $font-weight-regular;
padding: 3px 4px 4px;
border-radius: 4px;
line-height: 1;
}
.form-field-wrapper {
flex: 1;
}
// Form Row, Label, Field styles moved to components/_forms.scss
// Form Input - Step 1 Style
.register-form-container {
@@ -257,16 +195,7 @@
}
}
.char-counter-wrapper {
display: block;
text-align: right;
margin-top: 8px;
.char-counter {
font-size: 14px;
color: #94A3B8;
}
}
// .char-counter-wrapper moved to components/_forms.scss
// Icon Upload Box - Step 1 Style
.icon-upload-box {
@@ -350,31 +279,7 @@
}
}
.btn-upload {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 60px;
padding: 10px 24px;
background-color: #3ba4ed;
color: $white;
border: none;
border-radius: $border-radius-lg;
font-size: 18px;
font-weight: $font-weight-bold;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background-color: #2b94dd;
}
@include respond-to('md') {
font-size: 16px;
height: 50px;
}
}
// .btn-upload moved to components/_buttons.scss
// IP Input Row - Step 1 Style
.ip-input-row {
@@ -492,7 +397,7 @@
// Container with sidebar layout (Step 2) - extends common
.apikey-register-container {
@extend .apikey-container;
@extend .common-container;
padding: $spacing-2xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
@@ -960,17 +865,7 @@
}
// Custom Checkbox SVG Styles
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
// .visually-hidden moved to base/_utilities.scss
.custom-checkbox {
display: inline-block;
@@ -1521,9 +1416,9 @@ input[type="checkbox"]:checked + .custom-checkbox {
}
}
// Form Actions - extends common
.form-actions {
@extend .apikey-form-actions;
// Form Actions - Page specific overrides
// Base .form-actions is in components/_forms.scss
.register-form-container .form-actions {
justify-content: space-between;
padding: 0 $spacing-lg $spacing-lg;
margin-top: $spacing-2xl;
@@ -1532,36 +1427,12 @@ input[type="checkbox"]:checked + .custom-checkbox {
@media (max-width: $breakpoint-sm) {
padding: 0 $spacing-md $spacing-md;
gap: $spacing-sm;
}
// Button styles for mobile
.btn,
.btn-primary,
.btn-secondary,
.btn-cancel {
@media (max-width: $breakpoint-sm) {
width: 100%;
min-height: 48px;
padding: 14px $spacing-lg;
font-size: $font-size-base;
justify-content: center;
.icon {
font-size: 18px;
}
svg {
width: 18px;
height: 18px;
}
}
}
}
// Result Page (Step 3) - extends common section box
.register-result {
@extend .apikey-section-box;
@extend .common-section-box;
text-align: center;
margin: 0 auto;
height: 1045px;
@@ -1614,50 +1485,7 @@ input[type="checkbox"]:checked + .custom-checkbox {
}
}
.alert {
display: flex;
align-items: flex-start;
gap: $spacing-md;
padding: $spacing-md $spacing-lg;
border-radius: $border-radius-md;
margin-bottom: $spacing-xl;
&.alert-warning {
background: rgba($accent-yellow, 0.1);
border: 1px solid rgba($accent-yellow, 0.3);
color: #B89900;
.alert-icon {
font-size: 20px;
}
.alert-content {
flex: 1;
text-align: left;
strong {
font-weight: $font-weight-semibold;
}
}
}
&.alert-error {
background: rgba($accent-orange, 0.1);
border: 1px solid rgba($accent-orange, 0.3);
color: darken($accent-orange, 10%);
align-items: center;
svg {
flex-shrink: 0;
}
span {
flex: 1;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
}
}
}
// .alert moved to components/_alerts.scss
.key-info-card {
background: $white;
@@ -2055,3 +1883,7 @@ input[type="checkbox"]:checked + .custom-checkbox {
}
}
}
// .compound-input and .section-divider moved to components/_forms.scss
+27 -320
View File
@@ -1,340 +1,47 @@
// -----------------------------------------------------------------------------
// MyPage - API Key List Card Layout
// Reference: apps/mypage/apiKeyList.html
// MyPage Styles
// Used in: mypage profile pages (updateCorporateManager, updatePersonalUser, etc.)
// -----------------------------------------------------------------------------
// Container
.apikey-container {
max-width: $container-max-width;
margin: 0 auto;
padding: $spacing-2xl $spacing-lg;
// -----------------------------------------------------------------------------
// Corporate Transfer Section - Personal to corporate membership transition
// -----------------------------------------------------------------------------
.corporate-transfer-section {
margin-top: $spacing-2xl;
padding-top: $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-lg $spacing-md;
.btn-block {
width: 100%;
}
}
// Header Section
.apikey-header {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: $spacing-2xl;
padding-bottom: $spacing-lg;
border-bottom: 2px solid $border-gray;
// -----------------------------------------------------------------------------
// Withdrawal Section - Account deactivation/withdrawal area
// -----------------------------------------------------------------------------
.withdrawal-section {
margin-top: $spacing-3xl;
padding-top: $spacing-xl;
border-top: 1px solid $border-gray;
text-align: left;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: flex-start;
gap: $spacing-lg;
}
}
.apikey-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;
}
}
.apikey-actions {
.btn-create-app {
.withdrawal-link {
display: inline-flex;
align-items: center;
gap: $spacing-sm;
padding: 12px 24px;
color: $white;
border: none;
border-radius: $border-radius-lg;
font-size: $font-size-base;
font-weight: $font-weight-semibold;
gap: $spacing-xs;
color: $text-gray;
font-size: $font-size-sm;
text-decoration: none;
cursor: pointer;
transition: $transition-base;
box-shadow: $shadow-md;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-lg;
color: $accent-orange;
text-decoration: underline;
}
&:active {
transform: translateY(0);
}
.btn-icon {
font-size: 18px;
}
}
}
// Result Count
.apikey-result-count {
font-size: $font-size-base;
color: $text-gray;
margin-bottom: $spacing-xl;
strong {
color: $primary-blue;
font-weight: $font-weight-semibold;
}
}
// Card Grid - Single column layout (one card per row)
.apikey-card-grid {
display: grid;
grid-template-columns: 1fr;
gap: $spacing-lg;
margin-bottom: $spacing-2xl;
}
// API Key Card - Simplified horizontal layout
.apikey-card {
background: $white;
border: 1px solid $border-gray;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
transition: $transition-base;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
gap: $spacing-lg;
padding: $spacing-lg;
position: relative;
overflow: hidden;
text-decoration: none;
color: inherit;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 4px;
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s ease;
}
&:hover {
box-shadow: $shadow-lg;
transform: translateX(4px);
border-color: rgba($primary-blue, 0.3);
&::before {
transform: scaleY(1);
}
}
&:active {
transform: translateX(2px);
}
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: flex-start;
gap: $spacing-md;
}
}
// App Icon
.apikey-card-icon {
flex-shrink: 0;
width: 80px;
height: 80px;
border-radius: $border-radius-md;
overflow: hidden;
background: $gray-bg;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid $border-gray;
.app-icon-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.app-icon-placeholder {
color: $text-gray;
opacity: 0.5;
display: flex;
align-items: center;
justify-content: center;
}
@media (max-width: $breakpoint-sm) {
width: 60px;
height: 60px;
}
}
// App Content
.apikey-card-content {
flex: 1;
min-width: 0; // Allow text truncation
.apikey-card-title {
font-size: $font-size-lg;
font-weight: $font-weight-semibold;
color: $text-dark;
margin-bottom: $spacing-xs;
}
.apikey-card-description {
font-size: $font-size-sm;
color: $text-gray;
line-height: 1.5;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
// Status Wrapper
.apikey-card-status-wrapper {
flex-shrink: 0;
.apikey-card-status {
display: inline-flex;
align-items: center;
padding: 6px 16px;
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.15);
color: #4FA065;
}
&.status-inactive {
background-color: rgba($accent-orange, 0.15);
color: #D9534F;
}
&.status-pending {
background-color: rgba($accent-yellow, 0.15);
color: #F0AD4E;
}
}
}
// Empty State
.apikey-empty-state {
text-align: center;
padding: $spacing-5xl $spacing-lg;
background: $white;
border-radius: $border-radius-lg;
border: 2px dashed $border-gray;
.empty-icon {
font-size: 80px;
margin-bottom: $spacing-lg;
opacity: 0.6;
display: inline-block;
animation: bounce 2s infinite;
}
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;
margin-bottom: $spacing-xl;
}
.btn-create-app-large {
display: inline-flex;
align-items: center;
gap: $spacing-sm;
padding: 14px 32px;
color: $white;
border: none;
border-radius: $border-radius-lg;
font-size: $font-size-md;
font-weight: $font-weight-semibold;
cursor: pointer;
transition: $transition-base;
box-shadow: $shadow-lg;
&:hover {
transform: translateY(-3px);
box-shadow: $shadow-xl;
}
&:active {
transform: translateY(-1px);
}
.btn-icon {
font-size: 20px;
}
}
}
// Bounce animation for empty state icon
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
// Loading State (if needed)
.apikey-loading {
display: flex;
justify-content: center;
align-items: center;
padding: $spacing-5xl;
.spinner {
width: 48px;
height: 48px;
border: 4px solid $border-gray;
border-top-color: $primary-blue;
border-radius: $border-radius-circle;
animation: spin 1s linear infinite;
}
}
// -----------------------------------------------------------------------------
// API Key Registration Wizard Styles - Moved to _apikey-register.scss
// -----------------------------------------------------------------------------
// All registration-related styles have been moved to _apikey-register.scss
// This file now contains only API Key List styles
// Responsive adjustments for smaller screens
@media (max-width: $breakpoint-sm) {
.apikey-empty-state {
padding: $spacing-3xl $spacing-md;
.empty-icon {
font-size: 60px;
content: '>';
font-size: $font-size-xs;
}
}
}
@@ -15,12 +15,12 @@
}
.org-register-container {
max-width: 1066px;
margin: 0 auto;
padding: 0 $spacing-lg $spacing-4xl;
@extend .common-container;
@include respond-to('sm') {
padding: 0 $spacing-lg $spacing-xl;
padding: $spacing-2xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-lg $spacing-md;
}
}
@@ -187,33 +187,7 @@
}
}
// Form Actions
.form-actions {
display: flex;
gap: $spacing-md;
justify-content: center;
margin-top: $spacing-4xl;
padding-top: $spacing-2xl;
border-top: 1px solid $border-gray;
@include respond-to('sm') {
flex-direction: column-reverse;
gap: $spacing-sm;
margin-top: $spacing-3xl;
padding-top: $spacing-xl;
}
.action-btn-secondary,
.action-btn-primary {
min-width: 160px;
padding: $spacing-md $spacing-2xl;
@include respond-to('sm') {
width: 100%;
min-width: auto;
}
}
}
// .form-actions moved to components/_forms.scss
// File Upload Wrapper Override for Partnership Page
.partnership-card .file-upload-wrapper {
@@ -2,63 +2,63 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<body>
<th:block th:fragment="commonUserInfo">
<div class="org-form-group">
<label class="org-form-label">
<span>이메일 아이디</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:value="${loginId}" name="loginId" class="org-form-input input-readonly"
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">이메일 아이디</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:value="${loginId}" name="loginId" class="form-input input-readonly"
placeholder="이메일" disabled="disabled">
<input type="hidden" th:value="${loginId}" name="loginId">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span class="required">성명</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:value="${userName}" id="userName" name="userName" class="org-form-input"
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text required">성명</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:value="${userName}" id="userName" name="userName" class="form-input"
placeholder="성명">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span class="required">휴대폰 번호</span>
</label>
<div class="org-form-input-wrapper" th:with="parts=${#strings.arraySplit(mobileNumber, '-')}">
<div class="org-compound-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text required">휴대폰 번호</span>
</div>
<div class="form-field-wrapper input-with-button" th:with="parts=${#strings.arraySplit(mobileNumber, '-')}">
<div class="compound-input">
<input type="text"
th:value="${parts != null and parts.length > 0 ? parts[0] : ''}"
name="mobilePrefix"
class="org-form-input input-readonly"
class="form-input input-readonly"
maxlength="3"
disabled="disabled">
<span class="separator">-</span>
<input type="text"
th:value="${parts != null and parts.length > 1 ? parts[1] : ''}"
name="mobileMiddle"
class="org-form-input input-readonly"
class="form-input input-readonly"
maxlength="4"
disabled="disabled">
<span class="separator">-</span>
<input type="text"
th:value="${parts != null and parts.length > 2 ? parts[2] : ''}"
name="mobileLast"
class="org-form-input input-readonly"
class="form-input input-readonly"
maxlength="4"
disabled="disabled">
</div>
<button type="button" class="btn btn-secondary change_mobile_phone" id="changePhoneBtn">변경</button>
<button type="button" class="btn-input-action btn-change change_mobile_phone" id="changePhoneBtn">변경</button>
</div>
</div>
<div class="org-form-group" id="newPhoneNumberContainer" style="display: none;">
<label class="org-form-label">
<span class="required">새 휴대폰 번호</span>
</label>
<div class="org-form-input-wrapper">
<div class="org-compound-input">
<select class="org-form-select phone-number" id="phoneMobile" name="newMobilePrefix">
<div class="form-row" id="newPhoneNumberContainer" style="display: none;">
<div class="form-label-wrapper label-offset">
<span class="form-label-text required">새 휴대폰 번호</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="compound-input">
<select class="form-select phone-number" id="phoneMobile" name="newMobilePrefix">
<option value="">선택</option>
<option value="010">010</option>
<option value="011">011</option>
@@ -68,30 +68,30 @@
<option value="019">019</option>
</select>
<span class="separator">-</span>
<input type="text" id="newMobileMiddle" name="newMobileMiddle" class="org-form-input phone-number"
<input type="text" id="newMobileMiddle" name="newMobileMiddle" class="form-input phone-number"
maxlength="4" pattern="\d*" inputmode="numeric" placeholder="앞자리"/>
<span class="separator">-</span>
<input type="text" id="newMobileLast" name="newMobileLast" class="org-form-input phone-number"
<input type="text" id="newMobileLast" name="newMobileLast" class="form-input phone-number"
maxlength="4" pattern="\d*" inputmode="numeric" placeholder="뒷자리"/>
<input type="hidden" value="" name="mobileNumber" id="mobileNumber">
</div>
<button type="button" class="btn btn-primary btn_auth">
<button type="button" class="btn-input-action btn-auth btn_auth">
인증번호 받기
</button>
</div>
</div>
<div class="org-form-group" id="authNumberContainer" style="display: none;">
<label class="org-form-label">
<span class="required">인증번호 입력</span>
</label>
<div class="org-form-input-wrapper">
<div class="org-auth-input-group">
<div class="form-row" id="authNumberContainer" style="display: none;">
<div class="form-label-wrapper label-offset">
<span class="form-label-text required">인증번호 입력</span>
</div>
<div class="form-field-wrapper input-with-button">
<div class="auth-input-group">
<input type="text" id="authNumber" name="authNumber" maxlength="6" pattern="[0-9]*"
inputmode="numeric" class="org-form-input"
inputmode="numeric" class="form-input"
placeholder="SMS 인증번호 6자리">
<span class="org-timer" id="certify_time">03:00</span>
<span class="auth-timer" id="certify_time">03:00</span>
</div>
<button type="button" class="btn btn-primary btn_verify_auth">
<button type="button" class="btn-input-action btn-auth btn_verify_auth">
인증번호 확인
</button>
</div>
@@ -4,121 +4,123 @@
<body>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/user_register_title.png}" class="title-image">
<img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>내 정보 관리</h1>
</div>
</section>
<section layout:fragment="contentFragment">
<div class="org-register-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
<div class="common-title-bar">
<h2 class="common-title">법인 기본 정보</h2>
</div>
<div class="register-form-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<!-- 법인 기본 정보 -->
<div class="org-section-header org-section-header--agreement">
<h3>법인 기본 정보</h3>
</div>
<div class="org-form-container">
<div class="org-form-group">
<label class="org-form-label">
<span>제휴기관 ID</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" name="organizationId" class="org-form-input input-readonly"
<div class="register-form">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">제휴기관 ID</span>
</div>
<div class="form-field-wrapper">
<input type="text" name="organizationId" class="form-input input-readonly info-value-box"
th:value="${user.portalOrg.orgCode}"
placeholder="XXXXXX-01"
disabled="disabled">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>사업자등록정보</span>
</label>
<div class="org-form-input-wrapper">
<div class="org-compound-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">사업자등록정보</span>
</div>
<div class="form-field-wrapper">
<div class="compound-input">
<input type="text" th:field="*{portalOrg.compRegNo}" id="compRegNo"
class="org-form-input"
class="form-input"
style="display:none;">
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#strings.substring(user.portalOrg.compRegNo,0,3)}"
name="compRegNo1" class="org-form-input" maxlength="3">
name="compRegNo1" class="form-input" maxlength="3">
<span class="separator">-</span>
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#strings.substring(user.portalOrg.compRegNo,3,5)}"
name="compRegNo2" class="org-form-input" maxlength="2">
name="compRegNo2" class="form-input" maxlength="2">
<span class="separator">-</span>
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#strings.substring(user.portalOrg.compRegNo,5,10)}"
name="compRegNo3" class="org-form-input" maxlength="5">
name="compRegNo3" class="form-input" maxlength="5">
</div>
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>법인등록번호</span>
</label>
<div class="org-form-input-wrapper">
<div class="org-compound-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">법인등록번호</span>
</div>
<div class="form-field-wrapper">
<div class="compound-input">
<input type="text" th:field="*{portalOrg.corpRegNo}" id="corpRegNo"
class="org-form-input"
class="form-input"
style="display:none;">
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#strings.substring(user.portalOrg.corpRegNo,0,6)}"
name="corpRegNo1" class="org-form-input" maxlength="6">
name="corpRegNo1" class="form-input" maxlength="6">
<span class="separator">-</span>
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#strings.substring(user.portalOrg.corpRegNo,6,13)}"
name="corpRegNo2" class="org-form-input" maxlength="7">
name="corpRegNo2" class="form-input" maxlength="7">
</div>
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>법인명</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.orgName}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">법인명</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.orgName}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>대표자 성명</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.ceoName}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">대표자 성명</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.ceoName}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>사업장 소재지</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.orgAddr}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">사업장 소재지</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.orgAddr}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>업태</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.orgSectors}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">업태</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.orgSectors}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>업종</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.orgIndustryType}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">업종</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.orgIndustryType}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>회사 전화번호</span>
</label>
<div class="org-form-input-wrapper" th:with="orgPhoneParts=${user.portalOrg.orgPhoneNumber != null ? user.portalOrg.orgPhoneNumber.split('-') : {}}">
<div class="org-compound-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">회사 전화번호</span>
</div>
<div class="form-field-wrapper" th:with="orgPhoneParts=${user.portalOrg.orgPhoneNumber != null ? user.portalOrg.orgPhoneNumber.split('-') : {}}">
<div class="compound-input">
<input type="text" th:field="*{portalOrg.orgPhoneNumber}" id="orgPhoneNumber"
class="org-form-input" style="display:none;">
<select class="org-form-select phone-number" id="orgPhonePrefix">
class="form-input" style="display:none;">
<select class="form-select phone-number" id="orgPhonePrefix">
<option value="">선택</option>
<option value="02" th:selected="${#lists.size(orgPhoneParts) > 0 && orgPhoneParts[0] == '02'}">02</option>
<option value="031" th:selected="${#lists.size(orgPhoneParts) > 0 && orgPhoneParts[0] == '031'}">031</option>
@@ -143,32 +145,32 @@
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#lists.size(orgPhoneParts) > 1 ? orgPhoneParts[1] : ''}"
name="orgPhone1"
class="org-form-input phone-number" maxlength="4">
class="form-input phone-number" maxlength="4">
<span class="separator">-</span>
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#lists.size(orgPhoneParts) > 2 ? orgPhoneParts[2] : ''}"
name="orgPhone2"
class="org-form-input phone-number" maxlength="4">
class="form-input phone-number" maxlength="4">
</div>
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>서비스명</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:field="*{portalOrg.serviceName}" class="org-form-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">서비스명</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:field="*{portalOrg.serviceName}" class="form-input">
</div>
</div>
<div class="org-form-group">
<label class="org-form-label">
<span>고객센터 전화번호</span>
</label>
<div class="org-form-input-wrapper" th:with="scPhoneParts=${user.portalOrg.scPhoneNumber != null ? user.portalOrg.scPhoneNumber.split('-') : {}}">
<div class="org-compound-input">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">고객센터 전화번호</span>
</div>
<div class="form-field-wrapper" th:with="scPhoneParts=${user.portalOrg.scPhoneNumber != null ? user.portalOrg.scPhoneNumber.split('-') : {}}">
<div class="compound-input">
<input type="text" th:field="*{portalOrg.scPhoneNumber}" id="scPhoneNumber"
class="org-form-input" style="display:none;">
<select class="org-form-select phone-number" id="scPhonePrefix">
class="form-input" style="display:none;">
<select class="form-select phone-number" id="scPhonePrefix">
<option value="">선택</option>
<option value="02" th:selected="${#lists.size(scPhoneParts) > 0 && scPhoneParts[0] == '02'}">02</option>
<option value="031" th:selected="${#lists.size(scPhoneParts) > 0 && scPhoneParts[0] == '031'}">031</option>
@@ -193,35 +195,33 @@
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#lists.size(scPhoneParts) > 1 ? scPhoneParts[1] : ''}"
name="scPhone1"
class="org-form-input phone-number" maxlength="4">
class="form-input phone-number" maxlength="4">
<span class="separator">-</span>
<input type="text" pattern="\d*" inputmode="numeric"
th:value="${#lists.size(scPhoneParts) > 2 ? scPhoneParts[2] : ''}"
name="scPhone2"
class="org-form-input phone-number" maxlength="4">
class="form-input phone-number" maxlength="4">
</div>
</div>
</div>
</div>
<!-- 법인 관리자 정보 -->
<div class="org-section-header org-section-header--agreement" style="margin-top: 48px;">
<h3>법인 관리자 정보</h3>
<span class="required-badge">필수 입력</span>
<div class="register-title-bar" style="padding-left: 0px;">
<h2 class="register-title">법인 관리자 정보</h2>
</div>
<div class="org-form-container">
<div class="register-form">
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
<!-- Action Buttons -->
<div class="org-action-buttons">
<button type="button" class="btn btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-primary">수정 적용</button>
</div>
<!-- Action Buttons -->
</form>
</div>
<div class="form-actions">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary">수정 적용</button>
</div>
<!-- Withdrawal Button -->
<div class="withdrawal" style="margin-top: 32px; text-align: center;">
<a style="color: #64748B; text-decoration: underline; cursor: pointer;">회원탈퇴</a>
<div class="withdrawal-section">
<a class="withdrawal-link">회원탈퇴</a>
</div>
</div>
</section>
@@ -4,40 +4,42 @@
<body>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/user_register_title.png}" class="title-image">
<img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>내 정보 관리</h1>
</div>
</section>
<section layout:fragment="contentFragment" class="content">
<section layout:fragment="contentFragment">
<div class="org-register-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}">
<div class="org-section-header">
<h3>기본 정보</h3>
<span class="required-badge">필수 입력</span>
</div>
<div class="org-form-container">
<div class="org-form-group">
<label class="org-form-label">
<span>소속기관</span>
</label>
<div class="org-form-input-wrapper">
<input type="text" th:value="${user.portalOrg.orgName}" name="organization"
class="org-form-input input-readonly" placeholder="소속기관" disabled="disabled">
</div>
</div>
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
<!-- Action Buttons -->
<div class="org-action-buttons">
<button type="button" class="btn btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-primary submit-btn">수정 적용</button>
</div>
</form>
<div class="common-title-bar">
<h2 class="common-title">기본 정보</h2>
</div>
<div class="register-form-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="register-form">
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">소속기관</span>
</div>
<div class="form-field-wrapper">
<input type="text" th:value="${user.portalOrg.orgName}" name="organization"
class="form-input input-readonly info-value-box" placeholder="소속기관" disabled="disabled">
</div>
</div>
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
</form>
</div>
<!-- Action Buttons -->
<div class="form-actions">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary submit-btn">수정 적용</button>
</div>
<!-- Withdrawal Button -->
<div class="withdrawal" style="margin-top: 32px; text-align: center;">
<a style="color: #64748B; text-decoration: underline; cursor: pointer;">회원탈퇴</a>
<div class="withdrawal-section">
<a class="withdrawal-link">회원탈퇴</a>
</div>
</div>
</section>
@@ -145,7 +147,7 @@
}
// 회원탈퇴 버튼 이벤트
const withdrawalBtn = document.querySelector('.withdrawal a');
const withdrawalBtn = document.querySelector('.withdrawal-link');
if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault();
@@ -4,41 +4,42 @@
<body>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/user_register_title.png}" class="title-image">
<img th:src="@{/img/img_title_bg.png}" class="title-image">
<h1>내 정보 관리</h1>
</div>
</section>
<section layout:fragment="contentFragment" class="content">
<section layout:fragment="contentFragment">
<div class="org-register-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
<div class="org-section-header">
<h3>기본 정보</h3>
<span class="required-badge">필수 입력</span>
</div>
<div class="org-form-container">
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
<div class="common-title-bar">
<h2 class="common-title">기본 정보</h2>
</div>
<div class="register-form-container">
<form id="updateForm" method="post" th:action="@{/mypage/update}" th:object="${user}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="register-form">
<th:block th:replace="~{apps/mypage/components/commonUserInfo :: commonUserInfo}"></th:block>
</div>
<!-- Corporate Transfer Button -->
<div class="org-info-notice" style="margin-top: 32px;">
<button type="button" class="btn btn-secondary btn_business" style="width: 100%;">
법인회원 전환
</button>
</div>
<!-- Action Buttons -->
<div class="org-action-buttons">
<button type="button" class="btn btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-primary submit-btn">수정</button>
</div>
</form>
<!-- Corporate Transfer Button -->
<div class="corporate-transfer-section">
<button type="button" class="btn btn-secondary btn_business btn-block">
법인회원 전환
</button>
</div>
</form>
</div>
<!-- Action Buttons -->
<div class="form-actions">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary submit-btn">수정</button>
</div>
<input type="hidden" name="finalMobileNumber"/>
<!-- Withdrawal Button -->
<div class="withdrawal" style="margin-top: 32px; text-align: center;">
<a style="color: #64748B; text-decoration: underline; cursor: pointer;">회원탈퇴</a>
<div class="withdrawal-section">
<a class="withdrawal-link">회원탈퇴</a>
</div>
</div>
</section>
@@ -163,7 +164,7 @@
}
// 회원탈퇴 버튼 이벤트
const withdrawalBtn = document.querySelector('.withdrawal a');
const withdrawalBtn = document.querySelector('.withdrawal-link');
if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault();