Files
portal/app/resources/js/components/billing/AutopodborServicesPanel.vue
T
Дмитрий 9ff19f3cc3 @
feat(bot): всеобъемлющая инструкция (36 статей) + 31 узкая экскурсия, которая открывает окна

Живой урок владельца 12.07.2026: «дубли чистятся в 2-х местах, а бот выдумал третье»,
«стыдно такого бота показывать», «видео показываем полное, а я спрашиваю про одно —
надо резать», «надо не только кнопку показывать, а и что за ней».

База знаний: 17 обзорных статей → 36 узких по темам. Портал обойдён целиком
(5 агентов): все экраны, окна, подсказки «(?)», тексты ошибок, внутренние правила.
Новое, чего бот не знал: дубли в ДВУХ местах (Поле и Предложения) + окно «источник
уже есть»; правило 18:00; почему приходит меньше лимита; оплата по счёту и акты;
списания; безопасность и 2FA; отчёты; массовые действия; регистрация.

Экскурсии: 15 → 31, каждая на свою тему (3–7 шагов). Раннер GuidedTour научен
шагу `open` — сам открывает окно/вкладку и подсвечивает то, что ВНУТРИ
(окно дублей, форму подбора, диалог пополнения, панель тарифов, карточку сделки).
99 новых якорей data-tour расставлено по всему порталу (4 агента).

Поиск: веса в tsvector (заголовок A / синонимы B / текст C) — иначе на «чистка
дублей» первой всплывала статья «Списания» из-за мимоходного упоминания «поля».

Тесты: Pest бот 39/39, Vitest 1222/1222. Новые сторожа: у каждой статьи есть
экскурсия; экскурсия не короче 3 и не длиннее 7 шагов; каждая цель экскурсии
реально существует в разметке; экскурсии про действия обязаны открывать окно.
Живая проверка в браузере: экскурсия про дубли сама открыла окно объединения
и перешла на второе место чистки.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@
2026-07-12 12:56:22 +03:00

94 lines
3.6 KiB
Vue

<script setup lang="ts">
/**
* «Дополнительные услуги» в Биллинге — тарифы «Конкурентного поля»:
* сбор конкурентов (шаг 1) и сбор источников (шаг 2). Списываются только при успехе.
* Цены — из autopodbor store (system_settings: autopodbor_price_search_rub/_study_rub).
* Панель показывается только если фича включена.
*/
import { computed, onMounted } from 'vue';
import { useAutopodborStore } from '../../stores/autopodborStore';
const store = useAutopodborStore();
const enabled = computed(() => store.enabled);
const searchPrice = computed(() => store.prices.search);
const studyPrice = computed(() => store.prices.study);
onMounted(() => {
void store.loadState();
});
</script>
<template>
<v-card v-if="enabled" variant="flat" border class="mt-4 ap-services" data-tour="billing-services">
<v-card-title class="text-subtitle-1 font-weight-bold">Дополнительные услуги</v-card-title>
<v-card-subtitle class="pb-2">«Конкурентное поле» деньги списываются только при успешном результате</v-card-subtitle>
<v-card-text>
<div class="ap-row">
<div class="ap-row__name">
<div class="font-weight-medium">Сбор конкурентов</div>
<div class="text-caption text-medium-emphasis">Подбор похожих конкурентов по вашим примерам и региону</div>
</div>
<div class="ap-row__when text-caption text-medium-emphasis">при успешном подборе</div>
<div class="ap-row__price num">{{ searchPrice }} </div>
</div>
<v-divider class="my-2" />
<div class="ap-row">
<div class="ap-row__name">
<div class="font-weight-medium">Сбор источников</div>
<div class="text-caption text-medium-emphasis">Все источники одного конкурента (сайты и телефоны) для проектов</div>
</div>
<div class="ap-row__when text-caption text-medium-emphasis">при успешном изучении</div>
<div class="ap-row__price num">{{ studyPrice }} </div>
</div>
</v-card-text>
</v-card>
</template>
<style scoped>
.ap-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.ap-row__name {
min-width: 0;
flex: 1;
}
.ap-row__when {
flex-shrink: 0;
white-space: nowrap;
text-align: right;
min-width: 140px;
}
.ap-row__price {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-feature-settings: 'tnum';
font-weight: 600;
font-size: 16px;
color: #0f6e56;
white-space: nowrap;
}
/* Телефон: три колонки не помещаются — название крошилось по слову.
Раскладываем: название на всю ширину, ниже строкой «когда списываем» + цена. */
@media (max-width: 599.98px) {
.ap-row {
flex-wrap: wrap;
gap: 4px 12px;
}
.ap-row__name {
flex: 1 1 100%;
}
.ap-row__when {
min-width: 0;
text-align: left;
flex: 0 1 auto;
}
.ap-row__price {
margin-left: auto;
}
}
</style>