feat: Implement initial Vue 3 application structure with SSR, routing, authentication, and core dashboard components.
This commit is contained in:
105
src/components/dashboard/StatsCard.vue
Normal file
105
src/components/dashboard/StatsCard.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
interface Trend {
|
||||
value: number;
|
||||
isPositive: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
value: string | number;
|
||||
icon?: string;
|
||||
trend?: Trend;
|
||||
color?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
color: 'primary'
|
||||
});
|
||||
|
||||
const gradients = {
|
||||
primary: 'from-primary/20 to-primary/5',
|
||||
success: 'from-success/20 to-success/5',
|
||||
warning: 'from-yellow-100 to-yellow-50',
|
||||
danger: 'from-danger/20 to-danger/5',
|
||||
info: 'from-info/20 to-info/5',
|
||||
};
|
||||
|
||||
const iconColors = {
|
||||
primary: 'text-primary',
|
||||
success: 'text-success',
|
||||
warning: 'text-yellow-600',
|
||||
danger: 'text-danger',
|
||||
info: 'text-info',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'stats-card relative overflow-hidden rounded-2xl p-6 bg-gradient-to-br',
|
||||
gradients[color],
|
||||
'border border-white/50 shadow-sm hover:shadow-md transition-all duration-300',
|
||||
'group cursor-pointer'
|
||||
]"
|
||||
>
|
||||
<!-- Background Icon (decorative) -->
|
||||
<div
|
||||
v-if="icon"
|
||||
:class="[
|
||||
'absolute -right-4 -bottom-4 opacity-10 group-hover:opacity-20 transition-opacity',
|
||||
icon,
|
||||
'text-8xl'
|
||||
]"
|
||||
/>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="relative z-10">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-600 mb-1">{{ title }}</p>
|
||||
<p class="text-3xl font-bold text-gray-900">{{ value }}</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="icon"
|
||||
:class="[
|
||||
'w-12 h-12 rounded-xl flex items-center justify-center',
|
||||
'bg-white/80 shadow-sm',
|
||||
iconColors[color]
|
||||
]"
|
||||
>
|
||||
<span :class="[icon, 'w-6 h-6']" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trend Indicator -->
|
||||
<div v-if="trend" class="flex items-center gap-1 text-sm">
|
||||
<span
|
||||
:class="[
|
||||
'flex items-center gap-1 font-medium',
|
||||
trend.isPositive ? 'text-success' : 'text-danger'
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
'w-4 h-4',
|
||||
trend.isPositive ? 'i-heroicons-arrow-trending-up' : 'i-heroicons-arrow-trending-down'
|
||||
]"
|
||||
/>
|
||||
{{ Math.abs(trend.value) }}%
|
||||
</span>
|
||||
<span class="text-gray-500">vs last month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.stats-card {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.stats-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user