AAYMA
RU
Войти

ИИ-консультант для ekt.kz

Чат-консультант интернет-магазина ekt.kz: нечёткий поиск товаров, карточки с ценой и остатками по складам, подбор аналогов, условия оплаты/доставки…

Доведённый продукт
Команда «SKMA-» 21💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: SKMA-
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Доведённый продукт

Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина

Суть

Чат-консультант интернет-магазина ekt.kz: нечёткий поиск товаров, карточки с ценой и остатками по складам, подбор аналогов, условия оплаты/доставки, корзина с подтверждением и загрузка спецификаций CSV/XLSX/DOCX.

Как устроено

LangGraph-граф: guard (код, а не модель) обрабатывает «да/нет» на ожидающее предложение, остальное идёт в agent ⇄ ToolNode с инструментами search_products, get_product, find_analogs, get_purchase_terms, propose_add_to_cart (LangChain ChatOpenAI, около 6 раундов). Поиск через RapidFuzz с синонимами по локальному каталогу, карточки из API ekt.kz с кэшем в SQLite, корзины и pending actions в PostgreSQL. Без ключа работает режим правил по ключевым словам. Если в вопросе есть казахские буквы, готовый ответ переписывает отдельная модель, числовые фрагменты при этом сверяются.

Ключевые решения

  • LangGraph
  • LangChain tools
  • ToolNode
  • InMemorySaver checkpointer
  • RapidFuzz fuzzy search
  • spec file parsing
  • Kazakh answer rewriting
  • rule-based fallback

Особенности

Гибрид для казахского языка: ответ переписывает Sherkala-8B, при изменении чисел возвращается исходный ответ. Загрузка спецификации с сопоставлением строк каталогу и статусами наличия. Guard-узел в коде не даёт LLM менять корзину без явного «да».

Стек

Python · FastAPI · PostgreSQL 17 · SQLAlchemy AsyncIO · asyncpg · SQLite · HTTPX · Next.js 16 · React 19 · Tailwind CSS 4 · Docker Compose · Make · pytest

Модели

OpenAI gpt-5.4-mini · qwen/qwen3-next-80b-a3b-instruct (NVIDIA) · inception42/Llama-3.1-Sherkala-8B-Chat (vLLM)


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-f80b8ce9-skma.

README

README.md

ИИ-консультант для ekt.kz

Прототип для кейса HackAlem AI: чат-консультант интернет-магазина электротехники ТОО «Электрокомплект». Помогает покупателю найти товар по артикулу или описанию, узнать цену и наличие, подобрать альтернативу и подготовить корзину.

Что реализовано

  • Поиск по артикулу, коду производителя и названию, включая нечёткие совпадения и отдельные синонимы: например, «автомат» → «АВ».
  • Карточки товаров: цена в тенге, общий остаток и остатки по складам, характеристики, кратность, ссылка на товар.
  • Подбор аналогов в наличии из той же серии или категории с объяснением совпавших характеристик и параметров названия.
  • Ответы об оплате, доставке и минимальной партии из локального файла условий.
  • Предложение добавить товар в корзину, подтверждение или отказ. Корзина меняется после кнопки «Да, добавить» либо короткого подтверждения в чате при наличии ожидающего предложения.
  • Страница корзины /cart/[id] с количеством, стоимостью позиций и итогом.
  • Адаптивный чат с карточками товаров, состояниями загрузки и повтором запроса при ошибке; на небольшом экране чат занимает весь экран. При недоступности карточек каталог возвращает 503 вместо пустой выдачи, а чат показывает «Каталог временно недоступен» с кнопкой повтора. На мобильном экране открытый чат блокирует прокрутку фоновой страницы.
  • Два режима ассистента: LLM с вызовом инструментов и резервный режим правил. Для вопросов с характерными буквами казахского языка можно включить отдельную модель, которая переписывает готовый ответ на казахский.
  • Загрузка спецификации .csv, .xlsx или .docx кнопкой 📎 (до 2 МБ, первые 50 товарных строк): по каждой строке отображаются найденный товар, остаток и статус «В наличии», «Частично», «Нет в наличии» или «Не найдено». Для отсутствующего товара сервис пытается подобрать аналог; число таких проверок ограничено десятью. Содержание последней загруженной спецификации доступно LLM в следующих сообщениях этой сессии, поэтому можно попросить «добавь из файла автомат на 250А».

Как работает решение

  1. Покупатель открывает чат и вводит артикул или описание товара.
  2. Backend ищет кандидатов в локальном списке каталога, получает подробные карточки через API ekt.kz или из кэша.
  3. Ассистент формирует ответ, а интерфейс показывает структурированные карточки. Для отсутствующего товара предусмотрен подбор аналогов.
  4. По просьбе «добавь 2 шт» создаётся ожидающее предложение. Само предложение ещё не добавляет позицию в корзину.
  5. После подтверждения backend повторно запрашивает карточку, ограничивает количество доступным остатком с учётом уже добавленного и сохраняет корзину. При сбое API возможен возврат к кэшированным данным — см. ограничения ниже.
  6. Покупатель может загрузить спецификацию: backend разбирает файл, сопоставляет строки с каталогом и показывает результат в чате. Загрузка сама по себе не добавляет товары в корзину.
  7. Покупатель переходит на страницу корзины. Кнопка оформления открывает ekt.kz; автоматического переноса позиций на сайт партнёра пока нет.

LLM-режим построен на LangGraph (agent_graph.py):

START → guard ─┬─ confirm ─────────▶ END   короткое «да» на открытое предложение → корзина
               ├─ reject ──────────▶ END   «нет» → предложение отменено
               └─ agent ⇄ tools ───▶ END   ChatOpenAI + инструменты каталога
  • guard — код, а не модель: корзину меняет только явное «да» на последнее предложение.
  • agent — LangChain ChatOpenAI с инструментами @tool: search_products, get_product, find_analogs, get_purchase_terms, propose_add_to_cart. Инструменты выполняет ToolNode, лимит — около шести раундов «модель ⇄ инструменты».
  • История диалога — в чекпойнтере LangGraph (поток = session_id). В модель уходят последние 30 сообщений.

При пустом ключе LLM или ошибке её API используется обработка по ключевым словам и те же сервисы каталога и корзины. История LLM-диалога этой сессии при ошибке сбрасывается.

Технологии

  • Backend: Python 3.10+, FastAPI, Uvicorn, Pydantic Settings.
  • Хранение: PostgreSQL 17, SQLAlchemy AsyncIO и asyncpg; SQLite для кэша карточек.
  • Поиск и интеграции: RapidFuzz, HTTPX.
  • AI: LangGraph (граф диалога, чекпойнтер истории), LangChain (ChatOpenAI, @tool). По умолчанию — OpenAI https://api.openai.com/v1, модель gpt-5.4-mini. Подходит любой OpenAI-совместимый API с вызовом инструментов, например build.nvidia.com с моделью qwen/qwen3-next-80b-a3b-instruct или свой NIM на NVIDIA Brev.
  • Дополнительная модель для казахского: при настроенном KAZAKH_LLM_BASE_URL сервис обнаруживает характерные казахские буквы в вопросе и отправляет готовый ответ на переписывание отдельной OpenAI-совместимой модели. В конфигурации указана inception42/Llama-3.1-Sherkala-8B-Chat для vLLM. При ошибке модели, пустом ответе или изменении набора числовых фрагментов возвращается исходный ответ. Основная модель для диалога может быть OpenAI, NVIDIA или другим совместимым сервисом. Инструкция по NVIDIA API.
  • Frontend: TypeScript, Next.js 16 App Router, React 19, Tailwind CSS 4.
  • Запуск и проверки: Docker Compose, Make, pytest, Ruff, ESLint, TypeScript. Тесты backend используют SQLite в памяти через aiosqlite и подмены внешних API.

Архитектура проекта

