function showTab(id, event) {
// Remove active class from all tabs
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
// Add active class to clicked tab
event.target.classList.add('active');
// Hide all content divs
document.querySelectorAll('.content > div').forEach(div => {
div.classList.add('hidden');
div.classList.remove('slide-in');
});
// Show selected content with animation
const targetDiv = document.getElementById(id);
targetDiv.classList.remove('hidden');
setTimeout(() => targetDiv.classList.add('slide-in'), 10);
// Load coupon list when "list" tab is shown
if (id === 'list') {
loadCodeList();
// Set up search functionality when list tab is shown
setupSearchFunctionality();
}
// Handle translation upload tab
if (id === 'translation-upload') {
renderTranslationUploadSection();
}
}
/**
* Handles the logout process by showing confirmation modal
*/
async function handleLogout() {
document.getElementById('logoutModal').classList.add('show');
}
/**
* Closes the logout confirmation modal
*/
function closeLogoutModal() {
document.getElementById('logoutModal').classList.remove('show');
}
/**
* Confirms and executes the logout process
* Makes API call to logout endpoint and redirects user
*/
async function confirmLogout() {
const logoutBtn = document.querySelector('#logoutModal .btn-danger');
// Show loading state
logoutBtn.classList.add('loading');
logoutBtn.innerHTML = ' Logging out...';
try {
const response = await fetch('/admin/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
showNotification('Logged out successfully! Redirecting...', 'success');
setTimeout(() => {
window.location.href = '/login';
}, 1500);
} else {
showNotification('Error during logout. Please try again.', 'error');
}
} catch (error) {
console.error('Logout error:', error);
showNotification('Network error. Please try again.', 'error');
} finally {
// Reset button
logoutBtn.classList.remove('loading');
logoutBtn.innerHTML = ' Logout';
closeLogoutModal();
}
}
/**
* Generates coupon codes based on selected mode (single or bulk)
* Handles form validation and displays results
*/
async function generateCode() {
const mode = document.querySelector('input[name="genMode"]:checked').value;
const resultEl = document.getElementById('genResult');
const btn = event.target;
// Show loading state
btn.classList.add('loading');
btn.innerHTML = ' Generating...';
let payload = new FormData();
payload.append("mode", mode);
if (mode === "bulk") {
const count = parseInt(document.getElementById('bulkCount').value);
if (!count || count <= 0) {
resultEl.innerHTML = `
Warning! Please enter a valid number of codes to generate.
`;
// Reset button
btn.classList.remove('loading');
btn.innerHTML = ' Generate Codes';
return;
}
payload.append("count", count);
}
try {
const res = await fetch('/generate', {
method: 'POST',
body: payload
});
const data = await res.json();
if (mode === "single") {
resultEl.innerHTML = `
Success! Generated Code: ${data.code}
`;
} else {
resultEl.innerHTML = `
Success! Generated ${data.codes.length} codes:
${data.codes.map(code => `
${code}
`).join('')}
`;
}
} catch (error) {
resultEl.innerHTML = `
Error! Failed to generate codes. Please try again.
`;
} finally {
// Reset button
btn.classList.remove('loading');
btn.innerHTML = ' Generate Codes';
}
}
// Pagination variables
let currentPage = 1;
let totalPages = 1;
let totalCodes = 0;
const codesPerPage = 20;
/**
* Loads and displays coupon code list with pagination
*/
async function loadCodeList(page = 1) {
try {
const res = await fetch(`/list?page=${page}&limit=${codesPerPage}`);
const data = await res.json();
const tbody = document.querySelector('#codeTable tbody');
console.log("Loading coupon list...");
// Update pagination variables
currentPage = data.page;
totalPages = data.total_pages;
totalCodes = data.total;
if (data.codes.length === 0) {
tbody.innerHTML = `
No coupon codes found
`;
updatePaginationInfo();
updatePaginationControls();
return;
}
// Render current page rows
tbody.innerHTML = '';
data.codes.forEach(item => {
const usedAtDisplay = item.usage_count > 0 ? (item.used_at ? item.used_at : '--') : '--';
const row = `
${item.code}
${usedAtDisplay}
`;
tbody.innerHTML += row;
});
updatePaginationInfo();
updatePaginationControls();
} catch (error) {
console.error("Error loading coupon list:", error);
const tbody = document.querySelector('#codeTable tbody');
tbody.innerHTML = `
Error loading coupon codes
`;
updatePaginationInfo();
updatePaginationControls();
}
}
/**
* Toggles between single code entry and Excel file upload modes
*/
function toggleUploadMode(mode) {
const singleSection = document.getElementById("add-code-form");
const excelSection = document.getElementById("file-upload-section");
// Update radio option styling
document.querySelectorAll('input[name="uploadMode"]').forEach(radio => {
const option = radio.closest('.radio-option');
if (radio.checked) {
option.classList.add('active');
} else {
option.classList.remove('active');
}
});
if (mode === "single") {
singleSection.classList.remove("hidden");
singleSection.style.display = "block";
excelSection.classList.add("hidden");
excelSection.style.display = "none";
// Clear any existing file upload data
clearFile();
} else if (mode === "excel") {
singleSection.classList.add("hidden");
singleSection.style.display = "none";
excelSection.classList.remove("hidden");
excelSection.style.display = "block";
// Clear single code form
document.getElementById('new-code').value = '';
document.getElementById('new-usage').value = '';
}
}
/**
* Adds a new coupon code through the single entry form
* Validates input and sends data to server
*/
async function addNewCode(event) {
const codeInput = document.getElementById('new-code');
const usageInput = document.getElementById('new-usage');
const btn = event.target;
const code = codeInput.value.trim().toUpperCase();
const usage = parseInt(usageInput.value) || 0;
// Validation
if (!code) {
showNotification('Please enter a coupon code', 'error');
return;
}
if (code.length < 3) {
showNotification('Code must be at least 3 characters long', 'error');
return;
}
if (usage < 0) {
showNotification('Usage count cannot be negative', 'error');
return;
}
// Show loading state
btn.classList.add('loading');
btn.innerHTML = ' Adding...';
try {
const response = await fetch('/add-code', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code: code,
usage: usage
})
});
if (response.ok) {
showNotification('Code added successfully!', 'success');
// Clear inputs
codeInput.value = '';
usageInput.value = '';
// Reload code list if on list tab
if (typeof loadCodeList === 'function') {
loadCodeList(currentPage || 1);
}
// DO NOT hide the form - keep it visible for more entries
} else {
const error = await response.json();
showNotification(error.detail || 'Failed to add code', 'error');
}
} catch (error) {
console.error('Error adding code:', error);
showNotification('Network error. Please try again.', 'error');
} finally {
// Reset button
btn.classList.remove('loading');
btn.innerHTML = ' Add Code';
}
}
/**
* Initiates the delete process for a coupon code
* Shows confirmation modal
*/
function deleteCode(code) {
currentDeleteCode = code;
document.getElementById('deleteCodeName').textContent = code;
document.getElementById('deleteModal').classList.add('show');
}
/**
* Confirms and executes coupon code deletion
* Makes API call and updates the list
*/
async function confirmDeleteCode() {
const btn = event.target;
// Show loading state
btn.classList.add('loading');
btn.innerHTML = ' Deleting...';
try {
const response = await fetch(`/delete-code/${currentDeleteCode}`, {
method: 'DELETE'
});
if (response.ok) {
showNotification('Code deleted successfully!', 'success');
closeDeleteModal();
loadCodeList(currentPage);
} else {
const error = await response.json();
showNotification(error.detail || 'Failed to delete code', 'error');
}
} catch (error) {
console.error('Error deleting code:', error);
showNotification('Network error. Please try again.', 'error');
} finally {
// Reset button
btn.classList.remove('loading');
btn.innerHTML = ' Delete Code';
}
}
/**
* Closes the delete confirmation modal and resets state
*/
function closeDeleteModal() {
document.getElementById('deleteModal').classList.remove('show');
currentDeleteCode = null;
}
/**
* Displays a notification message with specified type and auto-dismiss
*/
function showNotification(message, type = 'info') {
// Remove existing notifications
const existingNotification = document.querySelector('.notification');
if (existingNotification) {
existingNotification.remove();
}
// Create notification element
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = `
${message}
`;
document.body.appendChild(notification);
// Auto-remove after 4 seconds
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 4000);
}
// Close modals when clicking outside
document.addEventListener('click', function(event) {
const deleteModal = document.getElementById('deleteModal');
if (event.target === deleteModal) {
closeDeleteModal();
}
});
// Close modals with Escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeDeleteModal();
}
});
/**
* Filters coupon codes based on search query
* Makes API call to search endpoint and displays results
*/
async function filterCoupons(query) {
console.log('filterCoupons called with query:', query); // Debug
const tbody = document.querySelector('#codeTable tbody');
const searchResultInfo = document.getElementById('searchResultInfo');
const noResultsInfo = document.getElementById('noResultsInfo');
const searchResultText = document.getElementById('searchResultText');
console.log('Elements found:', { tbody, searchResultInfo, noResultsInfo, searchResultText }); // Debug
if (!query.trim()) {
console.log('Empty query, calling showAllCoupons'); // Debug
showAllCoupons();
return;
}
try {
console.log('Making fetch request to:', `/search-codes?query=${encodeURIComponent(query)}`); // Debug
const res = await fetch(`/search-codes?query=${encodeURIComponent(query)}`);
console.log('Response status:', res.status); // Debug
const results = await res.json();
console.log('Search results:', results); // Debug
tbody.innerHTML = '';
if (results.length === 0) {
console.log('No results found, showing no results message'); // Debug
noResultsInfo.classList.add('show');
searchResultInfo.classList.remove('show');
return;
}
// Show rows with action buttons
results.forEach(item => {
const row = `
${item.code}
${item.used_at ? item.used_at : '-'}
`;
tbody.innerHTML += row;
});
console.log('Showing search results, hiding no results message'); // Debug
noResultsInfo.classList.remove('show');
searchResultInfo.classList.add('show');
searchResultText.textContent =
results.length === 1
? `Found coupon code: ${results[0].code}`
: `Found ${results.length} coupon codes matching "${query}"`;
} catch (err) {
console.error("Search failed:", err);
noResultsInfo.classList.add('show');
searchResultInfo.classList.remove('show');
}
}
/**
* Shows all coupon codes by clearing search filters
* Restores the normal paginated view
*/
function showAllCoupons() {
console.log('showAllCoupons called, currentPage:', currentPage); // Debug
const searchResultInfo = document.getElementById('searchResultInfo');
const noResultsInfo = document.getElementById('noResultsInfo');
// Hide info messages
searchResultInfo.classList.remove('show');
noResultsInfo.classList.remove('show');
// Reload the full coupon list to current page
loadCodeList(currentPage);
}
/**
* Clears the search input and shows all coupons
*/
function clearSearch() {
const searchInput = document.getElementById('searchInput');
const searchClear = document.getElementById('searchClear');
searchInput.value = '';
searchClear.classList.remove('show');
showAllCoupons();
}
/**
* Changes the current page for pagination
*/
function changePage(direction) {
const newPage = currentPage + direction;
if (newPage >= 1 && newPage <= totalPages) {
loadCodeList(newPage);
}
}
/**
* Navigates directly to a specific page
*/
function goToPage(page) {
if (page >= 1 && page <= totalPages && page !== currentPage) {
loadCodeList(page);
}
}
/**
* Updates the pagination information display
*/
function updatePaginationInfo() {
const start = totalCodes === 0 ? 0 : (currentPage - 1) * codesPerPage + 1;
const end = Math.min(currentPage * codesPerPage, totalCodes);
document.getElementById('paginationInfo').textContent =
`Showing ${start}-${end} of ${totalCodes} codes`;
}
/**
* Updates the pagination control buttons and page numbers
*/
function updatePaginationControls() {
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
const pageNumbers = document.getElementById('pageNumbers');
// Update prev/next buttons
prevBtn.disabled = currentPage <= 1;
nextBtn.disabled = currentPage >= totalPages;
// Generate page numbers
pageNumbers.innerHTML = '';
if (totalPages <= 7) {
// Show all pages if 7 or fewer
for (let i = 1; i <= totalPages; i++) {
createPageButton(i, pageNumbers);
}
} else {
// Show first page
createPageButton(1, pageNumbers);
if (currentPage > 4) {
pageNumbers.innerHTML += '... ';
}
// Show pages around current page
const start = Math.max(2, currentPage - 1);
const end = Math.min(totalPages - 1, currentPage + 1);
for (let i = start; i <= end; i++) {
createPageButton(i, pageNumbers);
}
if (currentPage < totalPages - 3) {
pageNumbers.innerHTML += '... ';
}
// Show last page
if (totalPages > 1) {
createPageButton(totalPages, pageNumbers);
}
}
}
/**
* Creates a page button for pagination controls
*/
function createPageButton(pageNum, container) {
const button = document.createElement('button');
button.className = `page-number ${pageNum === currentPage ? 'active' : ''}`;
button.textContent = pageNum;
button.onclick = () => goToPage(pageNum);
container.appendChild(button);
}
// Upload functionality
let previewData = [];
let currentDeleteCode = null;
/**
* Handles file selection for Excel upload
* Validates file type and size, then processes the file
*/
function handleFileSelect(event) {
console.log("File select triggered"); // Debug
const file = event.target.files[0];
if (!file) {
console.log("No file selected"); // Debug
return;
}
console.log("File selected:", file.name, file.size); // Debug
const fileInfo = document.getElementById('fileInfo');
const fileName = document.getElementById('fileName');
const validationErrors = document.getElementById('validationErrors');
const previewSection = document.getElementById('previewSection');
const uploadResult = document.getElementById('uploadResult');
// Reset previous states
validationErrors.style.display = 'none';
previewSection.style.display = 'none';
uploadResult.innerHTML = '';
// Validate file type
if (!file.name.match(/\.(xlsx|xls)$/i)) {
console.log("Invalid file type"); // Debug
showValidationErrors(['Please select a valid Excel file (.xlsx or .xls)']);
return;
}
// Validate file size (10MB limit)
if (file.size > 10 * 1024 * 1024) {
console.log("File too large"); // Debug
showValidationErrors(['File size must be less than 10MB']);
return;
}
// Show file info
fileName.textContent = file.name;
fileInfo.style.display = 'flex';
console.log("File info displayed"); // Debug
// Read and process file
const reader = new FileReader();
reader.onload = function(e) {
console.log("File read complete, processing..."); // Debug
try {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' });
console.log("Workbook read:", workbook.SheetNames); // Debug
// Get first worksheet
const worksheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[worksheetName];
// Convert to JSON
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
console.log("JSON data:", jsonData); // Debug
if (jsonData.length < 2) {
console.log("Not enough data rows"); // Debug
showValidationErrors(['Excel file must contain at least a header row and one data row']);
return;
}
// Process data
processExcelData(jsonData);
} catch (error) {
console.error('Error reading file:', error);
showValidationErrors(['Error reading Excel file. Please check file format.']);
}
};
reader.onerror = function(error) {
console.error('FileReader error:', error); // Debug
};
console.log("Starting file read..."); // Debug
reader.readAsArrayBuffer(file);
}
/**
* Processes Excel data and validates coupon codes
* Detects column headers and validates data format
*/
function processExcelData(data) {
const errors = [];
const processed = [];
const duplicates = new Set();
const seen = new Set();
// Expected headers (case insensitive)
const headers = data[0].map(h => String(h).toLowerCase().trim());
// Column index detection
const codeIndex = headers.findIndex(h => h.includes('code'));
const usageIndex = headers.findIndex(h => h.includes('usage') || h.includes('use'));
// Check if code column exists
if (codeIndex === -1) {
errors.push(`Missing required column: code`);
}
if (errors.length === 0) {
// Process data rows
for (let i = 1; i < data.length; i++) {
const row = data[i];
if (!row || row.length === 0) continue; // Skip empty rows
const code = String(row[codeIndex] || '').trim().toUpperCase();
let usage = 0;
// Handle usage column presence and validity
if (usageIndex !== -1) {
const rawUsage = row[usageIndex];
usage = isNaN(rawUsage) || rawUsage === null || rawUsage === "" ? 0 : parseInt(rawUsage);
}
// Validate code
if (!code) {
errors.push(`Row ${i + 1}: Code is required`);
continue;
}
if (code.length < 3) {
errors.push(`Row ${i + 1}: Code must be at least 3 characters`);
continue;
}
// Check for duplicates within file
if (seen.has(code)) {
duplicates.add(code);
continue;
}
seen.add(code);
// Validate usage count
if (usage < 0) {
errors.push(`Row ${i + 1}: Usage count cannot be negative`);
continue;
}
processed.push({
code,
usage
});
}
}
// Show results
if (errors.length > 0) {
showValidationErrors(errors);
}
if (processed.length > 0) {
previewData = processed;
showPreview(processed, duplicates.size);
}
}
/**
* Displays validation errors for Excel file processing
*/
function showValidationErrors(errors) {
const validationErrors = document.getElementById('validationErrors');
const errorList = document.getElementById('errorList');
errorList.innerHTML = '';
errors.forEach(error => {
const li = document.createElement('li');
li.textContent = error;
errorList.appendChild(li);
});
validationErrors.style.display = 'block';
}
/**
* Shows preview of processed Excel data before upload
* Displays statistics and sample rows
*/
function showPreview(data, duplicateCount) {
const previewSection = document.getElementById('previewSection');
const previewTableBody = document.getElementById('previewTableBody');
// Update stats
document.getElementById('totalCodes').textContent = data.length + duplicateCount;
document.getElementById('validCodes').textContent = data.length;
document.getElementById('duplicateCodes').textContent = duplicateCount;
// Show preview table (first 10 rows) - Fixed to match 2-column structure
previewTableBody.innerHTML = '';
const previewRows = data.slice(0, 10);
previewRows.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
${item.code}
${item.usage}
`;
previewTableBody.appendChild(row);
});
if (data.length > 10) {
const moreRow = document.createElement('tr');
moreRow.innerHTML = `
... and ${data.length - 10} more codes
`;
previewTableBody.appendChild(moreRow);
}
previewSection.style.display = 'block';
// Auto-upload to database
uploadCodes();
}
/**
* Uploads processed coupon codes to the database
* Makes API call with the validated coupon data
*/
async function uploadCodes() {
if (previewData.length === 0) {
return;
}
const uploadBtn = document.getElementById('uploadBtn');
if (uploadBtn) uploadBtn.style.display = 'none';
const uploadResult = document.getElementById('uploadResult');
try {
const response = await fetch('/upload-codes', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ codes: previewData })
});
const result = await response.json();
if (response.ok) {
uploadResult.innerHTML = `
Success! Successfully uploaded ${result.uploaded} coupon codes to database.
${result.skipped > 0 ? `${result.skipped} codes were skipped (already exist). ` : ''}
`;
// Clear form after successful upload
setTimeout(() => {
clearFile();
}, 3000);
} else {
uploadResult.innerHTML = `
Error! ${result.error || 'Failed to upload codes. Please try again.'}
`;
}
} catch (error) {
console.error('Upload error:', error);
uploadResult.innerHTML = `
Error! Network error. Please check your connection and try again.
`;
} finally {
// Reset button
uploadBtn.classList.remove('loading');
uploadBtn.innerHTML = ' Upload to Database';
}
}
/**
* Clears the file upload form and resets all related UI elements
*/
function clearFile() {
const fileInput = document.getElementById('excelFile');
const fileInfo = document.getElementById('fileInfo');
const validationErrors = document.getElementById('validationErrors');
const previewSection = document.getElementById('previewSection');
const uploadResult = document.getElementById('uploadResult');
fileInput.value = '';
fileInfo.style.display = 'none';
validationErrors.style.display = 'none';
previewSection.style.display = 'none';
uploadResult.innerHTML = '';
previewData = [];
}
/**
* Sets up drag and drop functionality for file upload area
*/
function setupDragAndDrop() {
const uploadArea = document.querySelector('.file-upload-area');
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
const fileInput = document.getElementById('excelFile');
fileInput.files = files;
handleFileSelect({ target: { files } });
}
});
}
/**
* Handles radio button styling changes for generation mode selection
*/
function handleRadioChange() {
const radios = document.querySelectorAll('input[name="genMode"]');
const bulkCountWrapper = document.getElementById('bulkCountWrapper');
radios.forEach(radio => {
const option = radio.closest('.radio-option');
if (radio.checked) {
option.classList.add('active');
// Show/hide bulk count input
if (radio.value === 'bulk') {
bulkCountWrapper.classList.remove('hidden');
} else {
bulkCountWrapper.classList.add('hidden');
}
} else {
option.classList.remove('active');
}
});
}
/**
* Renders the translation upload section based on current file status
* Checks if translation file exists and displays appropriate UI
*/
async function renderTranslationUploadSection() {
const section = document.getElementById('translationUploadSection');
section.innerHTML = ' Checking translation file status...
';
let fileExists = false;
let fileName = '';
try {
const res = await fetch('/translations/status');
if (res.ok) {
const data = await res.json();
fileExists = data.file_exists;
fileName = data.file_name || '';
console.log('Translation status:', data); // Debug log
} else {
console.error('Status check failed:', res.status);
fileExists = false;
}
} catch (e) {
console.error('Error checking translation status:', e);
fileExists = false;
}
if (fileExists) {
section.innerHTML = `
Translation file is present
${fileName ? `
${fileName}
` : ''}
The extension will use this file for translations
Download Translation File
Delete Translation File
`;
} else {
section.innerHTML = `
`;
}
}
/**
* Triggers the hidden file input element for translation file selection
*/
function triggerFileInput() {
document.getElementById('translationFile').click();
}
/**
* Handles translation file selection and displays selected filename
*/
function handleTranslationFileSelect(event) {
const file = event.target.files[0];
const fileNameDiv = document.getElementById('selectedFileName');
if (file) {
fileNameDiv.innerHTML = ` Selected: ${file.name}`;
fileNameDiv.style.color = '#059669';
} else {
fileNameDiv.innerHTML = '';
}
}
/**
* Uploads translation file to server
* Validates file selection and handles upload process
*/
async function uploadTranslationFile() {
const fileInput = document.getElementById('translationFile');
const resultDiv = document.getElementById('translationUploadResult');
const uploadBtn = document.getElementById('uploadTranslationBtn');
if (!fileInput || !fileInput.files.length) {
resultDiv.innerHTML = ' Please select a file to upload.
';
return;
}
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
// Show loading state
if (uploadBtn) {
uploadBtn.classList.add('loading');
uploadBtn.innerHTML = ' Uploading...';
uploadBtn.disabled = true;
}
resultDiv.innerHTML = ' Uploading...
';
try {
const response = await fetch('/upload-translations', {
method: 'POST',
body: formData
});
const data = await response.json();
if (response.ok) {
resultDiv.innerHTML = ` ${data.message || 'Upload successful!'} - File: ${file.name}
`;
// Wait a moment before refreshing the section to show the success message
setTimeout(async () => {
await renderTranslationUploadSection();
}, 2000);
} else {
resultDiv.innerHTML = ` Upload failed: ${data.detail || 'Unknown error'}
`;
}
} catch (err) {
console.error('Upload error:', err);
resultDiv.innerHTML = ' Upload failed. Please try again.
';
} finally {
// Reset button
if (uploadBtn) {
uploadBtn.classList.remove('loading');
uploadBtn.innerHTML = ' Upload Translation File';
uploadBtn.disabled = false;
}
}
}
/**
* Downloads the current translation file from server
* Handles file download and user feedback
*/
async function downloadTranslationFile() {
const resultDiv = document.getElementById('translationUploadResult');
try {
// Show loading state
resultDiv.innerHTML = ' Preparing download...
';
const response = await fetch('/download-translation', {
method: 'GET'
});
if (response.ok) {
// Get the filename from response headers or use default
const contentDisposition = response.headers.get('Content-Disposition');
let filename = 'translation.xlsx';
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/);
if (filenameMatch) {
filename = filenameMatch[1];
}
}
// Create blob and download
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
resultDiv.innerHTML = ` Download started successfully!
`;
// Clear success message after 3 seconds
setTimeout(() => {
resultDiv.innerHTML = '';
}, 3000);
} else {
const data = await response.json();
resultDiv.innerHTML = ` Download failed: ${data.detail || 'File not found'}
`;
}
} catch (err) {
console.error('Download error:', err);
resultDiv.innerHTML = ' Download failed. Please try again.
';
}
}
/**
* Deletes the current translation file from server
* Shows confirmation dialog and handles deletion process
*/
async function deleteTranslationFile() {
const resultDiv = document.getElementById('translationUploadResult');
// Show confirmation
if (!confirm('Are you sure you want to delete the translation file? This action cannot be undone.')) {
return;
}
resultDiv.innerHTML = ' Deleting...
';
try {
const response = await fetch('/delete-translation', {
method: 'DELETE'
});
const data = await response.json();
if (response.ok) {
resultDiv.innerHTML = ` ${data.message || 'Deleted successfully!'}
`;
// Wait a moment before refreshing the section to show the success message
setTimeout(async () => {
await renderTranslationUploadSection();
}, 2000);
} else {
resultDiv.innerHTML = ` Delete failed: ${data.detail || 'Unknown error'}
`;
}
} catch (err) {
console.error('Delete error:', err);
resultDiv.innerHTML = ' Delete failed. Please try again.
';
}
}
/**
* Initializes the dashboard when DOM content is loaded
* Sets up event listeners, initial states, and functionality
*/
document.addEventListener('DOMContentLoaded', function() {
// Set initial active states
const firstTab = document.querySelector('.tab');
if (firstTab) {
showTab('generate', { target: firstTab });
}
// Add event listeners to radio buttons
const radios = document.querySelectorAll('input[name="genMode"]');
radios.forEach(radio => {
radio.addEventListener('change', handleRadioChange);
});
// Initialize radio states
handleRadioChange();
// Add click handlers to radio options for better UX
document.querySelectorAll('.radio-option').forEach(option => {
option.addEventListener('click', function() {
const radio = this.querySelector('input[type="radio"]');
if (radio) {
radio.checked = true;
handleRadioChange();
}
});
});
// Add upload mode toggle handlers
const uploadRadios = document.querySelectorAll('input[name="uploadMode"]');
uploadRadios.forEach(radio => {
radio.addEventListener('change', function() {
toggleUploadMode(this.value);
});
});
// Add click handlers to upload radio options
document.querySelectorAll('.radio-option').forEach(option => {
option.addEventListener('click', function() {
const radioInput = this.querySelector('input[type="radio"]');
if (radioInput && radioInput.name === 'uploadMode') {
radioInput.checked = true;
toggleUploadMode(radioInput.value);
}
});
});
// Initialize upload mode
toggleUploadMode("single");
// Setup drag and drop for file upload
setupDragAndDrop();
});
/**
* Sets up search functionality for coupon code filtering
* Adds event listeners and handles search input changes
*/
function setupSearchFunctionality() {
console.log('Setting up search functionality...'); // Debug
const searchInput = document.getElementById('searchInput');
const searchClear = document.getElementById('searchClear');
console.log('Search input found:', searchInput); // Debug
console.log('Search clear found:', searchClear); // Debug
if (searchInput && searchClear) {
// Remove existing event listeners to prevent duplicates
searchInput.removeEventListener('input', searchInput.searchHandler);
// Create the event handler function
searchInput.searchHandler = function() {
const query = this.value.trim();
console.log('Search query:', query); // Debug
if (query) {
searchClear.classList.add('show');
console.log('Calling filterCoupons with:', query); // Debug
filterCoupons(query);
} else {
searchClear.classList.remove('show');
console.log('Calling showAllCoupons'); // Debug
showAllCoupons();
}
};
// Add the event listener
searchInput.addEventListener('input', searchInput.searchHandler);
console.log('Search event listener added successfully'); // Debug
} else {
console.error('Search elements not found!'); // Debug
}
}