Files
portal/app/resources/js/plugins/vuetify.ts
T
Дмитрий b264e7e983 fix,реклама: срок показа виден клиенту, подпись перестала врать, значки добиты до нуля
Три находки приёмки глазами, все по решению владельца 02.08.

1. Срок показа. Вопрос владельца: «цена на 40% больше, а в течение какого срока
показываться будет?». На экране этого не было нигде. Портал сроком не управляет
вовсе: ни в кампании, ни в задании роботу нет ни одной даты — их ставит кабинет
МТС своими умолчаниями. В сводку шага «Проверка и отправка» добавлена строка
«Срок показа: 7 дней, каждый человек увидит объявление один раз». Заодно снимает
путаницу с лимитом: лимит — потолок-предохранитель, а не длительность.

2. Подпись пустого счётчика охвата врала. Одна фраза стояла на два разных
положения: «Выберите, кому показывать». Когда клиент УЖЕ выбрал «Свой список
номеров», но не вставил их, портал звал сделать выбор, который сделан, и молчал
о том, чего на самом деле не хватает. Теперь в этом положении — «Вставьте
номера».

3. Значки: долг закрыт полностью. Оставались 23 сломанных имени вне рекламы —
личный кабинет, отдел продаж, проекты, счета, админка. Все заведены в карту
Lucide, список «известного долга» в стороже опустошён. Значит сторож теперь
охраняет без единой поблажки.

Букв в Lucide нет, поэтому имена вида mdi-alpha-* подобраны по смыслу канала:
Яндекс глобусом, ВК значком сети, Телеграм самолётиком. Точной буквы не будет,
искать её бесполезно — это записано в коде рядом.

Приёмка вырезанием, а не одним зелёным:
- опустошил список поблажек в стороже значков — красный ровно на 23 имени,
  с адресами файлов; после правки карты зелёный;
- тест срока показа до правки красный по верной причине: строки нет;
- тест подписи до правки красный: видел старую фразу.

Замеры: экраны 243 файла 1846 тестов 0 падений. Проверка типов 6 ошибок, все в
чужих файлах, столько же было до работы.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 22:42:21 +03:00

429 lines
14 KiB
TypeScript