Браузер обращается к /api/* на Next.js, который проксирует запросы в FastAPI. Маршруты backend передают работу сервисам:

  • agent_graph — граф LangGraph: guard, агент с инструментами, чекпойнтер;
  • assistant_service — инструменты для LLM, подтверждение корзины, резервные правила;
  • kazakh_service — обнаружение казахских букв и необязательное переписывание ответа;
  • attachment_service — разбор спецификаций и сопоставление строк с каталогом;
  • search_service — поиск и ранжирование аналогов;
  • catalog_service — локальный список товаров, API ekt.kz, кэш в памяти и SQLite;
  • cart_service — корзины и ожидающие подтверждения в PostgreSQL.

История LLM-диалогов хранится в InMemorySaver, а контекст последней спецификации — в памяти процесса backend. Идентификаторы сессии и корзины frontend сохраняет в localStorage.

backend/
  app/api/routes/    HTTP-маршруты chat, catalog, cart, health, items
  app/services/      ассистент, поиск, каталог, корзина, файлы и казахский ответ
  app/models/        таблицы carts, cart_items, pending_actions
  app/schemas/       модели запросов и ответов
  app/core/          настройки и подключение к БД
  data/              образец каталога, условия покупки, локальный каталог и кэш
  scripts/           синхронизация каталога, прогрев кэша, проверка LLM
  tests/             тесты и примеры ответов API
frontend/
  app/_components/   чат, карточки, таблица спецификации и подтверждение
  app/cart/[id]/     страница корзины
  public/guide/      иллюстрации пяти шагов на главной
  lib/api.ts         типизированный клиент backend
examples/            пример CSV-спецификации
docs/                отслеживаемый отчёт, скриншоты и требования безопасности
Makefile             команды установки, запуска и проверки
docker-compose.yml   PostgreSQL, backend и frontend

Установка и запуск

Все команды выполняются из корня репозитория. Нужен доступ к партнёрскому API для загрузки каталога и карточек. Ключ LLM необязателен.

Настройка окружения

Создайте файл настроек, если его ещё нет:

cp backend/.env.example backend/.env

В backend/.env укажите:

  • EKT_API_USER, EKT_API_PASSWORD — выданные партнёром учётные данные;
  • EKT_API_URL — по умолчанию https://ekt.kz/api;
  • EKT_SYNC_PAGES — число страниц для синхронизации, 0 означает все;
  • OPENAI_API_KEY — ключ выбранного LLM-сервиса; оставьте пустым для режима правил;
  • OPENAI_BASE_URL, OPENAI_MODEL — адрес сервиса и имя модели; по умолчанию https://api.openai.com/v1 и gpt-5.4-mini в коде; пример backend/.env.example задаёт NVIDIA https://integrate.api.nvidia.com/v1 и qwen/qwen3-next-80b-a3b-instruct;
  • OPENAI_REASONING_EFFORT — для reasoning-моделей (minimal / low / medium), пусто — значение модели по умолчанию;
  • KAZAKH_LLM_BASE_URL, KAZAKH_LLM_API_KEY, KAZAKH_LLM_MODEL — казахская модель для гибридного режима (vLLM на Brev); пустой адрес — режим выключен;
  • DATABASE_URL — для локального backend по умолчанию postgresql+asyncpg://app:app@localhost:5432/ekt.

В Docker Compose адрес БД для backend задаётся автоматически. Параметры PostgreSQL POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB можно задать в корневом .env; при локальном запуске согласуйте с ними DATABASE_URL. Реальные ключи и пароли не добавляйте в Git.

Вариант 1: Docker

Требования: Docker с актуальным Compose v2 и поддержкой env_file.required, Make; свободные порты 3000, 8000 и 5432.

make demo

Команда скачивает каталог, если backend/data/catalog.json отсутствует, прогревает карточки демо-товаров и аналогов, затем собирает и запускает стек. Отдельные шаги:

make docker-sync-catalog
make docker-warm-cache
make up

Для повторного запуска с подготовленными данными достаточно make up. make logs показывает логи, make down останавливает стек. PostgreSQL хранит данные в volume pgdata, каталог и кэш — в подключённой папке backend/data.

Вариант 2: локальная разработка

Требования: Python 3.10+ с модулем venv, Node.js 22 (как в Dockerfile), npm, Make и Docker Compose для PostgreSQL. Настройте backend/.env, как описано выше.

make install
cp frontend/.env.example frontend/.env.local
make sync-catalog
make warm-cache
make dev

Не перезаписывайте существующий frontend/.env.local; проверьте в нём BACKEND_URL=http://localhost:8000. make dev поднимает PostgreSQL в Docker, backend и frontend локально. Если полный Docker-стек уже запущен, сначала выполните make down, чтобы освободить порты.

Адреса после запуска

Как проверить решение

Сценарий для жюри

После загрузки каталога и прогрева кэша откройте интерфейс и нажмите «Консультант». Для воспроизводимой проверки режима правил оставьте OPENAI_API_KEY пустым. Внешний API ekt.kz с учётными данными всё равно нужен для карточек, остатков и проверки товарных строк спецификации.

  1. Введите Есть в наличии 200300285_? — проверьте карточку, цену, остаток и склады.
  2. Введите Есть в наличии 150100218_? — если товар отсутствует, появятся аналоги с обоснованием. Остатки зависят от текущих данных API и кэша.
  3. Спросите Какие условия доставки и оплаты? Минимальная партия? — получите ответ из локального файла условий.
  4. Снова выберите товар в наличии, затем введите добавь 2 шт — появится предложение с кнопками подтверждения и отказа.
  5. Нажмите «Нет» — позиция не должна добавиться. Повторите запрос и нажмите «Да, добавить» — откройте корзину и проверьте количество и итог.
  6. Запросите количество больше остатка — предложенное количество должно быть ограничено наличием. Сам запрос ещё не подтверждает добавление.
  7. Нажмите 📎 и загрузите пример спецификации — появится таблица со статусом каждой позиции. Статусы и аналоги зависят от актуального каталога. Затем напишите из файла добавь автомат на 250А — сколько есть (нужен ключ LLM).
  8. Если настроена вторая модель, задайте вопрос с казахскими буквами, например Бұл автомат қоймада бар ма?. При недоступности второй модели ответ останется от основного ассистента.

Формат файла: первая строка с колонками (можно после заголовка документа) «Артикул» / «Код», «Наименование» и «Количество». Достаточно одной из первых двух. Разделитель CSV — ; или ,, кодировка UTF-8 или Windows-1251. В Word берётся первая таблица с такими колонками (таблица реквизитов пропускается); если таблиц нет — строки текста с разделителем Tab, ; или |. Файл без заголовка тоже читается: первая колонка — товар, числовая — количество.

Автоматические проверки

После make install:

make check       # Ruff, ESLint, TypeScript и pytest
make build       # production-сборка frontend

Отдельно доступны make test, make lint и make typecheck. Тесты не требуют партнёрских ключей, работающей LLM или PostgreSQL: используются фикстуры, подменённые HTTP-ответы и SQLite в памяти. Покрыты поиск, адаптация карточек, аналоги, сценарии чата, подтверждение и отказ, ограничения количества, разбор CSV/XLSX/DOCX, обработку сбоев каталога и казахское переписывание.

Для запущенного приложения:

curl -fsS http://localhost:8000/api/health
curl -fsS http://localhost:8000/api/health/db
curl -fsS http://localhost:3000/api/health

Все три запроса должны вернуть {"status":"ok"}. Последний также проверяет проксирование через Next.js.

make llm-smoke прогоняет настоящий граф LangGraph с реальным каталогом и LLM на демо-вопросах и печатает ответы, вызванные инструменты и время ответа. Для него нужны ключ LLM, доступ к API партнёра и подготовленный каталог. Сравнение моделей: make llm-smoke MODELS="gpt-5.4-mini gpt-4.1-mini".

Отчёт о проверке интерфейса и известных ограничениях содержит скриншоты и результаты отдельного прогона проекта.

Данные и интеграции

  • Партнёрский API ekt.kz с Basic Auth: /api/products?page=N для списка товаров и /api/products/detail?id=... для подробностей, остатков и свойств.
  • Локальный каталог: make sync-catalog сохраняет список в backend/data/catalog.json. Если файла нет, поиск использует backend/data/catalog_sample.json. Образец содержит список товаров, но не заменяет API подробных карточек и не обеспечивает автономное демо.
  • Кэш карточек: память процесса и backend/data/details_cache.sqlite. Срок актуальности по умолчанию — 600 секунд; при ошибке API сервис может вернуть сохранённую карточку и после истечения этого срока.
  • Условия покупки: backend/data/terms.md. В файле указаны публичные страницы-источники; обновление условий вручную.
  • LLM: получает историю диалога и результаты инструментов через настроенный OpenAI-совместимый сервис. Структурированные карточки формируются из данных каталога, а текст ответа в LLM-режиме генерирует модель.

Полный каталог, кэш и файлы .env исключены из Git. В docs/ уже отслеживаются отчёт, скриншоты и требования безопасности; новые локальные файлы в этой папке игнорируются. Пример для загрузки хранится в examples/.

Ограничения текущей версии

  • Это прототип: оформление заказа и перенос корзины в систему партнёра не реализованы. Кнопка оформления открывает главную страницу ekt.kz.
  • Корзина не резервирует товар на складе. При недоступности API даже повторная проверка перед подтверждением может использовать устаревший кэш.
  • Одновременные подтверждения не защищены отдельной блокировкой строк БД; последовательный повтор проверяется тестами.
  • Кратность выводится в карточке, но не применяется как обязательное ограничение количества при добавлении.
  • Авторизации пользователей нет; доступ к корзине определяется её случайным идентификатором. История диалогов теряется при перезапуске backend и не разделяется между несколькими процессами.
  • Аналоги выбираются эвристически по категории, названию и совпадению свойств; техническая взаимозаменяемость не гарантируется.
  • Промпт требует от LLM использовать данные инструментов, однако отдельной проверки всех фактов в сгенерированном тексте нет. Проверка казахского переписывания сравнивает набор числовых фрагментов, но не проверяет сохранность названий и смысл каждого предложения.
  • Определение казахского языка основано на наличии характерных букв; интерфейс и ответы режима правил преимущественно русскоязычные. Дополнительная модель выключена, пока не задан её адрес.
  • Сертификаты не загружаются: certificate_url сейчас всегда пуст. Вложения поддерживаются только .csv, .xlsx (первый лист) и .docx. Старый .doc, PDF и распознавание фото не реализованы. Добавить все строки спецификации в корзину одним подтверждением нельзя: позиции добавляются по одной.
  • Внешние API влияют на доступность и скорость ответа. Условия покупки в локальном файле необходимо подтвердить у партнёра перед рабочим запуском.

Развёрнутая версия

Публичная ссылка на развёрнутый прототип в репозитории не указана. Для проверки используйте локальный запуск по инструкции выше. Сайт ekt.kz — сайт партнёра, а не развёрнутая версия этого приложения.

Звёзды
⭐ 0
Форки
1
Язык
Python
Последний push
в прошлом месяце
Коммиты
28
Авторы
3
Файлы
115
Ветки
1

Авторы коммитов

maxat 12Yersultan 10Sabyr 6

История коммитов

  • 2026-09-23 17:48docs: restore test screenshots under assetsYersultan
  • 2026-09-23 17:45docs: remove PNG files from docs rootYersultan
  • 2026-09-23 17:42docs: rename report to testreportYersultan
  • 2026-09-23 17:40Merge branch 'main' of https://github.com/BAITC-Hacks/hack-f80b8ce9-skmaSabyr
  • 2026-09-23 17:39docs: record visual tests with live OpenAI APIYersultan
  • 2026-09-23 17:39docs: collect user guide resources in assetsSabyr
  • 2026-09-23 17:39docs: move NVIDIA instructions into docsmaxat
  • 2026-09-23 17:38docs: align README with current projectmaxat
Все коммиты (28)
  • 2026-09-23 17:37docs: move user documentation into docsSabyr
  • 2026-09-23 17:31Merge branch 'main' of https://github.com/BAITC-Hacks/hack-f80b8ce9-skmaSabyr
  • 2026-09-23 17:31docs: add illustrated user guide and PDFSabyr
  • 2026-09-23 17:28feat(assistant): add Kazakh reply rewriting modemaxat
  • 2026-09-23 17:27fix: clarify catalog failures and improve mobile chat usabilityYersultan
  • 2026-09-23 17:15docs: update report with latest README test results and screenshotsYersultan
  • 2026-09-23 17:13docs: rename Docs directory to lowercase docsYersultan
  • 2026-09-23 16:59feat: support specification uploads and guided chat UImaxat
  • 2026-09-23 16:55docs: add test report and group security documentation under DocsYersultan
  • 2026-09-23 16:26chore: ignore local Hackton2026 workspace fileYersultan
  • 2026-09-23 16:23docs: remove README prompt file with Windows-incompatible nameYersultan
  • 2026-09-23 16:22feat(assistant): connect LLM through LangGraph toolsmaxat
  • 2026-09-23 16:12docs: add security requirements and acceptance criteriaSabyr
  • 2026-09-23 15:09chore(git): merge GitHub repository historymaxat
  • 2026-09-23 15:06docs: update Russian README for hackathon jurymaxat
  • 2026-09-23 15:02chore(git): ignore local documentationmaxat
  • 2026-09-23 14:59feat(backend): add catalog search, assistant and confirmed cartsmaxat
  • 2026-09-23 14:59feat(frontend): add consultant chat and cart pagesmaxat
  • 2026-09-23 14:59chore(dev): configure database and Docker demo workflowmaxat
  • 2026-09-23 12:43Initial commitmaxat
Структура репозитория (115 файлов)
.agents/skills/add-api-endpoint/SKILL.md
.agents/skills/add-frontend-page/SKILL.md
.agents/skills/hackathon-readme/SKILL.md
.agents/skills/run-and-verify/SKILL.md
.codex/skills
.gitignore
AGENTS.md
Makefile
README.md
backend/.dockerignore
backend/.env.example
backend/AGENTS.md
backend/Dockerfile
backend/app/__init__.py
backend/app/api/__init__.py
backend/app/api/router.py
backend/app/api/routes/__init__.py
backend/app/api/routes/cart.py
backend/app/api/routes/catalog.py
backend/app/api/routes/chat.py
backend/app/api/routes/health.py
backend/app/api/routes/items.py
backend/app/core/__init__.py
backend/app/core/config.py
backend/app/core/db.py
backend/app/main.py
backend/app/models/__init__.py
backend/app/models/cart.py
backend/app/schemas/__init__.py
backend/app/schemas/cart.py
backend/app/schemas/chat.py
backend/app/schemas/item.py
backend/app/schemas/product.py
backend/app/services/__init__.py
backend/app/services/agent_graph.py
backend/app/services/assistant_service.py
backend/app/services/attachment_service.py
backend/app/services/cart_service.py
backend/app/services/catalog_service.py
backend/app/services/item_service.py
backend/app/services/kazakh_service.py
backend/app/services/search_service.py
backend/data/catalog_sample.json
backend/data/terms.md
backend/pyproject.toml
backend/scripts/__init__.py
backend/scripts/llm_smoke.py
backend/scripts/sync_catalog.py
backend/scripts/warm_cache.py
backend/tests/__init__.py
backend/tests/catalog_data.py
backend/tests/conftest.py
backend/tests/fixtures/detail_515291.json
backend/tests/fixtures/products_page1.json
backend/tests/test_cart.py
backend/tests/test_catalog.py
backend/tests/test_chat.py
backend/tests/test_health.py
backend/tests/test_items.py
backend/tests/test_kazakh.py
backend/tests/test_search.py
backend/tests/test_upload.py
docker-compose.yml
docs/.gitattributes
docs/README.md
docs/assets/01-start.png
docs/assets/02-stock.png
docs/assets/03-analogs.png
docs/assets/04-terms.png
docs/assets/05-cart.png
docs/assets/fixed-desktop.png
docs/assets/fixed-mobile-error.png
docs/assets/fixed-mobile-upload.png
docs/assets/live-api-boundaries.png
docs/assets/live-api-catalog-error.png
docs/assets/live-api-desktop.png
docs/assets/live-api-file-context.png
docs/assets/live-api-mobile.png
docs/assets/retest-desktop.png
docs/assets/retest-mobile.png
docs/assets/retest-upload-desktop.png
docs/assets/spec-example.csv
docs/docs-security/security-section.md
docs/nvida-llm-instructions.md
docs/testreport.md
docs/user-guide.pdf
examples/spec-example.csv
frontend/.dockerignore
frontend/.env.example
frontend/AGENTS.md
frontend/Dockerfile
frontend/app/_components/assistant-banner.tsx
frontend/app/_components/chat-widget.tsx
frontend/app/_components/confirm-card.tsx
frontend/app/_components/product-card.tsx
frontend/app/_components/spec-table.tsx
frontend/app/cart/[id]/cart-view.tsx
frontend/app/cart/[id]/page.tsx
frontend/app/globals.css
frontend/app/items.tsx
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/eslint.config.mjs
frontend/lib/api.ts
frontend/next.config.ts
frontend/package-lock.json
frontend/package.json
frontend/postcss.config.mjs
frontend/public/.gitkeep
frontend/public/guide/01-start.png
frontend/public/guide/02-stock.png
frontend/public/guide/03-analogs.png
frontend/public/guide/04-terms.png
frontend/public/guide/05-cart.png
frontend/tsconfig.json

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.