dashbord-top-uslug-b2c-po-r.../brief.md

42 KiB
Raw Permalink Blame History

Дашборд: топ услуг B2C по регионам

Веб-дашборд «Топ услуг B2C по регионам»: топ услуг Казахтелекома по количеству покупок с разбивкой по всем регионам. В шапке слева — логотип Казахтелекома: файл kt-logo.svg в корне проекта (его содержимое — в разделе «Логотип» ниже, сохрани без изменений), подключи как Казахтелеком, справа от него заголовок «Топ услуг B2C по регионам». Экран собирай ТОЛЬКО из компонентов дизайн-системы KT AI (design-system/COMPONENTS.md), своих классов, стилей и цветов не выдумывай. Шапка — .kt-ai-pagehead с логотипом и заголовком. Под ней полоса показателей .kt-ai-kpi-strip из четырёх плашек .kt-ai-kpi (в каждой .value — число, .label — подпись, .hint — уточнение): всего покупок, всего регионов, услуга-лидер (название целиком, без обрезания), регион-лидер; на широком экране четыре в один ряд, на телефоне — в две колонки. Ниже два столбчатых графика в карточках .kt-ai-card — «Топ регионов по покупкам» и «Топ услуг по количеству»: чистый SVG или div-столбцы высотой 190–220px, столбцы 20–28px с радиусом 2px, подписи значений 11px mono muted, цвет столбцов — один токен var(--kt-ai-chart-blue) (второй график — var(--kt-ai-chart-green)), без радуги; подсказка значения при наведении — атрибут data-kt-tip и скрипт ./design-system/kt-ai-chart-tip.js. Ниже таблица топ-услуг по правилам ДС: frameless в .kt-ai-table-wrap, колонки «Услуга», «Регион», «Покупок», «Сумма, ₸», числа вправо с tabular-nums, лёгкие горизонтальные разделители, поиск по услуге и фильтр по региону над таблицей. Цвета, отступы, радиусы, шрифт — только токены var(--kt-ai-…) из ДС в светлой теме (<html lang="ru" data-theme="light">); ни одного своего hex-цвета, кроме файла логотипа. Перед публикацией проверь: в index.html не меньше десятка классов kt-ai-*. Только фронтенд — один статичный index.html с данными внутри, без серверной части (никаких package.json, server.js, node-зависимостей). Источник данных — таблица в разделе «Данные» ниже, используй её целиком и ничего не выдумывай. Все пути к CSS, скриптам, шрифтам и другим ресурсам — строго относительные (./...): сайт публикуется в подпапке, абсолютные пути с корня сломаются. Работай без уточняющих вопросов. Когда дашборд готов — опубликуй сайт.

Основа — готовая страница

Прототип уже свёрстан и проверен, его код — в этом файле между строками <<<BASE_HTML и BASE_HTML>>>. Не перепечатывай и не переписывай его — вырежи в index.html командой:

awk '/^<<<BASE_HTML$/{f=1;next} /^BASE_HTML>>>$/{f=0} f' brief.md > index.html
sed -i 's#\.\./\.\./ds/#./design-system/#g; s#\.\./\.\./kt-logo\.svg#./kt-logo.svg#g' index.html

Сохрани kt-logo.svg из раздела «Логотип». После этого внеси ТОЛЬКО правку владельца: Поменяй тайтл на DRB Казахтелеком B2C.. Больше ничего не меняй и опубликуй сайт.

Логотип

Сохрани этот SVG как файл kt-logo.svg в корне проекта, не меняя содержимое:

