1281 lines
63 KiB
Plaintext
1281 lines
63 KiB
Plaintext
<!-- Content Header (Page header) --><!DOCTYPE html>
|
||
|
||
<section class="content-header"><html lang="ru">
|
||
|
||
<div class="container-fluid"><head>
|
||
|
||
<div class="row mb-2"> <meta charset="UTF-8">
|
||
|
||
<div class="col-sm-6"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<h1><i class="fab fa-telegram mr-2 text-info"></i>Telegram Bot</h1> <title>Telegram Bot - SmartSolTech Admin</title>
|
||
|
||
</div>
|
||
|
||
<div class="col-sm-6"> <!-- Tailwind CSS -->
|
||
|
||
<ol class="breadcrumb float-sm-right"> <script src="https://cdn.tailwindcss.com"></script>
|
||
|
||
<li class="breadcrumb-item"><a href="/admin">Админ</a></li>
|
||
|
||
<li class="breadcrumb-item active">Telegram Bot</li> <!-- Font Awesome -->
|
||
|
||
</ol> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
||
</div>
|
||
|
||
</div> <!-- Custom CSS -->
|
||
|
||
</div> <link rel="stylesheet" href="/css/main.css">
|
||
|
||
</section> <link rel="stylesheet" href="/css/fixes.css">
|
||
|
||
</head>
|
||
|
||
<!-- Main content --><body class="bg-gray-100">
|
||
|
||
<section class="content">
|
||
|
||
<div class="container-fluid"> <!-- Main Content -->
|
||
|
||
<!-- Bot Status Card --> <main class="flex-1 p-8">
|
||
|
||
<div class="card"> <div class="space-y-6">
|
||
|
||
<div class="card-header"> <!-- Header -->
|
||
|
||
<h3 class="card-title">Статус бота</h3> <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
<div class="card-tools"> <div class="flex items-center justify-between">
|
||
|
||
<button id="refresh-status" class="btn btn-sm btn-default"> <div>
|
||
|
||
<i class="fas fa-sync-alt"></i> <h1 class="text-2xl font-bold text-gray-900 flex items-center">
|
||
|
||
</button> <i class="fab fa-telegram mr-3 text-blue-500"></i>
|
||
|
||
</div> Telegram Bot
|
||
|
||
</div> </h1>
|
||
|
||
<div class="card-body"> <p class="mt-2 text-gray-600">Настройка и управление уведомлениями через Telegram</p>
|
||
|
||
<div class="row"> </div>
|
||
|
||
<div class="col-md-6"> <div class="flex items-center space-x-3">
|
||
|
||
<div class="info-box"> <div class="flex items-center">
|
||
|
||
<span class="info-box-icon" id="status-icon"> <div class="w-3 h-3 rounded-full <%= botConfigured ? 'bg-green-500' : 'bg-red-500' %> mr-2"></div>
|
||
|
||
<i class="fas fa-question"></i> <span class="text-sm font-medium <%= botConfigured ? 'text-green-700' : 'text-red-700' %>">
|
||
|
||
</span> <%= botConfigured ? 'Подключен' : 'Не настроен' %>
|
||
|
||
<div class="info-box-content"> </span>
|
||
|
||
<span class="info-box-text">Статус подключения</span> </div>
|
||
|
||
<span class="info-box-number" id="status-text">Проверка...</span> </div>
|
||
|
||
</div> </div>
|
||
|
||
</div> </div>
|
||
|
||
</div>
|
||
|
||
<div class="col-md-6"> <!-- Bot Configuration -->
|
||
|
||
<div class="info-box"> <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
<span class="info-box-icon bg-info"> <h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
|
||
<i class="fab fa-telegram"></i> <i class="fas fa-cog mr-2 text-blue-500"></i>
|
||
|
||
</span> Конфигурация бота
|
||
|
||
<div class="info-box-content"> </h3>
|
||
|
||
<span class="info-box-text">Имя бота</span>
|
||
|
||
<span class="info-box-number" id="bot-name">Загрузка...</span> <form id="bot-config-form" class="space-y-6">
|
||
|
||
</div> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
|
||
</div> <!-- Bot Token -->
|
||
|
||
</div> <div>
|
||
|
||
</div> <label for="botToken" class="block text-sm font-medium text-gray-700 mb-2">
|
||
|
||
</div> Токен бота *
|
||
|
||
</div> </label>
|
||
|
||
<div class="relative">
|
||
|
||
<!-- Configuration Card --> <input type="password" id="botToken" name="botToken"
|
||
|
||
<div class="card"> value="<%= botToken %>"
|
||
|
||
<div class="card-header"> placeholder="1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||
|
||
<h3 class="card-title">Конфигурация</h3> class="block w-full pr-10 border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||
|
||
</div> <button type="button" onclick="toggleTokenVisibility()"
|
||
|
||
<form id="config-form"> class="absolute inset-y-0 right-0 pr-3 flex items-center">
|
||
|
||
<div class="card-body"> <i id="token-eye" class="fas fa-eye text-gray-400"></i>
|
||
|
||
<div class="form-group"> </button>
|
||
|
||
<label for="bot-token">Токен бота</label> </div>
|
||
|
||
<div class="input-group"> <p class="mt-1 text-sm text-gray-500">
|
||
|
||
<input type="password" class="form-control" id="bot-token" name="botToken" placeholder="Введите токен бота"> Получите токен от <a href="https://t.me/BotFather" target="_blank" class="text-blue-600 underline">@BotFather</a>
|
||
|
||
<div class="input-group-append"> </p>
|
||
|
||
<button type="button" class="btn btn-outline-secondary" onclick="toggleTokenVisibility()"> </div>
|
||
|
||
<i class="fas fa-eye" id="token-eye"></i>
|
||
|
||
</button> <!-- Default Chat ID -->
|
||
|
||
</div> <div>
|
||
|
||
</div> <label for="chatId" class="block text-sm font-medium text-gray-700 mb-2">
|
||
|
||
<small class="form-text text-muted"> ID чата по умолчанию
|
||
|
||
Получите токен у <a href="https://t.me/BotFather" target="_blank">@BotFather</a> </label>
|
||
|
||
</small> <input type="text" id="chatId" name="chatId"
|
||
|
||
</div> value="<%= chatId %>"
|
||
|
||
placeholder="-1001234567890"
|
||
|
||
<div class="form-group"> class="block w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||
|
||
<label for="admin-chat-id">ID чата администратора</label> <p class="mt-1 text-sm text-gray-500">
|
||
|
||
<input type="text" class="form-control" id="admin-chat-id" name="adminChatId" placeholder="Введите ID чата"> Оставьте пустым, если будете выбирать чат из списка
|
||
|
||
<small class="form-text text-muted"> </p>
|
||
|
||
Узнайте ваш ID чата у <a href="https://t.me/userinfobot" target="_blank">@userinfobot</a> </div>
|
||
|
||
</small> </div>
|
||
|
||
</div>
|
||
|
||
<div class="flex space-x-3">
|
||
|
||
<div class="form-group"> <button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
|
||
<div class="custom-control custom-switch"> <i class="fas fa-save mr-2"></i>
|
||
|
||
<input type="checkbox" class="custom-control-input" id="notifications-enabled" name="notificationsEnabled"> Сохранить настройки
|
||
|
||
<label class="custom-control-label" for="notifications-enabled">Включить уведомления</label> </button>
|
||
|
||
</div> <button type="button" onclick="getBotInfo()" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
|
||
</div> <i class="fas fa-info-circle mr-2"></i>
|
||
|
||
Получить информацию о боте
|
||
|
||
<div class="form-group"> </button>
|
||
|
||
<label>Типы уведомлений</label> </div>
|
||
|
||
<div class="row"> </form>
|
||
|
||
<div class="col-md-6">
|
||
|
||
<div class="custom-control custom-checkbox"> <div id="config-result" class="mt-4 hidden"></div>
|
||
|
||
<input type="checkbox" class="custom-control-input" id="notify-contacts" name="notifyContacts"> </div>
|
||
|
||
<label class="custom-control-label" for="notify-contacts">Новые контакты</label>
|
||
|
||
</div> <% if (botConfigured) { %>
|
||
|
||
<div class="custom-control custom-checkbox"> <!-- Bot Information -->
|
||
|
||
<input type="checkbox" class="custom-control-input" id="notify-orders" name="notifyOrders"> <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
<label class="custom-control-label" for="notify-orders">Новые заказы</label> <div class="flex items-center justify-between mb-6">
|
||
|
||
</div> <h3 class="text-lg font-semibold text-gray-900">
|
||
|
||
</div> <i class="fas fa-robot mr-2 text-green-500"></i>
|
||
|
||
<div class="col-md-6"> Информация о боте
|
||
|
||
<div class="custom-control custom-checkbox"> </h3>
|
||
|
||
<input type="checkbox" class="custom-control-input" id="notify-errors" name="notifyErrors"> <button onclick="refreshBotInfo()" class="text-blue-600 hover:text-blue-800 text-sm">
|
||
|
||
<label class="custom-control-label" for="notify-errors">Системные ошибки</label> <i class="fas fa-refresh mr-1"></i>
|
||
|
||
</div> Обновить
|
||
|
||
<div class="custom-control custom-checkbox"> </button>
|
||
|
||
<input type="checkbox" class="custom-control-input" id="notify-updates" name="notifyUpdates"> </div>
|
||
|
||
<label class="custom-control-label" for="notify-updates">Обновления системы</label>
|
||
|
||
</div> <div id="bot-info-container">
|
||
|
||
</div> <% if (botInfo) { %>
|
||
|
||
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||
|
||
</div> <div class="bg-gray-50 p-4 rounded-lg">
|
||
|
||
</div> <div class="text-sm text-gray-600">Имя бота</div>
|
||
|
||
<div class="card-footer"> <div class="text-lg font-semibold text-gray-900">@<%= botInfo.username %></div>
|
||
|
||
<button type="submit" class="btn btn-primary"> </div>
|
||
|
||
<i class="fas fa-save mr-1"></i>Сохранить <div class="bg-gray-50 p-4 rounded-lg">
|
||
|
||
</button> <div class="text-sm text-gray-600">Отображаемое имя</div>
|
||
|
||
<button type="button" class="btn btn-secondary ml-2" onclick="testConnection()"> <div class="text-lg font-semibold text-gray-900"><%= botInfo.first_name %></div>
|
||
|
||
<i class="fas fa-bolt mr-1"></i>Проверить подключение </div>
|
||
|
||
</button> <div class="bg-gray-50 p-4 rounded-lg">
|
||
|
||
</div> <div class="text-sm text-gray-600">ID бота</div>
|
||
|
||
</form> <div class="text-lg font-semibold text-gray-900"><%= botInfo.id %></div>
|
||
|
||
</div> </div>
|
||
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
|
||
<!-- Test Message Card --> <div class="text-sm text-gray-600">Может читать сообщения</div>
|
||
|
||
<div class="card"> <div class="text-lg font-semibold <%= botInfo.can_read_all_group_messages ? 'text-green-600' : 'text-red-600' %>">
|
||
|
||
<div class="card-header"> <%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %>
|
||
|
||
<h3 class="card-title">Тестовое сообщение</h3> </div>
|
||
|
||
</div> </div>
|
||
|
||
<div class="card-body"> </div>
|
||
|
||
<div class="form-group"> <% } else { %>
|
||
|
||
<label for="test-message">Сообщение</label> <div class="text-center py-4 text-gray-500">
|
||
|
||
<textarea class="form-control" id="test-message" rows="3" placeholder="Введите тестовое сообщение...">Тестовое сообщение от SmartSolTech Admin Panel</textarea> <i class="fas fa-robot text-4xl text-gray-300 mb-2"></i>
|
||
|
||
</div> <p>Настройте токен бота для получения информации</p>
|
||
|
||
<button type="button" class="btn btn-info" onclick="sendTestMessage()"> </div>
|
||
|
||
<i class="fas fa-paper-plane mr-1"></i>Отправить тест <% } %>
|
||
|
||
</button> </div>
|
||
|
||
</div> </div>
|
||
|
||
</div>
|
||
|
||
<!-- Available Chats -->
|
||
|
||
<!-- Webhook Configuration Card --> <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
<div class="card"> <div class="flex items-center justify-between mb-6">
|
||
|
||
<div class="card-header"> <h3 class="text-lg font-semibold text-gray-900">
|
||
|
||
<h3 class="card-title">Настройка Webhook</h3> <i class="fas fa-comments mr-2 text-blue-500"></i>
|
||
|
||
</div> Доступные чаты
|
||
|
||
<div class="card-body"> </h3>
|
||
|
||
<div class="form-group"> <button onclick="discoverChats()" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm transition-colors">
|
||
|
||
<label for="webhook-url">URL Webhook</label> <i class="fas fa-search mr-1"></i>
|
||
|
||
<div class="input-group"> Найти чаты
|
||
|
||
<input type="url" class="form-control" id="webhook-url" readonly> </button>
|
||
|
||
<div class="input-group-append"> </div>
|
||
|
||
<button type="button" class="btn btn-outline-secondary" onclick="copyWebhookUrl()">
|
||
|
||
<i class="fas fa-copy"></i> <div id="available-chats-container">
|
||
|
||
</button> <% if (availableChats && availableChats.length > 0) { %>
|
||
|
||
</div> <div class="grid gap-3">
|
||
|
||
</div> <% availableChats.forEach(chat => { %>
|
||
|
||
</div> <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
|
||
<div class="row"> <div class="flex items-center space-x-3">
|
||
|
||
<div class="col-md-6"> <div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
||
|
||
<button type="button" class="btn btn-success btn-block" onclick="setWebhook()"> <i class="fas <%= chat.type === 'group' || chat.type === 'supergroup' ? 'fa-users' : 'fa-user' %> text-blue-600 text-sm"></i>
|
||
|
||
<i class="fas fa-link mr-1"></i>Установить Webhook </div>
|
||
|
||
</button> <div>
|
||
|
||
</div> <div class="font-medium text-gray-900"><%= chat.title %></div>
|
||
|
||
<div class="col-md-6"> <div class="text-sm text-gray-500">
|
||
|
||
<button type="button" class="btn btn-warning btn-block" onclick="deleteWebhook()"> <%= chat.type %> • ID: <%= chat.id %>
|
||
|
||
<i class="fas fa-unlink mr-1"></i>Удалить Webhook <% if (chat.username) { %>• @<%= chat.username %><% } %>
|
||
|
||
</button> </div>
|
||
|
||
</div> </div>
|
||
|
||
</div> </div>
|
||
|
||
<div class="mt-3"> <button onclick="selectChat('<%= chat.id %>', '<%= chat.title %>')"
|
||
|
||
<small class="text-muted"> class="text-blue-600 hover:text-blue-800 text-sm">
|
||
|
||
Webhook позволяет боту получать обновления в реальном времени. Выбрать
|
||
|
||
URL автоматически генерируется на основе текущего домена. </button>
|
||
|
||
</small> </div>
|
||
|
||
</div> <% }); %>
|
||
|
||
</div> </div>
|
||
|
||
</div> <% } else { %>
|
||
|
||
<div class="text-center py-8 text-gray-500">
|
||
|
||
<!-- Activity Log Card --> <i class="fas fa-comments text-4xl text-gray-300 mb-4"></i>
|
||
|
||
<div class="card"> <p class="mb-2">Чаты не найдены</p>
|
||
|
||
<div class="card-header"> <p class="text-sm">Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"</p>
|
||
|
||
<h3 class="card-title">Журнал активности</h3> </div>
|
||
|
||
<div class="card-tools"> <% } %>
|
||
|
||
<button class="btn btn-sm btn-default" onclick="clearLog()"> </div>
|
||
|
||
<i class="fas fa-trash"></i> Очистить </div>
|
||
|
||
</button>
|
||
|
||
</div> <!-- Message Sender -->
|
||
|
||
</div> <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
<div class="card-body"> <h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
|
||
<div id="activity-log" style="max-height: 300px; overflow-y: auto;"> <i class="fas fa-paper-plane mr-2 text-purple-500"></i>
|
||
|
||
<!-- Log entries will be added here --> Отправить сообщение
|
||
|
||
</div> </h3>
|
||
|
||
</div>
|
||
|
||
</div> <form id="send-message-form" class="space-y-6">
|
||
|
||
</div> <!-- Message Content -->
|
||
|
||
</section> <div>
|
||
|
||
<label for="messageText" class="block text-sm font-medium text-gray-700 mb-2">
|
||
|
||
<!-- Scripts --> Текст сообщения *
|
||
|
||
<script> </label>
|
||
|
||
let activityLog = []; <textarea id="messageText" name="message" rows="4" required
|
||
|
||
placeholder="Введите сообщение..."
|
||
|
||
document.addEventListener('DOMContentLoaded', function() { class="block w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
|
||
|
||
loadConfig(); </div>
|
||
|
||
checkBotStatus();
|
||
|
||
generateWebhookUrl(); <!-- Recipients -->
|
||
|
||
initializeLog(); <div>
|
||
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
|
||
// Set up form submission Получатели
|
||
|
||
document.getElementById('config-form').addEventListener('submit', saveConfig); </label>
|
||
|
||
}); <div id="recipients-container" class="space-y-2">
|
||
|
||
<% if (availableChats && availableChats.length > 0) { %>
|
||
|
||
function addLog(message, type = 'info') { <% availableChats.forEach(chat => { %>
|
||
|
||
const timestamp = new Date().toLocaleString('ru-RU'); <label class="flex items-center">
|
||
|
||
const logEntry = { <input type="checkbox" name="chatIds" value="<%= chat.id %>"
|
||
|
||
timestamp, class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
|
||
message, <span class="ml-2 text-sm text-gray-900">
|
||
|
||
type <%= chat.title %>
|
||
|
||
}; <span class="text-gray-500">(<%= chat.type %>)</span>
|
||
|
||
</span>
|
||
|
||
activityLog.unshift(logEntry); </label>
|
||
|
||
if (activityLog.length > 100) { <% }); %>
|
||
|
||
activityLog = activityLog.slice(0, 100); <% } else { %>
|
||
|
||
} <div class="text-sm text-gray-500">
|
||
|
||
<i class="fas fa-info-circle mr-1"></i>
|
||
|
||
updateLogDisplay(); Сообщение будет отправлено в чат по умолчанию
|
||
|
||
} </div>
|
||
|
||
<% } %>
|
||
|
||
function updateLogDisplay() { </div>
|
||
|
||
const logContainer = document.getElementById('activity-log'); </div>
|
||
|
||
const typeColors = {
|
||
|
||
info: 'text-info', <!-- Message Options -->
|
||
|
||
success: 'text-success', <div class="bg-gray-50 p-4 rounded-lg">
|
||
|
||
error: 'text-danger', <h4 class="text-sm font-medium text-gray-900 mb-3">Настройки сообщения</h4>
|
||
|
||
warning: 'text-warning' <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||
|
||
}; <label class="flex items-center">
|
||
|
||
<input type="checkbox" name="disableWebPagePreview"
|
||
|
||
logContainer.innerHTML = activityLog.map(entry => ` class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
|
||
<div class="mb-2"> <span class="ml-2 text-sm text-gray-900">Отключить предпросмотр ссылок</span>
|
||
|
||
<small class="text-muted">[${entry.timestamp}]</small> </label>
|
||
|
||
<span class="${typeColors[entry.type] || 'text-info'}">${entry.message}</span> <label class="flex items-center">
|
||
|
||
</div> <input type="checkbox" name="disableNotification"
|
||
|
||
`).join(''); class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
|
||
} <span class="ml-2 text-sm text-gray-900">Тихое уведомление</span>
|
||
|
||
</label>
|
||
|
||
function initializeLog() { </div>
|
||
|
||
addLog('Telegram Bot панель загружена'); <div class="mt-4">
|
||
|
||
} <label for="parseMode" class="block text-sm font-medium text-gray-700 mb-1">
|
||
|
||
Формат текста
|
||
|
||
function clearLog() { </label>
|
||
|
||
if (confirm('Очистить журнал активности?')) { <select name="parseMode" id="parseMode"
|
||
|
||
activityLog = []; class="block w-full border border-gray-300 rounded-md px-3 py-2 text-sm">
|
||
|
||
updateLogDisplay(); <option value="HTML">HTML</option>
|
||
|
||
addLog('Журнал активности очищен'); <option value="Markdown">Markdown</option>
|
||
|
||
} <option value="">Обычный текст</option>
|
||
|
||
} </select>
|
||
|
||
</div>
|
||
|
||
async function loadConfig() { </div>
|
||
|
||
try {
|
||
|
||
const response = await fetch('/api/admin/telegram/config'); <!-- Submit Buttons -->
|
||
|
||
const data = await response.json(); <div class="flex justify-between">
|
||
|
||
<button type="button" onclick="previewMessage()"
|
||
|
||
if (data.success) { class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
||
|
||
const config = data.config; <i class="fas fa-eye mr-2"></i>
|
||
|
||
document.getElementById('bot-token').value = config.botToken || ''; Предпросмотр
|
||
|
||
document.getElementById('admin-chat-id').value = config.adminChatId || ''; </button>
|
||
|
||
document.getElementById('notifications-enabled').checked = config.notificationsEnabled || false; <div class="space-x-3">
|
||
|
||
document.getElementById('notify-contacts').checked = config.notifyContacts || false; <button type="button" onclick="testConnection()"
|
||
|
||
document.getElementById('notify-orders').checked = config.notifyOrders || false; class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
||
|
||
document.getElementById('notify-errors').checked = config.notifyErrors || false; <i class="fas fa-vial mr-2"></i>
|
||
|
||
document.getElementById('notify-updates').checked = config.notifyUpdates || false; Тест соединения
|
||
|
||
</button>
|
||
|
||
addLog('Конфигурация загружена'); <button type="submit" id="sendMessageBtn"
|
||
|
||
} class="inline-flex items-center px-6 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700">
|
||
|
||
} catch (error) { <i class="fas fa-paper-plane mr-2"></i>
|
||
|
||
console.error('Error loading config:', error); Отправить сообщение
|
||
|
||
addLog('Ошибка загрузки конфигурации: ' + error.message, 'error'); </button>
|
||
|
||
} </div>
|
||
|
||
} </div>
|
||
|
||
</form>
|
||
|
||
async function saveConfig(event) {
|
||
|
||
event.preventDefault(); <div id="send-result" class="mt-4 hidden"></div>
|
||
|
||
</div>
|
||
|
||
const formData = new FormData(event.target); <!-- Bot Status and Controls -->
|
||
|
||
const config = { <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
|
||
botToken: formData.get('botToken'), <!-- Connection Test -->
|
||
|
||
adminChatId: formData.get('adminChatId'), <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
notificationsEnabled: formData.has('notificationsEnabled'), <h3 class="text-lg font-semibold text-gray-900 mb-4">
|
||
|
||
notifyContacts: formData.has('notifyContacts'), <i class="fas fa-plug mr-2 text-green-500"></i>
|
||
|
||
notifyOrders: formData.has('notifyOrders'), Проверка подключения
|
||
|
||
notifyErrors: formData.has('notifyErrors'), </h3>
|
||
|
||
notifyUpdates: formData.has('notifyUpdates') <p class="text-gray-600 mb-4">
|
||
|
||
}; Отправить тестовое сообщение для проверки работоспособности бота.
|
||
|
||
</p>
|
||
|
||
try { <button id="test-connection" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
|
||
const response = await fetch('/api/admin/telegram/config', { <i class="fas fa-vial mr-2"></i>
|
||
|
||
method: 'POST', Тестировать подключение
|
||
|
||
headers: { </button>
|
||
|
||
'Content-Type': 'application/json' <div id="test-result" class="mt-4 hidden"></div>
|
||
|
||
}, </div>
|
||
|
||
body: JSON.stringify(config)
|
||
|
||
}); <!-- Send Custom Message -->
|
||
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
|
||
const data = await response.json(); <h3 class="text-lg font-semibold text-gray-900 mb-4">
|
||
|
||
<i class="fas fa-paper-plane mr-2 text-blue-500"></i>
|
||
|
||
if (data.success) { Отправить сообщение
|
||
|
||
addLog('Конфигурация сохранена', 'success'); </h3>
|
||
|
||
// Refresh bot status after saving config <form id="send-message-form">
|
||
|
||
setTimeout(checkBotStatus, 1000); <div class="mb-4">
|
||
|
||
} else { <label class="block text-sm font-medium text-gray-700 mb-2">
|
||
|
||
throw new Error(data.message); Сообщение
|
||
|
||
} </label>
|
||
|
||
} catch (error) { <textarea
|
||
|
||
console.error('Error saving config:', error); id="custom-message"
|
||
|
||
addLog('Ошибка сохранения конфигурации: ' + error.message, 'error'); rows="4"
|
||
|
||
} class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
|
||
} placeholder="Введите текст сообщения..."></textarea>
|
||
|
||
</div>
|
||
|
||
async function checkBotStatus() { <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||
|
||
try { <i class="fas fa-send mr-2"></i>
|
||
|
||
const response = await fetch('/api/admin/telegram/status'); Отправить
|
||
|
||
const data = await response.json(); </button>
|
||
|
||
</form>
|
||
|
||
const statusIcon = document.getElementById('status-icon'); <div id="send-result" class="mt-4 hidden"></div>
|
||
|
||
const statusText = document.getElementById('status-text'); </div>
|
||
|
||
const botName = document.getElementById('bot-name'); </div>
|
||
|
||
|
||
|
||
if (data.success && data.status.connected) { <!-- Notification Settings -->
|
||
|
||
statusIcon.className = 'info-box-icon bg-success'; <div class="bg-white shadow rounded-lg p-6">
|
||
|
||
statusIcon.innerHTML = '<i class="fas fa-check"></i>'; <h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
|
||
statusText.textContent = 'Подключен'; <i class="fas fa-bell mr-2 text-purple-500"></i>
|
||
|
||
botName.textContent = data.status.botInfo.first_name; Настройки уведомлений
|
||
|
||
addLog('Бот подключен: ' + data.status.botInfo.first_name, 'success'); </h3>
|
||
|
||
} else {
|
||
|
||
statusIcon.className = 'info-box-icon bg-danger'; <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
|
||
statusIcon.innerHTML = '<i class="fas fa-times"></i>'; <!-- Notification Types -->
|
||
|
||
statusText.textContent = 'Не подключен'; <div>
|
||
|
||
botName.textContent = 'Не подключен'; <h4 class="font-medium text-gray-900 mb-4">Типы уведомлений</h4>
|
||
|
||
addLog('Бот не подключен: ' + (data.message || 'Неизвестная ошибка'), 'error'); <div class="space-y-3">
|
||
|
||
} <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
|
||
} catch (error) { <div class="flex items-center">
|
||
|
||
console.error('Error checking bot status:', error); <i class="fas fa-envelope mr-3 text-blue-500"></i>
|
||
|
||
const statusIcon = document.getElementById('status-icon'); <span class="text-sm text-gray-900">Новые обращения</span>
|
||
|
||
const statusText = document.getElementById('status-text'); </div>
|
||
|
||
const botName = document.getElementById('bot-name'); <div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
|
||
</div>
|
||
|
||
statusIcon.className = 'info-box-icon bg-warning'; <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
|
||
statusIcon.innerHTML = '<i class="fas fa-exclamation"></i>'; <div class="flex items-center">
|
||
|
||
statusText.textContent = 'Ошибка проверки'; <i class="fas fa-calculator mr-3 text-green-500"></i>
|
||
|
||
botName.textContent = 'Ошибка'; <span class="text-sm text-gray-900">Расчеты стоимости</span>
|
||
|
||
addLog('Ошибка проверки статуса: ' + error.message, 'error'); </div>
|
||
|
||
} <div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
|
||
} </div>
|
||
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
|
||
async function testConnection() { <div class="flex items-center">
|
||
|
||
addLog('Проверка подключения...'); <i class="fas fa-briefcase mr-3 text-purple-500"></i>
|
||
|
||
await checkBotStatus(); <span class="text-sm text-gray-900">Новые проекты</span>
|
||
|
||
} </div>
|
||
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
|
||
async function sendTestMessage() { </div>
|
||
|
||
const message = document.getElementById('test-message').value; <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
|
||
if (!message.trim()) { <div class="flex items-center">
|
||
|
||
alert('Введите сообщение для отправки'); <i class="fas fa-cog mr-3 text-orange-500"></i>
|
||
|
||
return; <span class="text-sm text-gray-900">Новые услуги</span>
|
||
|
||
} </div>
|
||
|
||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||
|
||
try { </div>
|
||
|
||
addLog('Отправка тестового сообщения...'); </div>
|
||
|
||
</div>
|
||
|
||
const response = await fetch('/api/admin/telegram/test', {
|
||
|
||
method: 'POST', <!-- Bot Information -->
|
||
|
||
headers: { <div>
|
||
|
||
'Content-Type': 'application/json' <h4 class="font-medium text-gray-900 mb-4">Информация о боте</h4>
|
||
|
||
}, <div class="space-y-3">
|
||
|
||
body: JSON.stringify({ message }) <div class="flex justify-between">
|
||
|
||
}); <span class="text-sm text-gray-600">Статус:</span>
|
||
|
||
<span class="text-sm font-medium text-green-600">Активен</span>
|
||
|
||
const data = await response.json(); </div>
|
||
|
||
<div class="flex justify-between">
|
||
|
||
if (data.success) { <span class="text-sm text-gray-600">Токен:</span>
|
||
|
||
addLog('Тестовое сообщение отправлено', 'success'); <span class="text-sm font-mono text-gray-800">••••••••••</span>
|
||
|
||
} else { </div>
|
||
|
||
throw new Error(data.message); <div class="flex justify-between">
|
||
|
||
} <span class="text-sm text-gray-600">Chat ID:</span>
|
||
|
||
} catch (error) { <span class="text-sm font-mono text-gray-800">••••••••••</span>
|
||
|
||
console.error('Error sending test message:', error); </div>
|
||
|
||
addLog('Ошибка отправки сообщения: ' + error.message, 'error'); <div class="flex justify-between">
|
||
|
||
} <span class="text-sm text-gray-600">Последнее уведомление:</span>
|
||
|
||
} <span class="text-sm text-gray-600">Недавно</span>
|
||
|
||
</div>
|
||
|
||
function generateWebhookUrl() { </div>
|
||
|
||
const protocol = window.location.protocol; </div>
|
||
|
||
const host = window.location.host; </div>
|
||
|
||
const webhookUrl = `${protocol}//${host}/api/telegram/webhook`; </div>
|
||
|
||
document.getElementById('webhook-url').value = webhookUrl;
|
||
|
||
} <!-- Recent Notifications -->
|
||
|
||
<div class="bg-white shadow rounded-lg p-6">
|
||
|
||
async function setWebhook() { <h3 class="text-lg font-semibold text-gray-900 mb-6">
|
||
|
||
try { <i class="fas fa-history mr-2 text-gray-500"></i>
|
||
|
||
addLog('Установка webhook...'); Недавние уведомления
|
||
|
||
</h3>
|
||
|
||
const response = await fetch('/api/admin/telegram/webhook', {
|
||
|
||
method: 'POST' <div class="text-center py-8 text-gray-500">
|
||
|
||
}); <i class="fas fa-inbox text-4xl text-gray-300 mb-4"></i>
|
||
|
||
<p>Уведомления будут отображаться здесь после отправки</p>
|
||
|
||
const data = await response.json(); </div>
|
||
|
||
</div>
|
||
|
||
if (data.success) { <% } %>
|
||
|
||
addLog('Webhook установлен', 'success'); </div>
|
||
|
||
} else { </main>
|
||
|
||
throw new Error(data.message); </div>
|
||
|
||
}
|
||
|
||
} catch (error) { <!-- JavaScript -->
|
||
|
||
console.error('Error setting webhook:', error); <script src="/js/main.js"></script>
|
||
|
||
addLog('Ошибка установки webhook: ' + error.message, 'error');
|
||
|
||
} <script>
|
||
|
||
} // Bot Configuration
|
||
|
||
document.getElementById('bot-config-form').addEventListener('submit', async (e) => {
|
||
|
||
async function deleteWebhook() { e.preventDefault();
|
||
|
||
try {
|
||
|
||
addLog('Удаление webhook...'); const formData = new FormData(e.target);
|
||
|
||
const data = Object.fromEntries(formData);
|
||
|
||
const response = await fetch('/api/admin/telegram/webhook', {
|
||
|
||
method: 'DELETE' try {
|
||
|
||
}); showLoading('Сохранение настроек...');
|
||
|
||
|
||
|
||
const data = await response.json(); const response = await fetch('/admin/telegram/configure', {
|
||
|
||
method: 'POST',
|
||
|
||
if (data.success) { headers: {
|
||
|
||
addLog('Webhook удален', 'success'); 'Content-Type': 'application/json'
|
||
|
||
} else { },
|
||
|
||
throw new Error(data.message); body: JSON.stringify(data)
|
||
|
||
} });
|
||
|
||
} catch (error) {
|
||
|
||
console.error('Error deleting webhook:', error); const result = await response.json();
|
||
|
||
addLog('Ошибка удаления webhook: ' + error.message, 'error');
|
||
|
||
} if (result.success) {
|
||
|
||
} showResult('config-result', result.message, 'success');
|
||
|
||
// Update bot info if available
|
||
|
||
function copyWebhookUrl() { if (result.botInfo) {
|
||
|
||
const webhookInput = document.getElementById('webhook-url'); updateBotInfo(result.botInfo);
|
||
|
||
webhookInput.select(); }
|
||
|
||
document.execCommand('copy'); if (result.availableChats) {
|
||
|
||
updateAvailableChats(result.availableChats);
|
||
|
||
addLog('URL webhook скопирован в буфер обмена'); }
|
||
|
||
} } else {
|
||
|
||
showResult('config-result', result.message, 'error');
|
||
|
||
function toggleTokenVisibility() { }
|
||
|
||
const tokenInput = document.getElementById('bot-token'); } catch (error) {
|
||
|
||
const tokenEye = document.getElementById('token-eye'); showResult('config-result', 'Ошибка при сохранении настроек', 'error');
|
||
|
||
} finally {
|
||
|
||
if (tokenInput.type === 'password') { hideLoading();
|
||
|
||
tokenInput.type = 'text'; }
|
||
|
||
tokenEye.className = 'fas fa-eye-slash'; });
|
||
|
||
} else {
|
||
|
||
tokenInput.type = 'password'; // Send message form
|
||
|
||
tokenEye.className = 'fas fa-eye'; document.getElementById('send-message-form').addEventListener('submit', async (e) => {
|
||
|
||
} e.preventDefault();
|
||
|
||
}
|
||
|
||
</script> const formData = new FormData(e.target);
|
||
const message = formData.get('message');
|
||
const chatIds = formData.getAll('chatIds');
|
||
const parseMode = formData.get('parseMode');
|
||
const disableWebPagePreview = formData.has('disableWebPagePreview');
|
||
const disableNotification = formData.has('disableNotification');
|
||
|
||
if (!message.trim()) {
|
||
showResult('send-result', 'Введите текст сообщения', 'error');
|
||
return;
|
||
}
|
||
|
||
const data = {
|
||
message: message.trim(),
|
||
chatIds: chatIds.length > 0 ? chatIds : [],
|
||
parseMode,
|
||
disableWebPagePreview,
|
||
disableNotification
|
||
};
|
||
|
||
try {
|
||
const btn = document.getElementById('sendMessageBtn');
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Отправка...';
|
||
|
||
const response = await fetch('/admin/telegram/send', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showResult('send-result', result.message, 'success');
|
||
document.getElementById('messageText').value = '';
|
||
} else {
|
||
showResult('send-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('send-result', 'Ошибка при отправке сообщения', 'error');
|
||
} finally {
|
||
const btn = document.getElementById('sendMessageBtn');
|
||
btn.disabled = false;
|
||
btn.innerHTML = '<i class="fas fa-paper-plane mr-2"></i>Отправить сообщение';
|
||
}
|
||
});
|
||
|
||
// Utility functions
|
||
function toggleTokenVisibility() {
|
||
const input = document.getElementById('botToken');
|
||
const eye = document.getElementById('token-eye');
|
||
|
||
if (input.type === 'password') {
|
||
input.type = 'text';
|
||
eye.className = 'fas fa-eye-slash text-gray-400';
|
||
} else {
|
||
input.type = 'password';
|
||
eye.className = 'fas fa-eye text-gray-400';
|
||
}
|
||
}
|
||
|
||
async function getBotInfo() {
|
||
try {
|
||
showLoading('Получение информации о боте...');
|
||
|
||
const response = await fetch('/admin/telegram/info');
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
updateBotInfo(result.botInfo);
|
||
updateAvailableChats(result.availableChats);
|
||
showResult('config-result', 'Информация о боте обновлена', 'success');
|
||
} else {
|
||
showResult('config-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('config-result', 'Ошибка при получении информации о боте', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
async function refreshBotInfo() {
|
||
await getBotInfo();
|
||
}
|
||
|
||
async function discoverChats() {
|
||
try {
|
||
showLoading('Поиск доступных чатов...');
|
||
|
||
const response = await fetch('/admin/telegram/info');
|
||
const result = await response.json();
|
||
|
||
if (result.success && result.availableChats) {
|
||
updateAvailableChats(result.availableChats);
|
||
showNotification('Найдено чатов: ' + result.availableChats.length, 'success');
|
||
} else {
|
||
showNotification('Чаты не найдены', 'warning');
|
||
}
|
||
} catch (error) {
|
||
showNotification('Ошибка при поиске чатов', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
function selectChat(chatId, chatTitle) {
|
||
document.getElementById('chatId').value = chatId;
|
||
showNotification(`Выбран чат: ${chatTitle}`, 'success');
|
||
}
|
||
|
||
async function testConnection() {
|
||
try {
|
||
showLoading('Тестирование соединения...');
|
||
|
||
const response = await fetch('/admin/telegram/test', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showResult('send-result', 'Соединение успешно! Тестовое сообщение отправлено.', 'success');
|
||
} else {
|
||
showResult('send-result', result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showResult('send-result', 'Ошибка при тестировании соединения', 'error');
|
||
} finally {
|
||
hideLoading();
|
||
}
|
||
}
|
||
|
||
function previewMessage() {
|
||
const message = document.getElementById('messageText').value;
|
||
const parseMode = document.getElementById('parseMode').value;
|
||
|
||
if (!message.trim()) {
|
||
showNotification('Введите текст сообщения для предпросмотра', 'warning');
|
||
return;
|
||
}
|
||
|
||
// Simple preview modal (you can enhance this)
|
||
const preview = window.open('', 'preview', 'width=400,height=300');
|
||
preview.document.write(`
|
||
<html>
|
||
<head><title>Предпросмотр сообщения</title></head>
|
||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||
<h3>Предпросмотр сообщения (${parseMode || 'Обычный текст'})</h3>
|
||
<div style="border: 1px solid #ccc; padding: 10px; background: #f9f9f9;">
|
||
${parseMode === 'HTML' ? message : message.replace(/\n/g, '<br>')}
|
||
</div>
|
||
<button onclick="window.close()" style="margin-top: 10px;">Закрыть</button>
|
||
</body>
|
||
</html>
|
||
`);
|
||
}
|
||
|
||
// UI Helper functions
|
||
function updateBotInfo(botInfo) {
|
||
const container = document.getElementById('bot-info-container');
|
||
if (!botInfo) return;
|
||
|
||
container.innerHTML = `
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Имя бота</div>
|
||
<div class="text-lg font-semibold text-gray-900">@${botInfo.username}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Отображаемое имя</div>
|
||
<div class="text-lg font-semibold text-gray-900">${botInfo.first_name}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">ID бота</div>
|
||
<div class="text-lg font-semibold text-gray-900">${botInfo.id}</div>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg">
|
||
<div class="text-sm text-gray-600">Может читать сообщения</div>
|
||
<div class="text-lg font-semibold ${botInfo.can_read_all_group_messages ? 'text-green-600' : 'text-red-600'}">
|
||
${botInfo.can_read_all_group_messages ? 'Да' : 'Нет'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function updateAvailableChats(chats) {
|
||
const container = document.getElementById('available-chats-container');
|
||
if (!chats || chats.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="text-center py-8 text-gray-500">
|
||
<i class="fas fa-comments text-4xl text-gray-300 mb-4"></i>
|
||
<p class="mb-2">Чаты не найдены</p>
|
||
<p class="text-sm">Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const chatsHtml = chats.map(chat => `
|
||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||
<div class="flex items-center space-x-3">
|
||
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
||
<i class="fas ${chat.type === 'group' || chat.type === 'supergroup' ? 'fa-users' : 'fa-user'} text-blue-600 text-sm"></i>
|
||
</div>
|
||
<div>
|
||
<div class="font-medium text-gray-900">${chat.title}</div>
|
||
<div class="text-sm text-gray-500">
|
||
${chat.type} • ID: ${chat.id}
|
||
${chat.username ? '• @' + chat.username : ''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button onclick="selectChat('${chat.id}', '${chat.title}')"
|
||
class="text-blue-600 hover:text-blue-800 text-sm">
|
||
Выбрать
|
||
</button>
|
||
</div>
|
||
`).join('');
|
||
|
||
container.innerHTML = `<div class="grid gap-3">${chatsHtml}</div>`;
|
||
|
||
// Also update recipients list
|
||
updateRecipientsList(chats);
|
||
}
|
||
|
||
function updateRecipientsList(chats) {
|
||
const container = document.getElementById('recipients-container');
|
||
if (!chats || chats.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="text-sm text-gray-500">
|
||
<i class="fas fa-info-circle mr-1"></i>
|
||
Сообщение будет отправлено в чат по умолчанию
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const recipientsHtml = chats.map(chat => `
|
||
<label class="flex items-center">
|
||
<input type="checkbox" name="chatIds" value="${chat.id}"
|
||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||
<span class="ml-2 text-sm text-gray-900">
|
||
${chat.title}
|
||
<span class="text-gray-500">(${chat.type})</span>
|
||
</span>
|
||
</label>
|
||
`).join('');
|
||
|
||
container.innerHTML = recipientsHtml;
|
||
}
|
||
|
||
function showResult(elementId, message, type) {
|
||
const element = document.getElementById(elementId);
|
||
const className = type === 'success' ? 'bg-green-100 text-green-800' :
|
||
type === 'warning' ? 'bg-yellow-100 text-yellow-800' :
|
||
'bg-red-100 text-red-800';
|
||
const icon = type === 'success' ? 'fa-check' :
|
||
type === 'warning' ? 'fa-exclamation-triangle' :
|
||
'fa-times';
|
||
|
||
element.className = `mt-4 p-3 rounded-lg ${className}`;
|
||
element.innerHTML = `<i class="fas ${icon} mr-2"></i>${message}`;
|
||
element.classList.remove('hidden');
|
||
|
||
setTimeout(() => {
|
||
element.classList.add('hidden');
|
||
}, 5000);
|
||
}
|
||
|
||
function showNotification(message, type = 'info') {
|
||
const notification = document.createElement('div');
|
||
notification.className = `fixed top-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg text-white max-w-sm transform transition-all duration-300 translate-x-full`;
|
||
|
||
switch(type) {
|
||
case 'success':
|
||
notification.classList.add('bg-green-600');
|
||
break;
|
||
case 'error':
|
||
notification.classList.add('bg-red-600');
|
||
break;
|
||
case 'warning':
|
||
notification.classList.add('bg-yellow-600');
|
||
break;
|
||
default:
|
||
notification.classList.add('bg-blue-600');
|
||
}
|
||
|
||
notification.innerHTML = `
|
||
<div class="flex items-center">
|
||
<i class="fas ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : type === 'warning' ? 'fa-exclamation-triangle' : 'fa-info-circle'} mr-2"></i>
|
||
<span>${message}</span>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
setTimeout(() => {
|
||
notification.classList.remove('translate-x-full');
|
||
}, 100);
|
||
|
||
setTimeout(() => {
|
||
notification.classList.add('translate-x-full');
|
||
setTimeout(() => {
|
||
if (document.body.contains(notification)) {
|
||
document.body.removeChild(notification);
|
||
}
|
||
}, 300);
|
||
}, 4000);
|
||
}
|
||
|
||
function showLoading(message) {
|
||
// Create or show loading overlay
|
||
let overlay = document.getElementById('loading-overlay');
|
||
if (!overlay) {
|
||
overlay = document.createElement('div');
|
||
overlay.id = 'loading-overlay';
|
||
overlay.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
|
||
overlay.innerHTML = `
|
||
<div class="bg-white p-6 rounded-lg shadow-lg">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-spinner fa-spin text-blue-600 text-xl mr-3"></i>
|
||
<span id="loading-message">${message}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(overlay);
|
||
} else {
|
||
document.getElementById('loading-message').textContent = message;
|
||
overlay.style.display = 'flex';
|
||
}
|
||
}
|
||
|
||
function hideLoading() {
|
||
const overlay = document.getElementById('loading-overlay');
|
||
if (overlay) {
|
||
overlay.style.display = 'none';
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |