akiko19191's picture
Upload folder using huggingface_hub
6af31ea verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% if title %}{{ title }}{% else %}Matax Express Admin Panel{% endif %}</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc; /* Lighter gray background */
}
.link-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
background-color: white;
border: 1px solid #e2e8f0;
border-radius: 1rem; /* Slightly larger radius */
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.link-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.08);
}
.code-block {
background: #1e293b; /* Dark slate background */
border: 1px solid #334155;
border-radius: 0.75rem;
padding: 1rem 1.5rem;
font-family: 'Fira Code', monospace;
font-size: 0.9rem;
color: #cbd5e1;
overflow-x: auto;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.code-block pre {
margin: 0;
white-space: pre-wrap; /* Allow wrapping */
word-break: break-all;
}
.log-output {
background: #0f172a; /* Even darker for logs */
color: #94a3b8;
font-family: 'Fira Code', monospace;
font-size: 0.8rem;
height: 200px;
overflow-y: auto;
border-radius: 0.5rem;
padding: 0.75rem;
border: 1px solid #334155;
}
.progress-bar-fill {
transition: width 0.4s ease-in-out;
}
</style>
</head>
<body class="bg-gray-50 text-gray-800">
<!-- Navbar -->
<nav class="bg-white shadow-sm sticky top-0 left-0 w-full z-10 border-b border-gray-200">
<div class="max-w-7xl mx-auto px-6 py-4 flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-900">Matax Express Admin Panel</h1>
<div class="space-x-6 text-gray-600">
<a href="{{ url_for('zoho.index') }}" class="hover:text-blue-600 transition">Index</a>
</div>
</div>
</nav>
<!-- Content -->
<div class="max-w-7xl mx-auto px-6 pt-12 pb-10">
<h2 class="text-3xl font-bold mb-8 text-gray-900">Quick Actions</h2>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
<!-- MODIFIED: Inventory Sync Card -->
<div class="link-card p-6 flex flex-col justify-between space-y-4 col-span-1 md:col-span-2">
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-2">Sync Inventory from Zoho</h3>
<p class="text-gray-500 text-sm">Update your website inventory directly from Zoho. This may take a few minutes.</p>
</div>
<!-- Sync Progress UI (Initially hidden) -->
<div id="sync-progress-container" class="hidden space-y-3 pt-2">
<!-- Progress Bar -->
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progress-bar" class="bg-blue-600 h-2.5 rounded-full progress-bar-fill" style="width: 0%"></div>
</div>
<!-- Status Message -->
<p id="sync-status-message" class="text-center text-sm font-medium text-gray-600">Initializing...</p>
<!-- Live Log -->
<div id="sync-log-output" class="log-output"></div>
</div>
<!-- Sync Button -->
<button id="start-sync-button" class="w-full mt-auto bg-blue-600 text-white font-semibold py-2.5 px-4 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-4 focus:ring-blue-300 transition-all duration-300 flex items-center justify-center space-x-2 disabled:bg-gray-400 disabled:cursor-not-allowed">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L9 9.414V13a1 1 0 102 0V9.414l1.293 1.293a1 1 0 001.414-1.414z" clip-rule="evenodd" /></svg>
<span>Sync Inventory</span>
</button>
</div>
<a href="/api/sync_xero_users" class="link-card p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Sync Zoho Users</h3>
<p class="text-gray-500 text-sm">Keep your approved Zoho users up to date in the database.</p>
</a>
<a href="/api/sendmail" class="link-card p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Send Reminder Emails</h3>
<p class="text-gray-500 text-sm">Notify users with pending items in their cart.</p>
</a>
<a href="/api/pages/update_ui" class="link-card p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Update About UI</h3>
<p class="text-gray-500 text-sm">Revamp the look of the Contact and About Us pages.</p>
</a>
<a href="/api/pages/edit_homepage" class="link-card p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Update Homepage UI</h3>
<p class="text-gray-500 text-sm">Edit the Faq and Why choose us page.This may take some time to take effect</p>
</a>
</div>
<!-- Output Block for final results -->
<div id="output-wrapper" class="mt-12 hidden">
<h3 class="text-xl font-bold text-gray-800 mb-4">Sync Result Summary</h3>
<div class="code-block">
<pre id="final-output-pre">{% block content %}{% endblock %}</pre>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const startButton = document.getElementById('start-sync-button');
const progressContainer = document.getElementById('sync-progress-container');
const progressBar = document.getElementById('progress-bar');
const statusMessage = document.getElementById('sync-status-message');
const logOutput = document.getElementById('sync-log-output');
const outputWrapper = document.getElementById('output-wrapper');
const finalOutputPre = document.getElementById('final-output-pre');
let eventSource;
startButton.addEventListener('click', function() {
// --- 1. Reset UI State ---
startButton.disabled = true;
startButton.querySelector('span').textContent = 'Syncing...';
progressContainer.classList.remove('hidden');
progressBar.style.width = '0%';
progressBar.classList.remove('bg-green-500', 'bg-red-500');
progressBar.classList.add('bg-blue-600');
logOutput.innerHTML = '';
statusMessage.textContent = 'Connecting to server...';
outputWrapper.classList.add('hidden');
finalOutputPre.textContent = '';
addLogEntry('Initiating synchronization...', 'info');
// --- 2. Start Server-Sent Events Connection ---
// IMPORTANT: Ensure this URL points to your actual streaming endpoint.
const eventSourceURL = "{{ url_for('zoho.fetch_inventory_stream') }}";
eventSource = new EventSource(eventSourceURL);
eventSource.onopen = function() {
addLogEntry('Connection established. Starting sync process.', 'info');
};
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
// Update Progress Bar
progressBar.style.width = data.progress + '%';
// Update Status Message (show first line only for brevity)
statusMessage.textContent = data.message.split('\n')[0];
// Add full message to log
addLogEntry(data.message, 'message');
// Handle final states
if (data.status === 'complete' || data.status === 'error') {
eventSource.close();
if (data.status === 'complete') {
handleSyncComplete(data);
} else {
handleSyncError(data);
}
}
};
eventSource.onerror = function(err) {
console.error("EventSource failed:", err);
eventSource.close();
const errorData = { message: 'Connection to the server was lost. Please check the console and try again.' };
handleSyncError(errorData);
};
});
function handleSyncComplete(data) {
statusMessage.textContent = "Synchronization completed successfully!";
progressBar.classList.remove('bg-blue-600');
progressBar.classList.add('bg-green-500');
addLogEntry('Sync complete!', 'success');
if (data.final_code) {
finalOutputPre.textContent = data.final_code;
outputWrapper.classList.remove('hidden');
}
resetButtonAfterDelay();
}
function handleSyncError(data) {
statusMessage.textContent = "An error occurred during synchronization.";
progressBar.classList.remove('bg-blue-600');
progressBar.classList.add('bg-red-500');
addLogEntry(data.message, 'error');
resetButtonAfterDelay();
}
function addLogEntry(message, type) {
const timestamp = new Date().toLocaleTimeString();
const entry = document.createElement('div');
entry.classList.add('flex', 'items-start', 'space-x-2');
let colorClass = 'text-gray-400';
if (type === 'success') colorClass = 'text-green-400';
if (type === 'error') colorClass = 'text-red-400';
entry.innerHTML = `
<span class="font-mono text-gray-500">[${timestamp}]</span>
<span class="flex-1 ${colorClass}">${message.replace(/\n/g, '<br>')}</span>
`;
logOutput.appendChild(entry);
logOutput.scrollTop = logOutput.scrollHeight; // Auto-scroll to bottom
}
function resetButtonAfterDelay() {
setTimeout(() => {
startButton.disabled = false;
startButton.querySelector('span').textContent = 'Sync Inventory';
}, 2000);
}
});
</script>
</body>
</html>