feat(admin): show common placeholders group in email template editor

Companion to the bot-side change: the variables hint now renders two
groups — type-specific vars and common ones ({service_name},
{cabinet_url}, {support_username}, {username}, {email}, {date}) that
work in every template. The block is visible even for types with no
vars of their own (previously it was hidden entirely, looking like
placeholders didn't exist). Common chips insert at cursor the same way.
This commit is contained in:
c0mrade
2026-06-10 23:37:31 +03:00
parent 81581f5c9b
commit 8b2501d908
6 changed files with 53 additions and 22 deletions

View File

@@ -15,6 +15,8 @@ export interface EmailTemplateType {
export interface EmailTemplateListResponse { export interface EmailTemplateListResponse {
items: EmailTemplateType[]; items: EmailTemplateType[];
available_languages: string[]; available_languages: string[];
/** Placeholders available in every template regardless of type */
common_context_vars: string[];
} }
export interface EmailTemplateLanguageData { export interface EmailTemplateLanguageData {
@@ -30,6 +32,8 @@ export interface EmailTemplateDetail {
label: Record<string, string>; label: Record<string, string>;
description: Record<string, string>; description: Record<string, string>;
context_vars: string[]; context_vars: string[];
/** Placeholders available in every template regardless of type */
common_context_vars?: string[];
languages: Record<string, EmailTemplateLanguageData>; languages: Record<string, EmailTemplateLanguageData>;
} }

View File

@@ -1547,7 +1547,8 @@
"insertDefault": "Insert default template", "insertDefault": "Insert default template",
"insertDefaultConfirm": "Replace the current content with the default template?", "insertDefaultConfirm": "Replace the current content with the default template?",
"testRecipientPlaceholder": "Recipient email (defaults to yours)", "testRecipientPlaceholder": "Recipient email (defaults to yours)",
"testHint": "Sends the current editor content with sample values in place of variables." "testHint": "Sends the current editor content with sample values in place of variables.",
"variablesCommon": "Common variables (work in all templates)"
}, },
"payments": { "payments": {
"title": "Payments", "title": "Payments",

View File

@@ -1420,7 +1420,8 @@
"insertDefault": "درج قالب پیش‌فرض", "insertDefault": "درج قالب پیش‌فرض",
"insertDefaultConfirm": "محتوای فعلی با قالب پیش‌فرض جایگزین شود؟", "insertDefaultConfirm": "محتوای فعلی با قالب پیش‌فرض جایگزین شود؟",
"testRecipientPlaceholder": "ایمیل گیرنده (پیش‌فرض: ایمیل شما)", "testRecipientPlaceholder": "ایمیل گیرنده (پیش‌فرض: ایمیل شما)",
"testHint": "محتوای فعلی ویرایشگر با مقادیر نمونه به جای متغیرها ارسال می‌شود." "testHint": "محتوای فعلی ویرایشگر با مقادیر نمونه به جای متغیرها ارسال می‌شود.",
"variablesCommon": "متغیرهای عمومی (در همه قالب‌ها کار می‌کنند)"
}, },
"wheel": { "wheel": {
"title": "تنظیمات چرخ شانس", "title": "تنظیمات چرخ شانس",

View File

@@ -1569,7 +1569,8 @@
"insertDefault": "Вставить шаблон по умолчанию", "insertDefault": "Вставить шаблон по умолчанию",
"insertDefaultConfirm": "Заменить текущее содержимое шаблоном по умолчанию?", "insertDefaultConfirm": "Заменить текущее содержимое шаблоном по умолчанию?",
"testRecipientPlaceholder": "Email получателя (по умолчанию — ваш)", "testRecipientPlaceholder": "Email получателя (по умолчанию — ваш)",
"testHint": "Отправит текущее содержимое редактора с примерами значений вместо переменных." "testHint": "Отправит текущее содержимое редактора с примерами значений вместо переменных.",
"variablesCommon": "Общие переменные (работают во всех шаблонах)"
}, },
"payments": { "payments": {
"title": "Платежи", "title": "Платежи",

View File

@@ -1461,7 +1461,8 @@
"insertDefault": "插入默认模板", "insertDefault": "插入默认模板",
"insertDefaultConfirm": "用默认模板替换当前内容?", "insertDefaultConfirm": "用默认模板替换当前内容?",
"testRecipientPlaceholder": "收件人邮箱(默认为您的邮箱)", "testRecipientPlaceholder": "收件人邮箱(默认为您的邮箱)",
"testHint": "发送当前编辑器内容,变量将替换为示例值。" "testHint": "发送当前编辑器内容,变量将替换为示例值。",
"variablesCommon": "通用变量(适用于所有模板)"
}, },
"payments": { "payments": {
"title": "支付", "title": "支付",

View File

@@ -362,24 +362,47 @@ function TemplateEditor({
/> />
</div> </div>
{/* Context variables hint */} {/* Context variables hint: type-specific + common (available in all templates) */}
{detail.context_vars.length > 0 && ( {(detail.context_vars.length > 0 || (detail.common_context_vars?.length ?? 0) > 0) && (
<div className="rounded-lg border border-dark-700 bg-dark-900/60 p-2.5 sm:p-3"> <div className="space-y-2.5 rounded-lg border border-dark-700 bg-dark-900/60 p-2.5 sm:p-3">
<p className="mb-1.5 text-xs font-medium text-dark-300"> {detail.context_vars.length > 0 && (
{t('admin.emailTemplates.variables')} <div>
</p> <p className="mb-1.5 text-xs font-medium text-dark-300">
<div className="flex flex-wrap gap-1 sm:gap-1.5"> {t('admin.emailTemplates.variables')}
{detail.context_vars.map((v) => ( </p>
<code <div className="flex flex-wrap gap-1 sm:gap-1.5">
key={v} {detail.context_vars.map((v) => (
className="cursor-pointer rounded bg-dark-700 px-2 py-0.5 font-mono text-xs text-accent-400 transition-colors hover:bg-dark-600" <code
title={t('admin.emailTemplates.clickToInsert')} key={v}
onClick={() => insertVariable(v)} className="cursor-pointer rounded bg-dark-700 px-2 py-0.5 font-mono text-xs text-accent-400 transition-colors hover:bg-dark-600"
> title={t('admin.emailTemplates.clickToInsert')}
{`{${v}}`} onClick={() => insertVariable(v)}
</code> >
))} {`{${v}}`}
</div> </code>
))}
</div>
</div>
)}
{(detail.common_context_vars?.length ?? 0) > 0 && (
<div>
<p className="mb-1.5 text-xs font-medium text-dark-400">
{t('admin.emailTemplates.variablesCommon')}
</p>
<div className="flex flex-wrap gap-1 sm:gap-1.5">
{detail.common_context_vars!.map((v) => (
<code
key={v}
className="cursor-pointer rounded bg-dark-800 px-2 py-0.5 font-mono text-xs text-dark-300 ring-1 ring-dark-600 transition-colors hover:bg-dark-700 hover:text-accent-400"
title={t('admin.emailTemplates.clickToInsert')}
onClick={() => insertVariable(v)}
>
{`{${v}}`}
</code>
))}
</div>
</div>
)}
</div> </div>
)} )}