72 lines
2.2 KiB
Vue
72 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import type { Summary } from './type'
|
|
import { ChevronDown, ChevronUp } from 'lucide-vue-next'
|
|
import { cn } from '~/lib/utils'
|
|
|
|
const props = defineProps<{
|
|
stat?: Summary
|
|
isSkeleton?: boolean
|
|
}>()
|
|
|
|
const timeFrame = computed((): string => {
|
|
if (!props.stat?.timeframe) return 'from unknown timeframe'
|
|
|
|
let word: string = ''
|
|
switch (props.stat.timeframe) {
|
|
case 'daily':
|
|
word = 'from yesterday'
|
|
break
|
|
case 'weekly':
|
|
word = 'from last week'
|
|
break
|
|
case 'monthly':
|
|
word = 'from last month'
|
|
break
|
|
case 'yearly':
|
|
word = 'from last year'
|
|
break
|
|
}
|
|
|
|
return word
|
|
})
|
|
|
|
const isTrending = computed<boolean>(() => (props.stat?.trend ?? 0) > 0)
|
|
</script>
|
|
|
|
<template>
|
|
<Card v-if="props.isSkeleton">
|
|
<CardHeader class="flex flex-row items-center justify-between pb-2">
|
|
<Skeleton class="h-6 w-32 bg-gray-100 text-sm font-medium" />
|
|
<Skeleton class="h-4 w-4 bg-gray-100" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Skeleton class="mb-2 h-6 w-48 bg-gray-100 text-2xl" />
|
|
<Skeleton v-if="props.stat?.trend" class="h-4 w-64 bg-gray-100 text-xs font-medium" />
|
|
</CardContent>
|
|
</Card>
|
|
<Card v-else-if="props.stat && !props.isSkeleton" class="h-42">
|
|
<CardHeader class="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle class="text-sm font-medium"> {{ props.stat.title }} </CardTitle>
|
|
<component :is="props.stat.icon" class="bg-primary h-[40px] w-auto rounded-md p-2 text-white" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div class="text-2xl font-bold">
|
|
{{ props.stat.metric.toLocaleString('id-ID') }}
|
|
</div>
|
|
<p v-if="props.stat.trend !== 0" class="text-muted-foreground flex items-center gap-1 text-xs">
|
|
<component
|
|
:is="isTrending ? ChevronUp : ChevronDown"
|
|
:class="cn('h-4 w-4', { 'text-green-500': isTrending }, { 'text-red-500': !isTrending })"
|
|
/>
|
|
<span :class="cn('font-medium', { 'text-green-500': isTrending }, { 'text-red-500': !isTrending })">
|
|
{{ props.stat.trend.toFixed(1) }}%
|
|
<!-- {{ Math.abs(props.stat.trend).toFixed(1) }}% -->
|
|
</span>
|
|
<span>{{ timeFrame }}</span>
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</template>
|
|
|
|
<style scoped></style>
|