Compare commits

...

13 Commits

Author SHA1 Message Date
vchikalkin
7ecf72656b chore: update type definitions and react dependencies to latest versions 2025-12-30 16:50:24 +03:00
vchikalkin
241effd3b8 fix build 2025-10-27 14:05:20 +03:00
vchikalkin
64c9134cc2 feat: add URL_FAQ environment variable and update bot localization
- Introduced a new environment variable URL_FAQ for FAQ links.
- Updated Russian localization to include a button for FAQ access.
- Modified the main menu to include a URL button for the FAQ section.
2025-10-27 13:43:22 +03:00
vchikalkin
19b53db5f3 refactor: rename OFFER_URL and PRIVACY_URL to URL_OFFER and URL_PRIVACY
- Updated environment variable names for consistency across the application.
- Modified references in the deployment workflow, bot conversations, and web components to use the new variable names.
2025-10-27 13:26:00 +03:00
vchikalkin
a26c0eab8a hotfix: getRemainingOrdersCount: add master to filter 2025-10-16 17:28:59 +03:00
vchikalkin
3ac86cfeb0 mdx: whitespace fix 2025-10-14 20:06:33 +03:00
vchikalkin
d895433a65 chore: update deploy.yml to enhance .env file creation process
- Renamed steps for clarity in the deployment workflow.
- Updated the creation of the .env files to use secrets for sensitive URLs instead of hardcoded values, improving security and flexibility.
2025-10-14 20:01:12 +03:00
vchikalkin
3064887ecf Revert "chore: update deploy.yml to create .env files with improved variable handling"
This reverts commit bdcd11d97e59affe3d16a65e81b142dc486b889e.
2025-10-14 19:59:11 +03:00
vchikalkin
02e9d5c529 refactor: update contact information in offer and privacy documents
- Simplified contact information sections in both the offer and privacy policy documents.
- Removed specific Telegram link references and replaced them with a general note directing users to the bot description for contact details.
2025-10-14 16:42:46 +03:00
vchikalkin
f45140ef04 remove comment 2025-10-14 16:30:13 +03:00
vchikalkin
bdcd11d97e chore: update deploy.yml to create .env files with improved variable handling
- Renamed steps for clarity in the deployment workflow.
- Updated the creation of the fake and real .env files to include necessary environment variables and secrets.
- Removed hardcoded URLs and replaced them with references to secrets for better security and flexibility.
2025-10-14 16:28:01 +03:00
vchikalkin
6a0d34d37b refactor: update MDX link handling and improve document formatting
- Removed the custom link component in MDX and replaced it with standard anchor tags for external links.
- Updated offer and privacy policy documents to use environment variables for dynamic URLs instead of custom components.
- Improved formatting for better readability in the offer and privacy policy sections.
2025-10-14 16:16:30 +03:00
Vlad Chikalkin
2df80c90f6
Feature/mdx (#128)
* convert /documents/privacy to .mdx

* fix: update h2 styling in MDX components

- Changed h2 font weight from bold to semibold for improved visual hierarchy in rendered content.

* fix build

* feat: implement public offer document and layout

- Added a new layout component for the public offer document.
- Created the public offer page in MDX format, detailing terms and conditions for service usage.
- Removed the old offer page in TSX format.
- Updated links for offer and support to a new shared component for better maintainability.
- Integrated Tailwind CSS typography plugin for improved text styling.

* fix: correct formatting in privacy policy terms section

- Adjusted the formatting of terms and definitions in the privacy policy to ensure consistent presentation and clarity.
- Removed unnecessary hyphenation in the definition of "Разработчик" and "Политика" for improved readability.
2025-10-14 15:43:51 +03:00
35 changed files with 1899 additions and 1235 deletions

View File

@ -37,7 +37,7 @@ jobs:
cache_proxy:
- 'apps/cache-proxy/**'
# -----------------------------------------------------------
- name: Create fake .env file for build
- name: Create .env file for build
run: |
echo "BOT_TOKEN=fake" > .env
echo "LOGIN_GRAPHQL=fake" >> .env
@ -48,9 +48,10 @@ jobs:
echo "BOT_URL=http://localhost:3000" >> .env
echo "REDIS_PASSWORD=fake" >> .env
echo "BOT_PROVIDER_TOKEN=fake" >> .env
echo "OFFER_URL=http://localhost:3000/offer" >> .env
echo "PRIVACY_URL=http://localhost:3000/privacy" >> .env
echo "SUPPORT_TELEGRAM_URL=http://t.me/support" >> .env
echo "SUPPORT_TELEGRAM_URL=${{ secrets.SUPPORT_TELEGRAM_URL }}" >> .env
echo "URL_OFFER=${{ secrets.URL_OFFER }}" >> .env
echo "URL_PRIVACY=${{ secrets.URL_PRIVACY }}" >> .env
echo "URL_FAQ=${{ secrets.URL_FAQ }}" >> .env
- name: Set image tags
id: vars
@ -115,7 +116,7 @@ jobs:
ssh -i ~/.ssh/id_rsa -p ${{ secrets.VPS_PORT }} -o StrictHostKeyChecking=no ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} "mkdir -p /home/${{ secrets.VPS_USER }}/zapishis"
# --- НОВОЕ: Шаг 2: Создание основного .env БЕЗ ТЕГОВ ---
- name: Create real .env file (No Tags)
- name: Create .env file for deploy
run: |
# Включаем все секреты, КРОМЕ тегов
echo "BOT_TOKEN=${{ secrets.BOT_TOKEN }}" > .env
@ -129,8 +130,9 @@ jobs:
echo "REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}" >> .env
echo "BOT_PROVIDER_TOKEN=${{ secrets.BOT_PROVIDER_TOKEN }}" >> .env
echo "SUPPORT_TELEGRAM_URL=${{ secrets.SUPPORT_TELEGRAM_URL }}" >> .env
echo "OFFER_URL=${{ secrets.OFFER_URL }}" >> .env
echo "PRIVACY_URL=${{ secrets.PRIVACY_URL }}" >> .env
echo "URL_OFFER=${{ secrets.URL_OFFER }}" >> .env
echo "URL_PRIVACY=${{ secrets.URL_PRIVACY }}" >> .env
echo "URL_FAQ=${{ secrets.URL_FAQ }}" >> .env
# --- НОВОЕ: Шаг 3: Создание файлов тегов (.project.env) ---
- name: Create Project Tag Env Files

View File

