Files
portal/app/resources/js/components/dashboard/DashboardBalance.vue
T
Дмитрий 3cedf28f33
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
feat/ui: подсказки вопросик по карте — дашборд, мастер проекта, биллинг
Дашборд: вопросик у KPI Получено/Конверсия/Активные, у воронки и у прогноза хватит на N дней; pp на п.п.
Мастер проекта: подпись лимита заявок в день + вопросик у выбора источника Сайт/Звонок/СМС.
Биллинг: вопросик у Цены за лид 7 ступеней.
Тест FunnelChart 8/8, type-check чистый, затронутые спеки 58/60.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 13:41:27 +03:00

159 lines
4.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* DashboardBalance — карта баланса с runway-bar (segments по дням).
* Класс `.runway-fill` сохранён — Vitest тест считает их количество.
*
* Sprint 4 Phase B/3 — split DashboardView (audit O-refactor-04 закрытие).
*/
import { leadsWord, daysWord } from '../../utils/plural';
export interface Balance {
amount: string;
// B1-2: null = нет активных проектов (прогноз нечего считать) → «нет активных проектов».
runwayDays: number | null;
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-tooltip
text="«Хватит на N дней» — прогноз от дневного заказа ваших проектов. Если активных проектов нет, прогноз не считается."
location="top"
max-width="280"
>
<template #activator="{ props: tip }">
<v-icon
v-bind="tip"
size="14"
class="balance-hint ml-1"
icon="mdi-help-circle-outline"
aria-label="Что значит хватит на N дней"
tabindex="0"
/>
</template>
</v-tooltip>
<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">&nbsp;</span>
</div>
<div class="runway mt-3">
<div
class="runway-bar"
role="img"
:aria-label="
balance.runwayDays === null
? 'Прогноз недоступен нет активных проектов'
: `Хватит на ${balance.runwayDays} ${daysWord(balance.runwayDays)} из ${balance.runwayMax}`
"
>
<span
v-for="i in balance.runwayMax"
:key="i"
class="runway-fill"
:class="{ filled: balance.runwayDays !== null && i <= balance.runwayDays }"
/>
</div>
<div class="runway-foot text-caption">
<span v-if="balance.runwayDays === null"
> {{ balance.runwayLeads }} {{ leadsWord(balance.runwayLeads) }} · нет активных проектов</span
>
<span v-else
> {{ balance.runwayLeads }} {{ leadsWord(balance.runwayLeads) }} · хватит на
<strong>{{ balance.runwayDays }} {{ daysWord(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-hint {
color: #7a8c87;
cursor: help;
}
.balance-hint:hover {
color: #fff;
}
.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>