Compare commits

..

5 Commits

Author SHA1 Message Date
vchikalkin
ae92b81dfe 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:14 +03:00
vchikalkin
8765c1d74d 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.
2025-10-14 15:31:53 +03:00
vchikalkin
7560b1702f fix build 2025-10-14 15:23:13 +03:00
vchikalkin
b5813e3b53 fix: update h2 styling in MDX components
- Changed h2 font weight from bold to semibold for improved visual hierarchy in rendered content.
2025-10-14 15:02:17 +03:00
vchikalkin
13d85436a7 convert /documents/privacy to .mdx 2025-10-14 14:59:58 +03:00
18 changed files with 585 additions and 563 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import { OfferLink, SupportLink } from '@/components/documents/links';
import { env } from '@/config/env'; import { env } from '@/config/env';
export const metadata = { export const metadata = {
@ -11,7 +12,7 @@ export const metadata = {
#### 1. Термины и определения #### 1. Термины и определения
1.1. Оферта — настоящий документ, постоянно размещенный в сети Интернет по адресу <a href={env.URL_OFFER}>{env.URL_OFFER}</a>. 1.1. Оферта — настоящий документ, постоянно размещенный в сети Интернет по адресу <OfferLink />.
1.2. Акцепт — полное и безоговорочное принятие условий Оферты Пользователем путем оплаты доступа через встроенный платежный бот ЮKassa в Telegram. 1.2. Акцепт — полное и безоговорочное принятие условий Оферты Пользователем путем оплаты доступа через встроенный платежный бот ЮKassa в Telegram.
@ -96,4 +97,6 @@ export const metadata = {
#### 10. Контакты #### 10. Контакты
Если у Вас есть вопросы по настоящему договору публичной оферты персональных данных, пожалуйста, свяжитесь с Разработчиком. Контакты указаны в описании бота. Если у Вас есть вопросы по настоящему договору публичной оферты персональных данных, пожалуйста, свяжитесь с Разработчиком:
- **Telegram:** <SupportLink/>

View File

@ -1,3 +1,6 @@
import { SupportLink } from '@/components/documents/links';
import { env } from '@/config/env';
export const metadata = { export const metadata = {
title: 'Политика конфиденциальности', title: 'Политика конфиденциальности',
description: description:
@ -31,7 +34,8 @@ export const metadata = {
3.2. Разработчик вправе изменять настоящую Политику — изменения вступают в силу с момента их публикации. Вы обязаны самостоятельно отслеживать обновления. 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.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.4. Вы гарантируете, что используете Сервис в соответствии с действующим законодательством и обладаете правом взаимодействовать с ним (например, достигли возраста, необходимого для использования услуг).
@ -128,4 +132,6 @@ export const metadata = {
#### 11. Контакты #### 11. Контакты
Если у Вас есть вопросы по Политике конфиденциальности или запросы в отношении персональных данных, пожалуйста, свяжитесь с Разработчиком. Контакты указаны в описании бота. Если у Вас есть вопросы по Политике конфиденциальности или запросы в отношении персональных данных, пожалуйста, свяжитесь с Разработчиком:
- **Telegram:** <SupportLink/>

View File

@ -2,8 +2,8 @@ import { env } from '@/config/env';
export function OfferLink() { export function OfferLink() {
return ( return (
<a href={env.URL_OFFER} rel="noreferrer" target="_blank"> <a href={env.OFFER_URL} rel="noreferrer" target="_blank">
{env.URL_OFFER} {env.OFFER_URL}
</a> </a>
); );
} }

View File

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

View File

@ -1,7 +1,39 @@
import { type MDXComponents } from 'mdx/types'; import { type MDXComponents } from 'mdx/types';
import Link from 'next/link';
const components = {} satisfies MDXComponents; /**
* Этот объект определяет, какие React-компоненты
* будут использоваться для рендеринга MDX-тегов (<a>, <h1>, <p> и т.д.)
* и какие кастомные компоненты будут доступны прямо в MDX.
*/
const components: MDXComponents = {
// 🔗 Заменяем стандартные <a> на <Link> Next.js
a: ({ children, href = '', ...props }) => {
// внутренние ссылки → через <Link>
if (href.startsWith('/')) {
return (
<Link href={href} {...props}>
{children}
</Link>
);
}
export function useMDXComponents(): MDXComponents { // внешние ссылки → target="_blank"
return components; return (
<a href={href} rel="noopener noreferrer" target="_blank" {...props}>
{children}
</a>
);
},
};
/**
* Эта функция вызывается автоматически при рендере MDX.
* Через неё Next.js App Router объединяет глобальные и локальные MDX-компоненты.
*/
export function useMDXComponents(componentsArgument: MDXComponents): MDXComponents {
return {
...components,
...componentsArgument, // позволяет переопределять внутри MDX
};
} }

View File

@ -36,7 +36,7 @@
"graphql": "catalog:", "graphql": "catalog:",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
"lucide-react": "catalog:", "lucide-react": "catalog:",
"next": "^15.5.9", "next": "^15.5.0",
"next-auth": "^4.24.11", "next-auth": "^4.24.11",
"next-themes": "^0.4.4", "next-themes": "^0.4.4",
"postcss": "catalog:", "postcss": "catalog:",

View File

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

1016
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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