feat: Реализован полный CRUD для админ-панели и улучшена функциональность
- Portfolio CRUD: добавление, редактирование, удаление, переключение публикации - Services CRUD: полное управление услугами с возможностью активации/деактивации - Banner system: новая модель Banner с CRUD операциями и аналитикой кликов - Telegram integration: расширенные настройки бота, обнаружение чатов, отправка сообщений - Media management: улучшенная загрузка файлов с оптимизацией изображений и превью - UI improvements: обновлённые админ-панели с rich-text редактором и drag&drop загрузкой - Database: добавлена таблица banners с полями для баннеров и аналитики
This commit is contained in:
@@ -13,88 +13,69 @@
|
||||
<div class="hidden md:flex items-center space-x-6">
|
||||
<!-- Navigation Links -->
|
||||
<a href="/" class="<%= currentPage === 'home' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.home') %>
|
||||
<%= __('navigation.home') %>
|
||||
</a>
|
||||
<a href="/about" class="<%= currentPage === 'about' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.about') %>
|
||||
<%= __('navigation.about') %>
|
||||
</a>
|
||||
<a href="/services" class="<%= currentPage === 'services' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.services') %>
|
||||
<%= __('navigation.services') %>
|
||||
</a>
|
||||
<a href="/portfolio" class="<%= currentPage === 'portfolio' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.portfolio') %>
|
||||
<%= __('navigation.portfolio') %>
|
||||
</a>
|
||||
<a href="/calculator" class="<%= currentPage === 'calculator' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.calculator') %>
|
||||
<%= __('navigation.calculator') %>
|
||||
</a>
|
||||
<a href="/contact" class="<%= currentPage === 'contact' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-700 dark:text-gray-300 hover:text-blue-600' %> px-3 py-2 text-sm font-medium transition-colors nav-link">
|
||||
<%- __('navigation.contact') %>
|
||||
<%= __('navigation.contact') %>
|
||||
</a>
|
||||
|
||||
<!-- Language Dropdown -->
|
||||
<div class="relative group">
|
||||
<button class="flex items-center text-gray-700 dark:text-gray-300 hover:text-blue-600 px-3 py-2 text-sm font-medium transition-colors" id="language-dropdown">
|
||||
<i class="fas fa-globe mr-2"></i>
|
||||
<%- __('language.' + currentLanguage) %>
|
||||
<%
|
||||
let currentFlag = '🇰🇷';
|
||||
if (currentLanguage === 'en') currentFlag = '🇺🇸';
|
||||
else if (currentLanguage === 'ru') currentFlag = '🇷🇺';
|
||||
else if (currentLanguage === 'kk') currentFlag = '🇰🇿';
|
||||
%>
|
||||
<span class="mr-2"><%= currentFlag %></span>
|
||||
<%= __('language.' + currentLanguage) %>
|
||||
<i class="fas fa-chevron-down ml-1 text-xs"></i>
|
||||
</button>
|
||||
<div class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg border dark:border-gray-700 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
|
||||
<a href="/lang/ko" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-t-lg">
|
||||
🇰🇷 <%- __('language.korean') %>
|
||||
🇰🇷 <%= __('language.korean') %>
|
||||
</a>
|
||||
<a href="/lang/en" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
🇺🇸 <%- __('language.english') %>
|
||||
🇺🇸 <%= __('language.english') %>
|
||||
</a>
|
||||
<a href="/lang/ru" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
🇷🇺 <%- __('language.russian') %>
|
||||
🇷🇺 <%= __('language.russian') %>
|
||||
</a>
|
||||
<a href="/lang/kk" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-b-lg">
|
||||
🇰🇿 <%- __('language.kazakh') %>
|
||||
🇰🇿 <%= __('language.kazakh') %>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Theme Toggle -->
|
||||
<button id="theme-toggle" class="p-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 transition-colors" title="<%- __('theme.toggle') %>">
|
||||
<i class="fas fa-sun dark:hidden"></i>
|
||||
<i class="fas fa-moon hidden dark:block"></i>
|
||||
</button>
|
||||
|
||||
<!-- CTA Button -->
|
||||
<a href="/contact" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors btn-primary">
|
||||
<%- __('hero.cta_primary') %>
|
||||
</a>
|
||||
<!-- iOS Style Theme Toggle -->
|
||||
<div class="relative inline-block" title="<%= __('theme.toggle') %>">
|
||||
<input type="checkbox" id="theme-toggle" class="sr-only">
|
||||
<label for="theme-toggle" class="flex items-center cursor-pointer">
|
||||
<div class="relative w-12 h-6 bg-gray-300 dark:bg-gray-600 rounded-full transition-colors duration-200">
|
||||
<div class="absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow-md transform transition-transform duration-200 flex items-center justify-center theme-toggle-slider">
|
||||
<i class="fas fa-sun text-yellow-500 text-xs theme-sun-icon"></i>
|
||||
<i class="fas fa-moon text-blue-500 text-xs hidden theme-moon-icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile menu button -->
|
||||
<div class="md:hidden flex items-center space-x-2">
|
||||
<!-- Mobile Theme Toggle -->
|
||||
<button id="mobile-theme-toggle" class="p-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 transition-colors">
|
||||
<i class="fas fa-sun dark:hidden"></i>
|
||||
<i class="fas fa-moon hidden dark:block"></i>
|
||||
</button>
|
||||
|
||||
<!-- Mobile Language Toggle -->
|
||||
<div class="relative">
|
||||
<button id="mobile-language-toggle" class="p-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 transition-colors">
|
||||
<i class="fas fa-globe"></i>
|
||||
</button>
|
||||
<div id="mobile-language-menu" class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg border dark:border-gray-700 hidden">
|
||||
<a href="/lang/ko" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-t-lg">
|
||||
🇰🇷 <%- __('language.korean') %>
|
||||
</a>
|
||||
<a href="/lang/en" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
🇺🇸 <%- __('language.english') %>
|
||||
</a>
|
||||
<a href="/lang/ru" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
🇷🇺 <%- __('language.russian') %>
|
||||
</a>
|
||||
<a href="/lang/kk" class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-b-lg">
|
||||
🇰🇿 <%- __('language.kazakh') %>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hamburger Menu -->
|
||||
<button id="mobile-menu-toggle" class="p-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -109,30 +90,23 @@
|
||||
<div id="mobile-menu" class="md:hidden hidden bg-white dark:bg-gray-900 border-t dark:border-gray-700">
|
||||
<div class="px-2 pt-2 pb-3 space-y-1">
|
||||
<a href="/" class="<%= currentPage === 'home' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.home') %>
|
||||
<%= __('navigation.home') %>
|
||||
</a>
|
||||
<a href="/about" class="<%= currentPage === 'about' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.about') %>
|
||||
<%= __('navigation.about') %>
|
||||
</a>
|
||||
<a href="/services" class="<%= currentPage === 'services' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.services') %>
|
||||
<%= __('navigation.services') %>
|
||||
</a>
|
||||
<a href="/portfolio" class="<%= currentPage === 'portfolio' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.portfolio') %>
|
||||
<%= __('navigation.portfolio') %>
|
||||
</a>
|
||||
<a href="/calculator" class="<%= currentPage === 'calculator' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.calculator') %>
|
||||
<%= __('navigation.calculator') %>
|
||||
</a>
|
||||
<a href="/contact" class="<%= currentPage === 'contact' ? 'bg-blue-50 dark:bg-blue-900 text-blue-600' : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' %> block px-3 py-2 rounded-md text-base font-medium">
|
||||
<%- __('navigation.contact') %>
|
||||
<%= __('navigation.contact') %>
|
||||
</a>
|
||||
|
||||
<!-- Mobile CTA -->
|
||||
<div class="pt-4 pb-2">
|
||||
<a href="/contact" class="block w-full bg-blue-600 text-white text-center px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
||||
<%- __('hero.cta_primary') %>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -141,40 +115,53 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Theme Management
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const mobileThemeToggle = document.getElementById('mobile-theme-toggle');
|
||||
const html = document.documentElement;
|
||||
const slider = document.querySelector('.theme-toggle-slider');
|
||||
const sunIcon = document.querySelector('.theme-sun-icon');
|
||||
const moonIcon = document.querySelector('.theme-moon-icon');
|
||||
|
||||
// Get current theme
|
||||
const currentTheme = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
|
||||
// Apply theme
|
||||
if (currentTheme === 'dark') {
|
||||
html.classList.add('dark');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
}
|
||||
|
||||
// Theme toggle handlers
|
||||
function toggleTheme() {
|
||||
const isDark = html.classList.contains('dark');
|
||||
// Apply theme and update toggle
|
||||
function applyTheme(isDark) {
|
||||
if (isDark) {
|
||||
html.classList.remove('dark');
|
||||
localStorage.setItem('theme', 'light');
|
||||
fetch('/theme/light');
|
||||
} else {
|
||||
html.classList.add('dark');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
fetch('/theme/dark');
|
||||
themeToggle.checked = true;
|
||||
if (slider) {
|
||||
slider.style.transform = 'translateX(24px)';
|
||||
slider.style.backgroundColor = '#374151';
|
||||
}
|
||||
if (sunIcon) sunIcon.classList.add('hidden');
|
||||
if (moonIcon) moonIcon.classList.remove('hidden');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
themeToggle.checked = false;
|
||||
if (slider) {
|
||||
slider.style.transform = 'translateX(0)';
|
||||
slider.style.backgroundColor = '#ffffff';
|
||||
}
|
||||
if (sunIcon) sunIcon.classList.remove('hidden');
|
||||
if (moonIcon) moonIcon.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener('click', toggleTheme);
|
||||
// Initial theme application
|
||||
applyTheme(currentTheme === 'dark');
|
||||
|
||||
// Theme toggle handler
|
||||
function toggleTheme() {
|
||||
const isDark = html.classList.contains('dark');
|
||||
const newTheme = isDark ? 'light' : 'dark';
|
||||
|
||||
applyTheme(!isDark);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
fetch(`/theme/${newTheme}`);
|
||||
}
|
||||
|
||||
if (mobileThemeToggle) {
|
||||
mobileThemeToggle.addEventListener('click', toggleTheme);
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener('change', toggleTheme);
|
||||
}
|
||||
|
||||
// Mobile Menu Management
|
||||
@@ -187,19 +174,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile Language Menu
|
||||
const mobileLanguageToggle = document.getElementById('mobile-language-toggle');
|
||||
const mobileLanguageMenu = document.getElementById('mobile-language-menu');
|
||||
// Desktop Language Dropdown Management
|
||||
const languageDropdown = document.getElementById('language-dropdown');
|
||||
const languageMenu = languageDropdown ? languageDropdown.nextElementSibling : null;
|
||||
|
||||
if (mobileLanguageToggle && mobileLanguageMenu) {
|
||||
mobileLanguageToggle.addEventListener('click', function() {
|
||||
mobileLanguageMenu.classList.toggle('hidden');
|
||||
if (languageDropdown && languageMenu) {
|
||||
languageDropdown.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
languageMenu.classList.toggle('opacity-0');
|
||||
languageMenu.classList.toggle('invisible');
|
||||
});
|
||||
|
||||
// Close language menu when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
if (!mobileLanguageToggle.contains(event.target) && !mobileLanguageMenu.contains(event.target)) {
|
||||
mobileLanguageMenu.classList.add('hidden');
|
||||
if (!languageDropdown.contains(event.target) && !languageMenu.contains(event.target)) {
|
||||
languageMenu.classList.add('opacity-0');
|
||||
languageMenu.classList.add('invisible');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user