<svg width="199" height="138" viewBox="0 0 199 138" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M67.8375 1.6362C104.866 1.6362 134.898 31.6612 134.898 68.6902C134.898 105.726 104.865 135.744 67.8375 135.744C30.8085 135.744 0.776489 105.726 0.776489 68.6902C0.776489 31.6612 30.8095 1.6362 67.8375 1.6362Z" fill="#0096D7"/>
<path d="M67.8375 1.6362C104.866 1.6362 134.898 31.6612 134.898 68.6902C134.898 105.726 104.865 135.744 67.8375 135.744C30.8085 135.744 0.776489 105.726 0.776489 68.6902C0.776489 31.6612 30.8095 1.6362 67.8375 1.6362Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M133.576 86.4422H3.17049C3.17049 87.5492 3.73849 89.9082 4.50049 90.8642H133.956C133.769 89.7572 133.576 87.5492 133.576 86.4422ZM126.378 65.7272H0.531494C0.531494 66.8342 0.725494 69.5022 0.725494 70.1342H126.751C126.564 69.0412 126.378 66.8342 126.378 65.7272ZM126.378 44.4812H4.90349C4.52249 45.2652 3.95449 48.2562 3.57349 48.8882H126.753C126.566 47.7952 126.378 45.5882 126.378 44.4812ZM126.378 23.0622H18.4065C17.8315 23.5442 15.7395 26.2112 14.7895 27.4772H126.753C126.566 26.3632 126.378 24.1622 126.378 23.0622ZM133.576 108.22H13.2435C13.6245 109.636 15.7165 111.844 16.6655 112.635H133.955C133.768 111.521 133.574 109.32 133.574 108.22" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M132.145 1.0032C170.123 2.5342 199.702 34.1062 198.2 71.5152C196.697 108.925 164.68 138.022 126.71 136.505C88.7325 134.974 59.1605 103.402 60.6635 65.9932C62.1665 28.5832 94.1765 -0.5288 132.146 1.0032" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.0854 8.3662C117.62 4.8792 147.458 7.9992 167.058 19.9352L168.367 23.1782C174.349 25.0042 182.467 29.1602 187.73 33.0352C178.879 19.4602 165.606 8.5602 148.595 3.5782C133.331 -1.3398 111.753 0.192204 97.0864 8.3672" fill="#0096D7"/>
<path d="M97.0854 8.3662C117.62 4.8792 147.458 7.9992 167.058 19.9352L168.367 23.1782C174.349 25.0042 182.467 29.1602 187.73 33.0352C178.879 19.4602 165.606 8.5602 148.595 3.5782C133.331 -1.3398 111.753 0.192204 97.0864 8.3672L97.0854 8.3662Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M77.6375 24.1482C114.975 19.2732 154.441 20.7042 191.138 39.5492C196.344 48.8462 197.681 59.0992 198.091 68.6762C193.202 63.5352 184.804 58.2582 179.828 55.1952L179.008 51.8162C146.847 35.0562 109.128 25.9032 74.4655 27.8522C75.4645 26.7732 76.3135 25.3862 77.6365 24.1492" fill="#0096D7"/>
<path d="M77.6375 24.1482C114.975 19.2732 154.441 20.7042 191.138 39.5492C196.344 48.8462 197.681 59.0992 198.091 68.6762C193.202 63.5352 184.804 58.2582 179.828 55.1952L179.008 51.8162C146.847 35.0562 109.128 25.9032 74.4655 27.8522C75.4645 26.7732 76.3135 25.3862 77.6365 24.1492L77.6375 24.1482Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.3285 45.0132C108.893 43.5822 152.363 48.6662 197.768 72.8022C197.754 81.0922 195.181 89.7562 191.183 97.8882C186.143 92.4242 180.297 87.2402 173.769 83.1562L174.373 79.9922C137.812 57.8112 100.711 48.9892 63.8905 49.1042C64.2495 48.0042 65.1275 46.1272 65.3285 45.0132Z" fill="#0096D7"/>
<path d="M65.3285 45.0132C108.893 43.5822 152.363 48.6662 197.768 72.8022C197.754 81.0922 195.181 89.7562 191.183 97.8882C186.143 92.4242 180.297 87.2402 173.769 83.1562L174.373 79.9922C137.812 57.8112 100.711 48.9892 63.8905 49.1042C64.2495 48.0042 65.1275 46.1272 65.3285 45.0132Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M60.9855 65.8862C110.524 66.4322 152.967 77.2172 189.283 101.469C185.63 109.134 179.469 115.734 172.703 121.522C167.943 117.028 162.134 110.902 157.525 106.567L159.704 103.95C131.792 84.5012 99.6535 71.2642 60.8495 69.2222C61.0655 67.7912 60.9345 67.1442 60.9855 65.8862Z" fill="#0096D7"/>
<path d="M60.9855 65.8862C110.524 66.4322 152.967 77.2172 189.283 101.469C185.63 109.134 179.469 115.734 172.703 121.522C167.943 117.028 162.134 110.902 157.525 106.567L159.704 103.95C131.792 84.5012 99.6535 71.2642 60.8495 69.2222C61.0655 67.7912 60.9345 67.1442 60.9855 65.8862Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M63.1494 87.3122C102.155 88.4052 141.096 102.886 168.784 123.909C163.305 129.273 152.801 132.839 144.266 135.046L135.911 124.822L138.068 122.687C113.651 103.684 90.4344 94.4732 64.4284 91.2022C63.8314 90.2172 63.5734 88.6062 63.1494 87.3122Z" fill="#0096D7"/>
<path d="M63.1494 87.3122C102.155 88.4052 141.096 102.886 168.784 123.909C163.305 129.273 152.801 132.839 144.266 135.046L135.911 124.822L138.068 122.687C113.651 103.684 90.4344 94.4732 64.4284 91.2022C63.8314 90.2172 63.5734 88.6062 63.1494 87.3122Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M74.0714 109.119C96.7274 112.405 119.519 120.343 140.586 135.535C133.986 137.182 124.754 136.815 116.996 135.535C108.627 133.608 100.775 130.746 97.9714 129.035C101.624 129.344 105.441 129.653 109.252 129.977C107.009 126.209 88.1494 115.583 76.9554 112.728C76.1934 111.743 74.8194 110.255 74.0714 109.119Z" fill="#0096D7"/>
<path d="M74.0714 109.119C96.7274 112.405 119.519 120.343 140.586 135.535C133.986 137.182 124.754 136.815 116.996 135.535C108.627 133.608 100.775 130.746 97.9714 129.035C101.624 129.344 105.441 129.653 109.252 129.977C107.009 126.209 88.1494 115.583 76.9554 112.728C76.1934 111.743 74.8194 110.255 74.0714 109.119Z" stroke="#0096D7" stroke-width="1.553" stroke-miterlimit="22.926"/>
</svg>

