126 lines
2.6 KiB
Vue
126 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import UiIcon from '@/components/UiIcon.vue';
|
|
import type { HomeDashboardKpiDto } from '@mento/shared';
|
|
|
|
defineProps<{
|
|
kpis: HomeDashboardKpiDto[];
|
|
currency: string;
|
|
formatValue: (key: string, value: number) => string;
|
|
formatChange: (value: number | null) => string;
|
|
}>();
|
|
|
|
const icons: Record<string, 'calendar' | 'users' | 'briefcase' | 'check'> = {
|
|
sales: 'calendar',
|
|
customers: 'users',
|
|
'open-deals': 'briefcase',
|
|
followups: 'check',
|
|
};
|
|
|
|
const iconTones: Record<string, string> = {
|
|
sales: '#14b8a6',
|
|
customers: '#38bdf8',
|
|
'open-deals': '#fb923c',
|
|
followups: '#a78bfa',
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="kpi-row">
|
|
<article v-for="kpi in kpis" :key="kpi.key" class="kpi-card">
|
|
<div class="kpi-icon" :style="{ color: iconTones[kpi.key] ?? '#14b8a6' }">
|
|
<UiIcon :name="icons[kpi.key] ?? 'chart'" :size="20" />
|
|
</div>
|
|
<div class="kpi-text">
|
|
<p class="kpi-label">{{ kpi.label }}</p>
|
|
<strong class="kpi-value">{{ formatValue(kpi.key, kpi.value) }}</strong>
|
|
<span
|
|
v-if="kpi.changePercent != null"
|
|
class="kpi-change"
|
|
:class="{ down: kpi.changePercent < 0 }"
|
|
>
|
|
<span class="arrow">{{ kpi.changePercent >= 0 ? '↑' : '↓' }}</span>
|
|
{{ formatChange(kpi.changePercent) }} {{ kpi.changeLabel }}
|
|
</span>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.kpi-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
gap: 0.9rem;
|
|
}
|
|
|
|
.kpi-card {
|
|
display: flex;
|
|
gap: 0.85rem;
|
|
align-items: flex-start;
|
|
background: #fff;
|
|
border-radius: 22px;
|
|
padding: 1.1rem 1.15rem;
|
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
|
|
border: 1px solid #eef2f7;
|
|
}
|
|
|
|
.kpi-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 16px;
|
|
background: #f8fafc;
|
|
display: grid;
|
|
place-items: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.kpi-label {
|
|
margin: 0;
|
|
color: #94a3b8;
|
|
font-size: 0.84rem;
|
|
}
|
|
|
|
.kpi-value {
|
|
display: block;
|
|
margin-top: 0.25rem;
|
|
font-size: 1.18rem;
|
|
color: #0f172a;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
.kpi-change {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.2rem;
|
|
margin-top: 0.45rem;
|
|
padding: 0.18rem 0.5rem;
|
|
border-radius: 999px;
|
|
background: #dcfce7;
|
|
color: #16a34a;
|
|
font-size: 0.72rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.arrow {
|
|
font-size: 0.82rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
.kpi-change.down {
|
|
background: #fee2e2;
|
|
color: #dc2626;
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.kpi-row {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
}
|
|
|
|
@media (max-width: 700px) {
|
|
.kpi-row {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|