// @ts-expect-error vuetify/styles — CSS-импорт без d.ts
import 'vuetify/styles';
import { h, type Component } from 'vue';
import { createVuetify } from 'vuetify';
import { ru } from 'vuetify/locale';
import type { ThemeDefinition, IconSet, IconProps } from 'vuetify';
import {
Activity,
AlertCircle,
AlertTriangle,
Archive,
ArrowDown,
ArrowLeft,
ArrowRightLeft,
ArrowUp,
Ban,
BanknoteX,
Bell,
BellOff,
Bot,
Calendar,
CalendarClock,
CalendarDays,
CalendarRange,
Camera,
Check,
CheckCheck,
CheckCircle,
ChevronDown,
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
ChevronsUpDown,
ChevronUp,
Circle,
CircleDot,
CirclePause,
CirclePlay,
CircleStop,
CircleX,
Clock,
Code,
Columns3,
Copy,
CreditCard,
Crosshair,
Download,
ExternalLink,
Eye,
EyeOff,
FilterX,
FileCheck,
FileSpreadsheet,
FileText,
FileUp,
FlaskConical,
Folder,
Folders,
Globe,
HelpCircle,
History,
Hourglass,
Image as ImageIcon,
Info,
Key,
KeyRound,
LayoutDashboard,
LifeBuoy,
LineChart,
Link2,
List,
ListFilter,
LogOut,
Mail,
MapPinned,
Megaphone,
Menu,
MessageSquare,
MessageSquareText,
Minus,
MoreVertical,
Paperclip,
Pause,
Pencil,
Phone,
Play,
Plus,
PlusCircle,
Puzzle,
Radar,
ReceiptText,
RefreshCw,
Repeat,
Repeat2,
Rocket,
RotateCcw,
RotateCw,
RussianRuble,
Save,
Search,
Send,
SendHorizontal,
Settings,
Share2,
Shield,
ShieldCheck,
ShieldOff,
Square,
SquareCheck,
SquareMinus,
Star,
StarHalf,
Tag,
Target,
Trash2,
Undo2,
Upload,
User,
UserCheck,
UserCog,
UserPlus,
UserRoundSearch,
Users,
UserSearch,
UserX,
Wallet,
Webhook,
X,
XCircle,
} from 'lucide-vue-next';
/**
* Палитра Forest extended (Iteration 1 — Quiet Luxury redesign).
* Spec: docs/superpowers/specs/2026-05-12-portal-redesign-quiet-luxury-design.md §3
* CSS-токены: app/resources/css/tokens.css (single source of truth)
*
* 14 OKLCH-статусов воронки маппятся на slugs из db/schema.sql:2076 (lead_statuses)
* через `useStatusPill` composable, НЕ через Vuetify theme.
*/
const liderraForest: ThemeDefinition = {
dark: false,
colors: {
background: '#F6F3EC',
surface: '#FFFFFF',
primary: '#0F6E56',
'on-primary': '#FFFFFF',
secondary: '#012019',
'on-secondary': '#F6F3EC',
success: '#2E8B57',
warning: '#D9A441',
// Текстовые двойники success/warning. Сами брендовые цвета не трогаем —
// они остаются для заливок, иконок и рамок, где 4.5:1 не требуется. Но
// ТЕКСТОМ они нечитаемы: success даёт 4.25:1 на белой карточке и 3.83:1
// на ивори #F6F3EC, warning — 2.25/2.03 при норме WCAG 2.1 AA 4.5:1.
// Затемнённые двойники: 6.10/5.51 и 5.37/4.85 — тон сохранён.
// Использовать как text-success-strong / text-warning-strong.
'success-strong': '#256F46',
'warning-strong': '#8A6410',
error: '#B83A3A',
info: '#3F7C95',
// Расширения — для data viz и semantic uses
'liderra-plum': '#7A5BA3',
'liderra-salmon': '#CC6E50',
'liderra-teal-deep': '#0A5A47',
'liderra-muted': '#6B6356',
},
};
/**
* Liderra Lucide IconSet (CTO-19 closure, v1.83).
* Spec: docs/superpowers/specs/2026-05-13-cto-19-lucide-icon-migration-design.md
*
* Maps 80 mdi-* semantic-ID strings (used across 52 Vue/TS files in resources/js/)
* to Lucide Vue components for rendering. Views НЕ touched — mdi-* строки остаются
* как 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.
*/
export const lucideMap: Record<string, Component> = {
'mdi-account-arrow-right-outline': UserCheck,
'mdi-account-group-outline': Users,
'mdi-account-outline': User,
'mdi-account-plus-outline': UserPlus,
'mdi-account-switch': UserCog,
'mdi-alert-circle': AlertCircle,
'mdi-alert-circle-outline': AlertCircle,
'mdi-alert-outline': AlertTriangle,
'mdi-api': Webhook,
'mdi-archive': Archive,
'mdi-arrow-down': ArrowDown,
'mdi-arrow-left': ArrowLeft,
'mdi-arrow-up': ArrowUp,
'mdi-autorenew': RotateCw,
'mdi-bell-off-outline': BellOff,
'mdi-bell-outline': Bell,
'mdi-bullhorn-outline': Megaphone,
'mdi-camera': Camera,
'mdi-cash-plus': PlusCircle,
'mdi-chart-box-outline': LayoutDashboard,
'mdi-check': Check,
'mdi-check-circle': CheckCircle,
'mdi-check-circle-outline': CheckCircle,
'mdi-chevron-right': ChevronRight,
'mdi-clock-check-outline': Clock,
'mdi-clock-outline': Clock,
'mdi-close': X,
'mdi-close-circle': XCircle,
'mdi-cog-outline': Settings,
'mdi-comment-outline': MessageSquare,
'mdi-content-copy': Copy,
'mdi-content-save-outline': Save,
'mdi-credit-card-outline': CreditCard,
'mdi-currency-rub': RussianRuble,
'mdi-delete': Trash2,
'mdi-delete-outline': Trash2,
'mdi-dots-vertical': MoreVertical,
'mdi-download': Download,
'mdi-email-outline': Mail,
'mdi-eye': Eye,
'mdi-eye-off': EyeOff,
'mdi-eye-outline': Eye,
'mdi-file-pdf-box': FileText,
'mdi-filter-off': FilterX,
'mdi-folder-multiple-outline': Folders,
'mdi-folder-outline': Folder,
'mdi-format-list-bulleted': List,
'mdi-key': Key,
'mdi-lock-reset': KeyRound,
'mdi-logout': LogOut,
'mdi-magnify': Search,
'mdi-message-text': MessageSquareText,
'mdi-pause': Pause,
'mdi-pencil': Pencil,
'mdi-pencil-outline': Pencil,
'mdi-phone': Phone,
'mdi-play': Play,
'mdi-plus': Plus,
'mdi-plus-circle-outline': PlusCircle,
'mdi-progress-clock': Clock,
'mdi-pulse': Activity,
'mdi-puzzle-outline': Puzzle,
'mdi-receipt-text-check-outline': ReceiptText,
'mdi-refresh': RefreshCw,
'mdi-restore': RotateCcw,
'mdi-shield-account-outline': ShieldCheck,
'mdi-shield-key': Shield,
'mdi-shield-lock-outline': ShieldCheck,
'mdi-shield-off': ShieldOff,
'mdi-stop-circle-outline': CircleStop,
'mdi-swap-horizontal': ArrowRightLeft,
'mdi-tag-arrow-right': Tag,
'mdi-test-tube': FlaskConical,
'mdi-trash-can-outline': Trash2,
'mdi-view-column-outline': Columns3,
'mdi-view-dashboard-outline': LayoutDashboard,
'mdi-wallet-outline': Wallet,
'mdi-web': Globe,
'mdi-xml': Code,
// UI-аудит 21.06.2026 — иконки, ранее падавшие в HelpCircle-fallback («?»):
// bulk-бар проектов, импорт, экспорт отчётов/сделок, помощь, действия админки.
'mdi-account-cancel': UserX,
'mdi-cash-edit': Pencil,
'mdi-cash-refund': RotateCcw,
'mdi-database-import-outline': Upload,
'mdi-delete-forever': Trash2,
'mdi-earth': Globe,
'mdi-file-delimited-outline': FileText,
'mdi-file-document-outline': FileText,
'mdi-file-excel-outline': FileSpreadsheet,
'mdi-format-list-checks': List,
'mdi-information-outline': Info,
'mdi-lifebuoy': LifeBuoy,
'mdi-target': Target,
// Раздел «Рекламные возможности» (витрина будущих каналов).
'mdi-robot-outline': Bot,
'mdi-send-outline': Send,
// Vuetify-internal default mdi-* aliases (CTO-19 closure extension)
'mdi-chevron-down': ChevronDown,
'mdi-chevron-left': ChevronLeft,
'mdi-menu-down': ChevronDown,
'mdi-menu-right': ChevronRight,
'mdi-menu-up': ChevronUp,
'mdi-menu': Menu,
'mdi-page-first': ChevronsLeft,
'mdi-page-last': ChevronsRight,
'mdi-checkbox-marked': SquareCheck,
'mdi-checkbox-blank-outline': Square,
'mdi-minus-box': SquareMinus,
'mdi-radiobox-marked': CircleDot,
'mdi-radiobox-blank': Circle,
'mdi-circle': Circle,
'mdi-information': Info,
'mdi-minus': Minus,
'mdi-calendar': Calendar,
'mdi-calendar-month': CalendarDays,
'mdi-paperclip': Paperclip,
'mdi-unfold-more-horizontal': ChevronsUpDown,
'mdi-window-close': X,
'mdi-cached': RefreshCw,
'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,
// ——— Остальные экраны портала: закрытие долга 02.08.2026 ———
// Те же 43 сломанных имени, но вне рекламы: личный кабинет, отдел продаж,
// проекты, счета, админка. Раньше держались списком «известного долга» в
// стороже — список опустошён, значит на этих экранах вопросов в кружке
// больше нет.
//
// 🪤 Букв в Lucide нет: имена вида `mdi-alpha-*` подобраны ПО СМЫСЛУ канала —
// Яндекс глобусом, ВК значком сети, Телеграм самолётиком. Точной буквы «В»
// или «Я» не будет, и искать её бесполезно.
'mdi-account-check-outline': UserCheck,
'mdi-account-clock-outline': Hourglass,
'mdi-account-eye-outline': UserRoundSearch,
'mdi-account-multiple-outline': Users,
'mdi-account-plus': UserPlus,
'mdi-alpha-v-box-outline': Share2,
'mdi-alpha-v-circle': Share2,
'mdi-alpha-y-circle': Globe,
'mdi-bullseye-arrow': Crosshair,
'mdi-cash-remove': BanknoteX,
'mdi-chart-line': LineChart,
'mdi-clock-edit-outline': CalendarClock,
'mdi-close-circle-outline': CircleX,
'mdi-file-document-check-outline': FileCheck,
'mdi-filter-variant': ListFilter,
'mdi-history': History,
'mdi-map-search-outline': MapPinned,
'mdi-message-text-outline': MessageSquareText,
'mdi-open-in-new': ExternalLink,
'mdi-pause-circle-outline': CirclePause,
'mdi-radar': Radar,
'mdi-send-circle': Send,
'mdi-undo': Undo2,
};
const liderraLucideSet: IconSet = {
component: (props: IconProps) => {
const Icon = lucideMap[String(props.icon)] || HelpCircle;
return h(Icon, { size: 20, strokeWidth: 1.75 });
},
};
export const vuetify = createVuetify({
// Русская локаль Vuetify — иначе дефолтные строки таблиц/пагинации на
// английском («No data available», «Items per page», «0-0 of 0») на всех экранах.
locale: {
locale: 'ru',
fallback: 'en',
messages: { ru },
},
theme: {
defaultTheme: 'liderraForest',
themes: { liderraForest },
},
icons: {
defaultSet: 'liderra',
sets: { liderra: liderraLucideSet },
},
defaults: {
VBtn: {
variant: 'flat',
rounded: 'lg',
},
VCard: {
rounded: 'lg',
variant: 'flat',
border: true,
},
VTextField: {
variant: 'outlined',
density: 'comfortable',
color: 'primary',
},
VTextarea: {
variant: 'outlined',
density: 'comfortable',
color: 'primary',
},
VSelect: {
variant: 'outlined',
density: 'comfortable',
},
VChip: {
rounded: 'pill',
size: 'small',
},
VDataTable: {
density: 'comfortable',
},
VDialog: {
scrim: 'rgba(1, 32, 25, 0.32)',
},
},
});