235 lines
12 KiB
Plaintext
235 lines
12 KiB
Plaintext
@page
|
||
@model ListmonkIntegration.Pages.Templates.CreateModel
|
||
@{
|
||
ViewData["Title"] = "Создание шаблона письма";
|
||
}
|
||
|
||
<h1>Создание шаблона письма</h1>
|
||
<hr />
|
||
|
||
@if (TempData["SuccessMessage"] != null)
|
||
{
|
||
<div class="alert alert-success">
|
||
@TempData["SuccessMessage"]
|
||
</div>
|
||
}
|
||
|
||
@if (TempData["ErrorMessage"] != null)
|
||
{
|
||
<div class="alert alert-danger">
|
||
@TempData["ErrorMessage"]
|
||
</div>
|
||
}
|
||
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<form method="post">
|
||
<div class="mb-3">
|
||
<label class="form-label">Название шаблона</label>
|
||
<input type="text" asp-for="TemplateName" class="form-control" required placeholder="Например: Приветственное письмо" />
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Тема письма</label>
|
||
<input type="text" asp-for="Subject" class="form-control" required placeholder="Например: Добро пожаловать, {{ .Subscriber.Name }}!" />
|
||
<div class="form-text">Можно использовать переменные: {{ .Subscriber.Name }}, {{ .Subscriber.Email }}, {{ .Subscriber.Attribs.ИмяАтрибута }}</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Выберите список рассылки (для просмотра атрибутов)</label>
|
||
<select asp-for="SelectedListId" class="form-select" asp-items="Model.Lists" id="listSelect">
|
||
<option value="">-- Выберите список --</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Текст письма</label>
|
||
<textarea asp-for="Body" id="editor" class="form-control" rows="15" placeholder="Введите текст письма здесь..."></textarea>
|
||
<div class="form-text">
|
||
Используйте редактор для форматирования текста. HTML код генерируется автоматически.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<button type="submit" class="btn btn-primary">Создать шаблон</button>
|
||
<a asp-page="/Campaigns/Index" class="btn btn-secondary">Отмена</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<!-- Кнопки для вставки переменных -->
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<h5>Быстрая вставка переменных</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="mb-2">
|
||
<label class="form-label small text-muted">Стандартные поля:</label>
|
||
<div class="btn-group-vertical w-100">
|
||
<button type="button" class="btn btn-sm btn-outline-primary mb-1" onclick="insertVariable('{{ .Subscriber.Name }}')">
|
||
Имя (Name)
|
||
</button>
|
||
<button type="button" class="btn btn-sm btn-outline-primary mb-1" onclick="insertVariable('{{ .Subscriber.Email }}')">
|
||
Email
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<hr />
|
||
|
||
<div id="attributesButtons">
|
||
<label class="form-label small text-muted">Атрибуты из списка:</label>
|
||
<p class="text-muted small">Выберите список для загрузки атрибутов</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Примеры использования -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h5>Пример использования</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<small class="text-muted">
|
||
Переменные подставляются автоматически при отправке письма каждому получателю.
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@section Scripts {
|
||
<script src="https://cdn.tiny.cloud/1/k6rorn68v4xqaesxvaroqlkyhfmncfoq8bxgxv3nd39szqrc/tinymce/8/tinymce.min.js" referrerpolicy="origin" crossorigin="anonymous"></script>
|
||
<script>
|
||
console.log('Script started');
|
||
|
||
// Глобальная переменная для хранения ссылки на редактор
|
||
window.tinymceEditor = null;
|
||
|
||
// Инициализация TinyMCE
|
||
tinymce.init({
|
||
selector: '#editor', // ИСПРАВЛЕНО: конкретный ID вместо всех textarea
|
||
api_key: 'k6rorn68v4xqaesxvaroqlkyhfmncfoq8bxgxv3nd39szqrc',
|
||
height: 400,
|
||
plugins: 'lists link table code bold italic underline',
|
||
toolbar: 'bold italic underline | h1 h2 h3 | bullist numlist | link unlink | code',
|
||
menubar: false,
|
||
statusbar: true,
|
||
branding: false,
|
||
content_style: 'body { font-family: Arial, sans-serif; font-size: 14px; }',
|
||
|
||
// ИСПРАВЛЕНО: добавлена setup функция для сохранения редактора
|
||
setup: function(editor) {
|
||
window.tinymceEditor = editor;
|
||
console.log('TinyMCE editor saved to window.tinymceEditor');
|
||
|
||
editor.on('change', function() {
|
||
tinymce.triggerSave();
|
||
});
|
||
},
|
||
|
||
init_instance_callback: function(editor) {
|
||
console.log('TinyMCE initialized successfully');
|
||
}
|
||
});
|
||
|
||
// Глобальная функция для вставки переменной
|
||
window.insertVariable = function(variable) {
|
||
console.log('insertVariable called with:', variable);
|
||
|
||
try {
|
||
// Проверяем, существует ли редактор
|
||
if (window.tinymceEditor && !window.tinymceEditor.destroyed) {
|
||
// Вставляем в TinyMCE
|
||
window.tinymceEditor.insertContent(variable);
|
||
window.tinymceEditor.focus();
|
||
console.log('✓ Inserted into TinyMCE editor');
|
||
} else if (window.tinymce && window.tinymce.editors.length > 0) {
|
||
// Альтернативный способ - берём первый редактор
|
||
var editor = window.tinymce.editors[0];
|
||
editor.insertContent(variable);
|
||
editor.focus();
|
||
console.log('✓ Inserted via tinymce.editors');
|
||
} else {
|
||
// Если TinyMCE не инициализирован, вставляем в textarea
|
||
var textarea = document.querySelector('textarea[name="Body"]');
|
||
if (textarea) {
|
||
var start = textarea.selectionStart || 0;
|
||
var end = textarea.selectionEnd || 0;
|
||
var text = textarea.value;
|
||
textarea.value = text.substring(0, start) + variable + text.substring(end);
|
||
textarea.focus();
|
||
console.log('✓ Inserted into textarea (TinyMCE not ready)');
|
||
} else {
|
||
console.error('✗ Textarea not found');
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('✗ Error inserting variable:', error);
|
||
}
|
||
};
|
||
|
||
console.log('Script loaded, waiting for DOM...');
|
||
|
||
// Загрузка атрибутов при выборе списка
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
console.log('DOM loaded');
|
||
|
||
const listSelect = document.getElementById('listSelect');
|
||
if (listSelect) {
|
||
listSelect.addEventListener('change', async function() {
|
||
const listId = this.value;
|
||
const attributesDiv = document.getElementById('attributesButtons');
|
||
console.log('List selected, ID:', listId);
|
||
|
||
if (listId) {
|
||
attributesDiv.innerHTML = '<label class="form-label small text-muted">Атрибуты из списка:</label><div class="text-center"><div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Загрузка...</span></div></div>';
|
||
try {
|
||
const response = await fetch(`/api/lists/${listId}/attributes`);
|
||
console.log('API response status:', response.status);
|
||
if (response.ok) {
|
||
const attributes = await response.json();
|
||
console.log('Attributes loaded:', attributes);
|
||
displayAttributeButtons(attributes);
|
||
} else {
|
||
attributesDiv.innerHTML = '<label class="form-label small text-muted">Атрибуты из списка:</label><p class="text-danger small">Ошибка загрузки атрибутов.</p>';
|
||
}
|
||
} catch (error) {
|
||
console.error('Error loading attributes:', error);
|
||
attributesDiv.innerHTML = '<label class="form-label small text-muted">Атрибуты из списка:</label><p class="text-danger small">Ошибка загрузки атрибутов.</p>';
|
||
}
|
||
} else {
|
||
attributesDiv.innerHTML = '<label class="form-label small text-muted">Атрибуты из списка:</label><p class="text-muted small">Выберите список для загрузки атрибутов</p>';
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// Отображение кнопок для атрибутов
|
||
window.displayAttributeButtons = function(attributes) {
|
||
console.log('displayAttributeButtons called with:', attributes);
|
||
const attributesDiv = document.getElementById('attributesButtons');
|
||
|
||
if (attributes && attributes.length > 0) {
|
||
let html = '<label class="form-label small text-muted">Атрибуты из списка:</label><div class="d-flex flex-wrap gap-1">';
|
||
attributes.forEach(attr => {
|
||
const variable = `{{ .Subscriber.Attribs.${attr} }}`;
|
||
console.log('Creating button for attr:', attr, 'variable:', variable);
|
||
// Экранируем специальные символы для onclick
|
||
const escapedVar = variable.replace(/'/g, "\\'").replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
||
html += `<button type="button" class="btn btn-sm btn-outline-secondary m-1" onclick="insertVariable('${escapedVar}')">
|
||
${attr}
|
||
</button>`;
|
||
});
|
||
html += '</div>';
|
||
attributesDiv.innerHTML = html;
|
||
console.log('Buttons created');
|
||
} else {
|
||
attributesDiv.innerHTML = '<label class="form-label small text-muted">Атрибуты из списка:</label><p class="text-muted small">У выбранного списка нет пользовательских атрибутов.</p>';
|
||
}
|
||
};
|
||
|
||
console.log('All scripts initialized');
|
||
</script>
|
||
} |