---
# System prepended metadata

title: Meetly – a Telegram mini app

---

# Meetly -- a Telegram mini app

Slogan - Appointments made easy
Description - Meet anyone you want with Meetly - the app that makes booking appointments easy.
f
Logo link: https://looka.com/editor/156460361

Domain names:
- getmeet.ly - 75 USD
- meetly.link - 41.3 USD
- meetly.cc - 6 USD


---

Фича №1: Создать ссылку для бронирования

Сценарий 1
- Открывается страница https://meetly.cc/new (как webApp)
- Пользователь вводит:
    - date range
        - X days into the future
        - dateFrom & dateTo
        - Indefinitely into the future
    - duration
        - hours and minutes
    - schedule
        - working hours per day
    - user's timezone
- Нажимает Создать
    - создается Link { id: link-id, userId: ..., dateRange: ..., duration: ..., schedule: ..., timezone: ... }

---

- 2 вида ссылок
    - https://t.me/meetly_bot/app?startapp=l_%link-id%
    - https://go.meetly.cc/link-id (редирект на direct link)

---
    
Фича №2: Открыть ссылку и забронировать слот
- Пользователь выбирает слот
- Указывает свой email
- Отправляет запрос
    - создается Appointment { id, linkId: ..., userId: ..., email: ..., timezone: ... }
- Хост получает нотификацию о новом запросе

---

Фича №3: Подтверждение запроса
- Хост открывает уведомление в чате с ботом
- 

---

## TODO

- [x] - встроить telegram.js на страницу
- [x] - детекция tgUserId на странице (пробросить из telegram.js)
- [x] - поддержать роутинг по deeplink
    - с linkId -> на страницу createNewAppointment
    - иначе -> на страницу createNewLink
- [x] - отправка уведомления хосту при создании Appointment
    - добавления поля isApproved в Appointment
    - обновляем уведомление при апруве
    - isApproved=true
    - шлем уведомление requestingUserId
- [x] - предсоздание User в базе
    - add firstName, username
- [x] - страница создания ссылки
- [..] - стилизация обеих страниц (DaisyUI)
    ~~- вынести timezone selector в правый верхний угол~~
    - [x] - интеграция кнопок MainButton / BackButton
    - [..] - **страница создания встречи**
        - [x] - XX добавить в шапку "Book an appointment with Ivan Ivanov" (**useEnsureLink**)
        - [x] - открываем введение каждого поля поочередно, чтобы в фокусе всегде был только один инпут (timezone, date, timeSlot, email)
        - [x] - XX success screen после создания appointment
            - https://www.npmjs.com/package/react-confetti
        - [x] - XX link not found case
    - [..] - **страница создания ссылки**
        - [x] - XX шапка по стадиям с описаниями
        - [x] - XXX поддержать нерабочие дни
        - [x] - открываем введение каждого поля поочередно, чтобы в фокусе всегде был только один инпут (timezone, duration, schedule)
        - [x] - XX success screen после создания link
            - https://www.npmjs.com/package/react-confetti
    - [x] - XXX фикс цветов calendar
    - [x] - XXX фикс цветов timezone selector
    - [x] - XX фикс цветов main button (color, textColor)
    - [x] - XXXX ??? welcome screen
        - [x] показываем ссылку если есть (url, duration, schedule) или кнопку "Создать"
        ~~- [ ] react-icons~~
- [x] - XX присылать созданную ссылку ботом в чат
- [x] - XX добавить информацию о человеке и встрече в уведомления
- [x] - XXXXXX написать Readme
- [x] - XXX тестирование на разных клиентах
- [x] - XXXXX прогнать через light house, FCP/LCP, web-vitals метрики
- [x] - X remove Eruda
- [x] - XX remove TODOs & console.log()




## Доп фичи

- обернуть Client Components в Suspense
- requests auto-rejection by ttl
- предложить другое время
- поддержать dateRange для модели Link
~~- link id picker~~
- https://go.meetly.cc/link-id redirects to https://t.me/meetly_bot/app?startapp=l_%link-id%
- disable completely booked days в Calendar
- авторизация [через Google] + NextAuth.js https://blog.logrocket.com/using-next-js-route-handlers/#creating-secured-routes
- Connect Google Account
- Auto-add to Google Calendar
- Авторизация на meetly.cc через Telegram
- https://tanstack.com/query/latest/docs/react/guides/window-focus-refetching
- i18n
- Captcha
~~- requester не может создать несколько запросов на одно и то же время на разных ссылках~~
- add Sentry
~~- add HttpError~~
- WebApp.CloudStorage для Schedule
- написать интеграционные тесты
    - создание ссылки
    - запрос на встречу




