Гид по ресторанам
JoyTop
joy-top.uz работает: гид по еде в Ташкенте с картой, поиском, редакционными подборками и путём для владельцев заведений. Сайт, мобильное приложение и Telegram-бот стоят на одной базе данных.

«Find · Taste · Connect» — гид, по которому Ташкент выбирает, где поесть.
Гид по еде в Ташкенте, который составляют местные авторы, а не реклама: избранные места, проверенные меню и настоящие цены, редакционные подборки и карта для тех, кто уже знает, чего хочет. JoyTop — наш собственный продукт: Нодирхон — его сооснователь и технический директор.
Продуктовая стратегия · UI/UX-дизайн · Full-stack разработка · Редакционная система
В ресторанных списках выше тот, кто заплатил, а меню давно устарели.
Люди в Ташкенте выбирают, где поесть, по спискам, где выше оказывается тот, кто заплатил. Меню и цены в таких списках устаревают.
JoyTop задуман как гид, где места выбирают местные авторы, а в карточке — настоящее меню и настоящие цены. И всё это сразу на сайте, в мобильном приложении и в Telegram-боте.
Мы записали правила гида в базу и в карту, а не в память людей.
Вид метки на карте решает правило в коде: выбор эксперта получает самую заметную метку, а заведение без меню и фото — простая точка. Что входит в тариф заведения, решает база данных. Сайт, приложение и бот читают одни и те же записи.
Использовать единую модель заведений для веба и мобильного приложения.
Построить поиск рядом и карту на одной базе геоданных.
Сохранить редакционный порядок и дать команде инструменты для меню, подборок и проверки.
Задать вид метки на карте правилом: выбор эксперта получает самую заметную метку, а заведение без полного описания — простая точка.
Это дизайн JoyTop, собранный заново из его собственного кода.
Окно ниже — не картинка. Шрифты Fraunces и DM Sans, синий и оранжевый из токенов, фирменная метка-пиала с карты и печать оценки пересобраны вживую по исходникам JoyTop. Карта нарисована и не изображает настоящий город, а заведения в ней — образцы. Рядом — снимки публичного joy-top.uz для сравнения.
Образец А
Национальная
Образец Б
Плов
Образец В
Шашлык
Это не снимок экрана, а живой HTML и CSS по исходникам JoyTop. Выберите метку — она вырастет так же, как на joy-top.uz, а ниже появится строка заведения.
- Шрифты
- Fraunces · 300–700
- DM Sans · 300–700
- JetBrains Mono · 500
- Цвета
- --jt-blue-700 #0f1a6c
- --jt-blue-900 #060a38
- --jt-orange-500 #f6a34e
- --jt-orange-700 #c77e2e
- --jt-cream-100 #fbf8f4
- --jt-cream-300 #f3efeb
- --jt-ink-900 #15140f
- --jt-ink-500 #514e42
- --border-default rgba(15, 26, 108, 0.16)
- primary #2D4AA8
- Скругления
- 4px · .seal
- 12px · .searchBtn
- 16px · .searchBar
- Анимация
- scale(1.34)setPinActive()
- 0.18s cubic-bezier(.2,.8,.2,1)pin image
- Источник
- web/src/styles/tokens.css
- web/tailwind.config.ts
- web/app/fonts.ts
- web/app/[locale]/map/map.module.css
- web/app/[locale]/map/components/MapNav.tsx
- web/app/[locale]/map/markerDom.ts
- web/app/[locale]/map/pinAsset.ts
- web/public/brand/logomark.svg
- web/components/collections/Seal.tsx
- web/components/collections/Seal.module.css


