215 lines
8.0 KiB
JavaScript
215 lines
8.0 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) => {
|
|
// Update all theme toggle icons
|
|
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';
|
|
}
|
|
});
|
|
|
|
// Update all theme status texts
|
|
const textLabels = document.querySelectorAll('.theme-status-text');
|
|
textLabels.forEach(label => {
|
|
label.textContent = theme === 'dark' ? 'Dark 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';
|
|
|
|
// Update UI
|
|
html.setAttribute('data-theme', newTheme);
|
|
|
|
// Update All Icons and Labels
|
|
updateIcons(newTheme);
|
|
|
|
// Save preference
|
|
document.cookie = `theme=${newTheme}; path=/; max-age=${365 * 24 * 60 * 60}`;
|
|
localStorage.setItem('theme', newTheme);
|
|
});
|
|
};
|
|
|
|
// AJAX Category Filtering
|
|
const initCategoryAjax = () => {
|
|
const filters = document.querySelectorAll('.category-filter');
|
|
const gridContainer = document.getElementById('apk-grid-container');
|
|
const dropdownBtn = document.getElementById('category-dropdown-btn');
|
|
const latestTitle = document.getElementById('latest-title');
|
|
|
|
if (!gridContainer || filters.length === 0) return;
|
|
|
|
filters.forEach(filter => {
|
|
filter.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
|
|
const category = filter.getAttribute('data-category');
|
|
const url = filter.getAttribute('href');
|
|
const categoryName = filter.textContent;
|
|
|
|
// Update UI state
|
|
gridContainer.style.opacity = '0.5';
|
|
gridContainer.style.pointerEvents = 'none';
|
|
|
|
// Fetch data
|
|
fetch(url, {
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(response => response.text())
|
|
.then(data => {
|
|
gridContainer.innerHTML = data;
|
|
gridContainer.style.opacity = '1';
|
|
gridContainer.style.pointerEvents = 'all';
|
|
|
|
// Update dropdown button text
|
|
if (dropdownBtn) {
|
|
dropdownBtn.innerHTML = `${categoryName} <i class="bi bi-chevron-down ms-1 small"></i>`;
|
|
}
|
|
|
|
// Update URL without refreshing
|
|
window.history.pushState({ category: category }, '', url);
|
|
})
|
|
.catch(err => {
|
|
console.error('Fetch error:', err);
|
|
gridContainer.style.opacity = '1';
|
|
gridContainer.style.pointerEvents = 'all';
|
|
});
|
|
});
|
|
});
|
|
|
|
// Handle browser back/forward
|
|
window.addEventListener('popstate', (e) => {
|
|
window.location.reload(); // Simple solution for now
|
|
});
|
|
};
|
|
|
|
// 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%';
|
|
if (isUser) {
|
|
content.style.borderBottomRightRadius = '0';
|
|
} else {
|
|
content.style.borderBottomLeftRadius = '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 = '';
|
|
|
|
// Loading state
|
|
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> 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 || 'Sorry, something went wrong.');
|
|
}
|
|
})
|
|
.catch(err => {
|
|
messagesContainer.removeChild(loadingDiv);
|
|
appendMessage('Error connecting to AI assistant.');
|
|
console.error(err);
|
|
});
|
|
};
|
|
|
|
sendBtn.addEventListener('click', sendMessage);
|
|
chatInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') sendMessage();
|
|
});
|
|
};
|
|
|
|
// Initial Sync
|
|
const currentTheme = html.getAttribute('data-theme') || 'light';
|
|
updateIcons(currentTheme);
|
|
|
|
// Sync theme from localStorage if cookie is missing but localStorage has it
|
|
const savedTheme = localStorage.getItem('theme');
|
|
const currentCookie = document.cookie.split('; ').find(row => row.startsWith('theme='))?.split('=')[1];
|
|
|
|
if (savedTheme && !currentCookie) {
|
|
html.setAttribute('data-theme', savedTheme);
|
|
updateIcons(savedTheme);
|
|
document.cookie = `theme=${savedTheme}; path=/; max-age=${365 * 24 * 60 * 60}`;
|
|
}
|
|
|
|
initThemeToggle('theme-toggle');
|
|
initThemeToggle('theme-toggle-mobile');
|
|
initCategoryAjax();
|
|
initAIChat();
|
|
|
|
console.log('ApkNusa ready.');
|
|
}); |