<<<BASE_HTML

<html lang="ru" data-theme="light"> <head>

</head>
Казахтелеком

Топ услуг B2C по регионам

Июль 2026 · покупки и выручка по регионам

Покупки Выручка
—
Покупок
за июль 2026
—
Выручка
по 50 позициям
—
Регионов
в выборке
—
Услуга-лидер
по покупкам
<section class="section charts" aria-label="Графики">
  <div class="kt-ai-card chart-card" data-tone="blue">
    <h3><span class="ttl">Регионы</span> <span class="muted" id="c-regions-hint">по покупкам · нажмите, чтобы отфильтровать</span></h3>
    <div class="bars" id="c-regions"></div>
  </div>
  <div class="kt-ai-card chart-card" data-tone="green">
    <h3><span class="ttl">Услуги</span> <span class="muted" id="c-services-hint">по покупкам · топ-8</span></h3>
    <div class="bars" id="c-services"></div>
  </div>
</section>

<section class="section" aria-label="Реестр позиций">
  <div class="section-h"><h2>Позиции</h2><span class="muted">услуга × регион</span></div>
  <div class="tb">
    <div class="kt-ai-search-field grow" data-compact="true"><input class="kt-ai-search kt-ai-input" id="q" type="search" placeholder="Поиск по услуге…" autocomplete="off" aria-label="Поиск по услуге"></div>
    <div class="kt-ai-select-wrap"><select class="kt-ai-select" id="region" aria-label="Регион"><option value="">Все регионы</option></select></div>
    <span class="count" id="count"></span>
  </div>
  <div class="chips" id="chips"></div>
  <div class="kt-ai-table-wrap">
    <table class="kt-ai-table" id="tbl">
      <thead><tr>
        <th data-sort="service">Услуга</th>
        <th class="col-region" data-sort="region">Регион</th>
        <th class="num" data-sort="qty" data-dir="desc">Покупок</th>
        <th class="num" data-sort="sum">Сумма, ₸</th>
      </tr></thead>
      <tbody id="rows"></tbody>
    </table>
    <div class="kt-ai-empty" id="empty" hidden><div class="title">Ничего не найдено</div><div>Измените запрос или снимите фильтр по региону</div></div>
  </div>
  <div class="tfoot">
    <span id="foot"></span>
    <button class="kt-ai-btn" data-variant="ghost" data-size="sm" id="more" type="button" hidden>Показать ещё</button>
  </div>