Мы измеряли, прежде чем чинить, и ставили правила туда, где их не обойти.
Одна пространственная система данных питает веб, нативное мобильное приложение, Telegram-бот и операционные инструменты каталога.
- Где работает
- Next.js для веба, Expo для мобильных приложений и Telegraf-бот
- Данные
- PostgreSQL, Supabase, PostGIS, хранилище и построчные политики доступа
- Поиск
- MapLibre и объединённый в Postgres полнотекстовый, нечёткий и префиксный поиск
Поиск понимает опечатки и словоформы
Полнотекстовая выдача, нечёткое сравнение триграмм и префиксный поиск объединяются reciprocal-rank fusion, поэтому один путь обрабатывает точные названия, словоформы и опечатки.
География встроена в модель данных
PostGIS обслуживает поиск поблизости и многоуровневую карту из тех же записей, которыми пользуются веб, мобильное приложение и операционная команда.
У каждого свой доступ: читатель, владелец, редактор
Читатель, владелец заведения, эксперт и администратор работают с одним бэкендом, а построчные политики и серверные функции удерживают границы доступа и редакции.
Внешний поиск остаётся контролируемым резервом
Если собственных результатов мало, кэшированная выдача Яндекса подключается только после фильтрации по ресторанным рубрикам и не заменяет базу JoyTop.
Медленную карту разобрали замером
Без запаса данных карта открывалась долго. 14.08.2026 мы замерили это на рабочем сайте: почти половину ответа занимали одни и те же названия полей, повторённые у каждого заведения. В новом формате названия идут один раз, и на тех же данных ответ вдвое меньше. Приложения, уже установленные у людей, продолжают получать старый формат.
Причину нашли замером, а не догадкой — и ничего не сломали у тех, у кого приложение уже стоит.
Одна СМС вместо двух — на тестовой отправке
Код входа на кириллице уходил двумя платными частями СМС. Мы написали текст латиницей и проверили каждый символ: даже узбекская ʻ вернула бы вторую часть. Отправка на тестовый номер 04.08.2026 пришла одной частью — 110 сум вместо 220–320.
Такой текст входа стоит в 2–3 раза меньше — по настоящей отправке, а не по расчёту.
Одна жалоба — несколько причин
Для пользователя любая поломка входа выглядит одинаково: код не пришёл. Мы записали каждую причину с этим симптомом и настоящую ошибку для каждой. Служба входа скрывает текст ошибки, поэтому в инструкции записано, как его прочитать.
Поломку входа находят по записи, а не по памяти одного человека.
Каталог без догадок
Каталог пополнялся заведение за заведением по открытым источникам, и у каждого принятого факта записаны источник и степень уверенности. Автоматика только заполняет пустые поля — заменить уже записанное может лишь человек. Если заведение закрылось, сменило название, его сайт теперь принадлежит другому или оно повторяет другое, система не угадывает, а откладывает его на ручную проверку с названной причиной.
Лучше пустое поле, чем чужой телефон в карточке заведения.
Тариф держит база
Что входит в тариф заведения, проверяет база данных, а не экран приложения. Когда подписка истекает, заведение возвращается на базовый тариф, и 81-ю позицию меню база на нём не примет. Если тариф определить не удаётся, действует базовый, а два одновременных добавления не проходят лимит вдвоём.
Тариф значит одно и то же на сайте, в приложении и в самой базе.
429 миграций базы данных · 58 страниц сайта в основной ветке репозитория. Подсчитано нами 26.09.2026.
Карточка не покажет чужой телефон, а тариф не обойти через приложение.
Для владельца это значит вот что. Что входит в тариф, проверяет база, поэтому сайт, приложение и бот не разойдутся. Сомнительная запись в каталоге ждёт человека, а не угадывается. Если вход по СМС сломается, причину найдут по записи. А медленное место сначала измеряют и только потом переписывают.
Нужна такая же система для вашего бизнеса?
Опишите задачу в двух-трёх предложениях. Ответим в тот же рабочий день и предложим время для разговора.
Связаться с намиОбсудить задачу в Telegram
- Отвечаем в тот же рабочий день по ташкентскому времени.
- Пишите Нодирхону — он подключит того из нас, кто нужен вашей задаче.