Files
T
2025-04-03 16:30:28 +05:45

217 lines
6.7 KiB
JavaScript

// Utility functions for the teacher dashboard
// Global variables and constants
let fileIdToDelete = null;
const API_BASE_URL = '/app';
const DEFAULT_ERROR_MESSAGE = 'An error occurred. Please try again.';
// Type checking utilities
const isString = (value) => typeof value === 'string';
const isObject = (value) => value !== null && typeof value === 'object';
const isEmpty = (value) => value === null || value === undefined || value === '';
// CSRF token management
function getCSRFToken() {
try {
const cookies = document.cookie.split(';');
const csrfCookie = cookies.find(cookie => cookie.trim().startsWith('csrftoken='));
return csrfCookie ? decodeURIComponent(csrfCookie.split('=')[1]) : null;
} catch (error) {
console.error('Error getting CSRF token:', error);
return null;
}
}
// API request helper with better error handling and retries
async function apiRequest(url, options = {}, retries = 3) {
const defaultOptions = {
headers: {
'X-CSRFToken': getCSRFToken(),
'Accept': 'application/json',
'Content-Type': 'application/json'
},
credentials: 'same-origin'
};
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const response = await fetch(url, { ...defaultOptions, ...options });
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === retries) {
console.error(`API Request Error (attempt ${attempt}/${retries}):`, error);
throw error;
}
await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // Exponential backoff
}
}
}
// UI Helpers with enhanced styling and accessibility
function showLoadingSpinner(container, message = 'Loading...') {
if (!container) return;
container.innerHTML = `
<div class="text-center py-4" role="status" aria-live="polite">
<div class="spinner-border text-primary" role="presentation">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2 text-muted">${message}</p>
</div>
`;
}
function showError(container, error, withRetryButton = false) {
if (!container) return;
const errorMessage = error?.message || DEFAULT_ERROR_MESSAGE;
container.innerHTML = `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>
<strong>${errorMessage}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
${withRetryButton ? `
<div class="text-center mt-3">
<button class="btn btn-primary" onclick="window.location.reload()">
<i class="fas fa-sync me-1"></i> Retry
</button>
</div>
` : ''}
`;
}
function showSuccess(container, message, autoHide = true) {
if (!container) return;
container.innerHTML = `
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i>
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
if (autoHide) {
setTimeout(() => {
const alert = container.querySelector('.alert');
if (alert) {
alert.classList.remove('show');
setTimeout(() => alert.remove(), 150);
}
}, 5000);
}
}
// Modal Helpers with enhanced functionality
function showModal(modalId) {
const modal = document.getElementById(modalId);
if (!modal) return;
modal.style.display = 'block';
document.body.style.overflow = 'hidden';
// Focus trap for accessibility
const focusableElements = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (focusableElements.length > 0) {
focusableElements[0].focus();
}
}
function hideModal(modalId) {
const modal = document.getElementById(modalId);
if (!modal) return;
modal.style.display = 'none';
document.body.style.overflow = '';
}
// Form Helpers with validation
function resetForm(formOrId) {
let form;
// Handle both form element and form ID
if (typeof formOrId === 'string') {
form = document.getElementById(formOrId);
} else if (formOrId instanceof HTMLFormElement) {
form = formOrId;
} else {
console.warn('Invalid form reference:', formOrId);
return;
}
if (!form) {
console.warn('Form not found:', formOrId);
return;
}
// Reset form values
form.reset();
// Clear all alerts and validation states
form.querySelectorAll('.alert, .is-invalid, .is-valid').forEach(el => {
el.classList.remove('alert', 'is-invalid', 'is-valid');
});
// Reset all checkboxes to unchecked state
form.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
checkbox.checked = false;
});
// Reset all select elements to first option
form.querySelectorAll('select').forEach(select => {
if (select.options.length > 0) {
select.selectedIndex = 0;
}
});
}
// Date Helpers with enhanced formatting options
function formatDate(date, format = 'YYYY-MM-DD') {
const d = new Date(date);
if (isNaN(d.getTime())) return '';
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day);
}
function getCurrentDate() {
return formatDate(new Date());
}
// Validation Helpers
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function validatePhone(phone) {
const re = /^\+?[\d\s-]{10,}$/;
return re.test(phone);
}
// Export all functions
window.getCSRFToken = getCSRFToken;
window.apiRequest = apiRequest;
window.showLoadingSpinner = showLoadingSpinner;
window.showError = showError;
window.showSuccess = showSuccess;
window.showModal = showModal;
window.hideModal = hideModal;
window.resetForm = resetForm;
window.formatDate = formatDate;
window.getCurrentDate = getCurrentDate;
window.validateEmail = validateEmail;
window.validatePhone = validatePhone;