add license
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/* Custom CSS for Edu-Space Admin */
|
||||
|
||||
/* Dark Theme Enhancements */
|
||||
body {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
/* Card Styles */
|
||||
.card {
|
||||
border-radius: 15px;
|
||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.navbar {
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #5568d8 0%, #654198 100%);
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-control, .form-select {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #444;
|
||||
background-color: #2a2a2a;
|
||||
color: #fff;
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
background-color: #2a2a2a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.table {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.table {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.table:hover tbody tr:hover {
|
||||
background-color: rgba(102, 126, 234, 0.05);
|
||||
}
|
||||
|
||||
/* Badge Styles */
|
||||
.badge {
|
||||
font-size: 0.75em;
|
||||
padding: 0.5em 0.8em;
|
||||
}
|
||||
|
||||
/* Status Badge Colors */
|
||||
.bg-confirmed-custom {
|
||||
background-color: #28a745;
|
||||
}
|
||||
|
||||
.bg-pending-custom {
|
||||
background-color: #ffc107;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.bg-cancelled-custom {
|
||||
background-color: #dc3545;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #667eea;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Calendar Customizations */
|
||||
.fc-event {
|
||||
border-radius: 6px;
|
||||
border: none !important;
|
||||
padding: 4px 8px !important;
|
||||
font-size: 12px !important;
|
||||
margin: 1px 0 !important;
|
||||
}
|
||||
|
||||
.fc-day-sat, .fc-day-sun {
|
||||
background-color: rgba(255, 255, 255, 0.02) !important;
|
||||
}
|
||||
|
||||
/* Dashboard Stat Cards */
|
||||
.stat-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.stat-card-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
||||
.stat-card-success { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); }
|
||||
.stat-card-info { background: linear-gradient(135deg, #17a2b8 0%, #138496 100%); }
|
||||
.stat-card-warning { background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%); }
|
||||
|
||||
/* Quick Action Buttons */
|
||||
.quick-action-btn {
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 10px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.quick-action-btn:hover {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
/* Empty State Styles */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Notification Toastenhancements */
|
||||
.toast {
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Pagination Styles */
|
||||
.pagination .page-link {
|
||||
border-radius: 8px;
|
||||
margin: 0 2px;
|
||||
border: 1px solid #444;
|
||||
background-color: #2a2a2a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pagination .page-link:hover {
|
||||
background-color: #667eea;
|
||||
border-color: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
background-color: #667eea;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* Mobile Responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
.card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.navbar, .btn, .pagination, .dropdown {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #ddd !important;
|
||||
box-shadow: none !important;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: white !important;
|
||||
color: black !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
// Main JavaScript file for Edu-Space Admin
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
// Initialize popovers
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
||||
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl);
|
||||
});
|
||||
|
||||
// Auto-hide alerts after 5 seconds
|
||||
setTimeout(function() {
|
||||
$('.alert.fade').alert('close');
|
||||
}, 5000);
|
||||
|
||||
// Confirm delete actions
|
||||
$('.delete-confirm').on('click', function(e) {
|
||||
if (!confirm('Are you sure you want to delete this item? This action cannot be undone.')) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Form validation enhancements
|
||||
$('form').on('submit', function() {
|
||||
var form = $(this);
|
||||
var submitBtn = form.find('button[type="submit"]');
|
||||
|
||||
// Show loading state
|
||||
submitBtn.prop('disabled', true);
|
||||
var originalText = submitBtn.html();
|
||||
submitBtn.html('<i class="bi bi-hourglass-split me-2"></i>Loading...');
|
||||
|
||||
// Re-enable after 10 seconds in case of error
|
||||
setTimeout(function() {
|
||||
submitBtn.prop('disabled', false);
|
||||
submitBtn.html(originalText);
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
// Copy to clipboard functionality
|
||||
$('.copy-to-clipboard').on('click', function() {
|
||||
var text = $(this).data('text');
|
||||
if (!text) {
|
||||
text = $(this).text();
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
showToast('Copied to clipboard!', 'success');
|
||||
}).catch(function() {
|
||||
showToast('Failed to copy to clipboard', 'error');
|
||||
});
|
||||
});
|
||||
|
||||
// Quick search functionality
|
||||
$('#quickSearch').on('input', function() {
|
||||
var searchTerm = $(this).val().toLowerCase();
|
||||
|
||||
$('.searchable-item').each(function() {
|
||||
var item = $(this);
|
||||
var text = item.text().toLowerCase();
|
||||
|
||||
if (text.includes(searchTerm)) {
|
||||
item.show();
|
||||
} else {
|
||||
item.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize datepickers
|
||||
$('.datetimepicker').each(function() {
|
||||
$(this).flatpickr({
|
||||
enableTime: true,
|
||||
dateFormat: "Y-m-d H:i",
|
||||
time_24hr: true,
|
||||
minDate: "today"
|
||||
});
|
||||
});
|
||||
|
||||
$('.datepicker').each(function() {
|
||||
$(this).flatpickr({
|
||||
dateFormat: "Y-m-d",
|
||||
minDate: "today"
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-save functionality for multi-step forms
|
||||
$('.auto-save').on('change input', function() {
|
||||
var form = $(this).closest('form');
|
||||
var formData = form.serialize();
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem('autosave_' + form.attr('id'), formData);
|
||||
showToast('Draft saved automatically', 'info');
|
||||
});
|
||||
|
||||
// Restore auto-saved data
|
||||
$('form.auto-save-restore').each(function() {
|
||||
var form = $(this);
|
||||
var savedData = localStorage.getItem('autosave_' + form.attr('id'));
|
||||
|
||||
if (savedData) {
|
||||
// Show restore prompt
|
||||
if (confirm('Found unsaved changes. Would you like to restore them?')) {
|
||||
var params = new URLSearchParams(savedData);
|
||||
params.forEach(function(value, key) {
|
||||
form.find('[name="' + key + '"]').val(value);
|
||||
});
|
||||
showToast('Draft restored', 'info');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Print functionality
|
||||
$('.btn-print').on('click', function() {
|
||||
window.print();
|
||||
});
|
||||
|
||||
// Export functionality
|
||||
$('.btn-export').on('click', function() {
|
||||
var format = $(this).data('format');
|
||||
var url = $(this).data('url');
|
||||
|
||||
if (format === 'excel') {
|
||||
window.open(url + '?export=excel', '_blank');
|
||||
} else if (format === 'pdf') {
|
||||
window.open(url + '?export=pdf', '_blank');
|
||||
} else if (format === 'csv') {
|
||||
window.open(url + '?export=csv', '_blank');
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
$(document).on('keydown', function(e) {
|
||||
// Ctrl/Cmd + K for search
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
$('#quickSearch').focus();
|
||||
}
|
||||
|
||||
// Escape to close modals
|
||||
if (e.key === 'Escape') {
|
||||
$('.modal.show').modal('hide');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize tooltips dynamically for AI-generated content
|
||||
initializeDynamicTooltips();
|
||||
});
|
||||
|
||||
// Utility functions
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
var toastHtml = `
|
||||
<div class="toast align-items-center text-white bg-${type === 'error' ? 'danger' : type} border-0" role="alert">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
${message}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var toastContainer = $('.toast-container');
|
||||
var toastElement = $(toastHtml);
|
||||
toastContainer.append(toastElement);
|
||||
|
||||
var toast = new bootstrap.Toast(toastElement[0]);
|
||||
toast.show();
|
||||
|
||||
// Remove toast element after it's hidden
|
||||
toastElement.on('hidden.bs.toast', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
function confirmAction(message, callback) {
|
||||
if (confirm(message)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function formatDateTime(dateTimeString) {
|
||||
var date = new Date(dateTimeString);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function formatDate(dateString) {
|
||||
var date = new Date(dateString);
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
function formatTime(timeString) {
|
||||
var date = new Date(timeString);
|
||||
return date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||
}
|
||||
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
function initializeDynamicTooltips() {
|
||||
// Initialize tooltips for dynamically generated content
|
||||
$('[data-bs-toggle="tooltip"]:not([aria-describedby])').each(function() {
|
||||
new bootstrap.Tooltip(this);
|
||||
});
|
||||
}
|
||||
|
||||
// API Helper functions
|
||||
function apiCall(url, method = 'GET', data = null) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
method: method,
|
||||
data: data,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken || ''
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleApiError(xhr) {
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
showToast(xhr.responseJSON.message, 'error');
|
||||
} else {
|
||||
showToast('An error occurred. Please try again.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Form validation helpers
|
||||
function validateForm(form) {
|
||||
var isValid = true;
|
||||
var errors = [];
|
||||
|
||||
form.find('[required]').each(function() {
|
||||
var field = $(this);
|
||||
if (!field.val()) {
|
||||
isValid = false;
|
||||
errors.push(field.attr('data-label') || field.attr('name'));
|
||||
field.addClass('is-invalid');
|
||||
} else {
|
||||
field.removeClass('is-invalid');
|
||||
}
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
showToast('Please fill in the required fields: ' + errors.join(', '), 'error');
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// Image preview functionality
|
||||
function previewImage(input, previewElement) {
|
||||
if (input.files && input.files[0]) {
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
previewElement.attr('src', e.target.result);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Export to CSV function
|
||||
function exportToCSV(data, filename) {
|
||||
var csv = '';
|
||||
var headers = Object.keys(data[0]);
|
||||
|
||||
// Add headers
|
||||
csv += headers.join(',') + '\n';
|
||||
|
||||
// Add data
|
||||
data.forEach(function(row) {
|
||||
headers.forEach(function(header, index) {
|
||||
if (index > 0) csv += ',';
|
||||
csv += '"' + (row[header] || '') + '"';
|
||||
});
|
||||
csv += '\n';
|
||||
});
|
||||
|
||||
// Create download link
|
||||
var hiddenElement = document.createElement('a');
|
||||
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
|
||||
hiddenElement.target = '_blank';
|
||||
hiddenElement.download = filename + '.csv';
|
||||
hiddenElement.click();
|
||||
}
|
||||
Reference in New Issue
Block a user