3cedf28f33
Дашборд: вопросик у KPI Получено/Конверсия/Активные, у воронки и у прогноза хватит на N дней; pp на п.п. Мастер проекта: подпись лимита заявок в день + вопросик у выбора источника Сайт/Звонок/СМС. Биллинг: вопросик у Цены за лид 7 ступеней. Тест FunnelChart 8/8, type-check чистый, затронутые спеки 58/60. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
112 lines
3.6 KiB
Vue
112 lines
3.6 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* TierPricesPanel — свёрнутый по умолчанию блок с ценами 7 ступеней.
|
||
* Подсвечивает текущую ступень чипом «вы здесь». Источник данных —
|
||
* GET /api/billing/wallet → tiers_preview + current_tier.no.
|
||
*
|
||
* Billing v2 Spec A §3.4.8.
|
||
*/
|
||
interface TierPreview {
|
||
tier_no: number;
|
||
leads_in_tier: number | null;
|
||
price_rub: string;
|
||
}
|
||
|
||
const props = defineProps<{
|
||
tiers: TierPreview[];
|
||
currentTierNo: number | null;
|
||
}>();
|
||
|
||
function rangeText(idx: number): string {
|
||
let start = 1;
|
||
for (let i = 0; i < idx; i++) {
|
||
const cap = props.tiers[i].leads_in_tier;
|
||
if (cap !== null) start += cap;
|
||
}
|
||
const cap = props.tiers[idx].leads_in_tier;
|
||
if (cap === null) return `${start}+`;
|
||
return `${start}–${start + cap - 1}`;
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<v-expansion-panels class="mt-4 tier-prices">
|
||
<v-expansion-panel eager>
|
||
<template #title>
|
||
<span>Цены за лид (7 ступеней)</span>
|
||
<v-tooltip
|
||
text="Чем больше заявок в месяц — тем дешевле каждая. Ступень определяется по объёму, «вы здесь» — ваша текущая."
|
||
location="top"
|
||
max-width="280"
|
||
>
|
||
<template #activator="{ props: tip }">
|
||
<v-icon
|
||
v-bind="tip"
|
||
size="14"
|
||
class="tier-hint ml-1"
|
||
icon="mdi-help-circle-outline"
|
||
aria-label="Как считаются цены за лид"
|
||
tabindex="0"
|
||
@click.stop
|
||
/>
|
||
</template>
|
||
</v-tooltip>
|
||
</template>
|
||
<template #text>
|
||
<ul class="tier-list">
|
||
<li
|
||
v-for="(t, i) in tiers"
|
||
:key="t.tier_no"
|
||
data-test="tier-row"
|
||
:data-test-row="`tier-row-${t.tier_no}`"
|
||
class="tier-row"
|
||
:class="{ 'tier-row--current': t.tier_no === currentTierNo }"
|
||
>
|
||
<span class="tier-no num">№{{ t.tier_no }}</span>
|
||
<span class="tier-range">{{ rangeText(i) }} лидов</span>
|
||
<span class="tier-price num">{{ t.price_rub }} ₽</span>
|
||
<v-chip v-if="t.tier_no === currentTierNo" size="x-small" color="primary" variant="elevated"
|
||
>вы здесь</v-chip
|
||
>
|
||
</li>
|
||
</ul>
|
||
</template>
|
||
</v-expansion-panel>
|
||
</v-expansion-panels>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.tier-hint {
|
||
color: #9b9484;
|
||
cursor: help;
|
||
vertical-align: middle;
|
||
}
|
||
.tier-hint:hover {
|
||
color: #0f6e56;
|
||
}
|
||
.tier-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
.tier-row {
|
||
display: grid;
|
||
grid-template-columns: 60px 1fr auto auto;
|
||
gap: 12px;
|
||
align-items: center;
|
||
padding: 8px 12px;
|
||
}
|
||
.tier-row--current {
|
||
background: rgba(15, 110, 86, 0.07);
|
||
border-left: 3px solid #0f6e56;
|
||
}
|
||
.num {
|
||
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||
font-feature-settings: 'tnum';
|
||
}
|
||
.tier-price {
|
||
color: #0f6e56;
|
||
font-weight: 600;
|
||
}
|
||
</style>
|