</section>
</html> BASE_HTML>>>

Объём работы

Собирай ровно то, что написано в задании, и ничего сверх: без дополнительных разделов, журналов, историй, настроек и «на всякий случай» — их не просили. Свои файлы — только index.html (стили и скрипт внутри него) плюс то, что задание просит создать явно; без сборщиков и фреймворков, без комментариев в коде. Демо-данные короткие и реалистичные. Вопросов не задавай: чего в задании нет — реши сам разумно и коротко. Закончив, опубликуй сайт.

Состояние данных

Данные в приложении демонстрационные, и это видно: источника у чисел нет — вместо источника пиши «Демо-данные». Собери их по одному контракту.

  1. Управление и сообщение пустого состояния не прячутся никогда: скрывать можно только сам блок результата и только его собственный элемент. Подниматься к предку (parentElement, parentNode, closest(...)) ЗАПРЕЩЕНО — на прогоне 13.09 вместе с графиком исчезли фильтры, переключатель и сама надпись «Недостаточно данных».
  2. Фильтр обязан менять ВСЕ представления, которые задевает: таблицу, показатели, график и текст объяснения. Объяви реестр const FILTERS = [{id, label, affects}] со всеми видимыми фильтрами. Не меняет чего-то в демо — скажи честно: demoOnly: true и видимая подпись рядом с контролом <span data-kt-filter-note>в демо не меняет график</span>.
  3. Молчаливая подмена набора запрещена: DATA_SET[x] || DATA_SET["7d"] показывает выбранный период вместе с данными другого. Нет данных под выбранные фильтры — покажи пустое состояние с явной причиной («за выбранный период данных нет»), а не чужой набор. Демо-набор наполни так, чтобы под каждым значением каждого фильтра числа были РАЗНЫЕ, и один срез оставь пустым.
  4. Режим данных виден постоянно: data-kt-data-mode="demo" на <body> и один видимый чип <span class="kt-ai-chip" data-tone="orange">Демо-данные</span> в шапке, на любой ширине; своего класса под него не заводи. Режим live и чип «Данные из системы» — только если в задании названы система-источник и точка доступа к ней.
  5. Объявленный запрет обязан быть запретом, а не надписью. Если строка помечена как непроходная (конфликт данных, пустой обязательный срок, незаполненное поле), действие по ней НЕ ВЫПОЛНЯЕТСЯ: ни поштучно, ни массовой кнопкой «принять всё». Кнопку в такой строке ставь disabled, а рядом одной фразой скажи, ЧТО именно мешает и что сделать («срок не заполнен — укажите дату»). Массовое действие пропускает непроходные строки и честно пишет, сколько пропустило.