webdlh-net/Views/Shared/Partials/_Footer.cshtml

810 lines
32 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<footer class="bg-oren md:pt-8">
<!-- Main Footer Section -->
<div class="relative max-w-6xl bg-ijo md:rounded-2xl mx-auto overflow-hidden shadow-2xl">
<!-- Background Pattern -->
<div class="absolute inset-0 opacity-20">
<div class="absolute inset-0"
style="background-image: url('@Url.Content("~/assets/images/home/bg-green.png")'); background-size: fit; background-position: bottom; background-repeat: no-repeat;"></div>
<div class="absolute inset-0 bg-gradient-to-br from-green-500/30 to-emerald-800/30"></div>
</div>
<!-- Decorative Elements -->
<div class="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -translate-y-16 translate-x-16"></div>
<div class="absolute bottom-0 left-0 w-24 h-24 bg-white/5 rounded-full translate-y-12 -translate-x-12"></div>
<div class="relative container mx-auto px-6">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-0 md:gap-12">
<!-- Left Section -->
<div class="flex flex-col text-center justify-center md:text-left">
<div class="inline-block">
<h2 class="text-4xl lg:text-5xl font-black text-white mb-2 tracking-tight mt-6">SAVE OUR</h2>
<a href="#" class="flip-animate inline-block">
<span class="text-4xl lg:text-5xl font-black text-yellow-300 mb-4 tracking-tight" data-hover="FUTURE">EARTH</span>
</a>
</div>
<p class="text-green-100 text-lg leading-relaxed max-w-sm mx-auto lg:mx-0">
Bersama membangun Jakarta yang hijau dan berkelanjutan untuk generasi mendatang
</p>
@* Pengunjung *@
<div class="flex items-center justify-center lg:justify-start space-x-3 mt-6">
<div class="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20 hover:bg-white/20 transition-all duration-300 group">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-yellow-300/20 rounded-lg flex items-center justify-center flex-shrink-0">
<i class="text-yellow-300 w-5 h-5" data-lucide="users"></i>
</div>
<div class="text-left">
<div class="text-2xl lg:text-3xl font-bold text-yellow-300 group-hover:text-yellow-200 transition-colors" id="dailyVisitors">0</div>
<div class="text-green-100 text-sm font-medium">Daily Visitor</div>
</div>
</div>
</div>
<div class="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20 hover:bg-white/20 transition-all duration-300 group">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-yellow-300/20 rounded-lg flex items-center justify-center flex-shrink-0">
<i class="text-yellow-300 w-5 h-5" data-lucide="trending-up"></i>
</div>
<div class="text-left">
<div class="text-2xl lg:text-3xl font-bold text-yellow-300 group-hover:text-yellow-200 transition-colors" id="monthlyVisitors">0</div>
<div class="text-green-100 text-sm font-medium">Monthly Visitor</div>
</div>
</div>
</div>
</div>
</div>
<!-- Center Section -->
<div class="relative justify-center items-end hidden md:flex">
<div class="relative">
<div class="absolute inset-0 bg-white/20 rounded-full blur-3xl transform scale-300"></div>
<img src="@Url.Content("~/assets/images/home/monas.svg")" alt="Monas Jakarta" class="relative z-10 max-w-full drop-shadow-2xl">
</div>
</div>
<!-- Right Section -->
<div class="text-left py-12">
<div class="bg-white/10 backdrop-blur-sm rounded-xl p-4 lg:p-6 border border-white/20">
<h3 class="text-xl lg:text-2xl font-bold text-white mb-4 lg:mb-6 flex items-center justify-center lg:justify-start">
<i class="text-yellow-300 w-6 h-6 lg:w-8 lg:h-8 mr-2" data-lucide="phone-call"></i>
Kontak Kami
</h3>
<div class="space-y-3 lg:space-y-4">
<div class="flex items-start space-x-3">
<div class="w-7 h-7 lg:w-8 lg:h-8 bg-yellow-300/20 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="text-yellow-300 w-4 h-4"data-lucide="map-pin"></i>
</div>
<div class="text-left">
<p class="text-white font-medium text-sm lg:text-base">Alamat</p>
<p class="text-green-100 text-xs lg:text-sm leading-relaxed">
Jl. Mandala V No.67, RT.1/RW.2, Cililitan, Kramatjati, Kota Jakarta Timur, DKI Jakarta 13640
</p>
</div>
</div>
<div class="flex items-start space-x-3">
<div class="w-7 h-7 lg:w-8 lg:h-8 bg-yellow-300/20 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="text-yellow-300 w-4 h-4"data-lucide="phone"></i>
</div>
<div class="text-left">
<p class="text-white font-medium text-sm lg:text-base">Telepon</p>
<p class="text-green-100 text-xs lg:text-sm">(021) 8092744</p>
</div>
</div>
<div class="flex items-start space-x-3">
<div class="w-7 h-7 lg:w-8 lg:h-8 bg-yellow-300/20 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="text-yellow-300 w-4 h-4"data-lucide="mail"></i>
</div>
<div class="text-left">
<p class="text-white font-medium text-sm lg:text-base">Email</p>
<p class="text-green-100 texBt-xs lg:text-sm">dinaslh@jakarta.go.id</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Section 2: Tentang Kami dan Layanan -->
<div class="bg-[#f49827] py-8">
<div class="container max-w-6xl mx-auto px-4">
<div class="flex flex-col lg:flex-row justify-between items-center text-white space-y-8 lg:space-y-0">
<!-- Tentang Kami -->
<div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-8 items-start md:items-center">
<div>
<h4 class="font-semibold text-xl md:text-2xl">Tentang<br class="hidden md:block"> Kami</h4>
</div>
<div class="flex flex-col space-y-2">
@* <a href="#" class="hover:underline">Visi Misi</a> *@
<a href="@Url.Action("Organisasi", "Profil")" class="hover:underline">Struktur Organisasi</a>
</div>
</div>
<!-- Layanan -->
<div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-8 items-center">
<div>
<h4 class="font-semibold text-xl md:text-2xl">Layanan</h4>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 items-center text-center md:text-left md:items-start">
<a href="https://ewaste.dinaslhdki.id/" target="_blank" class="hover:underline">Penjemputan e-Waste</a>
<a href="@Url.Action("BulkyWaste", "Layanan")" target="_blank" class="hover:underline">Bulky Waste</a>
<a href="@Url.Action("Lab", "Layanan")" target="_blank" class="hover:underline">Uji Sampel Laboratorium</a>
<a href="@Url.Action("Ppid", "Layanan")" target="_blank" class="hover:underline">Permohonan Informasi Publik</a>
<a href="@Url.Action("BusToilet", "Layanan")" target="_blank" class="hover:underline">Bus Toilet</a>
<a href="https://bpsrw.dinaslhdki.id/" target="_blank" class="hover:underline">BPS-RW</a>
<a href="@Url.Action("Amdal", "Layanan")" target="_blank" class="hover:underline">AMDAL</a>
<a href="@Url.Action("Whistleblowing", "Layanan")" target="_blank" class="hover:underline">Whistleblowing System</a>
</div>
</div>
</div>
</div>
</div>
<!-- Bottom Section -->
<div class="bg-black py-6">
<div class="container max-w-6xl mx-auto px-4 md:pb-0 pb-16">
<div class="flex flex-col md:flex-row justify-between items-center text-white space-y-4 md:space-y-0">
<a href="@Url.Action("Index", "Home")" class="flex items-center space-x-3">
<img src="@Url.Content("~/logo-dlh.png")" class="h-8 md:h-10" alt="DLH Logo" />
<div class="text-center md:text-left">
<span class="text-sm md:text-lg font-bold text-white leading-tight">DINAS LINGKUNGAN HIDUP</span>
<span class="block text-xs md:text-sm text-white/70 font-medium">PROVINSI DKI JAKARTA</span>
</div>
</a>
<div class="text-xs md:text-sm text-center md:text-right">
<p>&copy; @DateTime.Now.Year Dinas Lingkungan Hidup Provinsi Jakarta</p>
</div>
</div>
</div>
</div>
<!-- Chat Button -->
<div id="chatButton" class="fixed bottom-22 right-3 md:bottom-5 md:right-5 p-4 cursor-pointer z-20">
<img src="@Url.Content("~/kura.svg")" alt="Chat Icon" class="w-24 h-24"/>
@* <div class="absolute -top-8 -left-5 bg-green-500 text-white md:text-xs text-sm px-2 py-1 rounded whitespace-nowrap">
Tanya EcoBot!
</div> *@
</div>
<!-- Chatbot -->
<div id="chatbot" class="fixed bottom-20 right-5 w-80 h-96 bg-white rounded-lg shadow-xl transform transition-all duration-300 scale-0 z-50 md:w-80 md:h-96 md:bottom-20 md:right-5 md:rounded-lg mobile-fullscreen">
<div class="flex justify-between items-center bg-blue-600 text-white p-4 rounded-t-lg md:rounded-t-lg">
<h3 class="font-semibold text-lg flex items-center">
<img src="@Url.Content("~/kura-tanya.svg")" alt="Chat Icon" class="w-6 h-6 lg:w-8 lg:h-8 mr-2"/>
Tanya Kura!
</h3>
<button id="closeChat" class="text-white font-bold text-xl">×</button>
</div>
<div class="chat-content p-4 h-72 overflow-y-auto bg-gray-50 rounded-b-lg md:rounded-b-lg mobile-chat-content">
<div class="chat-bubble bot" style="float: left; clear: both;">
<p>Halo! Selamat datang di layanan informasi Dinas Lingkungan Hidup DKI Jakarta! 👋</p>
<p>Saya di sini untuk membantu Anda. Ada yang ingin ditanyakan seputar layanan kami?</p>
</div>
<!-- Chat messages will appear here -->
</div>
<div class="flex items-center p-4 bg-green-100 border-t border-gray-300 rounded-b-lg md:rounded-b-lg">
<input type="text" id="userInput" placeholder="Type a message..." class="w-full p-2 rounded-lg border border-gray-300" />
<button id="sendMessage" class="ml-2 p-2 bg-green-600 text-white rounded-lg">Send</button>
</div>
</div>
</footer>
<style>
a.flip-animate {
perspective: 1000px;
text-decoration: none;
outline: none;
}
a.flip-animate span {
position: relative;
display: inline-block;
transition: transform 0.6s ease-in-out;
transform-origin: 50% 0;
transform-style: preserve-3d;
}
a.flip-animate span:before {
position: absolute;
top: 100%;
left: 0;
width: 100%;
height: 100%;
content: attr(data-hover);
transform: rotateX(-90deg);
transform-origin: 50% 0;
text-align: center;
color: #fde047;
backface-visibility: hidden;
}
a.flip-animate.flipped span {
transform: rotateX(90deg) translateY(-22px);
backface-visibility: hidden;
}
a.flip-animate.flipped span:before {
color: #fbbf24;
}
.chat-content {
background: linear-gradient(135deg, #e6f9e6 0%, #e0f7fa 100%);
border-radius: 0.75rem;
padding: 1rem;
min-height: 200px;
font-size: 1rem;
box-shadow: 0 2px 8px 0 #22c55e33;
}
.chat-bubble {
display: block;
padding: 0.5rem 1rem;
border-radius: 1.2em;
margin-bottom: 0.5rem;
max-width: 80%;
word-break: break-word;
box-shadow: 0 1px 4px 0 #22c55e22;
position: relative;
font-size: 0.98rem;
transition: background 0.2s;
clear: both;
}
.chat-bubble.user {
background: linear-gradient(135deg, #bbf7d0 0%, #4ade80 100%);
color: #134e1c;
float: right;
border-bottom-right-radius: 0.3em;
border: 1.5px solid #22c55e;
text-align: right;
font-size: 10pt;
}
.chat-bubble.bot {
background: linear-gradient(135deg, #fff 0%, #d1fae5 100%);
color: #134e1c;
float: left;
border-bottom-left-radius: 0.3em;
border: 1.5px solid #22c55e;
text-align: left;
font-size: 10pt;
}
.chat-bubble.time {
background: transparent;
color: #888;
font-size: 0.8rem;
box-shadow: none;
text-align: center;
margin: 0.5rem auto;
display: block;
}
.chat-content p {
margin: 0.2rem 0;
}
.chat-bubble.user:after {
content: "";
position: absolute;
right: -10px;
bottom: 0;
width: 16px;
height: 20px;
background: transparent;
background-image: radial-gradient(circle at 0 0, #4ade80 60%, transparent 61%);
z-index: 1;
}
.chat-bubble.bot:after {
content: "";
position: absolute;
left: -10px;
bottom: 0;
width: 16px;
height: 20px;
background: transparent;
background-image: radial-gradient(circle at 100% 0, #d1fae5 60%, transparent 61%);
z-index: 1;
}
#chatbot .bg-blue-600 {
background: linear-gradient(90deg, #22c55e 0%, #4ade80 100%) !important;
}
#chatbot h3 {
color: #fff;
letter-spacing: 1px;
}
#chatbot button#closeChat {
color: #fff;
background: none;
border: none;
font-size: 1.5rem;
transition: color 0.2s;
}
#chatbot button#closeChat:hover {
color: #22c55e;
}
#chatbot input[type="text"] {
border: 1.5px solid #22c55e;
background: #f0fdf4;
}
#chatbot button#sendMessage {
background: linear-gradient(90deg, #22c55e 0%, #4ade80 100%);
color: #fff;
font-weight: bold;
border: none;
transition: background 0.2s;
}
#chatbot button#sendMessage:hover {
background: linear-gradient(90deg, #4ade80 0%, #22c55e 100%);
}
/* Typing indicator animation */
.typing-indicator {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
background: linear-gradient(135deg, #fff 0%, #d1fae5 100%);
border: 1.5px solid #22c55e;
border-radius: 1.2em;
border-bottom-left-radius: 0.3em;
float: left;
clear: both;
margin-bottom: 0.5rem;
max-width: 80%;
box-shadow: 0 1px 4px 0 #22c55e22;
}
.typing-indicator span {
height: 8px;
width: 8px;
float: left;
margin: 0 1px;
background-color: #22c55e;
display: block;
border-radius: 50%;
opacity: 0.4;
animation: typing 1.4s infinite ease-in-out both;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
.typing-indicator span:nth-child(3) { animation-delay: 0s; }
@@keyframes typing {
0%, 80%, 100% {
transform: scale(0);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
.typing-text {
margin-left: 8px;
color: #22c55e;
font-size: 0.9rem;
font-style: italic;
}
@@media (max-width: 768px) {
#chatbot.mobile-fullscreen {
top: 0 !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
width: 100vw !important;
height: 100vh !important;
border-radius: 0 !important;
transform: none !important;
transition: transform 0.3s ease-in-out !important;
}
#chatbot.mobile-fullscreen.scale-0 {
transform: translateY(100%) !important;
}
#chatbot.mobile-fullscreen.scale-100 {
transform: translateY(0) !important;
}
#chatbot.mobile-fullscreen .chat-content.mobile-chat-content {
height: calc(100vh - 140px) !important;
}
#chatbot.mobile-fullscreen .rounded-t-lg,
#chatbot.mobile-fullscreen .rounded-b-lg {
border-radius: 0 !important;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const flipElement = document.querySelector('.flip-animate');
if (flipElement) {
setInterval(function() {
flipElement.classList.toggle('flipped');
}, 1500);
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const chatButton = document.getElementById("chatButton");
const chatbot = document.getElementById("chatbot");
const closeChat = document.getElementById("closeChat");
const sendMessage = document.getElementById("sendMessage");
const userInput = document.getElementById("userInput");
// Session configuration
const SESSION_DURATION = 30 * 60 * 1000;
const STORAGE_KEY = 'ecobot_chat_session';
// Load chat history from localStorage
function loadChatHistory() {
try {
const sessionData = localStorage.getItem(STORAGE_KEY);
if (sessionData) {
const data = JSON.parse(sessionData);
const now = new Date().getTime();
// Check if session is still valid
if (now - data.timestamp < SESSION_DURATION) {
const chatContent = document.querySelector(".chat-content");
// Clear default welcome message
chatContent.innerHTML = '';
// Load saved messages
data.messages.forEach(msg => {
const bubble = document.createElement("div");
bubble.className = `chat-bubble ${msg.type}`;
bubble.style.cssFloat = msg.type === 'user' ? 'right' : 'left';
bubble.style.clear = 'both';
bubble.innerHTML = msg.content;
chatContent.appendChild(bubble);
});
chatContent.scrollTop = chatContent.scrollHeight;
return true; // Session restored
} else {
// Session expired, clear storage
localStorage.removeItem(STORAGE_KEY);
}
}
} catch (error) {
console.error('Error loading chat history:', error);
localStorage.removeItem(STORAGE_KEY);
}
return false; // New session
}
// Save message to localStorage
function saveMessage(type, content) {
try {
let sessionData = localStorage.getItem(STORAGE_KEY);
let data;
if (sessionData) {
data = JSON.parse(sessionData);
} else {
data = {
timestamp: new Date().getTime(),
messages: []
};
}
data.messages.push({ type, content });
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
} catch (error) {
console.error('Error saving message:', error);
}
}
// Initialize chat session
const sessionRestored = loadChatHistory();
if (!sessionRestored) {
// New session - show welcome message and save it
const welcomeMsg = `
<p>Halo! Selamat datang di layanan informasi Dinas Lingkungan Hidup DKI Jakarta! 👋</p>
<p>Saya di sini untuk membantu Anda. Ada yang ingin ditanyakan seputar layanan kami?</p>
`;
saveMessage('bot', welcomeMsg);
}
chatButton.addEventListener("click", () => {
chatbot.classList.remove('scale-0');
chatbot.classList.add('scale-100');
});
closeChat.addEventListener("click", () => {
chatbot.classList.remove('scale-100');
chatbot.classList.add('scale-0');
});
sendMessage.addEventListener("click", sendChatMessage);
userInput.addEventListener("keydown", function(e) {
if (e.key === "Enter") sendChatMessage();
});
function sendChatMessage() {
const chatContent = document.querySelector(".chat-content");
const message = userInput.value.trim();
if (message) {
// User bubble (kanan)
const userBubble = document.createElement("div");
userBubble.className = "chat-bubble user";
userBubble.style.cssFloat = "right";
userBubble.style.clear = "both";
userBubble.textContent = message;
chatContent.appendChild(userBubble);
chatContent.scrollTop = chatContent.scrollHeight;
// Save user message
saveMessage('user', message);
userInput.value = '';
// Show typing indicator
const typingIndicator = document.createElement("div");
typingIndicator.className = "typing-indicator";
typingIndicator.innerHTML = `
<span></span>
<span></span>
<span></span>
`;
chatContent.appendChild(typingIndicator);
chatContent.scrollTop = chatContent.scrollHeight;
fetch('/api/apiproxy/chatbot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
message: message
})
})
.then(response => {
return response.json().catch(() => response.text());
})
.then(data => {
// Remove typing indicator
typingIndicator.remove();
let reply = "";
if (data && typeof data === "object" && data.code === 404 && data.message && data.message.includes("webhook")) {
reply = "Bot sedang offline. Silakan klik 'Execute workflow' di n8n, lalu coba lagi.";
} else if (typeof data === "string") {
try {
const parsed = JSON.parse(data);
reply = parsed.output || parsed.reply || parsed.message || parsed.text || "";
} catch {
reply = data;
}
} else if (typeof data === "object" && data !== null) {
reply = data.output || data.reply || data.message || data.text || "";
}
if (!reply) reply = "Maaf, saya tidak mengerti.";
// Format reply: convert **text** to bold and handle line breaks
reply = reply.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>').replace(/\*(.*?)\*/g, '<em>$1</em>').replace(/\n/g, "<br>");
// Bot bubble (kiri)
const botBubble = document.createElement("div");
botBubble.className = "chat-bubble bot";
botBubble.style.cssFloat = "left";
botBubble.style.clear = "both";
botBubble.innerHTML = reply;
chatContent.appendChild(botBubble);
chatContent.scrollTop = chatContent.scrollHeight;
// Save bot message
saveMessage('bot', reply);
})
.catch(error => {
// Remove typing indicator
typingIndicator.remove();
const errorMsg = "Maaf, terjadi kesalahan koneksi.";
const botBubble = document.createElement("div");
botBubble.className = "chat-bubble bot";
botBubble.style.cssFloat = "left";
botBubble.style.clear = "both";
botBubble.textContent = errorMsg;
chatContent.appendChild(botBubble);
chatContent.scrollTop = chatContent.scrollHeight;
// Save error message
saveMessage('bot', errorMsg);
console.error('Error:', error);
});
}
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Configuration untuk visitor stats
const CACHE_DURATION = 30 * 60 * 1000; // 30 menit cache
const RETRY_ATTEMPTS = 3;
const RETRY_DELAY = 2000; // 2 detik
const UPDATE_INTERVAL = 60 * 60 * 1000; // 1 jam
const VISITOR_CACHE_KEY = 'visitor_stats_cache'; // Ubah nama untuk menghindari konflik
// Function to get cached data
function getCachedData() {
try {
const cached = localStorage.getItem(VISITOR_CACHE_KEY);
if (cached) {
const data = JSON.parse(cached);
const now = new Date().getTime();
if (now - data.timestamp < CACHE_DURATION) {
return data.stats;
}
}
} catch (error) {
console.error('Error reading cache:', error);
}
return null;
}
// Function to save data to cache
function saveToCache(stats) {
try {
const cacheData = {
timestamp: new Date().getTime(),
stats: stats
};
localStorage.setItem(VISITOR_CACHE_KEY, JSON.stringify(cacheData));
} catch (error) {
console.error('Error saving to cache:', error);
}
}
// Function to clear cache
function clearCache() {
try {
localStorage.removeItem(VISITOR_CACHE_KEY);
} catch (error) {
console.error('Error clearing cache:', error);
}
}
// Sleep function for retry delay
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Function to fetch visitor data with retry logic
async function fetchVisitorStats(attempt = 1) {
try {
console.log(`Fetching visitor stats (attempt ${attempt}/${RETRY_ATTEMPTS})...`);
const response = await fetch('/api/Visitor/visitor-stats', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Cache-Control': 'no-cache'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Visitor stats fetched successfully:', data);
// Save to cache
saveToCache(data);
return data;
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error);
if (attempt < RETRY_ATTEMPTS) {
console.log(`Retrying in ${RETRY_DELAY}ms...`);
await sleep(RETRY_DELAY);
return fetchVisitorStats(attempt + 1);
} else {
console.error('All retry attempts failed');
throw error;
}
}
}
// Function to update visitor count display
function updateVisitorCount() {
const dailyElement = document.getElementById('dailyVisitors');
const monthlyElement = document.getElementById('monthlyVisitors');
if (!dailyElement || !monthlyElement) {
console.warn('Visitor count elements not found');
return;
}
// Check cache first
const cachedData = getCachedData();
if (cachedData) {
console.log('Using cached visitor data:', cachedData);
animateNumber(dailyElement, cachedData.dailyVisitorCount || 0);
animateNumber(monthlyElement, cachedData.monthlyVisitorCount || 0);
return;
}
// Show loading state
dailyElement.textContent = 'Loading...';
monthlyElement.textContent = 'Loading...';
// Fetch from API
fetchVisitorStats()
.then(data => {
console.log('API Response:', data);
// Animate numbers with actual data
animateNumber(dailyElement, data.dailyVisitorCount || 0);
animateNumber(monthlyElement, data.monthlyVisitorCount || 0);
})
.catch(error => {
console.error('Error fetching visitor data:', error);
// Try to use expired cache as fallback
try {
const expiredCache = localStorage.getItem(VISITOR_CACHE_KEY);
if (expiredCache) {
const data = JSON.parse(expiredCache);
console.log('Using expired cache as fallback:', data.stats);
animateNumber(dailyElement, data.stats.dailyVisitorCount || 0);
animateNumber(monthlyElement, data.stats.monthlyVisitorCount || 0);
return;
}
} catch (cacheError) {
console.error('Error using expired cache:', cacheError);
}
// Final fallback
dailyElement.textContent = 'N/A';
monthlyElement.textContent = 'N/A';
});
}
// Function to animate numbers
function animateNumber(element, target) {
if (!element) return;
let current = 0;
const increment = Math.ceil(target / 50);
const timer = setInterval(() => {
current += increment;
if (current >= target) {
element.textContent = target.toLocaleString();
clearInterval(timer);
} else {
element.textContent = Math.floor(current).toLocaleString();
}
}, 30);
}
// Function to force refresh (bypass cache)
function forceRefresh() {
console.log('Force refreshing visitor stats...');
clearCache();
updateVisitorCount();
}
// Call the function to update visitor count
updateVisitorCount();
// Auto-refresh every hour
setInterval(function() {
console.log('Auto-refreshing visitor stats...');
updateVisitorCount();
}, UPDATE_INTERVAL);
// Make refresh function available globally
window.refreshVisitorStats = forceRefresh;
// Clear cache on page unload if needed
window.addEventListener('beforeunload', function() {
// Optionally clear cache on page unload
// clearCache();
});
});
</script>