From 93cfca46dc133662329aa0ec8196f91f15ba4b3a Mon Sep 17 00:00:00 2001 From: kewldan <40865857+kewldan@users.noreply.github.com> Date: Sun, 12 Jul 2026 19:30:48 +0300 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=D0=BF=D1=80=D0=B5=D0=B4=D1=83?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=BA=D0=B5=20=D0=B4?= =?UTF-8?q?=D0=BB=D0=B8=D0=BD=D0=BD=D1=8B=D1=85=20=D1=82=D0=B5=D0=BA=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=20?= =?UTF-8?q?=D0=B2=20=D0=B1=D0=BE=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кабинет, в отличие от админки бота (лимит 4000 символов), позволяет сохранять юридические тексты любой длины — и до парного фикса в боте (BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot#3049) текст длиннее ~4096 символов ронял показ правил и политики при /start (MESSAGE_TOO_LONG). Теперь под текстовым полем политики, оферты и правил постоянно показывается счётчик символов, а для текстов длиннее одного сообщения — предупреждение с оценкой, на сколько сообщений бот разобьёт текст. Оценка зеркалит жадную упаковку абзацев из split_telegram_text бота (порог 3500 символов на кусок). Предупреждение не показывается там, где оно неуместно: - «Рекуррентные платежи» — документ не отображается в боте вовсе; - режим отображения «Только веб» — бот документ не показывает. Переводы добавлены в ru/en (с плюральными формами); остальные локали используют английский fallback через defaultValue. --- src/locales/en.json | 3 ++ src/locales/ru.json | 4 +++ src/pages/AdminLegalPages.tsx | 61 +++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/src/locales/en.json b/src/locales/en.json index 801f63c..972b711 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -4196,6 +4196,9 @@ "language": "Content language", "content": "Content", "contentPlaceholder": "HTML page content...", + "charCount": "Characters: {{count}}", + "botSplitEstimate_one": "⚠️ The bot will show it split into ~{{count}} message", + "botSplitEstimate_other": "⚠️ The bot will show it split into ~{{count}} messages", "save": "Save", "saving": "Saving...", "saveError": "Failed to save. Please try again.", diff --git a/src/locales/ru.json b/src/locales/ru.json index 92b5149..16b33d0 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -4741,6 +4741,10 @@ "language": "Язык контента", "content": "Текст", "contentPlaceholder": "HTML-текст страницы...", + "charCount": "Символов: {{count}}", + "botSplitEstimate_one": "⚠️ В боте будет показано по частям: ~{{count}} сообщение", + "botSplitEstimate_few": "⚠️ В боте будет показано по частям: ~{{count}} сообщения", + "botSplitEstimate_many": "⚠️ В боте будет показано по частям: ~{{count}} сообщений", "save": "Сохранить", "saving": "Сохранение...", "saveError": "Не удалось сохранить. Попробуйте ещё раз.", diff --git a/src/pages/AdminLegalPages.tsx b/src/pages/AdminLegalPages.tsx index d2c8508..0549a13 100644 --- a/src/pages/AdminLegalPages.tsx +++ b/src/pages/AdminLegalPages.tsx @@ -20,6 +20,62 @@ const DISPLAY_MODES: LegalDisplayMode[] = ['bot', 'web', 'both']; type DocumentKind = 'privacy-policy' | 'public-offer' | 'recurrent-payments'; +// Bot chunk/page size (split_telegram_text max_length): longer texts are +// delivered by the bot in several messages / paginated pages +const TELEGRAM_SPLIT_THRESHOLD = 3500; + +// Mirrors the bot's split_telegram_text greedy paragraph packing to estimate +// how many messages/pages the bot will produce for this text +function estimateTelegramParts(text: string): number { + const normalized = text.replace(/\r\n/g, '\n').trim(); + if (!normalized) return 0; + if (normalized.length <= TELEGRAM_SPLIT_THRESHOLD) return 1; + const paragraphs = normalized.split('\n\n').filter((p) => p.trim()); + let parts = 0; + let current = ''; + for (const paragraph of paragraphs) { + const candidate = current ? `${current}\n\n${paragraph}` : paragraph; + if (candidate.length <= TELEGRAM_SPLIT_THRESHOLD) { + current = candidate; + continue; + } + if (current) { + parts += 1; + current = ''; + } + if (paragraph.length <= TELEGRAM_SPLIT_THRESHOLD) { + current = paragraph; + } else { + parts += Math.ceil(paragraph.length / TELEGRAM_SPLIT_THRESHOLD); + } + } + if (current) parts += 1; + return parts; +} + +function ContentLengthMeta({ text, botVisible }: { text: string; botVisible: boolean }) { + const { t } = useTranslation(); + const parts = botVisible ? estimateTelegramParts(text) : 0; + return ( +
+ + {t('admin.legalPages.charCount', { + count: text.length, + defaultValue: 'Characters: {{count}}', + })} + + {parts > 1 && ( + + {t('admin.legalPages.botSplitEstimate', { + count: parts, + defaultValue: '⚠️ The bot will show it split into ~{{count}} messages', + })} + + )} +
+ ); +} + const DOCUMENT_API: Record< DocumentKind, { @@ -236,6 +292,10 @@ function DocumentEditor({ className="input min-h-[320px] w-full font-mono text-sm" placeholder={t('admin.legalPages.contentPlaceholder')} /> + {saveError &&

{saveError}

}