ToiMatch
Сервис подбора до трёх event-подрядчиков: каждый факт в объяснении сверяется с каталогом.
О проекте
Команда: Code & Design
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Доведённый продукт
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Сервис подбора до трёх event-подрядчиков: каждый факт в объяснении сверяется с каталогом. Есть рабочее место площадки с пошаговым стримом решений, чат-помощник для сборки пакета мероприятия, три языка и маскот.
Как устроено
NestJS-пайплайн состоит из четырёх частей. FilterService фильтрует по городу, категории, дате, формату, бюджету, языку и часам и строит воронку. RankingService задаёт детерминированный порядок с tie-break по id. ExplainerService собирает проверяемые варианты причин из полей каталога, отличий от других карточек и фрагментов анкет. LLM только выбирает вариант, свободный текст не пишет, затем код-критик сверяет факты, а результат кешируется в PostgreSQL. Шаги отдаются через SSE, чат работает через реестр инструментов, так что имена берутся из результатов инструментов. В режиме MOCK всё работает без сети.
Ключевые решения
- детерминированная фильтрация с воронкой
- выбор LLM из проверенных вариантов объяснений
- fact-check критик
- кеш объяснений
- SSE стрим пайплайна
- tool registry в чате
- injection guard
- MOCK LLM клиент
Метрики
время ответа 15–250 мс в режиме без ключа (MOCK)
Особенности
LLM не генерирует текст о подрядчике, а выбирает среди вариантов, собранных сервером из данных, поэтому галлюцинации исключены по построению. На экране /manager видна таблица «факт объяснения → поле каталога → ✓». Отдельный режим чата собирает пакет мероприятия с расчётом минимального бюджета. README на трёх языках, есть видео-демо.
Стек
Next.js 16 · React 19 · TypeScript · Tailwind CSS 4 · NestJS 11 · Prisma 6 · PostgreSQL 16 · Docker Compose
Модели
gpt-4o-mini · gpt-image-2
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-cf5b3e57-code-design.
README
README.mdToiMatch — подбор event-подрядчиков с доказательствами
HackAlem AI 2026 · трек 06 «Креативные индустрии» · задача Firebird «Умный подбор подрядчиков» (#79-lite) · команда Code & Design (Ян Пинчук, Никита Костров, Иван Лысов)
Запасное видео демо (2:36): docs/demo/toimatch-demo.mp4 — весь сценарий проверки с подписями.
Как запустить
Нужен только Docker (с Docker Compose v2). Ключи не нужны.
git clone https://github.com/BAITC-Hacks/hack-cf5b3e57-code-design.git
cd hack-cf5b3e57-code-design
docker compose up --build
Одна команда поднимает PostgreSQL, применяет миграции, загружает сид (66 подрядчиков), запускает API и фронт. Первая сборка занимает несколько минут.
- Сайт: http://localhost:3000 (подбор — http://localhost:3000/match)
- API: http://localhost:3001/api/v1/health →
{"ok":true,"mock":true}
Остановить и очистить базу: docker compose down -v.
Пример переменных окружения
Всё работает без файла .env. Для живой модели скопируйте .env.example в .env в корне проекта (файл в .gitignore), впишите ключ и поставьте MOCK=0. Команда та же: docker compose up --build.
# .env.example (корень проекта, для Docker Compose)
MOCK=1 # 1 — без модели; 0 — OpenAI (нужен ключ)
OPENAI_API_KEY= # необязательно
MODEL_MAIN=gpt-4o-mini
CORS_ORIGIN=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:3001
Для запуска без Docker есть отдельные примеры: back/.env.example и front/.env.example.
# back/.env.example
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/hackaton?schema=public"
PORT=3001
MOCK=1
OPENAI_API_KEY=
MODEL_MAIN=gpt-4o-mini
# front/.env.example
BACKEND_URL=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3001
Docker по умолчанию работает без ключа (MOCK=1): объяснения строятся из тех же проверенных фактов детерминированно, сеть не нужна. Для живой модели передайте OPENAI_API_KEY через окружение и установите MOCK=0; ключ не записывайте в репозиторий.
Запуск с живой моделью (по желанию)
Нужен Node.js 22+. База остаётся в Docker, бэк и фронт запускаются локально:
docker compose up -d postgres
cd back
cp .env.example .env # вписать OPENAI_API_KEY и установить MOCK=0
npm install
npx prisma migrate deploy
npx prisma db seed
npm run start:dev # API на http://localhost:3001
Во втором терминале:
cd front
cp .env.example .env.local
npm install
npm run dev # http://localhost:3000
Переменные окружения
| Переменная | Где | Зачем | Обязательна |
|---|---|---|---|
DATABASE_URL | back/.env | подключение к PostgreSQL | да, значение из back/.env.example |
PORT | back/.env | порт API | нет, 3001 |
OPENAI_API_KEY | back/.env / окружение Compose | живые объяснения и чат моделью при MOCK=0 | нет — без ключа работает MOCK |
MODEL_MAIN | back/.env | модель OpenAI | нет, gpt-4o-mini |
MOCK | back/.env / compose | 1 — принудительно без модели; 0 с ключом — OpenAI | нет; в Docker по умолчанию 1 |
BACKEND_URL | front/.env.local | адрес API для сервера Next.js | нет, http://localhost:3001 (в Docker — http://backend:3001) |
NEXT_PUBLIC_API_URL | front/.env.local | адрес API для браузера (стрим на /manager) | нет, http://localhost:3001 |
Настоящих ключей в репозитории нет: .env и .env.local в .gitignore, в репозитории только .env.example, back/.env.example и front/.env.example.
Проблема и для кого
Заказчик мероприятия в Казахстане открывает каталог подрядчиков своего города и тонет в похожих профилях: описания одинаковые («харизма», «топ-10»), непонятно, чему верить и на что смотреть. Менеджер площадки вручную собирает клиенту 3 варианта и объясняет выбор.
Ценность в цифрах.
- Время. Сейчас менеджер на один запрос открывает 10–50 профилей категории в городе и сверяет по каждому календарь, цену, формат и язык. По нашей оценке это десятки минут. ToiMatch отвечает за 15–250 мс (замер в режиме без ключа) и сразу показывает, почему выбран каждый вариант и кто отсеян.
- Кому. Выгоду получают заказчик, который быстрее выбирает, и площадка или агентство, которые встраивают подбор как услугу для клиентов. Каталог получает честную выдачу: недоступных и неподходящих подрядчиков сервис не показывает.
ToiMatch по городу, дате, типу события, категории и бюджету возвращает до трёх подрядчиков и для каждого — объяснение, почему именно он. Сначала сервис говорит, на что смотреть в этом случае (язык ведения, опыт с таким форматом, часы на площадке), затем объясняет каждую карточку по этим пунктам, а каждый факт в объяснении сверяется с данными каталога.
Что реализовано
- Подбор
POST /api/v1/match: фильтр по фактам, детерминированное ранжирование, объяснения, три явных исхода:found,no_category_in_city,all_filtered_out. - Пошаговый стрим пайплайна
GET /api/v1/match/stream(SSE) для экрана площадки. - Каталог
GET /api/v1/contractorsс фильтрами и профильGET /api/v1/contractors/:id. - Кеш объяснений в PostgreSQL: тот же запрос — тот же текст и порядок.
- Чат с режимами поиска и полного пакета мероприятия: перед сборкой пакета считает минимальный бюджет, честно показывает пустые категории, а итоговые имена и причины берёт из результатов инструментов, а не из свободного текста модели.
- Режим без ключа (MOCK): весь сценарий работает без сети и без наших аккаунтов.
- Фронт (Next.js): каталог
/, подбор/match, рабочее место площадки/manager(старый адрес/adminперенаправляет туда), профиль/contractor/[id], чат-помощник/chat; интерфейс на русском, казахском и английском; маскот-помощник Нурлан.
Как работает — от запроса до результата
- Заказчик выбирает, кто нужен (категория), город, дату, событие и бюджет; можно указать длительность и язык.
- Фильтр (код): город → категория → свободен в эту дату → берёт этот формат → в бюджете → язык → часы. На каждом шаге считается, сколько кандидатов выбыло и почему (воронка).
- Ранжирование (код): детерминированный порядок по измеримым критериям запроса, при равенстве — по
id. - Объяснения: сервер собирает проверяемые варианты причин из полей каталога, отличий от других карточек и точных фрагментов анкет. OpenAI выбирает подходящий вариант, но не пишет свободный текст о подрядчике; без ключа выбор детерминированный.
- Проверка фактов (код): причина сверяется с выбранной карточкой до показа и кеширования. Цена с подстановкой и цитаты из описания явно помечены; неподтверждённое объяснение заменяется безопасным вариантом.
- Ответ: исход, до трёх карточек, воронка и человеческая фраза «почему столько».
Технологии, модели и API
- Frontend: Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, шрифт Onest.
- Backend: NestJS 11, Prisma 6, PostgreSQL 16, Docker Compose.
- LLM: OpenAI
gpt-4o-mini(переменнаяMODEL_MAIN) выбирает вариант объяснения и участвует в проверке; без ключа — детерминированный клиент (back/src/matching/llm/mock-llm.client.ts). Итоговый текст всегда строит сервер из проверенных данных. - Изображения: фото подрядчиков и маскот сгенерированы OpenAI
gpt-image-2; анимация маскота — fal.ai (Kling v3); логотип — fal.ai (Recraft V4.1, SVG). - Общий контракт типов фронта и бэка:
shared/contract.ts.
Архитектура
Браузер (Next.js: /, /match, /manager, /contractor/[id], /chat)
│ POST /api/v1/match GET /api/v1/match/stream (SSE) GET /api/v1/contractors
▼
NestJS (back/src/matching)
├─ FilterService — фильтр и воронка (детерминированно)
├─ RankingService — порядок, tie-break по id
├─ ExplainerService — проверяемые варианты объяснений, критик, кеш
└─ LlmClient — OpenAI или MockLlmClient (без ключа)
▼
PostgreSQL (Prisma): contractors · enrichments · explanation_cache
Экраны
Подбор /match — запрос 1: три карточки, у каждой своё объяснение и проверенные факты.

Рабочее место площадки /manager — тот же запрос: поток решений по шагам и воронка отсева.

Как проверить (сценарий для жюри)
Все запросы работают без ключа. Кнопки с этими примерами есть на странице /match.
| # | Запрос | Ожидаемый результат |
|---|---|---|
| 1 | Ведущий · Алматы · корпоратив · 16.10.2026 · до 1 000 000 ₸ | found: 3 карточки из 4 подходящих; воронка 10 → 5 свободны → 5 берут корпоратив → 4 в бюджете |
| 1б | То же на 23.10.2026 | Другая тройка: часть ведущих занята 23.10 |
| 2 | Флорист · Алматы · свадьба · 15.10.2026 · до 300 000 ₸ | found, меньше трёх: в Алматы всего 2 флориста |
| 3а | Лайв-бэнд · Астана · свадьба · 14.11.2026 · до 1 500 000 ₸ | no_category_in_city: в Астане нет лайв-бэндов |
| 3б | Декоратор · Алматы · той · 14.11.2026 · до 3 000 000 ₸ | all_filtered_out: все 3 заняты 14.11 и ни один не оформляет той |
Повторный запуск того же запроса в MOCK-режиме даёт тот же порядок и те же тексты. У каждой показанной карточки причина индивидуальна и содержит проверяемую информацию о дате и цене; фрагмент анкеты помечен «со слов подрядчика», а подставленная цена — «ценовой ориентир». Пустой город/категория и отсеивание после фильтров различаются в outcome и тексте ответа.
Экран площадки /manager показывает тот же подбор пошагово (воронку в реальном времени) и таблицу: каждый факт объяснения → поле каталога → ✓.
Данные и интеграции
- Каталог: датасет организаторов — 66 анонимизированных профилей, из них 13 синтетических (
synthetic: true), календарь занятости 23.09–31.12.2026. Загружается сидом изback/prisma/seed-data/contractors.csv. В карточках помечены синтетические профили и проставленные при подготовке датасета цены и города. - Внешний API: только OpenAI (если задан ключ). Пример запроса:
POST /api/v1/match
Content-Type: application/json
{"city":"Алматы","date":"2026-10-15","eventType":"свадьба","category":"Флорист","budgetKzt":300000}
Ответ содержит outcome, summary, criteria, до трёх cards с индивидуальными reason, factsUsed и флагами достоверности данных, а также funnel с причинами отсева на каждом шаге.
Что взяли готовым
- Библиотеки с открытыми лицензиями: Next.js (MIT), React (MIT), Tailwind CSS (MIT), NestJS (MIT), Prisma (Apache-2.0), class-validator (MIT), Onest (SIL OFL).
- Датасет подрядчиков — от организаторов (Firebird).
- Фото подрядчиков — сгенерированы ИИ (OpenAI gpt-image-2), люди вымышленные; на сайте каждое фото помечено «Фото: ИИ-иллюстрация».
- Маскот Нурлан — сгенерирован ИИ (gpt-image-2), анимация — fal.ai (Kling v3); логотип — fal.ai (Recraft V4.1).
- ИИ-инструменты разработки: Claude Code и OpenAI Codex. Весь код решения написан 23.09.2026 с 13:00 — это видно по истории коммитов.
Ограничения
- Без ключа объяснения выбираются детерминированно из проверенных вариантов; с ключом OpenAI помогает выбрать вариант, при сбое — откат на безопасный выбор.
- Интерфейс полностью на русском, казахском и английском. Объяснения полностью готовы на русском. На казахском и английском в итоговых фразах пока остаются русские названия категорий и городов и даты в формате ГГГГ-ММ-ДД.
- Docker по умолчанию работает без модели; для живой модели передайте ключ через окружение и установите
MOCK=0. - Нет бронирования, оплаты, авторизации и уведомлений — это вне задачи.
- Каталог маленький (66 профилей), поэтому в редких категориях честно показываем меньше трёх.
Рабочая версия
Развёрнутой версии нет: жюри запускает проект локально одной командой docker compose up --build (см. «Как запустить»).
Авторы коммитов
История коммитов
- 2026-09-23 18:01Handle keep-only bundle category requestsPinchuk Yan
- 2026-09-23 17:54Fix bundle category questions and removalsPinchuk Yan
- 2026-09-23 17:53Add backup demo video and link it from README (RU/KK/EN)ivanlyssov
- 2026-09-23 17:45Слить последние изменения main перед публикациейNikita1658
- 2026-09-23 17:44Merge chat dialogue fixes into mainPinchuk Yan
- 2026-09-23 17:43Слить актуальный main перед публикациейNikita1658
- 2026-09-23 17:43Исправить открытие календаря подбораNikita1658
- 2026-09-23 17:42Добавить управление лентой категорийNikita1658
Все коммиты (88)
- 2026-09-23 17:41Fix conversational bundle matching and chat presentationPinchuk Yan
- 2026-09-23 17:37Оформить hero и блок категорий на главнойNikita1658
- 2026-09-23 17:36Merge remote-tracking branch 'origin/nikita_develop' into ivan/nikita-tryivanlyssov
- 2026-09-23 17:36README in Kazakh and English; fix section pointerivanlyssov
- 2026-09-23 17:34README: run instructions first with env examples; add root .env.example for Docker Composeivanlyssov
- 2026-09-23 17:33Chat remembers city/budget/guests across turns; compare any two dates; /match prefill from links; /admin redirect; UX copy fixesivanlyssov
- 2026-09-23 17:32промежуточный коммит.Nikita1658
- 2026-09-23 17:24README: value in numbers, honest limitations, screenshots from the final buildivanlyssov
- 2026-09-23 17:07Use the shared site header and footer on /match and /managerivanlyssov
- 2026-09-23 17:05Merge remote-tracking branch 'origin/main' into ivan/polishivanlyssov
- 2026-09-23 17:05Polish front: SEO, icons, manifest, OG image, /match and /manager redesign detailsivanlyssov
- 2026-09-23 16:57Sync back/package-lock.json with package.json (csv-parse) so npm ci in Docker worksivanlyssov
- 2026-09-23 16:56Integrate evidence-grounded matching with team frontendPinchuk Yan
- 2026-09-23 16:51Fix chat session cleanupPinchuk Yan
- 2026-09-23 16:46Implement evidence-grounded matching and demo workflowPinchuk Yan
- 2026-09-23 16:35Merge remote-tracking branch 'origin/main' into nikita_developNikita1658
- 2026-09-23 16:31Merge remote-tracking branch 'origin/nikita_develop' into ivan/polishivanlyssov
- 2026-09-23 16:30Update remaining interface stylesNikita1658
- 2026-09-23 16:29Refine matching form controlsNikita1658
- 2026-09-23 16:28Update contractor photo stylesNikita1658
- 2026-09-23 16:27Restore contractor card stylesNikita1658
- 2026-09-23 16:27Remove obsolete contractor card stylesNikita1658
- 2026-09-23 16:26Sync current interface stylesNikita1658
- 2026-09-23 16:26Refine match component stylesNikita1658
- 2026-09-23 16:25Update matching interface controlsNikita1658
- 2026-09-23 16:23README: add /match and /manager screenshotsivanlyssov
- 2026-09-23 16:19README: one-command Docker run, env vars, real API responsesivanlyssov
- 2026-09-23 16:15Fix build after merging Nikita's branch: restore i18n keys, one mascotivanlyssov
- 2026-09-23 16:12Merge remote-tracking branch 'origin/nikita_develop' into ivan/integrateivanlyssov
- 2026-09-23 16:11Fix budget input: allow round amounts like 1 000 000ivanlyssov
- 2026-09-23 16:09Слить срочное обновление mainNikita1658
- 2026-09-23 16:08Добавить полный запуск проекта через DockerNikita1658
- 2026-09-23 16:07Подключить чат к API ЯнаNikita1658
- 2026-09-23 16:07Обновить интерфейс под бренд ToiMatchNikita1658
- 2026-09-23 16:06Map the new date fact key in the manager fact tableivanlyssov
- 2026-09-23 16:05Merge branch 'ivan/merge-front' into ivan/restyleivanlyssov
- 2026-09-23 16:04Restyle front to the approved ToiMatch designivanlyssov
- 2026-09-23 16:01Merge main into Nikita's front: resolve conflicts with the matching backendivanlyssov
- 2026-09-23 15:56Слить свежий main и backend ЯнаNikita1658
- 2026-09-23 15:52Merge develop_yan: matching pipeline + chat assistant + Ivan block 1 + enrichmentPinchuk Yan
- 2026-09-23 15:51back: chat assistant + swap NVIDIA for OpenAI; front: dev harnessPinchuk Yan
- 2026-09-23 15:51match: Ivan's copy spec (block 1) + few-shot + offline enrichmentPinchuk Yan
- 2026-09-23 15:46Merge remote-tracking branch 'origin/main' into ivan/restyleivanlyssov
- 2026-09-23 15:44Слить актуальный main с дизайном и текстамиNikita1658
- 2026-09-23 15:41Add localized conversational matching assistantNikita1658
- 2026-09-23 15:40Replace admin auth with matching workspaceNikita1658
- 2026-09-23 15:40Build localized matching experienceNikita1658
- 2026-09-23 15:40Build localized contractor catalogNikita1658
- 2026-09-23 15:40Add localized frontend foundationNikita1658
- 2026-09-23 15:38Merge pull request #5 from BAITC-Hacks/ivan/copyivanlyssov
- 2026-09-23 15:37Merge pull request #4 from BAITC-Hacks/ivan/frontivanlyssov
- 2026-09-23 15:36Add ToiMatch logo files and faviconivanlyssov
- 2026-09-23 15:36Finalize DESIGN.md: ToiMatch brand, palette, logo, mascot and routesivanlyssov
- 2026-09-23 15:36Add README draft for the juryivanlyssov
- 2026-09-23 15:23Add contractor photos, Nurlan mascot and approved design mockupsivanlyssov
- 2026-09-23 15:08Restore backend production entry pointNikita1658
- 2026-09-23 15:08Honor locale in matching requestsNikita1658
- 2026-09-23 15:08Add copy spec for explanations, criteria and summariesivanlyssov
- 2026-09-23 15:02Remove out-of-scope admin authenticationNikita1658
- 2026-09-23 14:59Merge remote-tracking branch 'origin/main' into nikita_developNikita1658
- 2026-09-23 14:55chat infra: contract types, prisma models, locale in cache, /admin renamePinchuk Yan
- 2026-09-23 14:55Add role-protected contractor admin APINikita1658
- 2026-09-23 14:51Merge pull request #3 from BAITC-Hacks/develop_yanYPNM
- 2026-09-23 14:49организовал систему входа для админа.Nikita1658
- 2026-09-23 14:49Merge pull request #2 from BAITC-Hacks/ivan/designivanlyssov
- 2026-09-23 14:46back: matching pipeline + catalog endpointsPinchuk Yan
- 2026-09-23 14:36wip: matching module skeleton (filter, ranking, explainer, mock llm)Pinchuk Yan
- 2026-09-23 14:36docs+contract: add catalog page and endpointsPinchuk Yan
- 2026-09-23 14:35Add DESIGN.md draft with agreed UI decisionsivanlyssov
- 2026-09-23 14:35Fix demo date pair in SCOPE: use 16.10 vs 23.10ivanlyssov
- 2026-09-23 14:33Merge branch 'main' of https://github.com/BAITC-Hacks/hack-cf5b3e57-code-design into nikita_developNikita1658
- 2026-09-23 14:29docs: AGENTS.md at root/back/frontPinchuk Yan
- 2026-09-23 14:25seed: load 66 contractors from CSV via PrismaPinchuk Yan
- 2026-09-23 14:13shared: API contract + seed CSVPinchuk Yan
- 2026-09-23 13:58создал фалй начального экрана проекта.Nikita1658
- 2026-09-23 13:55prisma: replace User with Contractor/Enrichment/ExplanationCachePinchuk Yan
- 2026-09-23 13:51Merge pull request #1 from BAITC-Hacks/ivan/scopeivanlyssov
- 2026-09-23 13:50SCOPE: задача, критерии, big idea, пайплайн и демо-запросыivanlyssov
- 2026-09-23 13:24merge back/ into monorepoPinchuk Yan
- 2026-09-23 13:19Init commitPinchuk Yan
Структура репозитория (358 файлов)
.claude/orchestration.md .dockerignore .env.example .gitignore AGENTS.md DESIGN.md README.en.md README.kk.md README.md SCOPE.md back/.dockerignore back/.env.example back/.gitignore back/.prettierrc back/AGENTS.md back/Dockerfile back/README.md back/eslint.config.mjs back/nest-cli.json back/package-lock.json back/package.json back/prisma/migrations/20260923085538_contractors_initial/migration.sql back/prisma/migrations/20260923095322_chat_and_locale/migration.sql back/prisma/migrations/migration_lock.toml back/prisma/schema.prisma back/prisma/seed-data/contractors.csv back/prisma/seed.ts back/src/app.controller.spec.ts back/src/app.controller.ts back/src/app.module.ts back/src/app.service.ts back/src/chat/chat-intent.ts back/src/chat/chat.controller.ts back/src/chat/chat.module.ts back/src/chat/chat.service.ts back/src/chat/dto/create-session.dto.ts back/src/chat/dto/send-message.dto.ts back/src/chat/guards/injection.guard.ts back/src/chat/openai-chat.client.ts back/src/chat/tools/tools.registry.ts back/src/chat/tools/tools.service.ts back/src/contractors/contractors.controller.ts back/src/contractors/contractors.module.ts back/src/contractors/contractors.service.ts back/src/contractors/dto/list-contractors.query.dto.ts back/src/enrichment/enrich.ts back/src/health/health.controller.ts back/src/health/health.module.ts back/src/main.ts back/src/matching/copy/nouns.ts back/src/matching/copy/reasons.ts back/src/matching/criteria.ts back/src/matching/dto/match-request.dto.ts back/src/matching/evidence.ts back/src/matching/explainer.service.ts back/src/matching/filter.service.ts back/src/matching/llm/llm-client.ts back/src/matching/llm/mock-llm.client.ts back/src/matching/llm/openai-llm.client.ts back/src/matching/matching.controller.ts back/src/matching/matching.module.ts back/src/matching/matching.service.ts back/src/matching/ranking.service.ts back/src/matching/types.ts back/src/prisma/prisma.module.ts back/src/prisma/prisma.service.ts back/test/app.e2e-spec.ts back/test/chat-bundle.e2e-spec.ts back/test/jest-e2e.json back/test/match.e2e-spec.ts back/tsconfig.build.json back/tsconfig.json docker-compose.yml docs/copy-explanations.md docs/demo/toimatch-demo.mp4 docs/design/NOTES.md docs/design/admin.html docs/design/contractor.html docs/design/index.html docs/design/match.html docs/screenshots/manager.jpg docs/screenshots/match.jpg front/.env.example front/.gitignore front/AGENTS.md front/CLAUDE.md front/Dockerfile front/README.md front/app/admin/page.tsx front/app/apple-icon.png front/app/chat/api/[sessionId]/message/route.ts front/app/chat/api/[sessionId]/route.ts front/app/chat/api/session/route.ts front/app/chat/page.module.css front/app/chat/page.tsx front/app/contractor/[id]/page.module.css front/app/contractor/[id]/page.tsx front/app/dev/page.tsx front/app/globals.css front/app/icon.svg front/app/layout.tsx front/app/manager/page.tsx front/app/manifest.ts front/app/match/api/route.ts front/app/match/page.module.css front/app/match/page.tsx front/app/not-found.tsx front/app/opengraph-image.alt.txt front/app/opengraph-image.png front/app/page.module.css front/app/page.tsx front/app/robots.ts front/app/sitemap.ts front/app/twitter-image.alt.txt front/app/twitter-image.png front/components/catalog/availability-calendar.module.css front/components/catalog/availability-calendar.tsx front/components/catalog/catalog-filters/catalog-filters.module.css front/components/catalog/catalog-filters/catalog-filters.tsx front/components/catalog/catalog-header/catalog-header.module.css front/components/catalog/catalog-header/catalog-header.tsx front/components/catalog/catalog-page.module.css front/components/catalog/category-strip/category-strip.module.css front/components/catalog/category-strip/category-strip.tsx front/components/catalog/contractor-card/contractor-card.module.css front/components/catalog/contractor-card/contractor-card.tsx front/components/catalog/contractor-photo/contractor-photo.module.css front/components/catalog/contractor-photo/contractor-photo.tsx front/components/catalog/profile-page.module.css front/components/chat/chat-attachment-results/chat-attachment-results.tsx front/components/chat/chat-bundle-results/chat-bundle-results.module.css front/components/chat/chat-bundle-results/chat-bundle-results.tsx front/components/chat/chat-composer/chat-composer.module.css front/components/chat/chat-composer/chat-composer.tsx front/components/chat/chat-experience/chat-experience.module.css front/components/chat/chat-experience/chat-experience.tsx front/components/chat/chat-header/chat-header.module.css front/components/chat/chat-header/chat-header.tsx front/components/chat/chat-hero/chat-hero.module.css front/components/chat/chat-hero/chat-hero.tsx front/components/chat/chat-mode-switcher/chat-mode-switcher.module.css front/components/chat/chat-mode-switcher/chat-mode-switcher.tsx front/components/chat/chat-progress/chat-progress.module.css front/components/chat/chat-progress/chat-progress.tsx front/components/chat/chat-result-card/chat-result-card.module.css front/components/chat/chat-result-card/chat-result-card.tsx front/components/chat/chat-results/chat-results.module.css front/components/chat/chat-results/chat-results.tsx front/components/chat/chat-session-panel/chat-session-panel.module.css front/components/chat/chat-session-panel/chat-session-panel.tsx front/components/chat/chat-thread/chat-thread.module.css front/components/chat/chat-thread/chat-thread.tsx front/components/chat/format-dates.ts front/components/chat/quick-replies/quick-replies.module.css front/components/chat/quick-replies/quick-replies.tsx front/components/manager/comparison-panel/comparison-panel.module.css front/components/manager/comparison-panel/comparison-panel.tsx front/components/manager/comparison-panel/index.ts front/components/manager/criteria-panel/criteria-panel.module.css front/components/manager/criteria-panel/criteria-panel.tsx front/components/manager/criteria-panel/index.ts front/components/manager/critic-panel/critic-panel.module.css front/components/manager/critic-panel/critic-panel.tsx front/components/manager/critic-panel/index.ts front/components/manager/demo-presets/demo-presets.module.css front/components/manager/demo-presets/demo-presets.tsx front/components/manager/demo-presets/index.ts front/components/manager/fact-table/fact-table.module.css front/components/manager/fact-table/fact-table.tsx front/components/manager/funnel-view/funnel-view.module.css front/components/manager/funnel-view/funnel-view.tsx front/components/manager/funnel-view/index.ts front/components/manager/json-panel/index.ts front/components/manager/json-panel/json-panel.module.css front/components/manager/json-panel/json-panel.tsx front/components/manager/manager-contractor-card/index.ts front/components/manager/manager-contractor-card/manager-contractor-card.module.css front/components/manager/manager-contractor-card/manager-contractor-card.tsx front/components/manager/manager-header/index.ts front/components/manager/manager-header/manager-header.module.css front/components/manager/manager-header/manager-header.tsx front/components/manager/manager-hero/index.ts front/components/manager/manager-hero/manager-hero.module.css front/components/manager/manager-hero/manager-hero.tsx front/components/manager/manager-results/index.ts front/components/manager/manager-results/manager-results.module.css front/components/manager/manager-results/manager-results.tsx front/components/manager/manager-shell/index.ts front/components/manager/manager-shell/manager-shell.module.css front/components/manager/manager-shell/manager-shell.tsx front/components/manager/manager-workspace/index.ts front/components/manager/manager-workspace/manager-workspace.module.css front/components/manager/manager-workspace/manager-workspace.tsx front/components/manager/match-request-form/index.ts front/components/manager/match-request-form/match-request-form.module.css front/components/manager/match-request-form/match-request-form.tsx front/components/manager/pipeline-timeline/index.ts front/components/manager/pipeline-timeline/pipeline-timeline.module.css front/components/manager/pipeline-timeline/pipeline-timeline.tsx front/components/manager/shared/contractor-photo.module.css front/components/manager/shared/contractor-photo.tsx front/components/manager/shared/format.ts front/components/manager/shared/highlight.ts front/components/manager/shared/icon.tsx front/components/manager/shared/manager-data.ts front/components/manager/shared/manager-types.ts front/components/manager/shared/panel-heading.tsx front/components/manager/shared/panel.module.css front/components/match/category-picker/category-picker.module.css front/components/match/category-picker/category-picker.tsx front/components/match/compare-dates/compare-dates.module.css front/components/match/compare-dates/compare-dates.tsx front/components/match/criteria-list/criteria-list.module.css front/components/match/criteria-list/criteria-list.tsx front/components/match/demo-presets/demo-presets.module.css front/components/match/demo-presets/demo-presets.tsx front/components/match/format/format.ts front/components/match/funnel-summary/funnel-summary.module.css front/components/match/funnel-summary/funnel-summary.tsx front/components/match/icons/icons.tsx front/components/match/mascot-guide/mascot-guide.module.css front/components/match/mascot-guide/mascot-guide.tsx front/components/match/mascot/mascot.module.css front/components/match/mascot/mascot.tsx front/components/match/match-card/highlight-reason.ts front/components/match/match-card/match-card.module.css front/components/match/match-card/match-card.tsx front/components/match/match-experience/match-experience.module.css front/components/match/match-experience/match-experience.tsx front/components/match/match-form/match-form.module.css front/components/match/match-form/match-form.tsx front/components/match/match-header/match-header.module.css front/components/match/match-header/match-header.tsx front/components/match/match-hero/match-hero.module.css front/components/match/match-hero/match-hero.tsx front/components/match/match-results/match-results.module.css front/components/match/match-results/match-results.tsx front/components/match/match-status/match-status.module.css front/components/match/match-status/match-status.tsx front/components/match/outcome-banner/outcome-banner.module.css front/components/match/outcome-banner/outcome-banner.tsx front/components/shared/brand-logo/brand-logo.module.css front/components/shared/brand-logo/brand-logo.tsx front/components/shared/custom-select/custom-select.module.css front/components/shared/custom-select/custom-select.tsx front/components/shared/locale-switcher/locale-switcher.module.css front/components/shared/locale-switcher/locale-switcher.tsx front/components/shared/status-icons/status-icons.tsx front/components/site/site-copy.ts front/components/site/site-footer.tsx front/components/site/site-header.tsx front/components/site/site.module.css front/eslint.config.mjs front/lib/catalog-api.ts front/lib/chat/chat-api.ts front/lib/chat/chat-assistant.ts front/lib/chat/chat-proxy.ts front/lib/chat/chat-storage.ts front/lib/contractor-images.ts front/lib/i18n/config.ts front/lib/i18n/get-request-locale.ts front/lib/i18n/locale-provider.tsx front/lib/i18n/messages/catalog.ts front/lib/i18n/messages/chat.ts front/lib/i18n/messages/manager.ts front/lib/i18n/messages/match.ts front/lib/manager-stream.ts front/lib/match-api.ts front/next.config.ts front/package-lock.json front/package.json front/postcss.config.mjs front/public/brand/toimatch-logo-light.svg front/public/brand/toimatch-logo.svg front/public/brand/toimatch-mark.svg front/public/contractors/HK-10990.webp front/public/contractors/HK-11484.webp front/public/contractors/HK-16628.webp front/public/contractors/HK-19103.webp front/public/contractors/HK-20640.webp front/public/contractors/HK-23752.webp front/public/contractors/HK-25279.webp front/public/contractors/HK-26808.webp front/public/contractors/HK-27222.webp front/public/contractors/HK-29829.webp front/public/contractors/HK-30583.webp front/public/contractors/HK-31819.webp front/public/contractors/HK-35215.webp front/public/contractors/HK-35846.webp front/public/contractors/HK-35913.webp front/public/contractors/HK-36965.webp front/public/contractors/HK-37181.webp front/public/contractors/HK-39301.webp front/public/contractors/HK-39372.webp front/public/contractors/HK-42352.webp front/public/contractors/HK-44733.webp front/public/contractors/HK-44923.webp front/public/contractors/HK-45928.webp front/public/contractors/HK-46450.webp front/public/contractors/HK-50695.webp front/public/contractors/HK-53108.webp front/public/contractors/HK-57480.webp front/public/contractors/HK-58236.webp front/public/contractors/HK-58385.webp front/public/contractors/HK-60927.webp front/public/contractors/HK-61323.webp front/public/contractors/HK-62242.webp front/public/contractors/HK-64395.webp front/public/contractors/HK-68220.webp front/public/contractors/HK-69010.webp front/public/contractors/HK-72785.webp front/public/contractors/HK-72938.webp front/public/contractors/HK-74147.webp front/public/contractors/HK-74914.webp front/public/contractors/HK-75012.webp front/public/contractors/HK-76268.webp front/public/contractors/HK-76335.webp front/public/contractors/HK-77793.webp front/public/contractors/HK-77838.webp front/public/contractors/HK-80581.webp front/public/contractors/HK-83709.webp front/public/contractors/HK-88430.webp front/public/contractors/HK-90001.webp front/public/contractors/HK-90002.webp front/public/contractors/HK-90003.webp front/public/contractors/HK-90004.webp front/public/contractors/HK-90005.webp front/public/contractors/HK-90006.webp front/public/contractors/HK-90007.webp front/public/contractors/HK-90008.webp front/public/contractors/HK-90009.webp front/public/contractors/HK-90010.webp front/public/contractors/HK-90011.webp front/public/contractors/HK-90012.webp front/public/contractors/HK-90013.webp front/public/contractors/HK-91112.webp front/public/contractors/HK-92824.webp front/public/contractors/HK-97041.webp front/public/contractors/HK-97737.webp front/public/contractors/HK-98562.webp front/public/contractors/HK-99701.webp front/public/file.svg front/public/globe.svg front/public/icons/icon-192.png front/public/icons/icon-512.png front/public/icons/maskable-512.png front/public/mascot/nurlan-found.webp front/public/mascot/nurlan-hello.mp4 front/public/mascot/nurlan-hello.webm front/public/mascot/nurlan-hello.webp front/public/mascot/nurlan-sorry.webp front/public/mascot/nurlan-thinking.webp front/public/next.svg front/public/vercel.svg front/public/window.svg front/tsconfig.json shared/README.md shared/contract.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.