monacousa-portal/components/dashboard/PaymentCard.vue

348 lines
6.9 KiB
Vue

<template>
<div
v-motion
:initial="{ opacity: 0, scale: 0.95 }"
:enter="{
opacity: 1,
scale: 1,
transition: {
delay: 600,
duration: 600,
type: 'spring',
stiffness: 200
}
}"
class="payment-card"
>
<!-- Card Header -->
<div class="payment-header">
<div class="header-left">
<v-icon color="success" size="20">mdi-credit-card</v-icon>
<h3 class="payment-title">Payment Status</h3>
</div>
<v-chip
color="success"
variant="tonal"
size="small"
>
<v-icon start size="14">mdi-check-circle</v-icon>
Active
</v-chip>
</div>
<!-- Membership Info -->
<div
v-motion
:initial="{ opacity: 0 }"
:enter="{
opacity: 1,
transition: {
delay: 700,
duration: 500
}
}"
class="membership-info"
>
<div class="info-row">
<span class="info-label">Membership Type</span>
<span class="info-value">{{ membershipType }}</span>
</div>
<div class="info-row">
<span class="info-label">Next Payment</span>
<span class="info-value">{{ nextPaymentDate }}</span>
</div>
<div class="info-row">
<span class="info-label">Amount</span>
<span class="info-value amount">${{ membershipAmount }}</span>
</div>
</div>
<!-- Payment Method -->
<div
v-motion
:initial="{ opacity: 0, y: 10 }"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 800,
duration: 500
}
}"
class="payment-method"
>
<div class="method-header">
<span class="method-label">Payment Method</span>
<v-btn
variant="text"
color="error"
size="x-small"
@click="$emit('update-payment')"
>
Update
</v-btn>
</div>
<div class="method-card">
<v-icon color="primary" size="20">mdi-credit-card</v-icon>
<span class="card-number"> 4242</span>
<span class="card-exp">12/25</span>
</div>
</div>
<!-- Recent Payments -->
<div
v-motion
:initial="{ opacity: 0 }"
:enter="{
opacity: 1,
transition: {
delay: 900,
duration: 500
}
}"
class="recent-payments"
>
<h4 class="payments-title">Recent Payments</h4>
<div class="payments-list">
<div
v-for="(payment, index) in paymentHistory"
:key="payment.id"
v-motion
:initial="{ opacity: 0, x: -10 }"
:visibleOnce="{
opacity: 1,
x: 0,
transition: {
delay: 1000 + (index * 50),
duration: 400
}
}"
class="payment-item"
>
<v-icon
size="16"
:color="index === 0 ? 'success' : 'grey'"
>
mdi-check-circle
</v-icon>
<span class="payment-date">{{ payment.date }}</span>
<span class="payment-amount">${{ payment.amount }}</span>
</div>
</div>
</div>
<!-- Action Button -->
<v-btn
color="error"
variant="outlined"
block
class="mt-4"
prepend-icon="mdi-history"
@click="$emit('update-payment')"
>
View Payment History
</v-btn>
</div>
</template>
<script setup lang="ts">
interface Payment {
id: number;
date: string;
amount: string;
}
interface Props {
membershipType: string;
nextPaymentDate: string;
membershipAmount: string;
paymentHistory: Payment[];
}
defineProps<Props>();
defineEmits<{
'update-payment': [];
}>();
</script>
<style scoped lang="scss">
.payment-card {
height: 100%;
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.95),
rgba(255, 255, 255, 0.85)
);
backdrop-filter: blur(30px);
-webkit-backdrop-filter: blur(30px);
border-radius: 1.25rem;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.6);
padding: 1.5rem;
display: flex;
flex-direction: column;
}
.payment-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.header-left {
display: flex;
align-items: center;
gap: 0.75rem;
}
.payment-title {
font-size: 1.125rem;
font-weight: 600;
color: rgb(31, 41, 55);
margin: 0;
}
.membership-info {
background: linear-gradient(135deg,
rgba(34, 197, 94, 0.05),
rgba(34, 197, 94, 0.02)
);
border-radius: 0.75rem;
padding: 1rem;
margin-bottom: 1.25rem;
border: 1px solid rgba(34, 197, 94, 0.1);
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.375rem 0;
&:not(:last-child) {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
}
.info-label {
font-size: 0.8125rem;
color: rgb(107, 114, 128);
font-weight: 500;
}
.info-value {
font-size: 0.875rem;
color: rgb(31, 41, 55);
font-weight: 600;
&.amount {
font-size: 1.125rem;
background: linear-gradient(135deg, #22c55e, #16a34a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
.payment-method {
background: rgba(255, 255, 255, 0.5);
border-radius: 0.75rem;
padding: 1rem;
margin-bottom: 1.25rem;
}
.method-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.method-label {
font-size: 0.8125rem;
color: rgb(107, 114, 128);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.method-card {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.9),
rgba(255, 255, 255, 0.7)
);
border-radius: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.1);
}
.card-number {
flex: 1;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
color: rgb(31, 41, 55);
letter-spacing: 0.05em;
}
.card-exp {
font-size: 0.75rem;
color: rgb(107, 114, 128);
}
.recent-payments {
flex: 1;
margin-bottom: 1rem;
}
.payments-title {
font-size: 0.875rem;
font-weight: 600;
color: rgb(31, 41, 55);
margin: 0 0 0.75rem 0;
}
.payments-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.payment-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
background: rgba(255, 255, 255, 0.3);
border-radius: 0.5rem;
transition: all 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.5);
transform: translateX(2px);
}
}
.payment-date {
flex: 1;
font-size: 0.8125rem;
color: rgb(107, 114, 128);
}
.payment-amount {
font-size: 0.875rem;
font-weight: 600;
color: rgb(31, 41, 55);
}
@media (max-width: 640px) {
.payment-card {
padding: 1rem;
}
}
</style>