e280edd431
4 files reformatted (import list expansion, line-length wrapping). Vitest 88/683+3sk green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
126 lines
3.2 KiB
Vue
126 lines
3.2 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* DashboardBalance — карта баланса с runway-bar (segments по дням).
|
||
* Класс `.runway-fill` сохранён — Vitest тест считает их количество.
|
||
*
|
||
* Sprint 4 Phase B/3 — split DashboardView (audit O-refactor-04 закрытие).
|
||
*/
|
||
export interface Balance {
|
||
amount: string;
|
||
runwayDays: number;
|
||
runwayMax: number;
|
||
runwayLeads: number;
|
||
}
|
||
|
||
defineProps<{
|
||
balance: Balance;
|
||
}>();
|
||
</script>
|
||
|
||
<template>
|
||
<v-col cols="12" sm="6" md="3">
|
||
<v-card variant="flat" color="secondary" class="balance-card pa-4">
|
||
<div class="balance-row1">
|
||
<span class="balance-label">Баланс</span>
|
||
<v-chip size="x-small" color="primary" variant="elevated" class="ml-2">LIVE</v-chip>
|
||
</div>
|
||
<div class="balance-amount">
|
||
<span class="num">{{ balance.amount }}</span>
|
||
<span class="ru"> ₽</span>
|
||
</div>
|
||
<div class="runway mt-3">
|
||
<div
|
||
class="runway-bar"
|
||
role="img"
|
||
:aria-label="`Хватит на ${balance.runwayDays} дня из ${balance.runwayMax}`"
|
||
>
|
||
<span
|
||
v-for="i in balance.runwayMax"
|
||
:key="i"
|
||
class="runway-fill"
|
||
:class="{ filled: i <= balance.runwayDays }"
|
||
/>
|
||
</div>
|
||
<div class="runway-foot text-caption">
|
||
<span
|
||
>≈ {{ balance.runwayLeads }} лидов · хватит на
|
||
<strong>{{ balance.runwayDays }} дня</strong></span
|
||
>
|
||
<a href="/billing" class="ml-2 runway-action">пополнить →</a>
|
||
</div>
|
||
</div>
|
||
</v-card>
|
||
</v-col>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.balance-card {
|
||
color: #fff;
|
||
background: #012019 !important;
|
||
height: 100%;
|
||
}
|
||
|
||
.balance-row1 {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.balance-label {
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: #7a8c87;
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
}
|
||
|
||
.balance-amount {
|
||
margin-top: 8px;
|
||
font-size: 32px;
|
||
font-weight: 600;
|
||
line-height: 1.1;
|
||
}
|
||
.balance-amount .num {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
font-feature-settings: 'tnum';
|
||
color: #fff;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
.balance-amount .ru {
|
||
color: #7a8c87;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.runway-bar {
|
||
display: flex;
|
||
gap: 4px;
|
||
}
|
||
|
||
.runway-fill {
|
||
flex: 1;
|
||
height: 6px;
|
||
border-radius: 3px;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
.runway-fill.filled {
|
||
background: #32c8a9;
|
||
}
|
||
|
||
.runway-foot {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 6px;
|
||
color: #7a8c87;
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
}
|
||
.runway-foot strong {
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
.runway-action {
|
||
color: #32c8a9;
|
||
text-decoration: none;
|
||
}
|
||
.runway-action:hover {
|
||
color: #fff;
|
||
}
|
||
</style>
|