## Useful links
- https://tkdodo.eu/blog/react-query-and-forms
- CORS https://blog.logrocket.com/using-cors-next-js-handle-cross-origin-requests/#enabling-cors-all-api-routes
- How to Deploy Grammy to Vercel https://github.com/grammyjs/examples/tree/main/setups/next-node
- useQuery invalidation https://tanstack.com/query/latest/docs/react/guides/query-invalidation



---

# Meetly


Данный репозиторий представляет из себя реализацию Telegram Mini Apps сервиса — Meetly, который является аналогом популярного сервиса [Calendly](https://calendly.com).

Meetly — это [Telegram бот](https://t.me/meetly_bot) + [Mini App](https://t.me/meetly_bot/app), который позволяет создать персональную ссылку и использовать ее для приема запросов на встречи.

## Описание бизнес-логики

Каждый пользователь может иметь единственную ссылку. При повторном создании ссылки она будет обновлена с предоставленным расписанием и интервалом.

**Ссылка для отправки запросов**

Созданная ссылка содержит в себе:
- рабочие часы на каждый день недели
- длительность отрезка времени бронирования
- таймзону в которой находится пользователь создавший ссылку

**Запрос на бронь времени**

- каждый запрос содержит email пользователя, отправившего запрос
- может быть подтвержден или отклонен пользователем создавшим ссылку (в результате ботом будут отпрвлены соответствующие уведомления)

### Features

- создание ссылки
    - любой пользователь Telegram может создать персональную ссылку
    - за ссылкой закрепляется расписание пользователя и длительность отрезка времени бронь
- отправка запроса на бронь
    - любой пользователь Telegram, которой перешел по уже созданной ссылке, попадает в интерфейс выбора даты и времени
    - доступные отрезки для бронирования отображаются в часовом поясе пользователя, отправляющего запрос
- уведомления в боте
    - уведомление о полученном запросе на бронь
    - уведомление о подтвержденном / отклоненном запросе
    - уведомление о создании / обновлении ссылки для броней
- запрос персональной ссылки через команду /link
    - в ответ бот пришлет уже созданную ссылку

### Use-cases

**Отправка запроса на бронь**

Для того, чтобы отправить запрос на бронь времени человека X, нужно запросить у него его персональную ссылку и перейти по ней внутри Telegram. Далее в открывшемся UI выбрать подходящие дату и время и указать email.

**Создание персональной ссылки для получения запросов**

Для того, чтобы предоставить доступ к свободным отрезками времени в своем расписании, необходимо зайти на [страницу создания ссылки](https://t.me/meetly_bot/app?startapp=new), ввести в расписание рабочание дни и часы, а также длительность отрезка, на который будет бронироваться время.


## Техническая реализация

Проект реализован на базе [Next.js](https://nextjs.org/) framework (v14). С его помощью как отдаются клиентские страницы, так и обрабатываются запросы в API.

Список специфичных инструментов и технологий использованных на сервере:
- PostgreSQL + [Prisma](https://www.prisma.io/) - для работы с данными
- [grammY](https://grammy.dev/) - для взаимодействия с Telegram Bots API

Для реализации современного, адаптивного клиентского интерфейса использовались:
- [react-query](https://tanstack.com/query/v3/)
- [react-hook-form](https://react-hook-form.com/)
- [react-day-picker](https://react-day-picker.js.org/)
- [react-timezone-select](https://github.com/ndom91/react-timezone-select)
- a также [Tailwind CSS](https://tailwindcss.com/) вместе с [daisyUI](https://daisyui.com/)

В качестве инфраструктуры для deployment решения был выбран популярный cloud-oriented сервис [Vercel](https://vercel.com/).

### Структура кода

- `/app/layout.tsx` - точка входа в клиентское приложение
- `/app/page.tsx` - основная SSR страница
- `/app/api/**/route.ts` - файлы с реализациями обработчикой CRUD запросов к основным сущностям
- `/app/api/webhook/route.ts` - webhook для получения обновлений от Telegram Bots API
- `/components/*` - реализация обших клиентских компонентов
- `/components/CreateNewAppointment/*` - компоненты реализующие логику отправки запроса на бронь
- `/components/CreateNewLink/*` - компоненты реализующие логику создания и обновления ссылки
- `/components/MainPage/index.tsx` - реализация компонента MainPageComponent, который является точкой входа в клиентском UI
- `/components/WelcomePage/*` - реализация страницы, которая открывается при нажатии кнопки [Menu](https://t.me/meetly_bot/app)
- `/lib/models/*` - типы описывающие все сущности
- `/lib/services/*` - бизнес-логика
- `/lib/bot.ts` - реализация Telegram бота [@meetly_bot](https://t.me/meetly_bot)
- `/prisma/schema.prisma` - описание DB сущностей и их отношений

## Ссылки

- https://t.me/meetly_bot - Telegram бот
- https://t.me/meetly_bot/app - Telegram Mini App