fix(реклама): пачка 5 — приёмка глазами: 20 значков рисовались вопросом в кружке
Приёмка обоих рекламных экранов на локальном портале. Экраны работают: живой счётчик охвата правда ходит на сервер, ручка оценки правда только читает (после её вызовов ноль черновиков в базе), заголовок объявления сам появляется при ссылке на сайт, «Запустить» не нажимается при охвате меньше 367, «Принято, показов пока нет» и «часть объявлений отклонена — 2 из 15» на месте. Находка, которой не видел ни один из 1829 зелёных тестов: значки в портале не встроенные, имена mdi-* вручную сопоставлены с набором Lucide, а незнакомое имя молча подменяется на HelpCircle. Клиент видит вопрос в кружке вместо значка. Замер: 43 имени из 162 по всему порталу. На рекламных экранах таких было 20 — включая заголовок телеграм-экрана, ярлыки «Проверяем вручную» и «Запущена в кабинете МТС», поле «Картинка или видео» и ярлык «Черновик» у Яндекса. Шесть из них добавил я в пачках 1–4, четырнадцать ломались раньше. Все 20 починены, проверено глазами после пересборки. Заведён сторож tests/Frontend/znachki-est-v-karte.spec.ts: краснеет на любом имени вне карты и называет файл-виновника. До починки был красным ровно на тех 20 — проверено вырезанием. Оставшиеся 23 сломанных имени на экранах вне рекламы записаны в нём списком известного долга; третья проверка следит, чтобы список не протух. Карта значков открыта наружу (export) ради сторожа — поведение не меняется. Замеры: экраны 241 файл / 1832 теста / 0 падений (было 240/1829), Advertising 435, ClientTg 292, статанализ 0, pint чисто, типы — те же 6 чужих ошибок. На боевой ничего не катал. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,13 +13,17 @@ import {
|
||||
ArrowLeft,
|
||||
ArrowRightLeft,
|
||||
ArrowUp,
|
||||
Ban,
|
||||
Bell,
|
||||
BellOff,
|
||||
Bot,
|
||||
Calendar,
|
||||
CalendarClock,
|
||||
CalendarDays,
|
||||
CalendarRange,
|
||||
Camera,
|
||||
Check,
|
||||
CheckCheck,
|
||||
CheckCircle,
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
@@ -30,6 +34,8 @@ import {
|
||||
ChevronUp,
|
||||
Circle,
|
||||
CircleDot,
|
||||
CirclePause,
|
||||
CirclePlay,
|
||||
CircleStop,
|
||||
Clock,
|
||||
Code,
|
||||
@@ -40,18 +46,22 @@ import {
|
||||
Eye,
|
||||
EyeOff,
|
||||
FilterX,
|
||||
FileCheck,
|
||||
FileSpreadsheet,
|
||||
FileText,
|
||||
FileUp,
|
||||
FlaskConical,
|
||||
Folder,
|
||||
Folders,
|
||||
Globe,
|
||||
HelpCircle,
|
||||
Image as ImageIcon,
|
||||
Info,
|
||||
Key,
|
||||
KeyRound,
|
||||
LayoutDashboard,
|
||||
LifeBuoy,
|
||||
Link2,
|
||||
List,
|
||||
LogOut,
|
||||
Mail,
|
||||
@@ -71,12 +81,16 @@ import {
|
||||
Puzzle,
|
||||
ReceiptText,
|
||||
RefreshCw,
|
||||
Repeat,
|
||||
Repeat2,
|
||||
Rocket,
|
||||
RotateCcw,
|
||||
RotateCw,
|
||||
RussianRuble,
|
||||
Save,
|
||||
Search,
|
||||
Send,
|
||||
SendHorizontal,
|
||||
Settings,
|
||||
Shield,
|
||||
ShieldCheck,
|
||||
@@ -95,6 +109,7 @@ import {
|
||||
UserCog,
|
||||
UserPlus,
|
||||
Users,
|
||||
UserSearch,
|
||||
UserX,
|
||||
Wallet,
|
||||
Webhook,
|
||||
@@ -148,8 +163,13 @@ const liderraForest: ThemeDefinition = {
|
||||
* как semantic identifiers, рендерятся через Lucide stroke-based SVG icons.
|
||||
*
|
||||
* CLAUDE.md §2 «Иконки: Lucide» — бренд-spec compliance.
|
||||
*
|
||||
* Экспортируется наружу РАДИ СТОРОЖА: имя, которого тут нет, молча подменяется
|
||||
* на HelpCircle (см. ниже), и на экране вместо значка появляется вопрос в кружке.
|
||||
* Замер 02.08.2026: так молча ломались 43 имени из 162. Сторож —
|
||||
* tests/Frontend/znachki-est-v-karte.spec.ts.
|
||||
*/
|
||||
const lucideMap: Record<string, Component> = {
|
||||
export const lucideMap: Record<string, Component> = {
|
||||
'mdi-account-arrow-right-outline': UserCheck,
|
||||
'mdi-account-group-outline': Users,
|
||||
'mdi-account-outline': User,
|
||||
@@ -273,6 +293,32 @@ const lucideMap: Record<string, Component> = {
|
||||
'mdi-star': Star,
|
||||
'mdi-star-outline': Star,
|
||||
'mdi-star-half-full': StarHalf,
|
||||
|
||||
// ——— Рекламные экраны (приёмка глазами 02.08.2026) ———
|
||||
// Этих имён в карте не было, и на экране вместо значка стоял вопрос в
|
||||
// кружке: у ярлыков «Проверяем вручную» и «Запущена в кабинете МТС», у поля
|
||||
// «Картинка или видео», у заголовка телеграм-экрана. Ярлык состояния с
|
||||
// вопросом вместо значка читается как «портал сам не знает, что происходит».
|
||||
'mdi-account-search-outline': UserSearch,
|
||||
'mdi-calendar-clock': CalendarClock,
|
||||
'mdi-calendar-range': CalendarRange,
|
||||
'mdi-cancel': Ban,
|
||||
'mdi-check-all': CheckCheck,
|
||||
'mdi-file-check-outline': FileCheck,
|
||||
'mdi-file-outline': FileText,
|
||||
'mdi-file-upload-outline': FileUp,
|
||||
'mdi-help-circle-outline': HelpCircle,
|
||||
'mdi-image-outline': ImageIcon,
|
||||
'mdi-link-variant': Link2,
|
||||
'mdi-pause-circle': CirclePause,
|
||||
'mdi-play-circle': CirclePlay,
|
||||
'mdi-repeat': Repeat,
|
||||
'mdi-repeat-variant': Repeat2,
|
||||
'mdi-rocket-launch-outline': Rocket,
|
||||
'mdi-send-check-outline': SendHorizontal,
|
||||
'mdi-send-circle-outline': Send,
|
||||
'mdi-tag-outline': Tag,
|
||||
'mdi-upload': Upload,
|
||||
};
|
||||
|
||||
const liderraLucideSet: IconSet = {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { lucideMap } from '../../resources/js/plugins/vuetify';
|
||||
|
||||
/**
|
||||
* Сторож значков.
|
||||
*
|
||||
* Значки в портале не встроенные: имена вида `mdi-*` вручную сопоставлены с
|
||||
* компонентами Lucide в resources/js/plugins/vuetify.ts. Незнакомое имя НЕ
|
||||
* роняет сборку и НЕ пишет предупреждения — оно молча подменяется на HelpCircle,
|
||||
* и клиент видит вопрос в кружке вместо значка.
|
||||
*
|
||||
* Именно так это и вскрылось: 02.08.2026 приёмка глазами показала вопрос в
|
||||
* кружке у ярлыков «Проверяем вручную» и «Запущена в кабинете МТС», у поля
|
||||
* «Картинка или видео» и у самого заголовка телеграм-экрана. Все 1829 тестов
|
||||
* экранов при этом были зелёными — проверять было нечем.
|
||||
*
|
||||
* Сторож закрывает класс: любое новое имя обязано быть в карте.
|
||||
*/
|
||||
|
||||
const kornevayaPapka = path.resolve(__dirname, '../../resources/js');
|
||||
|
||||
/** Имена значков, сломанные ДО этой работы, на экранах вне рекламы. */
|
||||
const IZVESTNYY_DOLG = [
|
||||
'mdi-account-check-outline',
|
||||
'mdi-account-clock-outline',
|
||||
'mdi-account-eye-outline',
|
||||
'mdi-account-multiple-outline',
|
||||
'mdi-account-plus',
|
||||
'mdi-alpha-v-box-outline',
|
||||
'mdi-alpha-v-circle',
|
||||
'mdi-alpha-y-circle',
|
||||
'mdi-bullseye-arrow',
|
||||
'mdi-cash-remove',
|
||||
'mdi-chart-line',
|
||||
'mdi-clock-edit-outline',
|
||||
'mdi-close-circle-outline',
|
||||
'mdi-file-document-check-outline',
|
||||
'mdi-filter-variant',
|
||||
'mdi-history',
|
||||
'mdi-map-search-outline',
|
||||
'mdi-message-text-outline',
|
||||
'mdi-open-in-new',
|
||||
'mdi-pause-circle-outline',
|
||||
'mdi-radar',
|
||||
'mdi-send-circle',
|
||||
'mdi-undo',
|
||||
];
|
||||
|
||||
function sobratFayly(papka: string): string[] {
|
||||
const itog: string[] = [];
|
||||
for (const zapis of fs.readdirSync(papka, { withFileTypes: true })) {
|
||||
const polnyy = path.join(papka, zapis.name);
|
||||
if (zapis.isDirectory()) {
|
||||
itog.push(...sobratFayly(polnyy));
|
||||
} else if (zapis.name.endsWith('.vue') || zapis.name.endsWith('.ts')) {
|
||||
itog.push(polnyy);
|
||||
}
|
||||
}
|
||||
return itog;
|
||||
}
|
||||
|
||||
/** Где какое имя встречается — чтобы сторож называл виновника поимённо. */
|
||||
function sobratImena(): Map<string, string[]> {
|
||||
const najdeno = new Map<string, string[]>();
|
||||
for (const fayl of sobratFayly(kornevayaPapka)) {
|
||||
// Сама карта — не потребитель значков, а их список.
|
||||
if (fayl.endsWith(path.join('plugins', 'vuetify.ts'))) continue;
|
||||
|
||||
const tekst = fs.readFileSync(fayl, 'utf-8');
|
||||
for (const sovpadenie of tekst.matchAll(/mdi-[a-z0-9-]+/g)) {
|
||||
const imya = sovpadenie[0];
|
||||
const gde = path.relative(kornevayaPapka, fayl).replace(/\\/g, '/');
|
||||
const spisok = najdeno.get(imya) ?? [];
|
||||
if (!spisok.includes(gde)) spisok.push(gde);
|
||||
najdeno.set(imya, spisok);
|
||||
}
|
||||
}
|
||||
return najdeno;
|
||||
}
|
||||
|
||||
const ispolzuemye = sobratImena();
|
||||
|
||||
describe('значки: каждое имя mdi-* есть в карте Lucide', () => {
|
||||
it('экраны вообще используют значки — иначе сторож охраняет пустоту', () => {
|
||||
expect(ispolzuemye.size).toBeGreaterThan(100);
|
||||
});
|
||||
|
||||
it('ни одного нового имени вне карты (иначе на экране вопрос в кружке)', () => {
|
||||
const bezZnachka: string[] = [];
|
||||
for (const [imya, fajly] of ispolzuemye) {
|
||||
if (imya in lucideMap) continue;
|
||||
if (IZVESTNYY_DOLG.includes(imya)) continue;
|
||||
bezZnachka.push(`${imya} → ${fajly.join(', ')}`);
|
||||
}
|
||||
|
||||
expect(
|
||||
bezZnachka,
|
||||
'Эти имена рисуются вопросом в кружке. Добавьте их в lucideMap ' +
|
||||
'(resources/js/plugins/vuetify.ts) или возьмите имя, которое там уже есть:\n' +
|
||||
bezZnachka.join('\n'),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('список известного долга не протух: каждое имя в нём всё ещё используется и всё ещё вне карты', () => {
|
||||
const lishnie = IZVESTNYY_DOLG.filter((imya) => !ispolzuemye.has(imya) || imya in lucideMap);
|
||||
|
||||
expect(
|
||||
lishnie,
|
||||
'Эти имена больше не нужны в списке долга — уберите их оттуда, ' +
|
||||
'иначе список врёт о размере беды:\n' + lishnie.join('\n'),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -386,11 +386,78 @@ webm/mkv/mpeg/wmv проверяются формат и вес, остальн
|
||||
ошибок вместо прежних 7 (одна исчезла вместе с удалённым тестом), ни одной своей; формат PHP
|
||||
чист. **Глазами не принято — приёмка в пачке 5.**
|
||||
|
||||
### Пачка 5 — сверка и приёмка
|
||||
### Пачка 5 — сверка и приёмка — ✅ ПРИЁМКА ГЛАЗАМИ ПРОЙДЕНА 02.08.2026 (на боевой НЕ выкачено)
|
||||
|
||||
- пройти оба экрана глазами подряд, как клиент;
|
||||
- прогнать оба набора тестов;
|
||||
- обновить снимок боевого и промт следующей смене.
|
||||
Приёмка шла на **локальном** портале (`127.0.0.1:8765`, база `liderra` на своей машине, вход
|
||||
`admin@demo.local` / `password`). Боевой не трогали: демо-входа там нет с 01.07, доступ для
|
||||
приёмки на бою нужно просить у владельца.
|
||||
|
||||
Чтобы было на что смотреть, в **локальную** базу заведены напоказ: 4 телеграм-кампании
|
||||
(`launched` / `moderating` / `rejected` / `needs_review`, одна с нарочно длинным текстом),
|
||||
1 кампания Яндекса `running` с нулём показов и 15 объявлениями, из них 2 `REJECTED`.
|
||||
Убрать: `delete from client_tg_campaigns where tenant_id=1; delete from ad_campaigns where id=11;`
|
||||
|
||||
**Что подтвердилось живьём (не тестом):**
|
||||
|
||||
- живой счётчик охвата **правда ходит на сервер** — два запроса `POST /campaigns/estimate`
|
||||
(при открытии и после правки поля), оба 200;
|
||||
- ручка оценки **правда только читает**: после её вызовов `select count(*) from
|
||||
client_tg_campaigns` = 0, черновик не появился;
|
||||
- «Заголовок объявления» **сам появляется**, как только ссылка становится сайтом, с пояснением
|
||||
про требование МТС (пачка 1);
|
||||
- «Запустить» **не нажимается** при охвате меньше 367 (проверено свойством `disabled`, не глазами);
|
||||
- «Принято, показов пока нет» и приписка «часть объявлений отклонена — 2 из 15» — пачка 2
|
||||
работает, замечание З-6 закрыто;
|
||||
- миграция пачки 1 прогоняется чисто (проверено на локальной базе).
|
||||
|
||||
#### 🔴 Находка 1 — значки: 43 имени из 162 рисовались вопросом в кружке
|
||||
|
||||
Значки в портале **не встроенные**: имена вида `mdi-*` вручную сопоставлены с набором Lucide в
|
||||
`resources/js/plugins/vuetify.ts`. Незнакомое имя не роняет сборку и ничего не пишет в журнал —
|
||||
оно молча подменяется на `HelpCircle`. Клиент видит **вопрос в кружке вместо значка**.
|
||||
|
||||
Замер 02.08: сломаны **43 имени из 162** по всему порталу. На рекламных экранах — 20 из них, и
|
||||
это было видно на самом заметном: у заголовка телеграм-экрана, у ярлыков «Проверяем вручную» и
|
||||
«Запущена в кабинете МТС», у поля «Картинка или видео», у ярлыка «Черновик» на экране Яндекса.
|
||||
Ярлык состояния с вопросом читается как «портал сам не знает, что происходит».
|
||||
|
||||
Из 20 — **6 добавил я** в пачках 1–4, 14 ломались **до** этой работы.
|
||||
|
||||
**Починено 20** (все, что встречаются на рекламных экранах; попутно вылечились и несколько
|
||||
других экранов, где те же имена). **Осталось 23** имени на экранах вне рекламы —
|
||||
это отдельная работа, решать владельцу.
|
||||
|
||||
Заведён сторож `app/tests/Frontend/znachki-est-v-karte.spec.ts`: он краснеет на любом имени вне
|
||||
карты и называет файл-виновника. До починки был красным ровно на тех 20 — проверено. Оставшиеся
|
||||
23 записаны в нём списком известного долга, и третья проверка сторожа следит, чтобы этот список
|
||||
не протух (имя, которое починили или перестали использовать, обязано из него уйти).
|
||||
|
||||
**Почему этого не поймали 1829 зелёных тестов:** проверять было нечем — ни один тест не смотрел,
|
||||
существует ли значок с таким именем.
|
||||
|
||||
#### 🟡 Находка 2 — пузырь чата поддержки перекрывает кнопку мастера
|
||||
|
||||
Внизу мастера, когда страница прокручена до конца, пузырь чата (838–894 × 793–849) **налезает**
|
||||
на кнопку «Дальше» (785–873 × 792–828) — проверено координатами, не на глаз. Перекрытие около
|
||||
35×35 точек по правому краю кнопки.
|
||||
|
||||
Виджет чата общий для всего портала и висит там давно; чинить его молча в этой работе не стали.
|
||||
**Решение за владельцем.**
|
||||
|
||||
🪤 Ловушка при проверке: на снимке **всей страницы** прилипшие элементы рисуются не на своём
|
||||
месте. Сперва мне показалось, что пузырь перекрывает кнопку и на других экранах, — по
|
||||
координатам это не подтвердилось. Такие находки проверять замером, а не снимком.
|
||||
|
||||
#### 🟡 Находка 3 — подсказка «Выберите, кому показывать» не про то
|
||||
|
||||
На шаге 1 при выборе «Свой список номеров», пока список пуст, под ним написано
|
||||
«Выберите, кому показывать, — посчитаем охват.» Но выбор-то уже сделан — не хватает самих
|
||||
номеров. Поломкой не выглядит, но фраза не про ту ситуацию. Мелочь, **решение за владельцем.**
|
||||
|
||||
#### Что осталось за приёмкой глазами
|
||||
|
||||
**Боевой не смотрели.** Всё выше — локальная версия. На боевом экраны выглядят иначе как
|
||||
минимум одним: там нет полосы «Режим песочницы» (`TG_SANDBOX=false` с 02.08).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
|
||||
Две части: **сперва принять глазами, потом выкатить**. Между ними — «да» владельца.
|
||||
|
||||
> ✅ **Часть 1 сделана 02.08.2026 на ЛОКАЛЬНОМ портале.** Разбор находок — в главном документе,
|
||||
> раздел «Пачка 5». Коротко: экраны работают, живой счётчик и ручка оценки проверены живьём;
|
||||
> нашлась беда со значками (43 имени из 162 рисовались вопросом в кружке — 20 починено, заведён
|
||||
> сторож), плюс две мелочи на решение владельца. **Боевой не смотрели** — там нужен доступ
|
||||
> владельца. Осталась Часть 2 — выкат.
|
||||
|
||||
### Часть 1. Приёмка глазами — обязательно ДО выката
|
||||
|
||||
Ни одна из пяти пачек глазами не принята. Тесты зелёные, но **зелёный тест не доказывает,
|
||||
|
||||
Reference in New Issue
Block a user