38751-vm/assets/js/main.js
Flatlogic Bot dc6297b93b Bagus ini
2026-02-25 22:41:29 +00:00

312 lines
12 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
// Basic interaction for toasts
try {
const toasts = document.querySelectorAll('.toast');
toasts.forEach(toastEl => {
if (window.bootstrap && bootstrap.Toast) {
const toast = new bootstrap.Toast(toastEl, { delay: 5000 });
toast.show();
}
});
} catch (e) {
console.error('Toast error:', e);
}
const html = document.documentElement;
const updateIcons = (theme) => {
const icons = document.querySelectorAll('#theme-toggle i, #theme-toggle-mobile i');
icons.forEach(icon => {
if (theme === 'dark') {
icon.className = 'fa-solid fa-sun';
} else {
icon.className = 'fa-solid fa-moon';
}
});
const textLabels = document.querySelectorAll('.theme-status-text');
textLabels.forEach(label => {
label.textContent = theme === 'dark' ? (window.i18n?.dark_mode || 'Dark Mode') : (window.i18n?.light_mode || 'Light Mode');
});
};
// Theme Toggle Logic
const initThemeToggle = (btnId) => {
const themeToggle = document.getElementById(btnId);
if (!themeToggle) return;
themeToggle.addEventListener('click', () => {
const currentTheme = html.getAttribute('data-theme') || 'light';
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
updateIcons(newTheme);
document.cookie = `theme=${newTheme}; path=/; max-age=${365 * 24 * 60 * 60}`;
localStorage.setItem('theme', newTheme);
});
};
// Unified AJAX Content Loader
const loadContent = (url, updateUrl = true) => {
const gridContainer = document.getElementById('apk-grid-container');
if (!gridContainer) return;
gridContainer.style.opacity = '0.5';
gridContainer.style.pointerEvents = 'none';
fetch(url, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(response => response.text())
.then(data => {
gridContainer.innerHTML = data;
gridContainer.style.opacity = '1';
gridContainer.style.pointerEvents = 'all';
if (updateUrl) {
window.history.pushState({}, '', url);
}
// Scroll to grid top on mobile if needed
if (window.innerWidth < 992) {
const latestSection = document.getElementById('latest');
if (latestSection) {
window.scrollTo({
top: latestSection.offsetTop - 100,
behavior: 'smooth'
});
}
}
})
.catch(err => {
console.error('Fetch error:', err);
gridContainer.style.opacity = '1';
gridContainer.style.pointerEvents = 'all';
});
};
// AJAX Category Filtering
const initCategoryAjax = () => {
document.addEventListener('click', (e) => {
const filter = e.target.closest('.category-filter, .ajax-cat-link');
if (!filter) return;
e.preventDefault();
const url = filter.getAttribute('href');
const categoryName = filter.textContent.trim();
const dropdownBtn = document.getElementById('category-dropdown-btn');
if (dropdownBtn && filter.classList.contains('category-filter')) {
dropdownBtn.innerHTML = `${categoryName} <i class="bi bi-chevron-down ms-1 small"></i>`;
}
// Update active state for chips if they are chips
if (filter.classList.contains('ajax-cat-link')) {
document.querySelectorAll('.ajax-cat-link').forEach(btn => {
btn.classList.remove('btn-success');
btn.classList.add('btn-light');
});
filter.classList.remove('btn-light');
filter.classList.add('btn-success');
}
loadContent(url);
// Close search overlay if open
const searchOverlay = document.getElementById('search-overlay');
if (searchOverlay) searchOverlay.classList.remove('active');
});
};
// AJAX Search
const initSearchAjax = () => {
const searchForm = document.getElementById('ajax-search-form');
if (!searchForm) return;
searchForm.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(searchForm);
const query = formData.get('search');
const url = `/?search=${encodeURIComponent(query)}`;
loadContent(url);
const searchOverlay = document.getElementById('search-overlay');
if (searchOverlay) searchOverlay.classList.remove('active');
});
};
// Newsletter AJAX
const initNewsletterAjax = () => {
const btn = document.getElementById('newsletter-btn');
const emailInput = document.getElementById('newsletter-email');
const msg = document.getElementById('newsletter-msg');
if (!btn || !emailInput) return;
btn.addEventListener('click', () => {
const email = emailInput.value;
if (!email) return;
btn.disabled = true;
const originalText = btn.innerHTML;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
fetch('/api/newsletter/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
})
.then(res => res.json())
.then(data => {
btn.disabled = false;
btn.innerHTML = originalText;
if (data.success) {
msg.innerHTML = `<span class="text-success">${data.success}</span>`;
emailInput.value = '';
} else {
msg.innerHTML = `<span class="text-danger">${data.error}</span>`;
}
})
.catch(err => {
btn.disabled = false;
btn.innerHTML = originalText;
msg.innerHTML = `<span class="text-danger">${window.i18n?.error_generic || 'An error occurred.'}</span>`;
});
});
};
// AI Chat Assistant Logic
const initAIChat = () => {
const toggleBtn = document.getElementById('toggle-ai-chat');
const closeBtn = document.getElementById('close-ai-chat');
const chatWindow = document.getElementById('ai-chat-window');
const chatInput = document.getElementById('ai-chat-input');
const sendBtn = document.getElementById('send-ai-chat');
const messagesContainer = document.getElementById('ai-chat-messages');
if (!toggleBtn || !chatWindow) return;
toggleBtn.addEventListener('click', () => {
chatWindow.classList.toggle('d-none');
if (!chatWindow.classList.contains('d-none')) {
chatInput.focus();
}
});
closeBtn.addEventListener('click', () => {
chatWindow.classList.add('d-none');
});
const appendMessage = (message, isUser = false) => {
const div = document.createElement('div');
div.className = 'mb-3 d-flex ' + (isUser ? 'justify-content-end' : '');
const content = document.createElement('div');
content.className = (isUser ? 'bg-success text-white' : 'bg-white') + ' p-3 rounded-4 shadow-sm small';
content.style.maxWidth = '85%';
content.style.borderBottomRightRadius = isUser ? '0' : 'inherit';
content.style.borderBottomLeftRadius = isUser ? 'inherit' : '0';
content.textContent = message;
div.appendChild(content);
messagesContainer.appendChild(div);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};
const sendMessage = () => {
const message = chatInput.value.trim();
if (!message) return;
appendMessage(message, true);
chatInput.value = '';
const loadingDiv = document.createElement('div');
loadingDiv.className = 'mb-3 d-flex';
loadingDiv.innerHTML = `<div class="bg-white p-3 rounded-4 shadow-sm small" style="border-bottom-left-radius: 0 !important;"><div class="spinner-border spinner-border-sm text-success" role="status"></div> ${window.i18n?.thinking || 'Thinking...'}</div>`;
messagesContainer.appendChild(loadingDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
fetch('/api/ai/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: message })
})
.then(response => response.json())
.then(data => {
messagesContainer.removeChild(loadingDiv);
if (data.reply) {
appendMessage(data.reply);
} else {
appendMessage(data.error || (window.i18n?.something_wrong || 'Sorry, something went wrong.'));
}
})
.catch(err => {
if (messagesContainer.contains(loadingDiv)) messagesContainer.removeChild(loadingDiv);
appendMessage(window.i18n?.error_ai || 'Error connecting to AI assistant.');
console.error(err);
});
};
sendBtn.addEventListener('click', sendMessage);
chatInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
};
// Mobile Overlays & Utils
const initMobileUtils = () => {
const searchTrigger = document.getElementById('mobile-search-trigger');
const searchOverlay = document.getElementById('search-overlay');
const closeSearch = document.getElementById('close-search-overlay');
if (searchTrigger && searchOverlay) {
searchTrigger.addEventListener('click', (e) => {
e.preventDefault();
searchOverlay.classList.add('active');
const input = searchOverlay.querySelector('input');
if (input) setTimeout(() => input.focus(), 300);
});
}
if (closeSearch && searchOverlay) {
closeSearch.addEventListener('click', () => {
searchOverlay.classList.remove('active');
});
}
// Back to Top
const backToTop = document.getElementById('back-to-top');
if (backToTop) {
window.addEventListener('scroll', () => {
if (window.pageYOffset > 300) {
backToTop.classList.add('show');
} else {
backToTop.classList.remove('show');
}
});
backToTop.addEventListener('click', (e) => {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
};
// Initial Sync
const currentTheme = html.getAttribute('data-theme') || 'light';
updateIcons(currentTheme);
initThemeToggle('theme-toggle');
initThemeToggle('theme-toggle-mobile');
initCategoryAjax();
initSearchAjax();
initNewsletterAjax();
initAIChat();
initMobileUtils();
// Handle browser back/forward
window.addEventListener('popstate', () => {
loadContent(window.location.href, false);
});
console.log('ApkNusa AJAX Engine active.');
});