56 lines
1.8 KiB
Vue
56 lines
1.8 KiB
Vue
<script setup lang="ts">
|
|
import type { Component } from 'vue';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
|
|
interface StatItem {
|
|
label: string;
|
|
value: string | number;
|
|
color?: string; // tailwind text color class, e.g. 'text-green-600'
|
|
}
|
|
|
|
interface Props {
|
|
title: string;
|
|
icon?: Component;
|
|
mainLabel?: string;
|
|
mainValue: string | number;
|
|
subLabel?: string;
|
|
items: StatItem[];
|
|
cols?: 2 | 3;
|
|
}
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
cols: 3,
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<Card>
|
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle class="text-sm font-medium">{{ title }}</CardTitle>
|
|
<component v-if="icon" :is="icon" class="size-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<p v-if="mainLabel" class="text-xs text-muted-foreground">
|
|
{{ mainLabel }}
|
|
</p>
|
|
<p class="py-2 text-xl font-bold">{{ mainValue }}</p>
|
|
<p v-if="subLabel" class="text-xs text-muted-foreground">
|
|
{{ subLabel }}
|
|
</p>
|
|
</div>
|
|
<div class="border-t pt-2 text-center"
|
|
:class="cols === 2 ? 'grid grid-cols-2 gap-2' : 'grid grid-cols-3 gap-2'">
|
|
<div v-for="item in items" :key="item.label">
|
|
<p class="text-xs text-muted-foreground">{{ item.label }}</p>
|
|
<p class="text-sm font-semibold" :class="item.color ?? ''">
|
|
{{ item.value }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</template>
|