@ -55,6 +55,7 @@ btn-pro = 👑 Pro доступ
btn-subscribe = 👑 Приобрести Pro
btn-pro-info = Мой Pro доступ
btn-open-app = 📱 Открыть приложение
btn-faq = 📖 Инструкция
btn-documents = 📋 Документы
btn-back = ◀️ Назад

View File

@ -26,8 +26,8 @@ export async function addContact(conversation: Conversation<Context, Context>, c
combine(
t('msg-need-phone'),
t('share-phone-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
offerUrl: env.URL_OFFER,
privacyUrl: env.URL_PRIVACY,
}),
),
),
@ -42,8 +42,8 @@ export async function addContact(conversation: Conversation<Context, Context>, c
t('msg-send-client-contact-or-phone'),
t('msg-cancel-operation'),
t('share-contact-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
offerUrl: env.URL_OFFER,
privacyUrl: env.URL_PRIVACY,
}),
),
),

View File

@ -99,8 +99,8 @@ export async function subscription(conversation: Conversation<Context, Context>,
const agreementText = await conversation.external(({ t }) => {
return t('payment-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
offerUrl: env.URL_OFFER,
privacyUrl: env.URL_PRIVACY,
});
});

View File

@ -28,8 +28,8 @@ feature.command('start', logHandle('command-start'), async (ctx) => {
combine(
ctx.t('msg-welcome'),
ctx.t('share-phone-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
offerUrl: env.URL_OFFER,
privacyUrl: env.URL_PRIVACY,
}),
),
{

View File

@ -5,8 +5,8 @@ import { KEYBOARD_REMOVE } from '@/config/keyboards';
async function handler(ctx: Context) {
await ctx.reply(
ctx.t('agreement-links', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
offerUrl: env.URL_OFFER,
privacyUrl: env.URL_PRIVACY,
}),
{
...KEYBOARD_REMOVE,

View File

@ -4,8 +4,6 @@ export const envSchema = z.object({
BOT_PROVIDER_TOKEN: z.string(),
BOT_TOKEN: z.string(),
BOT_URL: z.string(),
OFFER_URL: z.string(),
PRIVACY_URL: z.string(),
RATE_LIMIT: z
.string()
.transform((value) => Number.parseInt(value, 10))
@ -20,6 +18,9 @@ export const envSchema = z.object({
.string()
.transform((value) => Number.parseInt(value, 10))
.default('6379'),
URL_FAQ: z.string(),
URL_OFFER: z.string(),
URL_PRIVACY: z.string(),
});
export const env = envSchema.parse(process.env);

View File

@ -58,6 +58,11 @@ export const mainMenu = new Menu<Context>('main-menu', { autoAnswer: true })
.row()
.text((ctx) => ctx.t('btn-documents'), handleDocuments)
.row()
.url(
(ctx) => ctx.t('btn-faq'),
() => env.URL_FAQ,
)
.row()
.url(
(ctx) => ctx.t('btn-open-app'),
() => {

View File

@ -0,0 +1,6 @@
import { DocumentsLayout } from '@/components/documents/layout';
import { type PropsWithChildren } from 'react';
export default function Layout({ children }: Readonly<PropsWithChildren>) {
return <DocumentsLayout title="Публичная оферта">{children}</DocumentsLayout>;
}

View File

@ -0,0 +1,99 @@
import { env } from '@/config/env';
export const metadata = {
title: 'Публичная оферта',
description: 'Публичная оферта бота / мини-приложения «Запишись.онлайн» (@zapishis_online_bot)',
};
### Договор-оферта на использование сервиса «Запишись.онлайн» (@zapishis_online_bot)
Настоящий документ является публичной офертой в соответствии с пунктом 2 статьи 437 Гражданского кодекса Российской Федерации и представляет собой предложение индивидуального предпринимателя (самозанятого) — далее именуемого «Администрация», заключить Договор на использование Сервиса (далее «Договор», «Оферта») с любым физическим лицом, принявшим условия настоящей Оферты (далее «Пользователь»).
#### 1. Термины и определения
1.1. Оферта — настоящий документ, постоянно размещенный в сети Интернет по адресу <a href={env.URL_OFFER}>{env.URL_OFFER}</a>.
1.2. Акцепт — полное и безоговорочное принятие условий Оферты Пользователем путем оплаты доступа через встроенный платежный бот ЮKassa в Telegram.
1.3. Сервис — Telegram-бот и мини-приложение, позволяющее пользователям создавать и принимать заказы, управлять расписанием и взаимодействовать друг с другом без необходимости регистрации.
1.4. Администрация — самозанятое лицо, являющееся разработчиком и правообладателем Сервиса.
1.5. Пользователь — любое физическое лицо, использующее Сервис в личных или профессиональных целях.
1.6. Доступ — право использования функционала Сервиса на определённый оплаченный период (например, неделя, месяц, год).
1.7. Оплата — денежные средства, перечисленные Пользователем через платёжный бот ЮKassa в Telegram.
#### 2. Акцепт оферты и заключение договора
2.1. Акцептом настоящей Оферты считается оплата Пользователем доступа к Сервису любым доступным способом.
2.2. С момента совершения оплаты Пользователь считается заключившим Договор с Администрацией на условиях, изложенных в настоящей Оферте.
2.3. Пользователь подтверждает, что ему понятны все условия настоящей Оферты и он принимает их без ограничений.
#### 3. Предмет договора
3.1. Администрация предоставляет Пользователю неисключительное право (доступ) на использование функционала Сервиса в пределах оплаченного периода времени.
3.2. Сервис предоставляется в онлайн-формате через Telegram-бота без установки дополнительного программного обеспечения.
3.3. Пользователь получает право использовать функционал Сервиса в личных целях, в том числе для организации и планирования заказов, встреч и тренировок.
#### 4. Порядок оплаты и использование
4.1. Оплата производится через встроенные инструменты Telegram-бота с использованием платёжной системы ЮKassa.
4.2. Комиссия платёжной системы включена в итоговую стоимость. Администрация не взимает дополнительных платежей.
4.3. Доступ активируется автоматически после успешного подтверждения оплаты.
4.4. Пользователь может продлить доступ путём повторной оплаты. Автоматическое продление не применяется.
4.5. Возврат денежных средств возможен только в случае технических ошибок, по письменному обращению на адрес поддержки.
#### 5. Права и обязанности сторон
5.1. Пользователь обязуется:
- не использовать Сервис в противоправных целях;
- не вмешиваться в работу Сервиса и не предпринимать действий, направленных на нарушение его функционирования;
- предоставлять достоверную информацию при оплате и использовании Сервиса;
- при добавлении контактов других лиц (например, клиентов, мастеров) гарантировать, что у него есть согласие этих лиц на передачу и обработку их персональных данных в рамках Сервиса;
5.2. Администрация обязуется:
- обеспечивать бесперебойную работу Сервиса, за исключением периодов технического обслуживания;
- обрабатывать персональные данные Пользователей в соответствии с Политикой конфиденциальности;
- принимать обращения и запросы Пользователей по вопросам работы Сервиса. 6. Ответственность сторон;
6.1. Сервис предоставляется «как есть». Администрация не несёт ответственности за временные сбои, потерю данных или недоступность Сервиса, возникшие по причинам, не зависящим от неё.
6.2. Пользователь несёт полную ответственность за корректность совершаемых платежей и действий, совершаемых через свой Telegram-аккаунт.
#### 7. Обработка персональных данных
7.1. Администрация обрабатывает персональные данные Пользователя в соответствии с Федеральным законом №152-ФЗ «О персональных данных» и Политикой конфиденциальности.
7.2. Использование Сервиса означает согласие Пользователя на обработку его персональных данных.
#### 8. Срок действия и расторжение договора
8.1. Договор вступает в силу с момента оплаты доступа и действует в течение оплаченного периода.
8.2. Пользователь может прекратить использование Сервиса в любое время без возврата оплаченных средств.
8.3. Администрация вправе приостановить доступ в случае нарушения Пользователем условий настоящей Оферты.
#### 9. Заключительные положения
9.1. Настоящий Договор регулируется законодательством Российской Федерации.
9.2. Все споры и разногласия решаются путём переговоров, а при недостижении соглашения — в судебном порядке по месту нахождения Администрации.
9.3. Администрация оставляет за собой право изменять условия Оферты с размещением новой редакции на сайте.
#### 10. Контакты
Если у Вас есть вопросы по настоящему договору публичной оферты персональных данных, пожалуйста, свяжитесь с Разработчиком. Контакты указаны в описании бота.

View File

@ -1,242 +0,0 @@
import { Container } from '@/components/layout';
import { PageHeader } from '@/components/navigation';
import { env } from '@/config/env';
export default function OfferPage() {
return (
<>
<PageHeader title="Публичная оферта" />
<Container className="prose prose-neutral dark:prose-invert">
<section className="mx-auto space-y-8">
<h1 className="text-2xl font-bold">
Договор-оферта на использование сервиса «Запишись.онлайн» (@zapishis_online_bot)
</h1>
<p className="mt-4">
Настоящий документ является публичной офертой в соответствии с пунктом 2 статьи 437
Гражданского кодекса Российской Федерации и представляет собой предложение
индивидуального предпринимателя (самозанятого) далее именуемого «Администрация»,
заключить Договор на использование Сервиса (далее «Договор», «Оферта») с любым
физическим лицом, принявшим условия настоящей Оферты (далее «Пользователь»).
</p>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">1. Термины и определения</h2>
<div className="space-y-3">
<p>
<strong>1.1.</strong> Оферта настоящий документ, постоянно размещенный в сети
Интернет по адресу <a href="#">{env.OFFER_URL}</a>.
</p>
<p>
<strong>1.2.</strong> Акцепт полное и безоговорочное принятие условий Оферты
Пользователем путем оплаты доступа через встроенный платежный бот ЮKassa в Telegram.
</p>
<p>
<strong>1.3.</strong> Сервис Telegram-бот и мини-приложение, позволяющее
пользователям создавать и принимать заказы, управлять расписанием и взаимодействовать
друг с другом без необходимости регистрации.
</p>
<p>
<strong>1.4.</strong> Администрация самозанятое лицо, являющееся разработчиком и
правообладателем Сервиса.
</p>
<p>
<strong>1.5.</strong> Пользователь любое физическое лицо, использующее Сервис в
личных или профессиональных целях.
</p>
<p>
<strong>1.6.</strong> Доступ право использования функционала Сервиса на определённый
оплаченный период (например, неделя, месяц, год).
</p>
<p>
<strong>1.7.</strong> Оплата денежные средства, перечисленные Пользователем через
платёжный бот ЮKassa в Telegram.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">2. Акцепт оферты и заключение договора</h2>
<div className="space-y-3">
<p>
<strong>2.1.</strong> Акцептом настоящей Оферты считается оплата Пользователем доступа
к Сервису любым доступным способом.
</p>
<p>
<strong>2.2.</strong> С момента совершения оплаты Пользователь считается заключившим
Договор с Администрацией на условиях, изложенных в настоящей Оферте.
</p>
<p>
<strong>2.3.</strong> Пользователь подтверждает, что ему понятны все условия настоящей
Оферты и он принимает их без ограничений.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">3. Предмет договора</h2>
<div className="space-y-3">
<p>
<strong>3.1.</strong> Администрация предоставляет Пользователю неисключительное право
(доступ) на использование функционала Сервиса в пределах оплаченного периода времени.
</p>
<p>
<strong>3.2.</strong> Сервис предоставляется в онлайн-формате через Telegram-бота без
установки дополнительного программного обеспечения.
</p>
<p>
<strong>3.3.</strong> Пользователь получает право использовать функционал Сервиса в
личных целях, в том числе для организации и планирования заказов, встреч и тренировок.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">4. Порядок оплаты и использование</h2>
<div className="space-y-3">
<p>
<strong>4.1.</strong> Оплата производится через встроенные инструменты Telegram-бота с
использованием платёжной системы ЮKassa.
</p>
<p>
<strong>4.2.</strong> Комиссия платёжной системы включена в итоговую стоимость.
Администрация не взимает дополнительных платежей.
</p>
<p>
<strong>4.3.</strong> Доступ активируется автоматически после успешного подтверждения
оплаты.
</p>
<p>
<strong>4.4.</strong> Пользователь может продлить доступ путём повторной оплаты.
Автоматическое продление не применяется.
</p>
<p>
<strong>4.5.</strong> Возврат денежных средств возможен только в случае технических
ошибок, по письменному обращению на адрес поддержки.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">5. Права и обязанности сторон</h2>
<div className="space-y-3">
<p>
<strong>5.1.</strong> Пользователь обязуется:
</p>
<ul className="list-inside list-disc space-y-1">
<li>не использовать Сервис в противоправных целях;</li>
<li>
не вмешиваться в работу Сервиса и не предпринимать действий, направленных на
нарушение его функционирования;
</li>
<li>предоставлять достоверную информацию при оплате и использовании Сервиса.</li>
<li>
при добавлении контактов других лиц (например, клиентов, мастеров) гарантировать,
что у него есть согласие этих лиц на передачу и обработку их персональных данных в
рамках Сервиса.
</li>
</ul>
<p>
<strong>5.2.</strong> Администрация обязуется:
</p>
<ul className="list-inside list-disc space-y-1">
<li>
обеспечивать бесперебойную работу Сервиса, за исключением периодов технического
обслуживания;
</li>
<li>
обрабатывать персональные данные Пользователей в соответствии с{' '}
<a href="/privacy">Политикой конфиденциальности</a>;
</li>
<li>принимать обращения и запросы Пользователей по вопросам работы Сервиса.</li>
</ul>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">6. Ответственность сторон</h2>
<div className="space-y-3">
<p>
<strong>6.1.</strong> Сервис предоставляется «как есть». Администрация не несёт
ответственности за временные сбои, потерю данных или недоступность Сервиса, возникшие
по причинам, не зависящим от неё.
</p>
<p>
<strong>6.2.</strong> Пользователь несёт полную ответственность за корректность
совершаемых платежей и действий, совершаемых через свой Telegram-аккаунт.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">7. Обработка персональных данных</h2>
<div className="space-y-3">
<p>
<strong>7.1.</strong> Администрация обрабатывает персональные данные Пользователя в
соответствии с Федеральным законом 152-ФЗ «О персональных данных» и{' '}
<a href="/privacy">Политикой конфиденциальности</a>.
</p>
<p>
<strong>7.2.</strong> Использование Сервиса означает согласие Пользователя на
обработку его персональных данных.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">8. Срок действия и расторжение договора</h2>
<div className="space-y-3">
<p>
<strong>8.1.</strong> Договор вступает в силу с момента оплаты доступа и действует в
течение оплаченного периода.
</p>
<p>
<strong>8.2.</strong> Пользователь может прекратить использование Сервиса в любое
время без возврата оплаченных средств.
</p>
<p>
<strong>8.3.</strong> Администрация вправе приостановить доступ в случае нарушения
Пользователем условий настоящей Оферты.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">9. Заключительные положения</h2>
<div className="space-y-3">
<p>
<strong>9.1.</strong> Настоящий Договор регулируется законодательством Российской
Федерации.
</p>
<p>
<strong>9.2.</strong> Все споры и разногласия решаются путём переговоров, а при
недостижении соглашения в судебном порядке по месту нахождения Администрации.
</p>
<p>
<strong>9.3.</strong> Администрация оставляет за собой право изменять условия Оферты с
размещением новой редакции на сайте.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">10. Контакты</h2>
<p>
Если у Вас есть вопросы по настоящему договору публичной оферты персональных данных,
пожалуйста, свяжитесь с Разработчиком:
</p>
<ul>
<li>
Telegram:{' '}
<strong>
<a href={env.SUPPORT_TELEGRAM_URL}>{env.SUPPORT_TELEGRAM_URL}</a>
</strong>
</li>
</ul>
<div className="h-10" />
</section>
</Container>
</>
);
}

View File

@ -0,0 +1,6 @@
import { DocumentsLayout } from '@/components/documents/layout';
import { type PropsWithChildren } from 'react';
export default function Layout({ children }: Readonly<PropsWithChildren>) {
return <DocumentsLayout title="Политика конфиденциальности">{children}</DocumentsLayout>;
}

View File

@ -0,0 +1,131 @@
export const metadata = {
title: 'Политика конфиденциальности',
description:
'Политика конфиденциальности бота / мини-приложения «Запишись.онлайн» (@zapishis_online_bot)',
};
### Политика конфиденциальности бота / мини-приложения «Запишись.онлайн» (@zapishis_online_bot)
#### 1. Термины и определения
- **Telegram** Telegram Messenger Inc. (платформа, на которой работает бот и мини-приложение).
- **Платформа** экосистема ботов и мини-приложений Telegram.
- **Разработчик** физическое лицо, самозанятый, владелец и оператор сервиса «Запишись.онлайн» (@zapishis_online_bot) - (далее — «Разработчик»).
- **Сторонний сервис** бот/мини-приложение Разработчика, предоставляемое в Платформе.
- **Пользователь** лицо, использующее Сторонний сервис через свою учетную запись Telegram (далее — «Вы»).
- **Политика** настоящий документ, регулирующий отношения между Разработчиком и Пользователем в части сбора и - обработки персональных данных.
#### 2. Общие положения
2.1. Настоящая Политика регулирует исключительно отношения между Разработчиком и Пользователем. Она не заменяет и не изменяет Политику конфиденциальности Telegram: [https://telegram.org/privacy](https://telegram.org/privacy).
2.2. Разработчик соблюдает применимые требования платформы Telegram к конфиденциальности и защите данных.
2.3. Использование Сервиса Пользователем и/или активация платного доступа означает согласие Пользователя с условиями настоящей Политики.
2.4. Если Вы не согласны с условиями Политики — прекратите использование Сервиса.
#### 3. Отказ от ответственности
3.1. Сторонний сервис является независимым приложением и не поддерживается, не одобряется и не аффилирован с Telegram (за исключением использования API и инфраструктуры Telegram).
3.2. Разработчик вправе изменять настоящую Политику — изменения вступают в силу с момента их публикации. Вы обязаны самостоятельно отслеживать обновления.
3.3. Используя Сервис, Вы подтверждаете, что ознакомлены и согласны с условиями использования Telegram для ботов и мини-приложений: [https://telegram.org/tos/bots](https://telegram.org/tos/bots), [https://telegram.org/tos/mini-apps](https://telegram.org/tos/mini-apps).
3.4. Вы гарантируете, что используете Сервис в соответствии с действующим законодательством и обладаете правом взаимодействовать с ним (например, достигли возраста, необходимого для использования услуг).
3.5. Вы обязуетесь предоставлять точную и актуальную информацию, если Сервис запрашивает её.
3.6. Любая информация, которую Вы делаете общедоступной самостоятельно (через профиль Telegram, публичные сообщения и т.п.), может стать доступна другим пользователям и не подпадает под защиту настоящей Политики в части конфиденциальности этой общедоступной информации.
#### 4. Сбор персональных данных
4.1. Telegram по умолчанию предоставляет сторонним сервисам ограниченный набор данных о Пользователе — подробнее: [https://telegram.org/privacy#6-bot-messages](https://telegram.org/privacy#6-bot-messages).
4.2. Сторонний сервис может дополнительно получать данные, которые Вы передаёте в чате бота или в мини-приложении (например, контакт, телефон), если Вы явно их отправляете.
4.3. В случае мини-приложения дополнительно могут передаваться данные в соответствии с правилами мини-приложений Telegram: [https://telegram.org/tos/mini-apps#4-privacy](https://telegram.org/tos/mini-apps#4-privacy).
4.4. Сторонний сервис может собирать также анонимную статистику использования (диагностика, события взаимодействия), не связываемую напрямую с персоной.
4.5. Пользователь может передавать данные третьих лиц (например, контактные данные клиентов или мастеров) для использования в Сервисе. При этом пользователь гарантирует, что эти лица дали согласие на обработку их персональных данных в рамках Сервиса.
#### 5. Какие данные мы собираем и как используем
5.1. Разработчик запрашивает, собирает и обрабатывает только те данные, которые необходимы для корректной работы функций Сервиса, в частности:
- Telegram ID и (опционально) отображаемое имя пользователя;
- телефон, только если Вы предоставили его добровольно (например, при регистрации);
- данные о заказах: дата/время, описание заказа, статус;
- информация о факте покупки Pro-доступа: период доступа, тип покупки (детали платёжной транзакции обрабатывает платёжный оператор — ЮKassa);
5.2. Цели обработки:
- предоставление и поддержка работы Сервиса (создание заказов, напоминания, управление доступом);
- подтверждение и учет оплат (взаимодействие с платёжным оператором для актуализации статуса доступа);
- реализация реферальной программы (хранение связей «кто пригласил/кого пригласили»);
- анализ использования и улучшение сервиса;
- выполнение юридических обязательств (хранение информации о транзакциях и др.);
> **Важно:** детальные платёжные данные (реквизиты карт и т.д.) не хранятся у Разработчика — их обрабатывает платёжный оператор (ЮKassa) и Telegram-платежный бот.
#### 6. Передача данных третьим лицам
6.1. Разработчик не передаёт персональные данные третьим лицам, за исключением следующих случаев:
- платёжному оператору (ЮKassa) и связанным службам для обработки платежей;
- Telegram как платформе для функционирования бота и мини-приложения;
- в случае необходимости — исполнителям, оказывающим техническую поддержку, при условии подписания ими обязательств о конфиденциальности;
- если передача требуется по закону (запросы уполномоченных органов и т.п.);
6.2. Разработчик не продаёт и не передаёт персональные данные для рекламных целей третьим лицам без Вашего отдельного согласия.
#### 7. Защита и хранение данных
7.1. Разработчик применяет разумные технические и организационные меры для защиты персональных данных (использование надежного VPS, ограничения доступа, резервное копирование и т.п.).
7.2. Доступ к персональным данным имеет только Разработчик (и/или доверенные исполнители технической поддержки при необходимости).
7.3. Данные хранятся на серверах, указанных Разработчиком. Если используются внешние сервисы/облачные хранилища — это будет указано в соответствующих местах Политики или сообщения при сборе данных.
#### 8. Права и обязанности сторон
8.1. Права Разработчика:
- вносить изменения в Политику с публикацией новой версии;
- ограничивать доступ к API/сервису при подозрении в злоупотреблениях;
- запросить подтверждение личности при необходимости обработки привилегированных запросов;
8.2. Обязанности Разработчика:
- обеспечивать доступность Политики и исполнять её условия;
- обрабатывать законные запросы пользователей о доступе, изменении или удалении данных в разумные сроки (не позднее 30 дней, если иное не установлено законом);
- соблюдать применимое законодательство о защите персональных данных;
8.3. Права Пользователя:
- запросить копию своих персональных данных, хранящихся у Разработчика;
- потребовать исправления неточных данных;
- потребовать удаления персональных данных в пределах, допустимых законом (с сохранением данных, необходимых для выполнения юридических обязательств, например, по учёту платежей);
- отозвать согласие на обработку персональных данных, если такое согласие предоставлялось добровольно;
- подать жалобу в уполномоченные органы по защите персональных данных, если считает, что его права нарушены;
8.4. Обязанности Пользователя:
- предоставлять точную и актуальную информацию;
- не использовать Сервис в нарушении законодательства и условий Telegram.
#### 9. Реклама и использование данных для аналитики
9.1. На текущем этапе Разработчик не использует персональные данные для демонстрации таргетированной рекламы третьих лиц без явного согласия Пользователя.
9.2. Разработчик может собирать агрегированную (анонимную) статистику использования Сервиса для улучшения функционала.
#### 10. Изменения Политики
10.1. Разработчик вправе вносить изменения в настоящую Политику. Все изменения публикуются на этой странице и вступают в силу с момента публикации.
#### 11. Контакты
Если у Вас есть вопросы по Политике конфиденциальности или запросы в отношении персональных данных, пожалуйста, свяжитесь с Разработчиком. Контакты указаны в описании бота.

View File

@ -1,266 +0,0 @@
import { Container } from '@/components/layout';
import { PageHeader } from '@/components/navigation';
import { env } from '@/config/env';
export default function PrivacyPolicyPage() {
return (
<>
<PageHeader title="Политика конфиденциальности" />
<Container className="prose prose-neutral md:prose-lg dark:prose-invert max-w-none">
<h1 className="text-2xl font-bold">
Политика конфиденциальности бота / мини-приложения «Запишись.онлайн»
(@zapishis_online_bot)
</h1>
<h2 className="text-2xl font-semibold">1. Термины и определения</h2>
<ol>
<li>
<strong>Telegram</strong> Telegram Messenger Inc. (платформа, на которой работает бот
и мини-приложение).
</li>
<li>
<strong>Платформа</strong> экосистема ботов и мини-приложений Telegram.
</li>
<li>
<strong>Разработчик</strong> физическое лицо, самозанятый, владелец и оператор сервиса
«Запишись.онлайн» (@zapishis_online_bot) (далее «Разработчик»).
</li>
<li>
<strong>Сторонний сервис</strong> бот/мини-приложение Разработчика, предоставляемое в
Платформе.
</li>
<li>
<strong>Пользователь</strong> лицо, использующее Сторонний сервис через свою учетную
запись Telegram (далее «Вы»).
</li>
<li>
<strong>Политика</strong> настоящий документ, регулирующий отношения между
Разработчиком и Пользователем в части сбора и обработки персональных данных.
</li>
</ol>
<h2 className="text-2xl font-semibold">2. Общие положения</h2>
<p>
2.1. Настоящая Политика регулирует исключительно отношения между Разработчиком и
Пользователем. Она не заменяет и не изменяет Политику конфиденциальности Telegram:{' '}
<a href="https://telegram.org/privacy">https://telegram.org/privacy</a>.
</p>
<p>
2.2. Разработчик соблюдает применимые требования платформы Telegram к конфиденциальности и
защите данных.
</p>
<p>
2.3. Использование Сервиса Пользователем и/или активация платного доступа означает
согласие Пользователя с условиями настоящей Политики.
</p>
<p>2.4. Если Вы не согласны с условиями Политики прекратите использование Сервиса.</p>
<h2 className="text-2xl font-semibold">3. Отказ от ответственности</h2>
<p>
3.1. Сторонний сервис является независимым приложением и не поддерживается, не одобряется
и не аффилирован с Telegram (за исключением использования API и инфраструктуры Telegram).
</p>
<p>
3.2. Разработчик вправе изменять настоящую Политику изменения вступают в силу с момента
их публикации. Вы обязаны самостоятельно отслеживать обновления.
</p>
<p>
3.3. Используя Сервис, Вы подтверждаете, что ознакомлены и согласны с условиями
использования Telegram для ботов и мини-приложений:{' '}
<a href="https://telegram.org/tos/bots">https://telegram.org/tos/bots</a>,{' '}
<a href="https://telegram.org/tos/mini-apps">https://telegram.org/tos/mini-apps</a>.
</p>
<p>
3.4. Вы гарантируете, что используете Сервис в соответствии с действующим
законодательством и обладаете правом взаимодействовать с ним (например, достигли возраста,
необходимого для использования услуг).
</p>
<p>
3.5. Вы обязуетесь предоставлять точную и актуальную информацию, если Сервис запрашивает
её.
</p>
<p>
3.6. Любая информация, которую Вы делаете общедоступной самостоятельно (через профиль
Telegram, публичные сообщения и т.п.), может стать доступна другим пользователям и не
подпадает под защиту настоящей Политики в части конфиденциальности этой общедоступной
информации.
</p>
<h2 className="text-2xl font-semibold">4. Сбор персональных данных</h2>
<p>
4.1. Telegram по умолчанию предоставляет сторонним сервисам ограниченный набор данных о
Пользователе подробнее:{' '}
<a href="https://telegram.org/privacy#6-bot-messages">
https://telegram.org/privacy#6-bot-messages
</a>
.
</p>
<p>
4.2. Сторонний сервис может дополнительно получать данные, которые Вы передаёте в чате
бота или в мини-приложении (например, контакт, телефон), если Вы явно их отправляете.
</p>
<p>
4.3. В случае мини-приложения дополнительно могут передаваться данные в соответствии с
правилами мини-приложений Telegram:{' '}
<a href="https://telegram.org/tos/mini-apps#4-privacy">
https://telegram.org/tos/mini-apps#4-privacy
</a>
.
</p>
<p>
4.4. Сторонний сервис может собирать также анонимную статистику использования
(диагностика, события взаимодействия), не связываемую напрямую с персоной.
</p>
<p>
4.5. Пользователь может передавать данные третьих лиц (например, контактные данные
клиентов или мастеров) для использования в Сервисе. При этом пользователь гарантирует, что
эти лица дали согласие на обработку их персональных данных в рамках Сервиса.
</p>
<h2 className="text-2xl font-semibold">5. Какие данные мы собираем и как используем</h2>
<p>
5.1. Разработчик запрашивает, собирает и обрабатывает только те данные, которые необходимы
для корректной работы функций Сервиса, в частности:
</p>
<ul>
<li>Telegram ID и (опционально) отображаемое имя пользователя;</li>
<li>телефон, только если Вы предоставили его добровольно (например, при регистрации);</li>
<li>данные о заказах: дата/время, описание заказа, статус;</li>
<li>
информация о факте покупки Pro-доступа: период доступа, тип покупки (детали платёжной
транзакции обрабатывает платёжный оператор ЮKassa).
</li>
</ul>
<p>5.2. Цели обработки:</p>
<ul>
<li>
предоставление и поддержка работы Сервиса (создание заказов, напоминания, управление
доступом);
</li>
<li>
подтверждение и учет оплат (взаимодействие с платёжным оператором для актуализации
статуса доступа);
</li>
<li>
реализация реферальной программы (хранение связей «кто пригласил/кого пригласили»);
</li>
<li>анализ использования и улучшение сервиса;</li>
<li>выполнение юридических обязательств (хранение информации о транзакциях и др.).</li>
</ul>
<p className="note">
Важно: детальные платёжные данные (реквизиты карт и т.д.) не хранятся у Разработчика их
обрабатывает платёжный оператор (ЮKassa) и Telegram-платежный бот.
</p>
<h2 className="text-2xl font-semibold">6. Передача данных третьим лицам</h2>
<p>
6.1. Разработчик не передаёт персональные данные третьим лицам, за исключением следующих
случаев:
</p>
<ul>
<li>платёжному оператору (ЮKassa) и связанным службам для обработки платежей;</li>
<li>Telegram как платформе для функционирования бота и мини-приложения;</li>
<li>
в случае необходимости исполнителям, оказывающим техническую поддержку, при условии
подписания ими обязательств о конфиденциальности;
</li>
<li>если передача требуется по закону (запросы уполномоченных органов и т.п.).</li>
</ul>
<p>
6.2. Разработчик не продаёт и не передаёт персональные данные для рекламных целей третьим
лицам без Вашего отдельного согласия.
</p>
<h2 className="text-2xl font-semibold">7. Защита и хранение данных</h2>
<p>
7.1. Разработчик применяет разумные технические и организационные меры для защиты
персональных данных (использование надежного VPS, ограничения доступа, резервное
копирование и т.п.).
</p>
<p>
7.2. Доступ к персональным данным имеет только Разработчик (и/или доверенные исполнители
технической поддержки при необходимости).
</p>
<p>
7.3. Данные хранятся на серверах, указанных Разработчиком. Если используются внешние
сервисы/облачные хранилища это будет указано в соответствующих местах Политики или
сообщения при сборе данных.
</p>
<h2 className="text-2xl font-semibold">8. Права и обязанности сторон</h2>
<p>8.1. Права Разработчика:</p>
<ul>
<li>вносить изменения в Политику с публикацией новой версии;</li>
<li>ограничивать доступ к API/сервису при подозрении в злоупотреблениях;</li>
<li>
запросить подтверждение личности при необходимости обработки привилегированных запросов.
</li>
</ul>
<p>8.2. Обязанности Разработчика:</p>
<ul>
<li>обеспечивать доступность Политики и исполнять её условия;</li>
<li>
обрабатывать законные запросы пользователей о доступе, изменении или удалении данных в
разумные сроки (не позднее 30 дней, если иное не установлено законом);
</li>
<li>соблюдать применимое законодательство о защите персональных данных.</li>
</ul>
<p>8.3. Права Пользователя:</p>
<ul>
<li>запросить копию своих персональных данных, хранящихся у Разработчика;</li>
<li>потребовать исправления неточных данных;</li>
<li>
потребовать удаления персональных данных в пределах, допустимых законом (с сохранением
данных, необходимых для выполнения юридических обязательств, например, по учёту
платежей);
</li>
<li>
отозвать согласие на обработку персональных данных, если такое согласие предоставлялось
добровольно;
</li>
<li>
подать жалобу в уполномоченные органы по защите персональных данных, если считает, что
его права нарушены.
</li>
</ul>
<p>8.4. Обязанности Пользователя:</p>
<ul>
<li>предоставлять точную и актуальную информацию;</li>
<li>не использовать Сервис в нарушении законодательства и условий Telegram.</li>
</ul>
<h2 className="text-2xl font-semibold">9. Реклама и использование данных для аналитики</h2>
<p>
9.1. На текущем этапе Разработчик не использует персональные данные для демонстрации
таргетированной рекламы третьих лиц без явного согласия Пользователя.
</p>
<p>
9.2. Разработчик может собирать агрегированную (анонимную) статистику использования
Сервиса для улучшения функционала.
</p>
<h2 className="text-2xl font-semibold">10. Изменения Политики</h2>
<p>
10.1. Разработчик вправе вносить изменения в настоящую Политику. Все изменения публикуются
на этой странице и вступают в силу с момента публикации.
</p>
<h2 className="text-2xl font-semibold">11. Контакты</h2>
<p>
Если у Вас есть вопросы по Политике конфиденциальности или запросы в отношении
персональных данных, пожалуйста, свяжитесь с Разработчиком:
</p>
<ul>
<li>
Telegram:{' '}
<strong>
<a href={env.SUPPORT_TELEGRAM_URL}>{env.SUPPORT_TELEGRAM_URL}</a>
</strong>
</li>
</ul>
<div className="h-10" />
</Container>
</>
);
}

View File

@ -2,11 +2,9 @@ import { AuthProvider } from '@/providers/auth';
import { ErrorProvider } from '@/providers/error';
import { QueryProvider } from '@/providers/query';
import { ThemeProvider } from '@/providers/theme-provider';
import { I18nProvider } from '@/utils/i18n/provider';
import '@repo/ui/globals.css';
import { cn } from '@repo/ui/lib/utils';
import { type Metadata } from 'next';
import { getLocale } from 'next-intl/server';
import { Inter } from 'next/font/google';
import { type PropsWithChildren } from 'react';
@ -17,19 +15,15 @@ export const metadata: Metadata = {
};
export default async function RootLayout({ children }: Readonly<PropsWithChildren>) {
const locale = await getLocale();
return (
<html lang={locale}>
<html lang="ru">
<body className={cn(inter.className, 'flex min-h-screen flex-col bg-app-background')}>
<ErrorProvider>
<I18nProvider>
<ThemeProvider>
<AuthProvider>
<QueryProvider>{children}</QueryProvider>
</AuthProvider>
</ThemeProvider>
</I18nProvider>
<ThemeProvider>
<AuthProvider>
<QueryProvider>{children}</QueryProvider>
</AuthProvider>
</ThemeProvider>
</ErrorProvider>
</body>
</html>

View File

@ -0,0 +1,18 @@
import { Container } from '@/components/layout';
import { PageHeader } from '@/components/navigation';
import { type PropsWithChildren } from 'react';
export function DocumentsLayout({
children,
title,
}: Readonly<PropsWithChildren> & { readonly title: string }) {
return (
<>
<PageHeader title={title} />
<Container className="prose prose-neutral md:prose-lg dark:prose-invert max-w-none">
{children}
</Container>
<div className="h-10" />
</>
);
}

View File

@ -0,0 +1,17 @@
import { env } from '@/config/env';
export function OfferLink() {
return (
<a href={env.URL_OFFER} rel="noreferrer" target="_blank">
{env.URL_OFFER}
</a>
);
}
export function SupportLink() {
return (
<a href={env.SUPPORT_TELEGRAM_URL} rel="noreferrer" target="_blank">
{env.SUPPORT_TELEGRAM_URL}
</a>
);
}

View File

@ -4,8 +4,8 @@ import { z } from 'zod';
export const envSchema = z.object({
__DEV_TELEGRAM_ID: z.string().default(''),
BOT_URL: z.string(),
OFFER_URL: z.string(),
SUPPORT_TELEGRAM_URL: z.string(),
URL_OFFER: z.string(),
});
export const env = envSchema.parse(process.env);

View File

@ -0,0 +1,7 @@
import { type MDXComponents } from 'mdx/types';
const components = {} satisfies MDXComponents;
export function useMDXComponents(): MDXComponents {
return components;
}

View File

@ -1,12 +1,16 @@
import createNextIntlPlugin from 'next-intl/plugin';
import createMDX from '@next/mdx';
const withNextIntl = createNextIntlPlugin('./utils/i18n/i18n.ts');
const nextConfig = withNextIntl({
const nextConfig = createMDX({
extension: /\.mdx?$/u,
})({
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
mdxRs: true,
},
output: 'standalone',
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
reactStrictMode: true,
transpilePackages: ['@repo/ui'],
});

View File

@ -14,6 +14,9 @@
"test:e2e": "playwright test"
},
"dependencies": {
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^15.5.5",
"@playwright/test": "^1.49.1",
"@repo/graphql": "workspace:*",
"@repo/typescript-config": "workspace:*",
@ -22,9 +25,10 @@
"@tanstack/react-query": "^5.64.1",
"@telegram-apps/sdk-react": "^2.0.19",
"@testing-library/react": "^16.1.0",
"@types/mdx": "^2.0.13",
"@types/node": "catalog:",
"@types/react-dom": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "catalog:",
"dayjs": "catalog:",
@ -32,14 +36,13 @@
"graphql": "catalog:",
"jsdom": "^25.0.1",
"lucide-react": "catalog:",
"next": "^15.5.9",
"next-auth": "^4.24.11",
"next-intl": "^3.26.0",
"next-themes": "^0.4.4",
"next": "^15.5.0",
"postcss": "catalog:",
"radashi": "catalog:",
"react-dom": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"tailwindcss": "catalog:",
"typescript": "catalog:",
"use-debounce": "^10.0.4",

View File

@ -2,10 +2,8 @@
'use client';
import { useBackButton, useClientOnce, useDidMount, useViewport } from '@/hooks/telegram';
import { setLocale } from '@/utils/i18n/locale';
import { init } from '@/utils/telegram/init';
import { initData, useSignal } from '@telegram-apps/sdk-react';
import { type PropsWithChildren, useEffect } from 'react';
import { type PropsWithChildren } from 'react';
export function TelegramProvider(props: Readonly<PropsWithChildren>) {
// Unfortunately, Telegram Mini Apps does not allow us to use all features of
@ -31,12 +29,5 @@ function RootInner({ children }: PropsWithChildren) {
useViewport();
useBackButton();
const initDataUser = useSignal(initData.user);
// Set the user locale.
useEffect(() => {
if (initDataUser) setLocale(initDataUser.languageCode);
}, [initDataUser]);
return children;
}

View File

@ -1,6 +0,0 @@
{
"i18n": {
"header": "Application supports i18n",
"footer": "You can select a different language from the dropdown menu."
}
}

View File

@ -1,6 +0,0 @@
{
"i18n": {
"header": "Поддержка i18n",
"footer": "Вы можете выбрать другой язык в выпадающем меню."
}
}

View File

@ -1,10 +0,0 @@
export const defaultLocale = 'ru';
export const timeZone = 'Europe/Moscow';
export const locales = [defaultLocale, 'ru'] as const;
export const localesMap = [
{ key: 'en', title: 'English' },
{ key: 'ru', title: 'Русский' },
];

View File

@ -1,18 +0,0 @@
import { defaultLocale, locales } from './config';
import { getLocale } from './locale';
import { type Locale } from './types';
import { getRequestConfig } from 'next-intl/server';
const requestConfig = getRequestConfig(async () => {
const locale = (await getLocale()) as Locale;
return {
locale,
messages:
locale === defaultLocale || !locales.includes(locale)
? (await import(`@/public/locales/${defaultLocale}.json`)).default
: (await import(`@/public/locales/${locale}.json`)).default,
};
});
export default requestConfig;

View File

@ -1,20 +0,0 @@
// use server is required
'use server';
import { defaultLocale } from './config';
import { type Locale } from './types';
import { cookies } from 'next/headers';
// In this example the locale is read from a cookie. You could alternatively
// also read it from a database, backend service, or any other source.
const COOKIE_NAME = 'NEXT_LOCALE';
const getLocale = async () => {
return (await cookies()).get(COOKIE_NAME)?.value || defaultLocale;
};
const setLocale = async (locale?: string) => {
(await cookies()).set(COOKIE_NAME, (locale as Locale) || defaultLocale);
};
export { getLocale, setLocale };

View File

@ -1,14 +0,0 @@
/* eslint-disable canonical/id-match */
import { timeZone } from './config';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
import { type PropsWithChildren } from 'react';
export async function I18nProvider({ children }: Readonly<PropsWithChildren>) {
const messages = await getMessages();
return (
<NextIntlClientProvider messages={messages} timeZone={timeZone}>
{children}
</NextIntlClientProvider>
);
}

View File

@ -1,5 +0,0 @@
import { type locales } from './config';
type Locale = (typeof locales)[number];
export type { Locale };

View File

@ -341,6 +341,8 @@ export class SubscriptionsService extends BaseService {
const now = dayjs();
const { customer } = await this._getUser();
const { orders } = await ordersService.getOrders({
filters: {
datetime_end: {
@ -349,6 +351,13 @@ export class SubscriptionsService extends BaseService {
datetime_start: {
gte: now.startOf('month').toISOString(),
},
slot: {
master: {
documentId: {
eq: customer.documentId,
},
},
},
state: {
eq: GQL.Enum_Order_State.Completed,

View File

@ -40,6 +40,7 @@
"@radix-ui/react-select": "^2.1.4",
"@radix-ui/react-slot": "^1.1.1",
"@repo/typescript-config": "workspace:*",
"@tailwindcss/typography": "^0.5.19",
"@types/react": "catalog:",
"autoprefixer": "catalog:",
"class-variance-authority": "^0.7.1",

View File

@ -1,3 +1,4 @@
import typography from '@tailwindcss/typography';
import { type Config } from 'tailwindcss';
import tailwindcssAnimate from 'tailwindcss-animate';
@ -5,12 +6,12 @@ const config = {
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx,mdx}',
'./src/**/*.{ts,tsx}',
'../../packages/ui/src/**/*.{ts,tsx}',
],
darkMode: ['class'],
plugins: [tailwindcssAnimate],
plugins: [tailwindcssAnimate, typography],
prefix: '',
theme: {
container: {

2134
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@ packages:
catalog:
"@apollo/client": ^3.12.4
"@types/node": ^20
"@types/react": ^19.1.11
"@types/react-dom": ^19.1.8
"@types/react": ^19.1.17
"@types/react-dom": ^19.1.17
"@vchikalkin/eslint-config-awesome": ^2.2.2
autoprefixer: ^10.4.20
dayjs: ^1.11.3
@ -19,8 +19,8 @@ catalog:
postcss: ^8.4.49
postcss-load-config: ^6.0.1
prettier: ^3.2.5
react: ^19.1.1
react-dom: ^19.1.1
react: ^19.1.4
react-dom: ^19.1.4
radashi: ^12.5.1
rimraf: ^6.0.1
tailwindcss: ^3.4.15

View File

@ -18,8 +18,9 @@
"REDIS_HOST",
"REDIS_PORT",
"REDIS_PASSWORD",
"OFFER_URL",
"PRIVACY_URL"
"URL_OFFER",
"URL_PRIVACY",
"URL_FAQ"
]
},
"lint": {