| @ -0,0 +1,57 @@ | |||
| import DataTable from 'datatables.net-bs5'; | |||
| import 'datatables.net-buttons-bs5'; | |||
| import 'datatables.net-buttons/js/buttons.html5'; | |||
| import 'datatables.net-buttons/js/buttons.print'; | |||
| import 'datatables.net-responsive-bs5'; | |||
| // Data Tables functionality | |||
| export function initializeDataTables() { | |||
| // Basic DataTable | |||
| const basicTable = document.getElementById('basic-datatable'); | |||
| if (basicTable) { | |||
| new DataTable(basicTable, { | |||
| responsive: true, | |||
| dom: 'Bfrtip', | |||
| buttons: [ | |||
| 'copy', 'csv', 'excel', 'pdf', 'print' | |||
| ] | |||
| }); | |||
| } | |||
| // Advanced DataTable with custom controls | |||
| const advancedTable = document.getElementById('advanced-datatable'); | |||
| if (advancedTable) { | |||
| new DataTable(advancedTable, { | |||
| responsive: true, | |||
| dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6"f>>rtip', | |||
| pageLength: 10, | |||
| lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, "All"]], | |||
| columnDefs: [ | |||
| { orderable: false, targets: -1 } // Disable sorting on last column (actions) | |||
| ], | |||
| order: [[0, 'asc']], | |||
| language: { | |||
| search: '_INPUT_', | |||
| searchPlaceholder: 'Search records...', | |||
| lengthMenu: 'Show _MENU_ records per page', | |||
| paginate: { | |||
| previous: '<i class="fas fa-chevron-left"></i>', | |||
| next: '<i class="fas fa-chevron-right"></i>' | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| // Initialize tooltips | |||
| const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); | |||
| tooltipTriggerList.map(function (tooltipTriggerEl) { | |||
| return new bootstrap.Tooltip(tooltipTriggerEl); | |||
| }); | |||
| } | |||
| // Initialize on DOM ready | |||
| if (document.readyState === 'loading') { | |||
| document.addEventListener('DOMContentLoaded', initializeDataTables); | |||
| } else { | |||
| initializeDataTables(); | |||
| } | |||
| @ -0,0 +1,246 @@ | |||
| import Chart from 'chart.js/auto'; | |||
| // Influencer Dashboard functionality | |||
| export function initializeInfluencerDashboard() { | |||
| // Initialize Engagement Chart | |||
| const engagementCtx = document.getElementById('engagementChart'); | |||
| if (engagementCtx) { | |||
| new Chart(engagementCtx, { | |||
| type: 'line', | |||
| data: { | |||
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | |||
| datasets: [ | |||
| { | |||
| label: 'Instagram', | |||
| data: [3.2, 3.5, 3.8, 4.2, 4.5, 4.8, 5.1, 5.3, 5.2, 5.5, 5.8, 6.1], | |||
| borderColor: '#E1306C', | |||
| backgroundColor: 'rgba(225, 48, 108, 0.1)', | |||
| tension: 0.4 | |||
| }, | |||
| { | |||
| label: 'TikTok', | |||
| data: [2.8, 3.1, 3.6, 4.0, 4.3, 4.7, 5.0, 5.4, 5.7, 6.0, 6.3, 6.8], | |||
| borderColor: '#000000', | |||
| backgroundColor: 'rgba(0, 0, 0, 0.1)', | |||
| tension: 0.4 | |||
| }, | |||
| { | |||
| label: 'YouTube', | |||
| data: [2.5, 2.6, 2.8, 3.0, 3.2, 3.1, 3.3, 3.5, 3.4, 3.6, 3.8, 3.9], | |||
| borderColor: '#FF0000', | |||
| backgroundColor: 'rgba(255, 0, 0, 0.1)', | |||
| tension: 0.4 | |||
| }, | |||
| { | |||
| label: 'Twitter', | |||
| data: [1.8, 2.0, 2.1, 2.3, 2.5, 2.7, 2.6, 2.8, 2.9, 3.0, 3.1, 3.2], | |||
| borderColor: '#1DA1F2', | |||
| backgroundColor: 'rgba(29, 161, 242, 0.1)', | |||
| tension: 0.4 | |||
| } | |||
| ] | |||
| }, | |||
| options: { | |||
| responsive: true, | |||
| maintainAspectRatio: false, | |||
| plugins: { | |||
| legend: { | |||
| display: true, | |||
| position: 'bottom' | |||
| }, | |||
| tooltip: { | |||
| mode: 'index', | |||
| intersect: false, | |||
| callbacks: { | |||
| label: function(context) { | |||
| return context.dataset.label + ': ' + context.parsed.y.toFixed(1) + '%'; | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| scales: { | |||
| y: { | |||
| beginAtZero: true, | |||
| max: 8, | |||
| ticks: { | |||
| callback: function(value) { | |||
| return value + '%'; | |||
| } | |||
| }, | |||
| title: { | |||
| display: true, | |||
| text: 'Engagement Rate' | |||
| } | |||
| }, | |||
| x: { | |||
| grid: { | |||
| display: false | |||
| } | |||
| } | |||
| }, | |||
| interaction: { | |||
| mode: 'nearest', | |||
| axis: 'x', | |||
| intersect: false | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| // Initialize Demographics Chart | |||
| const demographicsCtx = document.getElementById('demographicsChart'); | |||
| if (demographicsCtx) { | |||
| new Chart(demographicsCtx, { | |||
| type: 'doughnut', | |||
| data: { | |||
| labels: ['18-24', '25-34', '35-44', '45-54', '55+'], | |||
| datasets: [{ | |||
| data: [35, 42, 15, 6, 2], | |||
| backgroundColor: [ | |||
| '#667eea', | |||
| '#48bb78', | |||
| '#f39c12', | |||
| '#e74c3c', | |||
| '#9b59b6' | |||
| ], | |||
| borderWidth: 0 | |||
| }] | |||
| }, | |||
| options: { | |||
| responsive: true, | |||
| maintainAspectRatio: false, | |||
| plugins: { | |||
| legend: { | |||
| position: 'right', | |||
| labels: { | |||
| padding: 20, | |||
| usePointStyle: true, | |||
| font: { | |||
| size: 12 | |||
| }, | |||
| generateLabels: function(chart) { | |||
| const data = chart.data; | |||
| if (data.labels.length && data.datasets.length) { | |||
| return data.labels.map((label, i) => { | |||
| const dataset = data.datasets[0]; | |||
| const value = dataset.data[i]; | |||
| return { | |||
| text: label + ' (' + value + '%)', | |||
| fillStyle: dataset.backgroundColor[i], | |||
| hidden: false, | |||
| index: i | |||
| }; | |||
| }); | |||
| } | |||
| return []; | |||
| } | |||
| } | |||
| }, | |||
| tooltip: { | |||
| callbacks: { | |||
| label: function(context) { | |||
| return context.label + ': ' + context.parsed + '%'; | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| cutout: '70%' | |||
| } | |||
| }); | |||
| } | |||
| // Animate progress bars on scroll | |||
| const progressBars = document.querySelectorAll('.progress-bar'); | |||
| const observerOptions = { | |||
| threshold: 0.1, | |||
| rootMargin: '0px 0px -100px 0px' | |||
| }; | |||
| const progressObserver = new IntersectionObserver((entries) => { | |||
| entries.forEach(entry => { | |||
| if (entry.isIntersecting) { | |||
| const progressBar = entry.target; | |||
| const width = progressBar.style.width; | |||
| progressBar.style.width = '0%'; | |||
| setTimeout(() => { | |||
| progressBar.style.width = width; | |||
| progressBar.style.transition = 'width 1s ease-out'; | |||
| }, 100); | |||
| progressObserver.unobserve(progressBar); | |||
| } | |||
| }); | |||
| }, observerOptions); | |||
| progressBars.forEach(bar => { | |||
| progressObserver.observe(bar); | |||
| }); | |||
| // Campaign actions | |||
| document.addEventListener('click', function(e) { | |||
| if (e.target.closest('.dropdown-item')) { | |||
| const action = e.target.textContent.trim(); | |||
| const row = e.target.closest('tr'); | |||
| const campaignName = row.querySelector('h6').textContent; | |||
| switch(action) { | |||
| case 'View Details': | |||
| console.log('View details for:', campaignName); | |||
| break; | |||
| case 'Analytics': | |||
| console.log('Show analytics for:', campaignName); | |||
| break; | |||
| case 'Pause': | |||
| if (confirm(`Are you sure you want to pause the "${campaignName}" campaign?`)) { | |||
| const statusBadge = row.querySelector('.badge'); | |||
| statusBadge.classList.remove('bg-success'); | |||
| statusBadge.classList.add('bg-secondary'); | |||
| statusBadge.textContent = 'Paused'; | |||
| showNotification(`Campaign "${campaignName}" has been paused.`, 'info'); | |||
| } | |||
| break; | |||
| case 'Start': | |||
| const statusBadge = row.querySelector('.badge'); | |||
| statusBadge.classList.remove('bg-warning', 'text-dark'); | |||
| statusBadge.classList.add('bg-success'); | |||
| statusBadge.textContent = 'Active'; | |||
| showNotification(`Campaign "${campaignName}" has been started.`, 'success'); | |||
| break; | |||
| } | |||
| } | |||
| }); | |||
| // Add new campaign button | |||
| const newCampaignBtn = document.querySelector('.btn-primary.btn-sm'); | |||
| if (newCampaignBtn) { | |||
| newCampaignBtn.addEventListener('click', function() { | |||
| console.log('Open new campaign modal'); | |||
| showNotification('New campaign feature coming soon!', 'info'); | |||
| }); | |||
| } | |||
| } | |||
| // Notification helper | |||
| function showNotification(message, type = 'info') { | |||
| const alertHtml = ` | |||
| <div class="alert alert-${type} alert-dismissible fade show position-fixed top-0 end-0 m-3" role="alert" style="z-index: 1050;"> | |||
| ${message} | |||
| <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | |||
| </div> | |||
| `; | |||
| document.body.insertAdjacentHTML('beforeend', alertHtml); | |||
| setTimeout(() => { | |||
| const alert = document.querySelector('.alert'); | |||
| if (alert) { | |||
| alert.remove(); | |||
| } | |||
| }, 5000); | |||
| } | |||
| // Initialize on DOM ready | |||
| if (document.readyState === 'loading') { | |||
| document.addEventListener('DOMContentLoaded', initializeInfluencerDashboard); | |||
| } else { | |||
| initializeInfluencerDashboard(); | |||
| } | |||
| @ -0,0 +1,260 @@ | |||
| // Settings page functionality | |||
| export function initializeSettings() { | |||
| // Color scheme buttons | |||
| const colorSchemeButtons = document.querySelectorAll('.color-scheme-btn'); | |||
| colorSchemeButtons.forEach(button => { | |||
| button.addEventListener('click', function() { | |||
| colorSchemeButtons.forEach(btn => btn.classList.remove('active')); | |||
| this.classList.add('active'); | |||
| const color = this.dataset.color; | |||
| // Here you would apply the color scheme | |||
| console.log('Color scheme changed to:', color); | |||
| }); | |||
| }); | |||
| // Font size range | |||
| const fontSizeRange = document.getElementById('fontSize'); | |||
| if (fontSizeRange) { | |||
| fontSizeRange.addEventListener('input', function() { | |||
| document.documentElement.style.fontSize = this.value + 'px'; | |||
| }); | |||
| } | |||
| // Enable desktop notifications | |||
| const desktopNotifications = document.getElementById('desktopNotifications'); | |||
| if (desktopNotifications) { | |||
| desktopNotifications.addEventListener('change', function() { | |||
| if (this.checked) { | |||
| if ('Notification' in window) { | |||
| Notification.requestPermission().then(permission => { | |||
| if (permission === 'granted') { | |||
| new Notification('Desktop notifications enabled!', { | |||
| body: 'You will now receive desktop notifications.', | |||
| icon: '/assets/images/logo.png' | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| // Send test email button | |||
| const sendTestEmailBtn = document.querySelector('.btn-outline-primary'); | |||
| if (sendTestEmailBtn && sendTestEmailBtn.textContent.includes('Send Test Email')) { | |||
| sendTestEmailBtn.addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalText = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>Sending...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Test Email Sent!'; | |||
| btn.classList.remove('btn-outline-primary'); | |||
| btn.classList.add('btn-success'); | |||
| setTimeout(() => { | |||
| btn.innerHTML = originalText; | |||
| btn.classList.remove('btn-success'); | |||
| btn.classList.add('btn-outline-primary'); | |||
| btn.disabled = false; | |||
| }, 3000); | |||
| }, 2000); | |||
| }); | |||
| } | |||
| // Copy API key | |||
| const copyApiKeyBtn = document.querySelector('.input-group .btn-outline-secondary'); | |||
| if (copyApiKeyBtn) { | |||
| copyApiKeyBtn.addEventListener('click', function() { | |||
| const apiKeyInput = document.getElementById('apiKey'); | |||
| apiKeyInput.select(); | |||
| document.execCommand('copy'); | |||
| // Show tooltip | |||
| const tooltip = new bootstrap.Tooltip(this, { | |||
| title: 'Copied!', | |||
| trigger: 'manual' | |||
| }); | |||
| tooltip.show(); | |||
| setTimeout(() => { | |||
| tooltip.hide(); | |||
| }, 2000); | |||
| }); | |||
| } | |||
| // Generate new API key | |||
| const generateKeyBtn = document.querySelector('.btn-primary'); | |||
| if (generateKeyBtn && generateKeyBtn.textContent.includes('Generate New Key')) { | |||
| generateKeyBtn.addEventListener('click', function() { | |||
| if (confirm('Are you sure you want to generate a new API key? The old key will be invalidated.')) { | |||
| const newKey = 'sk_test_' + Math.random().toString(36).substr(2, 20); | |||
| document.getElementById('apiKey').value = newKey; | |||
| } | |||
| }); | |||
| } | |||
| // Clear cache buttons | |||
| document.querySelectorAll('.btn-outline-secondary').forEach(button => { | |||
| if (button.textContent.includes('Clear') && button.textContent.includes('Cache')) { | |||
| button.addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalText = btn.textContent; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>Clearing...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Cleared!'; | |||
| btn.classList.remove('btn-outline-secondary'); | |||
| btn.classList.add('btn-success'); | |||
| setTimeout(() => { | |||
| btn.textContent = originalText; | |||
| btn.classList.remove('btn-success'); | |||
| btn.classList.add('btn-outline-secondary'); | |||
| btn.disabled = false; | |||
| }, 2000); | |||
| }, 1500); | |||
| }); | |||
| } | |||
| }); | |||
| // Backup now button | |||
| const backupBtn = document.querySelector('.btn-primary'); | |||
| if (backupBtn && backupBtn.textContent === 'Backup Now') { | |||
| backupBtn.addEventListener('click', function() { | |||
| const btn = this; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>Creating backup...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Backup completed!'; | |||
| btn.classList.remove('btn-primary'); | |||
| btn.classList.add('btn-success'); | |||
| // Show download link | |||
| const downloadLink = document.createElement('a'); | |||
| downloadLink.href = '#'; | |||
| downloadLink.className = 'btn btn-sm btn-outline-primary ms-2'; | |||
| downloadLink.innerHTML = '<i class="fas fa-download me-1"></i>Download'; | |||
| btn.parentNode.insertBefore(downloadLink, btn.nextSibling); | |||
| setTimeout(() => { | |||
| btn.textContent = 'Backup Now'; | |||
| btn.classList.remove('btn-success'); | |||
| btn.classList.add('btn-primary'); | |||
| btn.disabled = false; | |||
| downloadLink.remove(); | |||
| }, 5000); | |||
| }, 3000); | |||
| }); | |||
| } | |||
| // Save buttons functionality | |||
| document.querySelectorAll('button[type="submit"]').forEach(button => { | |||
| button.addEventListener('click', function(e) { | |||
| e.preventDefault(); | |||
| const btn = this; | |||
| const originalText = btn.textContent; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>Saving...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Saved!'; | |||
| btn.classList.add('btn-success'); | |||
| setTimeout(() => { | |||
| btn.textContent = originalText; | |||
| btn.classList.remove('btn-success'); | |||
| btn.disabled = false; | |||
| }, 2000); | |||
| }, 1500); | |||
| }); | |||
| }); | |||
| // Two-factor authentication toggle | |||
| const enable2FA = document.getElementById('enable2FA'); | |||
| if (enable2FA) { | |||
| enable2FA.addEventListener('change', function() { | |||
| if (this.checked) { | |||
| // Show QR code modal | |||
| const modal = new bootstrap.Modal(document.getElementById('twoFactorModal') || createTwoFactorModal()); | |||
| modal.show(); | |||
| } | |||
| }); | |||
| } | |||
| // Integration connect buttons | |||
| document.querySelectorAll('.list-group-item .btn-outline-primary').forEach(button => { | |||
| if (button.textContent === 'Connect') { | |||
| button.addEventListener('click', function() { | |||
| const btn = this; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span>'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.textContent = 'Connected'; | |||
| btn.classList.remove('btn-outline-primary'); | |||
| btn.classList.add('btn-success'); | |||
| btn.disabled = true; | |||
| }, 2000); | |||
| }); | |||
| } | |||
| }); | |||
| // Handle form validation | |||
| const forms = document.querySelectorAll('form'); | |||
| forms.forEach(form => { | |||
| form.addEventListener('submit', function(event) { | |||
| event.preventDefault(); | |||
| event.stopPropagation(); | |||
| if (form.checkValidity()) { | |||
| // Form is valid, would submit here | |||
| console.log('Form submitted'); | |||
| } | |||
| form.classList.add('was-validated'); | |||
| }); | |||
| }); | |||
| } | |||
| // Create Two-Factor Authentication Modal | |||
| function createTwoFactorModal() { | |||
| const modalHtml = ` | |||
| <div class="modal fade" id="twoFactorModal" tabindex="-1" aria-labelledby="twoFactorModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title" id="twoFactorModalLabel">Enable Two-Factor Authentication</h5> | |||
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |||
| </div> | |||
| <div class="modal-body text-center"> | |||
| <p>Scan this QR code with your authenticator app:</p> | |||
| <div class="bg-light p-4 d-inline-block mb-3"> | |||
| <i class="fas fa-qrcode fa-8x text-dark"></i> | |||
| </div> | |||
| <p>Or enter this code manually:</p> | |||
| <code class="fs-5">JBSWY3DPEHPK3PXP</code> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |||
| <button type="button" class="btn btn-primary">Verify & Enable</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| `; | |||
| document.body.insertAdjacentHTML('beforeend', modalHtml); | |||
| return document.getElementById('twoFactorModal'); | |||
| } | |||
| // Initialize on DOM ready | |||
| if (document.readyState === 'loading') { | |||
| document.addEventListener('DOMContentLoaded', initializeSettings); | |||
| } else { | |||
| initializeSettings(); | |||
| } | |||
| @ -0,0 +1,366 @@ | |||
| // Timeline functionality | |||
| export function initializeTimeline() { | |||
| let visibleActivities = new Set(['user', 'system', 'security', 'transaction', 'milestone']); | |||
| // Filter checkboxes | |||
| const filterCheckboxes = document.querySelectorAll('.activity-filters input[type="checkbox"]'); | |||
| filterCheckboxes.forEach(checkbox => { | |||
| checkbox.addEventListener('change', updateVisibility); | |||
| }); | |||
| function updateVisibility() { | |||
| visibleActivities.clear(); | |||
| filterCheckboxes.forEach(checkbox => { | |||
| if (checkbox.checked) { | |||
| visibleActivities.add(checkbox.value); | |||
| } | |||
| }); | |||
| // Update timeline items visibility | |||
| document.querySelectorAll('.timeline-item').forEach(item => { | |||
| const type = item.dataset.type; | |||
| if (visibleActivities.has(type)) { | |||
| item.style.display = 'flex'; | |||
| } else { | |||
| item.style.display = 'none'; | |||
| } | |||
| }); | |||
| } | |||
| // Apply filters button | |||
| document.getElementById('applyFilters').addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalText = btn.textContent; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Applying...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| updateVisibility(); | |||
| showNotification('Filters applied successfully!', 'success'); | |||
| btn.textContent = originalText; | |||
| btn.disabled = false; | |||
| }, 500); | |||
| }); | |||
| // Reset filters | |||
| document.getElementById('resetFilters').addEventListener('click', function() { | |||
| filterCheckboxes.forEach(checkbox => { | |||
| checkbox.checked = true; | |||
| }); | |||
| document.getElementById('dateFilter').value = 'week'; | |||
| document.getElementById('userSearch').value = ''; | |||
| updateVisibility(); | |||
| }); | |||
| // Refresh timeline | |||
| document.getElementById('refreshTimeline').addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalHtml = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Refreshing...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| // Add new activity | |||
| addNewActivity(); | |||
| showNotification('Timeline refreshed!', 'info'); | |||
| btn.innerHTML = originalHtml; | |||
| btn.disabled = false; | |||
| }, 1000); | |||
| }); | |||
| // Export timeline | |||
| document.getElementById('exportTimeline').addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalHtml = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Exporting...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| // Generate and download timeline data | |||
| exportTimelineData(); | |||
| btn.innerHTML = '<i class="fas fa-check me-1"></i>Exported!'; | |||
| setTimeout(() => { | |||
| btn.innerHTML = originalHtml; | |||
| btn.disabled = false; | |||
| }, 2000); | |||
| }, 1500); | |||
| }); | |||
| // Load more activities | |||
| document.getElementById('loadMore').addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalHtml = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| loadMoreActivities(); | |||
| btn.innerHTML = originalHtml; | |||
| btn.disabled = false; | |||
| }, 1000); | |||
| }); | |||
| // Like and Reply buttons | |||
| document.addEventListener('click', function(e) { | |||
| if (e.target.closest('.btn-outline-primary') && e.target.textContent.includes('Like')) { | |||
| const btn = e.target; | |||
| btn.classList.toggle('active'); | |||
| const icon = btn.querySelector('i'); | |||
| if (btn.classList.contains('active')) { | |||
| icon.classList.remove('far'); | |||
| icon.classList.add('fas'); | |||
| btn.classList.remove('btn-outline-primary'); | |||
| btn.classList.add('btn-primary'); | |||
| } else { | |||
| icon.classList.remove('fas'); | |||
| icon.classList.add('far'); | |||
| btn.classList.remove('btn-primary'); | |||
| btn.classList.add('btn-outline-primary'); | |||
| } | |||
| } | |||
| if (e.target.closest('.btn-outline-secondary') && e.target.textContent.includes('Reply')) { | |||
| // Show reply input | |||
| const timelineBody = e.target.closest('.timeline-body'); | |||
| if (!timelineBody.querySelector('.reply-input')) { | |||
| const replyHtml = ` | |||
| <div class="reply-input mt-3"> | |||
| <div class="input-group"> | |||
| <input type="text" class="form-control" placeholder="Write a reply..."> | |||
| <button class="btn btn-primary" type="button">Send</button> | |||
| </div> | |||
| </div> | |||
| `; | |||
| timelineBody.insertAdjacentHTML('beforeend', replyHtml); | |||
| } | |||
| } | |||
| }); | |||
| // Send reply | |||
| document.addEventListener('click', function(e) { | |||
| if (e.target.textContent === 'Send' && e.target.closest('.reply-input')) { | |||
| const input = e.target.previousElementSibling; | |||
| const replyText = input.value.trim(); | |||
| if (replyText) { | |||
| const replyHtml = ` | |||
| <div class="card bg-light mt-2"> | |||
| <div class="card-body py-2"> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=Current+User&background=667eea&color=fff" alt="Avatar" class="rounded-circle me-2" width="24" height="24"> | |||
| <div class="flex-grow-1"> | |||
| <small class="fw-bold">You</small> | |||
| <p class="mb-0 small">${replyText}</p> | |||
| </div> | |||
| <small class="text-muted">Just now</small> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| `; | |||
| const replyInput = e.target.closest('.reply-input'); | |||
| replyInput.insertAdjacentHTML('beforebegin', replyHtml); | |||
| replyInput.remove(); | |||
| showNotification('Reply posted!', 'success'); | |||
| } | |||
| } | |||
| }); | |||
| // Block IP button | |||
| document.addEventListener('click', function(e) { | |||
| if (e.target.textContent === 'Block IP') { | |||
| const btn = e.target; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Blocking...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Blocked'; | |||
| btn.classList.remove('btn-danger'); | |||
| btn.classList.add('btn-success'); | |||
| showNotification('IP address has been blocked!', 'success'); | |||
| }, 1000); | |||
| } | |||
| }); | |||
| function addNewActivity() { | |||
| const newActivity = ` | |||
| <div class="timeline-item new-item" data-type="user"> | |||
| <div class="timeline-marker bg-primary"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <img src="https://ui-avatars.com/api/?name=New+Activity&background=667eea&color=fff" alt="Avatar" class="rounded-circle me-2" width="32" height="32"> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">System <small class="text-muted">new user registered</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>Just now</small> | |||
| </div> | |||
| <span class="badge bg-info">New</span> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <p class="mb-0">A new user has joined the platform.</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| `; | |||
| const todaySection = document.querySelector('.timeline-date'); | |||
| todaySection.insertAdjacentHTML('afterend', newActivity); | |||
| // Animate new item | |||
| setTimeout(() => { | |||
| document.querySelector('.new-item').classList.add('animate-in'); | |||
| }, 100); | |||
| } | |||
| function loadMoreActivities() { | |||
| const activities = [ | |||
| { | |||
| type: 'user', | |||
| title: 'Sarah Wilson', | |||
| action: 'uploaded documents', | |||
| time: '3 days ago', | |||
| body: 'Uploaded 5 new documents to the shared folder.', | |||
| avatar: 'Sarah+Wilson', | |||
| bgColor: '9b59b6' | |||
| }, | |||
| { | |||
| type: 'system', | |||
| title: 'System Update', | |||
| action: 'installed successfully', | |||
| time: '4 days ago', | |||
| body: 'Version 2.1.0 has been installed with new features and bug fixes.', | |||
| icon: 'fas fa-download text-info' | |||
| }, | |||
| { | |||
| type: 'transaction', | |||
| title: 'Payment Received', | |||
| action: 'from client', | |||
| time: '5 days ago', | |||
| body: 'Invoice #INV-2024-0156 has been paid.', | |||
| amount: '+$1,250.00' | |||
| } | |||
| ]; | |||
| const loadMoreBtn = document.getElementById('loadMore'); | |||
| const timeline = document.getElementById('timeline'); | |||
| activities.forEach(activity => { | |||
| const activityHtml = createActivityHtml(activity); | |||
| loadMoreBtn.insertAdjacentHTML('beforebegin', activityHtml); | |||
| }); | |||
| // Add date separator | |||
| const dateSeparator = ` | |||
| <div class="timeline-date"> | |||
| <h6 class="text-muted">Last Week</h6> | |||
| </div> | |||
| `; | |||
| loadMoreBtn.insertAdjacentHTML('beforebegin', dateSeparator); | |||
| } | |||
| function createActivityHtml(activity) { | |||
| let headerContent = ''; | |||
| if (activity.avatar) { | |||
| headerContent = `<img src="https://ui-avatars.com/api/?name=${activity.avatar}&background=${activity.bgColor || '667eea'}&color=fff" alt="Avatar" class="rounded-circle me-2" width="32" height="32">`; | |||
| } else if (activity.icon) { | |||
| headerContent = `<i class="${activity.icon} me-2"></i>`; | |||
| } | |||
| const markerColor = { | |||
| user: 'bg-primary', | |||
| system: 'bg-warning', | |||
| security: 'bg-danger', | |||
| transaction: 'bg-success', | |||
| milestone: 'bg-info' | |||
| }[activity.type] || 'bg-secondary'; | |||
| return ` | |||
| <div class="timeline-item" data-type="${activity.type}"> | |||
| <div class="timeline-marker ${markerColor}"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| ${headerContent} | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">${activity.title} <small class="text-muted">${activity.action}</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>${activity.time}</small> | |||
| </div> | |||
| ${activity.amount ? `<span class="fw-bold text-success">${activity.amount}</span>` : ''} | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <p class="mb-0">${activity.body}</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| `; | |||
| } | |||
| function exportTimelineData() { | |||
| const visibleItems = document.querySelectorAll('.timeline-item:not([style*="display: none"])'); | |||
| const data = []; | |||
| visibleItems.forEach(item => { | |||
| const header = item.querySelector('.timeline-header'); | |||
| const body = item.querySelector('.timeline-body'); | |||
| const time = header.querySelector('.text-muted').textContent; | |||
| const title = header.querySelector('h6').textContent; | |||
| const content = body.querySelector('p')?.textContent || ''; | |||
| data.push({ | |||
| time: time, | |||
| title: title, | |||
| content: content, | |||
| type: item.dataset.type | |||
| }); | |||
| }); | |||
| // Convert to CSV | |||
| const csv = 'Time,Title,Content,Type\n' + | |||
| data.map(row => `"${row.time}","${row.title}","${row.content}","${row.type}"`).join('\n'); | |||
| // Download | |||
| const blob = new Blob([csv], { type: 'text/csv' }); | |||
| const url = window.URL.createObjectURL(blob); | |||
| const a = document.createElement('a'); | |||
| a.href = url; | |||
| a.download = 'timeline_export.csv'; | |||
| a.click(); | |||
| window.URL.revokeObjectURL(url); | |||
| } | |||
| function showNotification(message, type = 'info') { | |||
| const alertHtml = ` | |||
| <div class="alert alert-${type} alert-dismissible fade show position-fixed top-0 end-0 m-3" role="alert" style="z-index: 1050;"> | |||
| ${message} | |||
| <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | |||
| </div> | |||
| `; | |||
| document.body.insertAdjacentHTML('beforeend', alertHtml); | |||
| setTimeout(() => { | |||
| const alert = document.querySelector('.alert'); | |||
| if (alert) { | |||
| alert.remove(); | |||
| } | |||
| }, 3000); | |||
| } | |||
| } | |||
| // Initialize on DOM ready | |||
| if (document.readyState === 'loading') { | |||
| document.addEventListener('DOMContentLoaded', initializeTimeline); | |||
| } else { | |||
| initializeTimeline(); | |||
| } | |||
| @ -0,0 +1,374 @@ | |||
| import DataTable from 'datatables.net-bs5'; | |||
| import 'datatables.net-responsive'; | |||
| import 'datatables.net-responsive-bs5'; | |||
| import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css'; | |||
| // User Management functionality | |||
| export function initializeUserManagement() { | |||
| let selectedUsers = new Set(); | |||
| // Initialize DataTable | |||
| const table = new DataTable('#userTable', { | |||
| responsive: true, | |||
| pageLength: 10, | |||
| order: [[5, 'desc']], // Sort by joined date | |||
| columnDefs: [ | |||
| { orderable: false, targets: [0, 7] }, // Disable sorting for checkbox and actions | |||
| { searchable: false, targets: [0, 7] } | |||
| ], | |||
| language: { | |||
| search: '_INPUT_', | |||
| searchPlaceholder: 'Search users...', | |||
| lengthMenu: '_MENU_ users per page', | |||
| info: 'Showing _START_ to _END_ of _TOTAL_ users', | |||
| paginate: { | |||
| previous: '<i class="fas fa-chevron-left"></i>', | |||
| next: '<i class="fas fa-chevron-right"></i>' | |||
| } | |||
| }, | |||
| drawCallback: function() { | |||
| // Re-initialize tooltips after table redraw | |||
| const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); | |||
| tooltipTriggerList.map(function (tooltipTriggerEl) { | |||
| return new bootstrap.Tooltip(tooltipTriggerEl); | |||
| }); | |||
| } | |||
| }); | |||
| // Hide default search box as we're using custom search | |||
| document.querySelector('.dataTables_filter').style.display = 'none'; | |||
| // Custom search functionality | |||
| const searchInput = document.getElementById('searchInput'); | |||
| searchInput.addEventListener('keyup', function() { | |||
| table.search(this.value).draw(); | |||
| }); | |||
| // Filter functionality | |||
| const roleFilter = document.getElementById('roleFilter'); | |||
| const statusFilter = document.getElementById('statusFilter'); | |||
| roleFilter.addEventListener('change', function() { | |||
| filterTable(); | |||
| }); | |||
| statusFilter.addEventListener('change', function() { | |||
| filterTable(); | |||
| }); | |||
| function filterTable() { | |||
| const role = roleFilter.value; | |||
| const status = statusFilter.value; | |||
| // Custom filtering logic | |||
| $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) { | |||
| const roleCell = data[3]; // Role column | |||
| const statusCell = data[4]; // Status column | |||
| let roleMatch = true; | |||
| let statusMatch = true; | |||
| if (role && !roleCell.toLowerCase().includes(role.toLowerCase())) { | |||
| roleMatch = false; | |||
| } | |||
| if (status && !statusCell.toLowerCase().includes(status.toLowerCase())) { | |||
| statusMatch = false; | |||
| } | |||
| return roleMatch && statusMatch; | |||
| }); | |||
| table.draw(); | |||
| // Clear custom filter | |||
| $.fn.dataTable.ext.search.pop(); | |||
| } | |||
| // Reset filters | |||
| document.getElementById('resetFilters').addEventListener('click', function() { | |||
| searchInput.value = ''; | |||
| roleFilter.value = ''; | |||
| statusFilter.value = ''; | |||
| table.search('').draw(); | |||
| }); | |||
| // Select all checkbox | |||
| const selectAllCheckbox = document.getElementById('selectAll'); | |||
| selectAllCheckbox.addEventListener('change', function() { | |||
| const checkboxes = document.querySelectorAll('#userTable tbody input[type="checkbox"]'); | |||
| checkboxes.forEach(checkbox => { | |||
| checkbox.checked = this.checked; | |||
| const row = checkbox.closest('tr'); | |||
| if (this.checked) { | |||
| selectedUsers.add(row); | |||
| row.classList.add('table-active'); | |||
| } else { | |||
| selectedUsers.delete(row); | |||
| row.classList.remove('table-active'); | |||
| } | |||
| }); | |||
| updateBulkActions(); | |||
| }); | |||
| // Individual checkbox handling | |||
| document.addEventListener('change', function(e) { | |||
| if (e.target.type === 'checkbox' && e.target.closest('#userTable tbody')) { | |||
| const row = e.target.closest('tr'); | |||
| if (e.target.checked) { | |||
| selectedUsers.add(row); | |||
| row.classList.add('table-active'); | |||
| } else { | |||
| selectedUsers.delete(row); | |||
| row.classList.remove('table-active'); | |||
| selectAllCheckbox.checked = false; | |||
| } | |||
| updateBulkActions(); | |||
| } | |||
| }); | |||
| // Update bulk actions visibility | |||
| function updateBulkActions() { | |||
| const bulkActions = document.getElementById('bulkActions'); | |||
| const selectedCount = document.getElementById('selectedCount'); | |||
| if (selectedUsers.size > 0) { | |||
| bulkActions.style.display = 'block'; | |||
| selectedCount.textContent = selectedUsers.size; | |||
| } else { | |||
| bulkActions.style.display = 'none'; | |||
| } | |||
| } | |||
| // Export functionality | |||
| document.getElementById('exportBtn').addEventListener('click', function() { | |||
| const btn = this; | |||
| const originalText = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Exporting...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| // Simulate export | |||
| const csvContent = generateCSV(); | |||
| downloadCSV(csvContent, 'users_export.csv'); | |||
| btn.innerHTML = '<i class="fas fa-check me-1"></i>Exported!'; | |||
| setTimeout(() => { | |||
| btn.innerHTML = originalText; | |||
| btn.disabled = false; | |||
| }, 2000); | |||
| }, 1500); | |||
| }); | |||
| function generateCSV() { | |||
| const headers = ['Name', 'Email', 'Username', 'Role', 'Status', 'Joined Date']; | |||
| const rows = []; | |||
| // Get visible rows from DataTable | |||
| table.rows({ search: 'applied' }).every(function() { | |||
| const data = this.data(); | |||
| const name = $(data[1]).find('h6').text(); | |||
| const username = $(data[1]).find('small').text().replace('@', ''); | |||
| const email = data[2]; | |||
| const role = $(data[3]).text(); | |||
| const status = $(data[4]).text(); | |||
| const joined = data[5]; | |||
| rows.push([name, email, username, role, status, joined]); | |||
| }); | |||
| // Create CSV content | |||
| let csv = headers.join(',') + '\n'; | |||
| rows.forEach(row => { | |||
| csv += row.map(cell => `"${cell}"`).join(',') + '\n'; | |||
| }); | |||
| return csv; | |||
| } | |||
| function downloadCSV(content, filename) { | |||
| const blob = new Blob([content], { type: 'text/csv' }); | |||
| const url = window.URL.createObjectURL(blob); | |||
| const a = document.createElement('a'); | |||
| a.setAttribute('hidden', ''); | |||
| a.setAttribute('href', url); | |||
| a.setAttribute('download', filename); | |||
| document.body.appendChild(a); | |||
| a.click(); | |||
| document.body.removeChild(a); | |||
| } | |||
| // Add user form | |||
| const addUserForm = document.getElementById('addUserForm'); | |||
| addUserForm.addEventListener('submit', function(e) { | |||
| e.preventDefault(); | |||
| const submitBtn = this.querySelector('button[type="submit"]'); | |||
| const originalText = submitBtn.innerHTML; | |||
| submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Adding...'; | |||
| submitBtn.disabled = true; | |||
| setTimeout(() => { | |||
| // Get form data | |||
| const formData = { | |||
| firstName: document.getElementById('firstName').value, | |||
| lastName: document.getElementById('lastName').value, | |||
| email: document.getElementById('email').value, | |||
| username: document.getElementById('username').value, | |||
| role: document.getElementById('role').value, | |||
| status: document.getElementById('status').value, | |||
| sendInvite: document.getElementById('sendInvite').checked | |||
| }; | |||
| // Add new row to table | |||
| const newRow = createUserRow(formData); | |||
| table.row.add($(newRow)).draw(); | |||
| // Close modal | |||
| const modal = bootstrap.Modal.getInstance(document.getElementById('addUserModal')); | |||
| modal.hide(); | |||
| // Reset form | |||
| addUserForm.reset(); | |||
| // Show success message | |||
| showNotification('User added successfully!', 'success'); | |||
| submitBtn.innerHTML = originalText; | |||
| submitBtn.disabled = false; | |||
| }, 1500); | |||
| }); | |||
| function createUserRow(data) { | |||
| const name = `${data.firstName} ${data.lastName}`; | |||
| const initials = `${data.firstName[0]}${data.lastName[0]}`; | |||
| const bgColor = getRandomColor(); | |||
| const roleClass = getRoleBadgeClass(data.role); | |||
| const statusClass = getStatusBadgeClass(data.status); | |||
| const joinedDate = new Date().toLocaleDateString('en-US', { | |||
| year: 'numeric', | |||
| month: 'short', | |||
| day: 'numeric' | |||
| }); | |||
| return ` | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=${bgColor}&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">${name}</h6> | |||
| <small class="text-muted">@${data.username}</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>${data.email}</td> | |||
| <td><span class="badge bg-${roleClass}">${capitalizeFirst(data.role)}</span></td> | |||
| <td><span class="badge bg-${statusClass}">${capitalizeFirst(data.status)}</span></td> | |||
| <td>${joinedDate}</td> | |||
| <td>Just now</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| `; | |||
| } | |||
| function getRandomColor() { | |||
| const colors = ['667eea', '48bb78', 'f39c12', 'e74c3c', '3498db', '9b59b6', '1abc9c']; | |||
| return colors[Math.floor(Math.random() * colors.length)]; | |||
| } | |||
| function getRoleBadgeClass(role) { | |||
| const classes = { | |||
| admin: 'danger', | |||
| manager: 'primary', | |||
| user: 'info', | |||
| guest: 'secondary' | |||
| }; | |||
| return classes[role] || 'secondary'; | |||
| } | |||
| function getStatusBadgeClass(status) { | |||
| const classes = { | |||
| active: 'success', | |||
| pending: 'warning text-dark', | |||
| inactive: 'secondary', | |||
| suspended: 'danger' | |||
| }; | |||
| return classes[status] || 'secondary'; | |||
| } | |||
| function capitalizeFirst(str) { | |||
| return str.charAt(0).toUpperCase() + str.slice(1); | |||
| } | |||
| function showNotification(message, type = 'info') { | |||
| const alertHtml = ` | |||
| <div class="alert alert-${type} alert-dismissible fade show position-fixed top-0 end-0 m-3" role="alert" style="z-index: 1050;"> | |||
| ${message} | |||
| <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | |||
| </div> | |||
| `; | |||
| document.body.insertAdjacentHTML('beforeend', alertHtml); | |||
| setTimeout(() => { | |||
| const alert = document.querySelector('.alert'); | |||
| if (alert) { | |||
| alert.remove(); | |||
| } | |||
| }, 5000); | |||
| } | |||
| // Handle action buttons | |||
| document.addEventListener('click', function(e) { | |||
| if (e.target.closest('.dropdown-item')) { | |||
| const action = e.target.textContent.trim(); | |||
| const row = e.target.closest('tr'); | |||
| const userName = row.querySelector('h6').textContent; | |||
| switch(action) { | |||
| case 'View': | |||
| // Show user details | |||
| console.log('View user:', userName); | |||
| break; | |||
| case 'Edit': | |||
| // Open edit modal | |||
| console.log('Edit user:', userName); | |||
| break; | |||
| case 'Delete': | |||
| if (confirm(`Are you sure you want to delete ${userName}?`)) { | |||
| table.row(row).remove().draw(); | |||
| showNotification('User deleted successfully!', 'success'); | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| }); | |||
| } | |||
| // Initialize on DOM ready | |||
| if (document.readyState === 'loading') { | |||
| document.addEventListener('DOMContentLoaded', initializeUserManagement); | |||
| } else { | |||
| initializeUserManagement(); | |||
| } | |||
| @ -1,179 +1,307 @@ | |||
| <!doctype html> | |||
| <html lang="en" class="h-100"> | |||
| <html lang="en"> | |||
| <head> | |||
| {{> layouts/head pageTitle="Page Not Found" }} | |||
| {{> layouts/head pageTitle="404 - Page Not Found" }} | |||
| </head> | |||
| <body> | |||
| <div class="dashboard-main-wrapper"> | |||
| {{> layouts/header }} | |||
| {{> layouts/sidebar }} | |||
| <div class="dashboard-wrapper"> | |||
| <div class="container-fluid dashboard-content"> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class="error-container"> | |||
| <div class="error-content text-center"> | |||
| <div class="error-number"> | |||
| <h1 class="display-1 fw-bold text-primary">404</h1> | |||
| </div> | |||
| <div class="error-illustration mb-4"> | |||
| <svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" class="img-fluid"> | |||
| <!-- Background circles --> | |||
| <circle cx="200" cy="150" r="120" fill="#E8EAED" opacity="0.5"/> | |||
| <circle cx="200" cy="150" r="90" fill="#F5F5F5" opacity="0.7"/> | |||
| <!-- Magnifying glass --> | |||
| <circle cx="180" cy="130" r="50" fill="none" stroke="#667EEA" stroke-width="8"/> | |||
| <line x1="215" y1="165" x2="245" y2="195" stroke="#667EEA" stroke-width="8" stroke-linecap="round"/> | |||
| <!-- Question mark inside magnifying glass --> | |||
| <text x="180" y="145" font-family="Arial, sans-serif" font-size="48" font-weight="bold" text-anchor="middle" fill="#667EEA">?</text> | |||
| <!-- Floating elements --> | |||
| <circle cx="100" cy="80" r="8" fill="#FFD93D" opacity="0.8"> | |||
| <animate attributeName="cy" values="80;70;80" dur="3s" repeatCount="indefinite"/> | |||
| </circle> | |||
| <circle cx="300" cy="100" r="6" fill="#6BCF7F" opacity="0.8"> | |||
| <animate attributeName="cy" values="100;90;100" dur="2.5s" repeatCount="indefinite"/> | |||
| </circle> | |||
| <circle cx="320" cy="200" r="10" fill="#FF6B6B" opacity="0.8"> | |||
| <animate attributeName="cy" values="200;190;200" dur="3.5s" repeatCount="indefinite"/> | |||
| </circle> | |||
| <circle cx="80" cy="180" r="7" fill="#4ECDC4" opacity="0.8"> | |||
| <animate attributeName="cy" values="180;170;180" dur="2.8s" repeatCount="indefinite"/> | |||
| </circle> | |||
| </svg> | |||
| </div> | |||
| <h2 class="error-title mb-3">Oops! Page Not Found</h2> | |||
| <p class="error-description text-muted mb-4"> | |||
| The page you are looking for might have been removed, had its name changed, or is temporarily unavailable. | |||
| </p> | |||
| <div class="error-search mb-5"> | |||
| <form class="d-flex justify-content-center" style="max-width: 500px; margin: 0 auto;"> | |||
| <div class="input-group"> | |||
| <input type="text" class="form-control" placeholder="Search for pages..." aria-label="Search"> | |||
| <button class="btn btn-primary" type="submit"> | |||
| <i class="fas fa-search"></i> | |||
| </button> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <div class="error-actions"> | |||
| <a href="/" class="btn btn-primary me-3"> | |||
| <i class="fas fa-home me-2"></i>Go to Homepage | |||
| </a> | |||
| <button onclick="history.back()" class="btn btn-outline-primary"> | |||
| <i class="fas fa-arrow-left me-2"></i>Go Back | |||
| </button> | |||
| </div> | |||
| <div class="error-suggestions mt-5"> | |||
| <h5 class="mb-3">Here are some helpful links:</h5> | |||
| <div class="row justify-content-center"> | |||
| <div class="col-md-8"> | |||
| <div class="list-group list-group-horizontal-md text-center"> | |||
| <a href="/" class="list-group-item list-group-item-action"> | |||
| <i class="fas fa-tachometer-alt fa-2x mb-2 text-primary"></i> | |||
| <p class="mb-0">Dashboard</p> | |||
| </a> | |||
| <a href="/pages/settings.html" class="list-group-item list-group-item-action"> | |||
| <i class="fas fa-cog fa-2x mb-2 text-primary"></i> | |||
| <p class="mb-0">Settings</p> | |||
| </a> | |||
| <a href="#" class="list-group-item list-group-item-action" data-bs-toggle="modal" data-bs-target="#supportModal"> | |||
| <i class="fas fa-headset fa-2x mb-2 text-primary"></i> | |||
| <p class="mb-0">Support</p> | |||
| </a> | |||
| <a href="#" class="list-group-item list-group-item-action" onclick="location.reload()"> | |||
| <i class="fas fa-redo fa-2x mb-2 text-primary"></i> | |||
| <p class="mb-0">Refresh</p> | |||
| </a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="error-footer mt-5 text-muted"> | |||
| <small>Error Code: 404 | Request ID: #{{generateRequestId}}</small> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/footer }} | |||
| </div> | |||
| </div> | |||
| <!-- Support Modal --> | |||
| <div class="modal fade" id="supportModal" tabindex="-1" aria-labelledby="supportModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title" id="supportModalLabel">Contact Support</h5> | |||
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |||
| </div> | |||
| <div class="modal-body"> | |||
| <form> | |||
| <div class="mb-3"> | |||
| <label for="supportEmail" class="form-label">Your Email</label> | |||
| <input type="email" class="form-control" id="supportEmail" required> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="supportSubject" class="form-label">Subject</label> | |||
| <input type="text" class="form-control" id="supportSubject" value="404 Error - Page Not Found" readonly> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="supportMessage" class="form-label">Message</label> | |||
| <textarea class="form-control" id="supportMessage" rows="4" required>I encountered a 404 error while trying to access: {{currentUrl}}</textarea> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label class="form-label">Error Details</label> | |||
| <div class="bg-light p-3 rounded"> | |||
| <small class="text-muted"> | |||
| <strong>URL:</strong> <span id="errorUrl"></span><br> | |||
| <strong>Time:</strong> <span id="errorTime"></span><br> | |||
| <strong>Browser:</strong> <span id="errorBrowser"></span> | |||
| </small> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |||
| <button type="button" class="btn btn-primary" onclick="submitSupport()">Send Report</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/scripts }} | |||
| <script> | |||
| // Generate random request ID | |||
| function generateRequestId() { | |||
| return Math.random().toString(36).substr(2, 9).toUpperCase(); | |||
| } | |||
| // Set request ID | |||
| document.addEventListener('DOMContentLoaded', function() { | |||
| const requestIdElement = document.querySelector('.error-footer small'); | |||
| if (requestIdElement) { | |||
| requestIdElement.innerHTML = requestIdElement.innerHTML.replace('{{generateRequestId}}', generateRequestId()); | |||
| } | |||
| // Fill error details in modal | |||
| document.getElementById('errorUrl').textContent = window.location.href; | |||
| document.getElementById('errorTime').textContent = new Date().toLocaleString(); | |||
| document.getElementById('errorBrowser').textContent = navigator.userAgent.split(' ').slice(-2).join(' '); | |||
| }); | |||
| // Submit support function | |||
| function submitSupport() { | |||
| const btn = event.target; | |||
| const originalText = btn.innerHTML; | |||
| btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Sending...'; | |||
| btn.disabled = true; | |||
| setTimeout(() => { | |||
| btn.innerHTML = '<i class="fas fa-check me-2"></i>Sent!'; | |||
| btn.classList.remove('btn-primary'); | |||
| btn.classList.add('btn-success'); | |||
| setTimeout(() => { | |||
| const modal = bootstrap.Modal.getInstance(document.getElementById('supportModal')); | |||
| modal.hide(); | |||
| // Reset button | |||
| btn.innerHTML = originalText; | |||
| btn.classList.remove('btn-success'); | |||
| btn.classList.add('btn-primary'); | |||
| btn.disabled = false; | |||
| }, 1500); | |||
| }, 2000); | |||
| } | |||
| // Search functionality | |||
| document.querySelector('.error-search form').addEventListener('submit', function(e) { | |||
| e.preventDefault(); | |||
| const searchTerm = e.target.querySelector('input').value; | |||
| if (searchTerm) { | |||
| // In a real app, this would perform a search | |||
| window.location.href = '/?search=' + encodeURIComponent(searchTerm); | |||
| } | |||
| }); | |||
| </script> | |||
| <style> | |||
| .error-container { | |||
| min-height: 100vh; | |||
| min-height: calc(100vh - 200px); | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |||
| position: relative; | |||
| overflow: hidden; | |||
| padding: 2rem 0; | |||
| } | |||
| .error-container::before { | |||
| content: ''; | |||
| position: absolute; | |||
| width: 200%; | |||
| height: 200%; | |||
| background: radial-gradient(circle, rgba(255,255,255,0.1) 1px, transparent 1px); | |||
| background-size: 50px 50px; | |||
| animation: float 20s infinite linear; | |||
| .error-content { | |||
| max-width: 800px; | |||
| margin: 0 auto; | |||
| } | |||
| @keyframes float { | |||
| 0% { transform: translate(-50%, -50%) rotate(0deg); } | |||
| 100% { transform: translate(-50%, -50%) rotate(360deg); } | |||
| .error-number h1 { | |||
| font-size: 10rem; | |||
| line-height: 1; | |||
| margin-bottom: 0; | |||
| background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); | |||
| -webkit-background-clip: text; | |||
| -webkit-text-fill-color: transparent; | |||
| background-clip: text; | |||
| } | |||
| .error-content { | |||
| text-align: center; | |||
| position: relative; | |||
| z-index: 1; | |||
| padding: 2rem; | |||
| .error-title { | |||
| font-size: 2rem; | |||
| color: #2d3748; | |||
| } | |||
| .error-code { | |||
| font-size: 10rem; | |||
| font-weight: 700; | |||
| color: white; | |||
| text-shadow: 3px 3px 0px rgba(0,0,0,0.1); | |||
| margin: 0; | |||
| line-height: 1; | |||
| position: relative; | |||
| display: inline-block; | |||
| .error-description { | |||
| font-size: 1.125rem; | |||
| line-height: 1.7; | |||
| } | |||
| .error-code::after { | |||
| content: '404'; | |||
| position: absolute; | |||
| left: 0; | |||
| top: 0; | |||
| color: rgba(255,255,255,0.1); | |||
| transform: translate(5px, 5px); | |||
| z-index: -1; | |||
| .error-illustration svg { | |||
| filter: drop-shadow(0 10px 40px rgba(0, 0, 0, 0.08)); | |||
| } | |||
| .astronaut { | |||
| font-size: 6rem; | |||
| animation: bounce 3s infinite; | |||
| display: block; | |||
| margin: 2rem auto; | |||
| .list-group-horizontal-md .list-group-item { | |||
| border: 1px solid #e9ecef; | |||
| transition: all 0.3s ease; | |||
| } | |||
| @keyframes bounce { | |||
| 0%, 100% { transform: translateY(0) rotate(0deg); } | |||
| 25% { transform: translateY(-10px) rotate(-5deg); } | |||
| 75% { transform: translateY(-10px) rotate(5deg); } | |||
| .list-group-horizontal-md .list-group-item:hover { | |||
| background-color: #f8f9fa; | |||
| transform: translateY(-2px); | |||
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); | |||
| } | |||
| .search-box { | |||
| max-width: 400px; | |||
| margin: 2rem auto; | |||
| .list-group-horizontal-md .list-group-item i { | |||
| transition: transform 0.3s ease; | |||
| } | |||
| .quick-links { | |||
| display: flex; | |||
| gap: 1rem; | |||
| justify-content: center; | |||
| flex-wrap: wrap; | |||
| margin-top: 2rem; | |||
| .list-group-horizontal-md .list-group-item:hover i { | |||
| transform: scale(1.1); | |||
| } | |||
| @media (max-width: 768px) { | |||
| .error-code { | |||
| .error-number h1 { | |||
| font-size: 6rem; | |||
| } | |||
| .astronaut { | |||
| font-size: 4rem; | |||
| .error-title { | |||
| font-size: 1.5rem; | |||
| } | |||
| .error-illustration svg { | |||
| width: 100%; | |||
| height: auto; | |||
| max-width: 300px; | |||
| } | |||
| .error-actions .btn { | |||
| display: block; | |||
| width: 100%; | |||
| margin-bottom: 0.5rem; | |||
| } | |||
| .error-actions .btn.me-3 { | |||
| margin-right: 0 !important; | |||
| } | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="error-container"> | |||
| <div class="error-content"> | |||
| <div class="text-white"> | |||
| <span class="astronaut">🚀</span> | |||
| <h1 class="error-code">404</h1> | |||
| <h2 class="h3 mb-3 text-white">Oops! Lost in Space</h2> | |||
| <p class="lead text-white-50 mb-4"> | |||
| The page you're looking for has drifted away into the digital cosmos. | |||
| <br>Let's get you back on track! | |||
| </p> | |||
| <div class="search-box"> | |||
| <form class="d-flex gap-2" onsubmit="return false;"> | |||
| <input type="text" class="form-control form-control-lg" placeholder="Search for pages..."> | |||
| <button type="submit" class="btn btn-light btn-lg"> | |||
| <i class="fas fa-search"></i> | |||
| </button> | |||
| </form> | |||
| </div> | |||
| <div class="quick-links"> | |||
| <a href="/" class="btn btn-light btn-lg"> | |||
| <i class="fas fa-home me-2"></i>Go Home | |||
| </a> | |||
| <a href="#" onclick="history.back()" class="btn btn-outline-light btn-lg"> | |||
| <i class="fas fa-arrow-left me-2"></i>Go Back | |||
| </a> | |||
| </div> | |||
| <div class="mt-5 text-white-50"> | |||
| <p class="mb-2">Popular destinations:</p> | |||
| <div class="d-flex gap-3 justify-content-center flex-wrap"> | |||
| <a href="/" class="text-white-50 text-decoration-none">Dashboard</a> | |||
| <span class="text-white-50">•</span> | |||
| <a href="/pages/charts/index.html" class="text-white-50 text-decoration-none">Charts</a> | |||
| <span class="text-white-50">•</span> | |||
| <a href="/pages/tables/data-tables.html" class="text-white-50 text-decoration-none">Tables</a> | |||
| <span class="text-white-50">•</span> | |||
| <a href="/pages/misc/login.html" class="text-white-50 text-decoration-none">Login</a> | |||
| </div> | |||
| </div> | |||
| <div class="mt-5"> | |||
| <a href="#" class="text-white-50 text-decoration-none small"> | |||
| <i class="fas fa-envelope me-1"></i>Contact Support | |||
| </a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <script> | |||
| // Fun Easter egg: Make astronaut fly when clicked | |||
| document.querySelector('.astronaut').addEventListener('click', function() { | |||
| this.style.animation = 'none'; | |||
| setTimeout(() => { | |||
| this.style.animation = 'bounce 3s infinite, flyAway 1s ease-out'; | |||
| setTimeout(() => { | |||
| this.style.animation = 'bounce 3s infinite'; | |||
| }, 1000); | |||
| }, 10); | |||
| }); | |||
| // Add flyAway animation dynamically | |||
| const style = document.createElement('style'); | |||
| style.textContent = ` | |||
| @keyframes flyAway { | |||
| 0% { transform: translateY(0) scale(1); opacity: 1; } | |||
| 100% { transform: translateY(-200px) scale(0.5); opacity: 0; } | |||
| /* Animation for floating elements */ | |||
| @keyframes float { | |||
| 0%, 100% { | |||
| transform: translateY(0); | |||
| } | |||
| `; | |||
| document.head.appendChild(style); | |||
| // Search functionality (demo) | |||
| document.querySelector('form').addEventListener('submit', function(e) { | |||
| e.preventDefault(); | |||
| const searchTerm = this.querySelector('input').value; | |||
| if (searchTerm) { | |||
| alert(`Searching for: ${searchTerm}\n\nIn a real application, this would search your site!`); | |||
| 50% { | |||
| transform: translateY(-10px); | |||
| } | |||
| }); | |||
| </script> | |||
| } | |||
| </style> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,697 @@ | |||
| <!doctype html> | |||
| <html lang="en"> | |||
| <head> | |||
| {{> layouts/head pageTitle="Settings" }} | |||
| </head> | |||
| <body> | |||
| <div class="dashboard-main-wrapper"> | |||
| {{> layouts/header }} | |||
| {{> layouts/sidebar activeMenu="settings" }} | |||
| <div class="dashboard-wrapper"> | |||
| <div class="container-fluid dashboard-content"> | |||
| <!-- Page Header --> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class="page-header"> | |||
| <h2 class="pageheader-title">Settings</h2> | |||
| <p class="pageheader-text">Manage your application settings and preferences</p> | |||
| <div class="page-breadcrumb"> | |||
| <nav aria-label="breadcrumb"> | |||
| <ol class="breadcrumb"> | |||
| <li class="breadcrumb-item"><a href="/" class="breadcrumb-link">Dashboard</a></li> | |||
| <li class="breadcrumb-item active" aria-current="page">Settings</li> | |||
| </ol> | |||
| </nav> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="row"> | |||
| <!-- Settings Navigation --> | |||
| <div class="col-xl-3 col-lg-4 col-md-12 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| <div class="nav flex-column nav-pills" id="settings-tabs" role="tablist" aria-orientation="vertical"> | |||
| <a class="nav-link active" id="general-tab" data-bs-toggle="pill" href="#general" role="tab" aria-controls="general" aria-selected="true"> | |||
| <i class="fas fa-cog me-2"></i> General | |||
| </a> | |||
| <a class="nav-link" id="account-tab" data-bs-toggle="pill" href="#account" role="tab" aria-controls="account" aria-selected="false"> | |||
| <i class="fas fa-user me-2"></i> Account | |||
| </a> | |||
| <a class="nav-link" id="security-tab" data-bs-toggle="pill" href="#security" role="tab" aria-controls="security" aria-selected="false"> | |||
| <i class="fas fa-shield-alt me-2"></i> Security | |||
| </a> | |||
| <a class="nav-link" id="email-tab" data-bs-toggle="pill" href="#email" role="tab" aria-controls="email" aria-selected="false"> | |||
| <i class="fas fa-envelope me-2"></i> Email | |||
| </a> | |||
| <a class="nav-link" id="notifications-tab" data-bs-toggle="pill" href="#notifications" role="tab" aria-controls="notifications" aria-selected="false"> | |||
| <i class="fas fa-bell me-2"></i> Notifications | |||
| </a> | |||
| <a class="nav-link" id="appearance-tab" data-bs-toggle="pill" href="#appearance" role="tab" aria-controls="appearance" aria-selected="false"> | |||
| <i class="fas fa-palette me-2"></i> Appearance | |||
| </a> | |||
| <a class="nav-link" id="integrations-tab" data-bs-toggle="pill" href="#integrations" role="tab" aria-controls="integrations" aria-selected="false"> | |||
| <i class="fas fa-plug me-2"></i> Integrations | |||
| </a> | |||
| <a class="nav-link" id="advanced-tab" data-bs-toggle="pill" href="#advanced" role="tab" aria-controls="advanced" aria-selected="false"> | |||
| <i class="fas fa-tools me-2"></i> Advanced | |||
| </a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Settings Content --> | |||
| <div class="col-xl-9 col-lg-8 col-md-12 col-sm-12 col-12"> | |||
| <div class="tab-content" id="settings-content"> | |||
| <!-- General Settings --> | |||
| <div class="tab-pane fade show active" id="general" role="tabpanel" aria-labelledby="general-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">General Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <div class="mb-4"> | |||
| <label for="siteName" class="form-label">Site Name</label> | |||
| <input type="text" class="form-control" id="siteName" value="Concept Dashboard"> | |||
| <small class="form-text text-muted">The name of your website displayed in the header and title</small> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="siteDescription" class="form-label">Site Description</label> | |||
| <textarea class="form-control" id="siteDescription" rows="3">A modern Bootstrap 5 admin dashboard template</textarea> | |||
| <small class="form-text text-muted">Brief description of your website for SEO purposes</small> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="siteUrl" class="form-label">Site URL</label> | |||
| <input type="url" class="form-control" id="siteUrl" value="https://example.com"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="timezone" class="form-label">Timezone</label> | |||
| <select class="form-select" id="timezone"> | |||
| <option value="UTC">UTC</option> | |||
| <option value="America/New_York" selected>Eastern Time (US & Canada)</option> | |||
| <option value="America/Chicago">Central Time (US & Canada)</option> | |||
| <option value="America/Denver">Mountain Time (US & Canada)</option> | |||
| <option value="America/Los_Angeles">Pacific Time (US & Canada)</option> | |||
| <option value="Europe/London">London</option> | |||
| <option value="Europe/Paris">Paris</option> | |||
| <option value="Asia/Tokyo">Tokyo</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="dateFormat" class="form-label">Date Format</label> | |||
| <select class="form-select" id="dateFormat"> | |||
| <option value="MM/DD/YYYY">MM/DD/YYYY</option> | |||
| <option value="DD/MM/YYYY">DD/MM/YYYY</option> | |||
| <option value="YYYY-MM-DD">YYYY-MM-DD</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="language" class="form-label">Language</label> | |||
| <select class="form-select" id="language"> | |||
| <option value="en" selected>English</option> | |||
| <option value="es">Spanish</option> | |||
| <option value="fr">French</option> | |||
| <option value="de">German</option> | |||
| <option value="ja">Japanese</option> | |||
| </select> | |||
| </div> | |||
| <div class="d-flex justify-content-end"> | |||
| <button type="button" class="btn btn-outline-secondary me-2">Reset</button> | |||
| <button type="submit" class="btn btn-primary">Save Changes</button> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Account Settings --> | |||
| <div class="tab-pane fade" id="account" role="tabpanel" aria-labelledby="account-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Account Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <div class="mb-4"> | |||
| <label for="username" class="form-label">Username</label> | |||
| <input type="text" class="form-control" id="username" value="johndoe"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="displayName" class="form-label">Display Name</label> | |||
| <input type="text" class="form-control" id="displayName" value="John Doe"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="accountEmail" class="form-label">Email Address</label> | |||
| <input type="email" class="form-control" id="accountEmail" value="john.doe@example.com"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="phone" class="form-label">Phone Number</label> | |||
| <input type="tel" class="form-control" id="phone" value="+1 (555) 123-4567"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="bio" class="form-label">Bio</label> | |||
| <textarea class="form-control" id="bio" rows="3">Product designer with 5+ years of experience</textarea> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label class="form-label">Profile Picture</label> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="me-3"> | |||
| <div class="avatar-upload"> | |||
| <div class="avatar-preview"> | |||
| <div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;"> | |||
| <span class="h3 mb-0">JD</span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div> | |||
| <button type="button" class="btn btn-sm btn-primary">Change Avatar</button> | |||
| <button type="button" class="btn btn-sm btn-outline-danger ms-2">Remove</button> | |||
| <p class="mb-0 mt-2 text-muted small">JPG, GIF or PNG. Max size of 2MB</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Delete Account</h6> | |||
| <p class="text-muted">Once you delete your account, there is no going back. Please be certain.</p> | |||
| <button type="button" class="btn btn-danger">Delete Account</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Security Settings --> | |||
| <div class="tab-pane fade" id="security" role="tabpanel" aria-labelledby="security-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Security Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <h6 class="mb-3">Change Password</h6> | |||
| <div class="mb-3"> | |||
| <label for="currentPassword" class="form-label">Current Password</label> | |||
| <input type="password" class="form-control" id="currentPassword"> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="newPassword" class="form-label">New Password</label> | |||
| <input type="password" class="form-control" id="newPassword"> | |||
| <small class="form-text text-muted">Use 8 or more characters with a mix of letters, numbers & symbols</small> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="confirmPassword" class="form-label">Confirm New Password</label> | |||
| <input type="password" class="form-control" id="confirmPassword"> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary mb-4">Update Password</button> | |||
| <hr> | |||
| <h6 class="mb-3">Two-Factor Authentication</h6> | |||
| <p class="text-muted">Add an extra layer of security to your account by enabling two-factor authentication.</p> | |||
| <div class="form-check form-switch mb-4"> | |||
| <input class="form-check-input" type="checkbox" id="enable2FA"> | |||
| <label class="form-check-label" for="enable2FA"> | |||
| Enable Two-Factor Authentication | |||
| </label> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Sessions</h6> | |||
| <p class="text-muted">Manage and logout your active sessions on other browsers and devices.</p> | |||
| <div class="list-group mb-3"> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex w-100 justify-content-between align-items-center"> | |||
| <div> | |||
| <h6 class="mb-1">Chrome on Windows</h6> | |||
| <p class="mb-0 text-muted small">Your current session</p> | |||
| </div> | |||
| <span class="badge bg-success">Active</span> | |||
| </div> | |||
| </div> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex w-100 justify-content-between align-items-center"> | |||
| <div> | |||
| <h6 class="mb-1">Safari on iPhone</h6> | |||
| <p class="mb-0 text-muted small">Last active 2 hours ago</p> | |||
| </div> | |||
| <button class="btn btn-sm btn-outline-danger">Logout</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <button type="button" class="btn btn-outline-danger">Logout Other Sessions</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Email Settings --> | |||
| <div class="tab-pane fade" id="email" role="tabpanel" aria-labelledby="email-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Email Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <h6 class="mb-3">Email Configuration</h6> | |||
| <div class="mb-3"> | |||
| <label for="mailDriver" class="form-label">Mail Driver</label> | |||
| <select class="form-select" id="mailDriver"> | |||
| <option value="smtp" selected>SMTP</option> | |||
| <option value="sendmail">Sendmail</option> | |||
| <option value="mailgun">Mailgun</option> | |||
| <option value="ses">Amazon SES</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="smtpHost" class="form-label">SMTP Host</label> | |||
| <input type="text" class="form-control" id="smtpHost" value="smtp.gmail.com"> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="smtpPort" class="form-label">SMTP Port</label> | |||
| <input type="number" class="form-control" id="smtpPort" value="587"> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="smtpUsername" class="form-label">SMTP Username</label> | |||
| <input type="text" class="form-control" id="smtpUsername" value="your-email@gmail.com"> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="smtpPassword" class="form-label">SMTP Password</label> | |||
| <input type="password" class="form-control" id="smtpPassword" placeholder="••••••••"> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="smtpEncryption" class="form-label">Encryption</label> | |||
| <select class="form-select" id="smtpEncryption"> | |||
| <option value="tls" selected>TLS</option> | |||
| <option value="ssl">SSL</option> | |||
| <option value="none">None</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="fromEmail" class="form-label">From Email Address</label> | |||
| <input type="email" class="form-control" id="fromEmail" value="noreply@example.com"> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="fromName" class="form-label">From Name</label> | |||
| <input type="text" class="form-control" id="fromName" value="Concept Dashboard"> | |||
| </div> | |||
| <div class="d-flex justify-content-between"> | |||
| <button type="button" class="btn btn-outline-primary">Send Test Email</button> | |||
| <button type="submit" class="btn btn-primary">Save Configuration</button> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Notification Settings --> | |||
| <div class="tab-pane fade" id="notifications" role="tabpanel" aria-labelledby="notifications-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Notification Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <h6 class="mb-3">Email Notifications</h6> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="emailComments" checked> | |||
| <label class="form-check-label" for="emailComments"> | |||
| Comments on your posts | |||
| </label> | |||
| </div> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="emailFollowers" checked> | |||
| <label class="form-check-label" for="emailFollowers"> | |||
| New followers | |||
| </label> | |||
| </div> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="emailMentions"> | |||
| <label class="form-check-label" for="emailMentions"> | |||
| Mentions and tags | |||
| </label> | |||
| </div> | |||
| <div class="form-check form-switch mb-4"> | |||
| <input class="form-check-input" type="checkbox" id="emailNewsletter" checked> | |||
| <label class="form-check-label" for="emailNewsletter"> | |||
| Newsletter and product updates | |||
| </label> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Push Notifications</h6> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="pushMessages" checked> | |||
| <label class="form-check-label" for="pushMessages"> | |||
| Direct messages | |||
| </label> | |||
| </div> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="pushTasks" checked> | |||
| <label class="form-check-label" for="pushTasks"> | |||
| Task assignments | |||
| </label> | |||
| </div> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="pushReminders" checked> | |||
| <label class="form-check-label" for="pushReminders"> | |||
| Reminders | |||
| </label> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Desktop Notifications</h6> | |||
| <p class="text-muted">Show notifications on desktop when the app is in background</p> | |||
| <div class="form-check form-switch mb-4"> | |||
| <input class="form-check-input" type="checkbox" id="desktopNotifications"> | |||
| <label class="form-check-label" for="desktopNotifications"> | |||
| Enable desktop notifications | |||
| </label> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary">Save Preferences</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Appearance Settings --> | |||
| <div class="tab-pane fade" id="appearance" role="tabpanel" aria-labelledby="appearance-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Appearance Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <h6 class="mb-3">Theme</h6> | |||
| <div class="row mb-4"> | |||
| <div class="col-md-4"> | |||
| <div class="form-check card-radio"> | |||
| <input class="form-check-input" type="radio" name="theme" id="themeLight" value="light" checked> | |||
| <label class="form-check-label card p-3 text-center" for="themeLight"> | |||
| <i class="fas fa-sun fa-2x mb-2"></i> | |||
| <p class="mb-0">Light</p> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| <div class="col-md-4"> | |||
| <div class="form-check card-radio"> | |||
| <input class="form-check-input" type="radio" name="theme" id="themeDark" value="dark"> | |||
| <label class="form-check-label card p-3 text-center" for="themeDark"> | |||
| <i class="fas fa-moon fa-2x mb-2"></i> | |||
| <p class="mb-0">Dark</p> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| <div class="col-md-4"> | |||
| <div class="form-check card-radio"> | |||
| <input class="form-check-input" type="radio" name="theme" id="themeAuto" value="auto"> | |||
| <label class="form-check-label card p-3 text-center" for="themeAuto"> | |||
| <i class="fas fa-adjust fa-2x mb-2"></i> | |||
| <p class="mb-0">Auto</p> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <h6 class="mb-3">Color Scheme</h6> | |||
| <div class="d-flex gap-2 mb-4"> | |||
| <button type="button" class="btn btn-primary color-scheme-btn active" data-color="primary">Primary</button> | |||
| <button type="button" class="btn btn-success color-scheme-btn" data-color="success">Success</button> | |||
| <button type="button" class="btn btn-danger color-scheme-btn" data-color="danger">Danger</button> | |||
| <button type="button" class="btn btn-warning color-scheme-btn" data-color="warning">Warning</button> | |||
| <button type="button" class="btn btn-info color-scheme-btn" data-color="info">Info</button> | |||
| <button type="button" class="btn btn-dark color-scheme-btn" data-color="dark">Dark</button> | |||
| </div> | |||
| <h6 class="mb-3">Layout</h6> | |||
| <div class="mb-3"> | |||
| <label for="sidebarLayout" class="form-label">Sidebar Layout</label> | |||
| <select class="form-select" id="sidebarLayout"> | |||
| <option value="fixed" selected>Fixed</option> | |||
| <option value="scrollable">Scrollable</option> | |||
| <option value="compact">Compact</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label for="contentWidth" class="form-label">Content Width</label> | |||
| <select class="form-select" id="contentWidth"> | |||
| <option value="fluid" selected>Fluid</option> | |||
| <option value="boxed">Boxed</option> | |||
| </select> | |||
| </div> | |||
| <h6 class="mb-3">Font Size</h6> | |||
| <div class="mb-4"> | |||
| <input type="range" class="form-range" min="12" max="20" value="16" id="fontSize"> | |||
| <div class="d-flex justify-content-between"> | |||
| <small>Small</small> | |||
| <small>Large</small> | |||
| </div> | |||
| </div> | |||
| <button type="submit" class="btn btn-primary">Apply Changes</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Integrations Settings --> | |||
| <div class="tab-pane fade" id="integrations" role="tabpanel" aria-labelledby="integrations-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Integrations</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <div class="list-group"> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex align-items-center justify-content-between"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="icon-circle bg-primary-light text-primary me-3"> | |||
| <i class="fab fa-google"></i> | |||
| </div> | |||
| <div> | |||
| <h6 class="mb-0">Google Analytics</h6> | |||
| <small class="text-muted">Track website traffic and user behavior</small> | |||
| </div> | |||
| </div> | |||
| <div class="form-check form-switch"> | |||
| <input class="form-check-input" type="checkbox" id="googleAnalytics" checked> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex align-items-center justify-content-between"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="icon-circle bg-info-light text-info me-3"> | |||
| <i class="fab fa-slack"></i> | |||
| </div> | |||
| <div> | |||
| <h6 class="mb-0">Slack</h6> | |||
| <small class="text-muted">Get notifications in your Slack workspace</small> | |||
| </div> | |||
| </div> | |||
| <button class="btn btn-sm btn-outline-primary">Connect</button> | |||
| </div> | |||
| </div> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex align-items-center justify-content-between"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="icon-circle bg-success-light text-success me-3"> | |||
| <i class="fab fa-stripe"></i> | |||
| </div> | |||
| <div> | |||
| <h6 class="mb-0">Stripe</h6> | |||
| <small class="text-muted">Accept payments and manage subscriptions</small> | |||
| </div> | |||
| </div> | |||
| <div class="form-check form-switch"> | |||
| <input class="form-check-input" type="checkbox" id="stripe"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex align-items-center justify-content-between"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="icon-circle bg-warning-light text-warning me-3"> | |||
| <i class="fab fa-mailchimp"></i> | |||
| </div> | |||
| <div> | |||
| <h6 class="mb-0">Mailchimp</h6> | |||
| <small class="text-muted">Email marketing automation</small> | |||
| </div> | |||
| </div> | |||
| <button class="btn btn-sm btn-outline-primary">Connect</button> | |||
| </div> | |||
| </div> | |||
| <div class="list-group-item"> | |||
| <div class="d-flex align-items-center justify-content-between"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="icon-circle bg-danger-light text-danger me-3"> | |||
| <i class="fab fa-github"></i> | |||
| </div> | |||
| <div> | |||
| <h6 class="mb-0">GitHub</h6> | |||
| <small class="text-muted">Source code management</small> | |||
| </div> | |||
| </div> | |||
| <span class="badge bg-success">Connected</span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="mt-4"> | |||
| <h6 class="mb-3">API Keys</h6> | |||
| <div class="mb-3"> | |||
| <label for="apiKey" class="form-label">API Key</label> | |||
| <div class="input-group"> | |||
| <input type="text" class="form-control" id="apiKey" value="sk_test_4eC39HqLyjWDarjtT1zdp7dc" readonly> | |||
| <button class="btn btn-outline-secondary" type="button"> | |||
| <i class="fas fa-copy"></i> | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <button class="btn btn-primary">Generate New Key</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Advanced Settings --> | |||
| <div class="tab-pane fade" id="advanced" role="tabpanel" aria-labelledby="advanced-tab"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Advanced Settings</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <form> | |||
| <h6 class="mb-3">Developer Options</h6> | |||
| <div class="form-check form-switch mb-3"> | |||
| <input class="form-check-input" type="checkbox" id="debugMode"> | |||
| <label class="form-check-label" for="debugMode"> | |||
| Debug Mode | |||
| </label> | |||
| <small class="form-text text-muted d-block">Show detailed error messages and logs</small> | |||
| </div> | |||
| <div class="form-check form-switch mb-4"> | |||
| <input class="form-check-input" type="checkbox" id="maintenanceMode"> | |||
| <label class="form-check-label" for="maintenanceMode"> | |||
| Maintenance Mode | |||
| </label> | |||
| <small class="form-text text-muted d-block">Temporarily disable the site for maintenance</small> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Cache</h6> | |||
| <p class="text-muted">Clear various types of cached data to improve performance</p> | |||
| <div class="d-flex gap-2 mb-4"> | |||
| <button type="button" class="btn btn-outline-secondary">Clear App Cache</button> | |||
| <button type="button" class="btn btn-outline-secondary">Clear View Cache</button> | |||
| <button type="button" class="btn btn-outline-secondary">Clear All Cache</button> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3">Database</h6> | |||
| <div class="mb-3"> | |||
| <label for="dbBackup" class="form-label">Automatic Backup</label> | |||
| <select class="form-select" id="dbBackup"> | |||
| <option value="daily" selected>Daily</option> | |||
| <option value="weekly">Weekly</option> | |||
| <option value="monthly">Monthly</option> | |||
| <option value="never">Never</option> | |||
| </select> | |||
| </div> | |||
| <button type="button" class="btn btn-primary mb-4">Backup Now</button> | |||
| <hr> | |||
| <h6 class="mb-3">Export/Import</h6> | |||
| <p class="text-muted">Export your settings or import from a backup</p> | |||
| <div class="d-flex gap-2 mb-4"> | |||
| <button type="button" class="btn btn-outline-primary"> | |||
| <i class="fas fa-download me-2"></i>Export Settings | |||
| </button> | |||
| <button type="button" class="btn btn-outline-primary"> | |||
| <i class="fas fa-upload me-2"></i>Import Settings | |||
| </button> | |||
| </div> | |||
| <hr> | |||
| <h6 class="mb-3 text-danger">Danger Zone</h6> | |||
| <p class="text-muted">These actions are irreversible. Please be careful.</p> | |||
| <button type="button" class="btn btn-outline-danger me-2">Reset All Settings</button> | |||
| <button type="button" class="btn btn-danger">Delete All Data</button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/footer }} | |||
| </div> | |||
| </div> | |||
| {{> layouts/scripts }} | |||
| <script type="module"> | |||
| import { initializeSettings } from '@js/pages/settings.js'; | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,342 @@ | |||
| <!doctype html> | |||
| <html lang="en"> | |||
| <head> | |||
| {{> layouts/head pageTitle="Timeline" }} | |||
| </head> | |||
| <body> | |||
| <div class="dashboard-main-wrapper"> | |||
| {{> layouts/header }} | |||
| {{> layouts/sidebar activeMenu="timeline" }} | |||
| <div class="dashboard-wrapper"> | |||
| <div class="container-fluid dashboard-content"> | |||
| <!-- Page Header --> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class="page-header"> | |||
| <h2 class="pageheader-title">Activity Timeline</h2> | |||
| <p class="pageheader-text">Track user activities, system events, and important milestones</p> | |||
| <div class="page-breadcrumb"> | |||
| <nav aria-label="breadcrumb"> | |||
| <ol class="breadcrumb"> | |||
| <li class="breadcrumb-item"><a href="/" class="breadcrumb-link">Dashboard</a></li> | |||
| <li class="breadcrumb-item active" aria-current="page">Timeline</li> | |||
| </ol> | |||
| </nav> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="row"> | |||
| <!-- Timeline Filters --> | |||
| <div class="col-xl-3 col-lg-4 col-md-12 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Filters</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <div class="mb-4"> | |||
| <label class="form-label">Date Range</label> | |||
| <select class="form-select" id="dateFilter"> | |||
| <option value="today">Today</option> | |||
| <option value="week" selected>This Week</option> | |||
| <option value="month">This Month</option> | |||
| <option value="year">This Year</option> | |||
| <option value="custom">Custom Range</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label class="form-label">Activity Type</label> | |||
| <div class="activity-filters"> | |||
| <div class="form-check mb-2"> | |||
| <input class="form-check-input" type="checkbox" value="user" id="filterUser" checked> | |||
| <label class="form-check-label" for="filterUser"> | |||
| <i class="fas fa-user text-primary me-2"></i>User Activities | |||
| </label> | |||
| </div> | |||
| <div class="form-check mb-2"> | |||
| <input class="form-check-input" type="checkbox" value="system" id="filterSystem" checked> | |||
| <label class="form-check-label" for="filterSystem"> | |||
| <i class="fas fa-cog text-warning me-2"></i>System Events | |||
| </label> | |||
| </div> | |||
| <div class="form-check mb-2"> | |||
| <input class="form-check-input" type="checkbox" value="security" id="filterSecurity" checked> | |||
| <label class="form-check-label" for="filterSecurity"> | |||
| <i class="fas fa-shield-alt text-danger me-2"></i>Security | |||
| </label> | |||
| </div> | |||
| <div class="form-check mb-2"> | |||
| <input class="form-check-input" type="checkbox" value="transaction" id="filterTransaction" checked> | |||
| <label class="form-check-label" for="filterTransaction"> | |||
| <i class="fas fa-dollar-sign text-success me-2"></i>Transactions | |||
| </label> | |||
| </div> | |||
| <div class="form-check mb-2"> | |||
| <input class="form-check-input" type="checkbox" value="milestone" id="filterMilestone" checked> | |||
| <label class="form-check-label" for="filterMilestone"> | |||
| <i class="fas fa-flag text-info me-2"></i>Milestones | |||
| </label> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="mb-4"> | |||
| <label class="form-label">Users</label> | |||
| <input type="text" class="form-control" placeholder="Search users..." id="userSearch"> | |||
| </div> | |||
| <button class="btn btn-primary w-100 mb-2" id="applyFilters">Apply Filters</button> | |||
| <button class="btn btn-outline-secondary w-100" id="resetFilters">Reset</button> | |||
| </div> | |||
| </div> | |||
| <!-- Statistics --> | |||
| <div class="card mt-3"> | |||
| <div class="card-header"> | |||
| <h5 class="mb-0">Activity Stats</h5> | |||
| </div> | |||
| <div class="card-body"> | |||
| <div class="d-flex justify-content-between mb-3"> | |||
| <span class="text-muted">Total Activities</span> | |||
| <span class="fw-bold">1,234</span> | |||
| </div> | |||
| <div class="d-flex justify-content-between mb-3"> | |||
| <span class="text-muted">Today</span> | |||
| <span class="fw-bold text-primary">47</span> | |||
| </div> | |||
| <div class="d-flex justify-content-between mb-3"> | |||
| <span class="text-muted">This Week</span> | |||
| <span class="fw-bold text-success">312</span> | |||
| </div> | |||
| <div class="d-flex justify-content-between"> | |||
| <span class="text-muted">Active Users</span> | |||
| <span class="fw-bold text-info">89</span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Timeline Content --> | |||
| <div class="col-xl-9 col-lg-8 col-md-12 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <div class="d-flex justify-content-between align-items-center"> | |||
| <h5 class="mb-0">Recent Activities</h5> | |||
| <div> | |||
| <button class="btn btn-sm btn-outline-secondary me-2" id="exportTimeline"> | |||
| <i class="fas fa-download me-1"></i>Export | |||
| </button> | |||
| <button class="btn btn-sm btn-primary" id="refreshTimeline"> | |||
| <i class="fas fa-sync me-1"></i>Refresh | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="card-body"> | |||
| <div class="timeline" id="timeline"> | |||
| <!-- Today --> | |||
| <div class="timeline-date"> | |||
| <h6 class="text-muted">Today</h6> | |||
| </div> | |||
| <div class="timeline-item" data-type="user"> | |||
| <div class="timeline-marker bg-primary"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <img src="https://ui-avatars.com/api/?name=John+Doe&background=667eea&color=fff" alt="Avatar" class="rounded-circle me-2" width="32" height="32"> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">John Doe <small class="text-muted">updated profile</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>2 hours ago</small> | |||
| </div> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View Details</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-share me-2"></i>Share</a></li> | |||
| </ul> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <p class="mb-0">Changed profile picture and updated bio information.</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-item" data-type="system"> | |||
| <div class="timeline-marker bg-warning"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <i class="fas fa-server text-warning me-2"></i> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">System <small class="text-muted">backup completed</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>3 hours ago</small> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <p class="mb-2">Daily backup completed successfully.</p> | |||
| <div class="progress" style="height: 10px;"> | |||
| <div class="progress-bar bg-success" role="progressbar" style="width: 100%"></div> | |||
| </div> | |||
| <small class="text-muted">Backup size: 2.4 GB</small> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-item" data-type="security"> | |||
| <div class="timeline-marker bg-danger"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <i class="fas fa-shield-alt text-danger me-2"></i> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">Security Alert <small class="text-muted">failed login attempts</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>5 hours ago</small> | |||
| </div> | |||
| <span class="badge bg-danger">High</span> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <p class="mb-0">Multiple failed login attempts detected from IP: 192.168.1.100</p> | |||
| <div class="mt-2"> | |||
| <button class="btn btn-sm btn-danger me-2">Block IP</button> | |||
| <button class="btn btn-sm btn-outline-secondary">View Details</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-item" data-type="transaction"> | |||
| <div class="timeline-marker bg-success"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <img src="https://ui-avatars.com/api/?name=Jane+Smith&background=48bb78&color=fff" alt="Avatar" class="rounded-circle me-2" width="32" height="32"> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">Jane Smith <small class="text-muted">completed purchase</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>6 hours ago</small> | |||
| </div> | |||
| <span class="fw-bold text-success">+$299.00</span> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="flex-grow-1"> | |||
| <p class="mb-0">Premium Plan Subscription</p> | |||
| <small class="text-muted">Order #12345</small> | |||
| </div> | |||
| <button class="btn btn-sm btn-outline-primary">View Invoice</button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Yesterday --> | |||
| <div class="timeline-date"> | |||
| <h6 class="text-muted">Yesterday</h6> | |||
| </div> | |||
| <div class="timeline-item" data-type="milestone"> | |||
| <div class="timeline-marker bg-info"> | |||
| <i class="fas fa-trophy text-white small"></i> | |||
| </div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <i class="fas fa-flag text-info me-2"></i> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">Milestone Achieved <small class="text-muted">1000 users registered</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>Yesterday at 3:45 PM</small> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <div class="card bg-light"> | |||
| <div class="card-body text-center"> | |||
| <i class="fas fa-users fa-3x text-info mb-3"></i> | |||
| <h4 class="mb-1">1,000 Users!</h4> | |||
| <p class="mb-0 text-muted">Congratulations on reaching this milestone!</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-item" data-type="user"> | |||
| <div class="timeline-marker bg-primary"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <img src="https://ui-avatars.com/api/?name=Mike+Johnson&background=f39c12&color=fff" alt="Avatar" class="rounded-circle me-2" width="32" height="32"> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">Mike Johnson <small class="text-muted">commented on</small> Sales Report Q4</h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>Yesterday at 2:15 PM</small> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <div class="card bg-light"> | |||
| <div class="card-body"> | |||
| <p class="mb-0">"Great work on the Q4 report! The revenue growth is impressive."</p> | |||
| </div> | |||
| </div> | |||
| <div class="mt-2"> | |||
| <button class="btn btn-sm btn-outline-primary me-2"> | |||
| <i class="far fa-thumbs-up me-1"></i>Like | |||
| </button> | |||
| <button class="btn btn-sm btn-outline-secondary"> | |||
| <i class="far fa-comment me-1"></i>Reply | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-item" data-type="system"> | |||
| <div class="timeline-marker bg-warning"></div> | |||
| <div class="timeline-content"> | |||
| <div class="timeline-header"> | |||
| <i class="fas fa-database text-warning me-2"></i> | |||
| <div class="flex-grow-1"> | |||
| <h6 class="mb-0">Database <small class="text-muted">optimization completed</small></h6> | |||
| <small class="text-muted"><i class="far fa-clock me-1"></i>Yesterday at 11:30 AM</small> | |||
| </div> | |||
| </div> | |||
| <div class="timeline-body"> | |||
| <div class="row g-2"> | |||
| <div class="col-6"> | |||
| <div class="text-center"> | |||
| <h5 class="mb-0 text-success">-45%</h5> | |||
| <small class="text-muted">Query Time</small> | |||
| </div> | |||
| </div> | |||
| <div class="col-6"> | |||
| <div class="text-center"> | |||
| <h5 class="mb-0 text-primary">+30%</h5> | |||
| <small class="text-muted">Performance</small> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Load More --> | |||
| <div class="text-center mt-4"> | |||
| <button class="btn btn-outline-primary" id="loadMore"> | |||
| <i class="fas fa-arrow-down me-2"></i>Load More Activities | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/footer }} | |||
| </div> | |||
| </div> | |||
| {{> layouts/scripts }} | |||
| <script type="module"> | |||
| import { initializeTimeline } from '@js/pages/timeline.js'; | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,469 @@ | |||
| <!doctype html> | |||
| <html lang="en"> | |||
| <head> | |||
| {{> layouts/head pageTitle="User Management" }} | |||
| </head> | |||
| <body> | |||
| <div class="dashboard-main-wrapper"> | |||
| {{> layouts/header }} | |||
| {{> layouts/sidebar activeMenu="users" }} | |||
| <div class="dashboard-wrapper"> | |||
| <div class="container-fluid dashboard-content"> | |||
| <!-- Page Header --> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class="page-header"> | |||
| <h2 class="pageheader-title">User Management</h2> | |||
| <p class="pageheader-text">Manage system users, roles, permissions, and access control</p> | |||
| <div class="page-breadcrumb"> | |||
| <nav aria-label="breadcrumb"> | |||
| <ol class="breadcrumb"> | |||
| <li class="breadcrumb-item"><a href="/" class="breadcrumb-link">Dashboard</a></li> | |||
| <li class="breadcrumb-item active" aria-current="page">Users</li> | |||
| </ol> | |||
| </nav> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- User Stats --> | |||
| <div class="row mb-4"> | |||
| <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="flex-grow-1"> | |||
| <h5 class="text-muted mb-2">Total Users</h5> | |||
| <h3 class="mb-0">2,543</h3> | |||
| <p class="mb-0 text-success"> | |||
| <i class="fas fa-arrow-up me-1"></i>12.5% | |||
| </p> | |||
| </div> | |||
| <div class="flex-shrink-0"> | |||
| <div class="icon-circle bg-primary-light text-primary"> | |||
| <i class="fas fa-users"></i> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="flex-grow-1"> | |||
| <h5 class="text-muted mb-2">Active Users</h5> | |||
| <h3 class="mb-0">1,893</h3> | |||
| <p class="mb-0 text-success"> | |||
| <i class="fas fa-arrow-up me-1"></i>8.2% | |||
| </p> | |||
| </div> | |||
| <div class="flex-shrink-0"> | |||
| <div class="icon-circle bg-success-light text-success"> | |||
| <i class="fas fa-user-check"></i> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="flex-grow-1"> | |||
| <h5 class="text-muted mb-2">New Users</h5> | |||
| <h3 class="mb-0">127</h3> | |||
| <p class="mb-0 text-info"> | |||
| <i class="fas fa-minus me-1"></i>0% | |||
| </p> | |||
| </div> | |||
| <div class="flex-shrink-0"> | |||
| <div class="icon-circle bg-info-light text-info"> | |||
| <i class="fas fa-user-plus"></i> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12"> | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| <div class="d-flex align-items-center"> | |||
| <div class="flex-grow-1"> | |||
| <h5 class="text-muted mb-2">Suspended</h5> | |||
| <h3 class="mb-0">23</h3> | |||
| <p class="mb-0 text-danger"> | |||
| <i class="fas fa-arrow-down me-1"></i>4.3% | |||
| </p> | |||
| </div> | |||
| <div class="flex-shrink-0"> | |||
| <div class="icon-circle bg-danger-light text-danger"> | |||
| <i class="fas fa-user-slash"></i> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- User Table --> | |||
| <div class="row"> | |||
| <div class="col-12"> | |||
| <div class="card"> | |||
| <div class="card-header"> | |||
| <div class="d-flex justify-content-between align-items-center"> | |||
| <h5 class="mb-0">User List</h5> | |||
| <div> | |||
| <button class="btn btn-sm btn-outline-secondary me-2" id="exportBtn"> | |||
| <i class="fas fa-download me-1"></i>Export | |||
| </button> | |||
| <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addUserModal"> | |||
| <i class="fas fa-plus me-1"></i>Add User | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="card-body"> | |||
| <!-- Filters --> | |||
| <div class="row mb-3"> | |||
| <div class="col-md-3"> | |||
| <input type="text" class="form-control" id="searchInput" placeholder="Search users..."> | |||
| </div> | |||
| <div class="col-md-2"> | |||
| <select class="form-select" id="roleFilter"> | |||
| <option value="">All Roles</option> | |||
| <option value="admin">Admin</option> | |||
| <option value="manager">Manager</option> | |||
| <option value="user">User</option> | |||
| <option value="guest">Guest</option> | |||
| </select> | |||
| </div> | |||
| <div class="col-md-2"> | |||
| <select class="form-select" id="statusFilter"> | |||
| <option value="">All Status</option> | |||
| <option value="active">Active</option> | |||
| <option value="inactive">Inactive</option> | |||
| <option value="suspended">Suspended</option> | |||
| <option value="pending">Pending</option> | |||
| </select> | |||
| </div> | |||
| <div class="col-md-2"> | |||
| <button class="btn btn-outline-secondary w-100" id="resetFilters"> | |||
| <i class="fas fa-redo me-1"></i>Reset | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <!-- Table --> | |||
| <div class="table-responsive"> | |||
| <table id="userTable" class="table table-hover"> | |||
| <thead> | |||
| <tr> | |||
| <th> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox" id="selectAll"> | |||
| </div> | |||
| </th> | |||
| <th>User</th> | |||
| <th>Email</th> | |||
| <th>Role</th> | |||
| <th>Status</th> | |||
| <th>Joined</th> | |||
| <th>Last Active</th> | |||
| <th>Actions</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=John+Doe&background=667eea&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">John Doe</h6> | |||
| <small class="text-muted">@johndoe</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>john.doe@example.com</td> | |||
| <td><span class="badge bg-danger">Admin</span></td> | |||
| <td><span class="badge bg-success">Active</span></td> | |||
| <td>Jan 15, 2024</td> | |||
| <td>2 hours ago</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=Jane+Smith&background=48bb78&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">Jane Smith</h6> | |||
| <small class="text-muted">@janesmith</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>jane.smith@example.com</td> | |||
| <td><span class="badge bg-primary">Manager</span></td> | |||
| <td><span class="badge bg-success">Active</span></td> | |||
| <td>Feb 28, 2024</td> | |||
| <td>5 minutes ago</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=Mike+Johnson&background=f39c12&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">Mike Johnson</h6> | |||
| <small class="text-muted">@mikej</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>mike.j@example.com</td> | |||
| <td><span class="badge bg-info">User</span></td> | |||
| <td><span class="badge bg-warning text-dark">Pending</span></td> | |||
| <td>Mar 10, 2024</td> | |||
| <td>1 day ago</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=Sarah+Davis&background=e74c3c&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">Sarah Davis</h6> | |||
| <small class="text-muted">@sarahd</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>sarah.davis@example.com</td> | |||
| <td><span class="badge bg-info">User</span></td> | |||
| <td><span class="badge bg-danger">Suspended</span></td> | |||
| <td>Dec 20, 2023</td> | |||
| <td>1 week ago</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox"> | |||
| </div> | |||
| </td> | |||
| <td> | |||
| <div class="d-flex align-items-center"> | |||
| <img src="https://ui-avatars.com/api/?name=Tom+Wilson&background=3498db&color=fff" alt="Avatar" class="rounded-circle me-3" width="40" height="40"> | |||
| <div> | |||
| <h6 class="mb-0">Tom Wilson</h6> | |||
| <small class="text-muted">@tomw</small> | |||
| </div> | |||
| </div> | |||
| </td> | |||
| <td>tom.wilson@example.com</td> | |||
| <td><span class="badge bg-secondary">Guest</span></td> | |||
| <td><span class="badge bg-secondary">Inactive</span></td> | |||
| <td>Nov 5, 2023</td> | |||
| <td>1 month ago</td> | |||
| <td> | |||
| <div class="dropdown"> | |||
| <button class="btn btn-sm btn-light" type="button" data-bs-toggle="dropdown"> | |||
| <i class="fas fa-ellipsis-v"></i> | |||
| </button> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-eye me-2"></i>View</a></li> | |||
| <li><a class="dropdown-item" href="#"><i class="fas fa-edit me-2"></i>Edit</a></li> | |||
| <li><hr class="dropdown-divider"></li> | |||
| <li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete</a></li> | |||
| </ul> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <!-- Pagination --> | |||
| <div class="d-flex justify-content-between align-items-center mt-4"> | |||
| <div> | |||
| Showing 1 to 5 of 2,543 entries | |||
| </div> | |||
| <nav> | |||
| <ul class="pagination mb-0"> | |||
| <li class="page-item disabled"> | |||
| <a class="page-link" href="#" tabindex="-1">Previous</a> | |||
| </li> | |||
| <li class="page-item active"><a class="page-link" href="#">1</a></li> | |||
| <li class="page-item"><a class="page-link" href="#">2</a></li> | |||
| <li class="page-item"><a class="page-link" href="#">3</a></li> | |||
| <li class="page-item"><a class="page-link" href="#">...</a></li> | |||
| <li class="page-item"><a class="page-link" href="#">509</a></li> | |||
| <li class="page-item"> | |||
| <a class="page-link" href="#">Next</a> | |||
| </li> | |||
| </ul> | |||
| </nav> | |||
| </div> | |||
| <!-- Bulk Actions --> | |||
| <div class="bulk-actions mt-3" id="bulkActions" style="display: none;"> | |||
| <div class="d-flex align-items-center"> | |||
| <span class="me-3"><span id="selectedCount">0</span> users selected</span> | |||
| <button class="btn btn-sm btn-outline-primary me-2"> | |||
| <i class="fas fa-envelope me-1"></i>Send Email | |||
| </button> | |||
| <button class="btn btn-sm btn-outline-warning me-2"> | |||
| <i class="fas fa-ban me-1"></i>Suspend | |||
| </button> | |||
| <button class="btn btn-sm btn-outline-danger"> | |||
| <i class="fas fa-trash me-1"></i>Delete | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/footer }} | |||
| </div> | |||
| </div> | |||
| <!-- Add User Modal --> | |||
| <div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title" id="addUserModalLabel">Add New User</h5> | |||
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | |||
| </div> | |||
| <form id="addUserForm"> | |||
| <div class="modal-body"> | |||
| <div class="mb-3"> | |||
| <label for="firstName" class="form-label">First Name</label> | |||
| <input type="text" class="form-control" id="firstName" required> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="lastName" class="form-label">Last Name</label> | |||
| <input type="text" class="form-control" id="lastName" required> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="email" class="form-label">Email</label> | |||
| <input type="email" class="form-control" id="email" required> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="username" class="form-label">Username</label> | |||
| <input type="text" class="form-control" id="username" required> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="role" class="form-label">Role</label> | |||
| <select class="form-select" id="role" required> | |||
| <option value="">Select Role</option> | |||
| <option value="admin">Admin</option> | |||
| <option value="manager">Manager</option> | |||
| <option value="user">User</option> | |||
| <option value="guest">Guest</option> | |||
| </select> | |||
| </div> | |||
| <div class="mb-3"> | |||
| <label for="status" class="form-label">Status</label> | |||
| <select class="form-select" id="status" required> | |||
| <option value="active">Active</option> | |||
| <option value="pending">Pending</option> | |||
| <option value="inactive">Inactive</option> | |||
| </select> | |||
| </div> | |||
| <div class="form-check"> | |||
| <input class="form-check-input" type="checkbox" id="sendInvite" checked> | |||
| <label class="form-check-label" for="sendInvite"> | |||
| Send invitation email to user | |||
| </label> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> | |||
| <button type="submit" class="btn btn-primary">Add User</button> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{> layouts/scripts }} | |||
| <script type="module"> | |||
| import { initializeUserManagement } from '@js/pages/users.js'; | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,194 @@ | |||
| // Settings page styles | |||
| .nav-pills { | |||
| .nav-link { | |||
| color: $gray-700; | |||
| border-radius: 0.375rem; | |||
| padding: 0.75rem 1rem; | |||
| margin-bottom: 0.25rem; | |||
| transition: all 0.3s ease; | |||
| &:hover { | |||
| background-color: $gray-100; | |||
| color: $primary; | |||
| } | |||
| &.active { | |||
| background-color: $primary; | |||
| color: $white; | |||
| i { | |||
| color: $white; | |||
| } | |||
| } | |||
| i { | |||
| width: 1.25rem; | |||
| text-align: center; | |||
| } | |||
| } | |||
| } | |||
| // Card radio buttons for theme selection | |||
| .card-radio { | |||
| position: relative; | |||
| .form-check-input { | |||
| position: absolute; | |||
| opacity: 0; | |||
| &:checked + .form-check-label { | |||
| border-color: $primary; | |||
| background-color: rgba($primary, 0.1); | |||
| i { | |||
| color: $primary; | |||
| } | |||
| } | |||
| } | |||
| .form-check-label { | |||
| cursor: pointer; | |||
| border: 2px solid $gray-300; | |||
| transition: all 0.3s ease; | |||
| &:hover { | |||
| border-color: $gray-400; | |||
| } | |||
| } | |||
| } | |||
| // Color scheme buttons | |||
| .color-scheme-btn { | |||
| position: relative; | |||
| &.active::after { | |||
| content: '\f00c'; | |||
| font-family: 'Font Awesome 6 Free'; | |||
| font-weight: 900; | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| color: white; | |||
| font-size: 0.875rem; | |||
| } | |||
| } | |||
| // Icon circles for integrations | |||
| .icon-circle { | |||
| width: 48px; | |||
| height: 48px; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| border-radius: 50%; | |||
| font-size: 1.25rem; | |||
| &.bg-primary-light { | |||
| background-color: rgba($primary, 0.1); | |||
| color: $primary; | |||
| } | |||
| &.bg-info-light { | |||
| background-color: rgba($info, 0.1); | |||
| color: $info; | |||
| } | |||
| &.bg-success-light { | |||
| background-color: rgba($success, 0.1); | |||
| color: $success; | |||
| } | |||
| &.bg-warning-light { | |||
| background-color: rgba($warning, 0.1); | |||
| color: $warning; | |||
| } | |||
| &.bg-danger-light { | |||
| background-color: rgba($danger, 0.1); | |||
| color: $danger; | |||
| } | |||
| } | |||
| // Settings content spacing | |||
| .tab-content { | |||
| .card { | |||
| .card-header { | |||
| background-color: $white; | |||
| border-bottom: 2px solid $gray-200; | |||
| h5 { | |||
| font-weight: 600; | |||
| color: $gray-800; | |||
| } | |||
| } | |||
| .card-body { | |||
| padding: 2rem; | |||
| } | |||
| } | |||
| } | |||
| // Form styling | |||
| .form-label { | |||
| font-weight: 500; | |||
| color: $gray-700; | |||
| margin-bottom: 0.5rem; | |||
| } | |||
| .form-text { | |||
| font-size: 0.875rem; | |||
| } | |||
| // Session list items | |||
| .list-group-item { | |||
| border-left: none; | |||
| border-right: none; | |||
| &:first-child { | |||
| border-top: none; | |||
| } | |||
| &:last-child { | |||
| border-bottom: none; | |||
| } | |||
| } | |||
| // API key input group | |||
| .input-group { | |||
| .form-control[readonly] { | |||
| background-color: $gray-100; | |||
| font-family: $font-family-monospace; | |||
| font-size: 0.875rem; | |||
| } | |||
| } | |||
| // Responsive adjustments | |||
| @include media-breakpoint-down(lg) { | |||
| .nav-pills { | |||
| display: flex; | |||
| flex-wrap: nowrap; | |||
| overflow-x: auto; | |||
| -webkit-overflow-scrolling: touch; | |||
| margin-bottom: 1rem; | |||
| .nav-link { | |||
| white-space: nowrap; | |||
| margin-bottom: 0; | |||
| margin-right: 0.5rem; | |||
| } | |||
| } | |||
| } | |||
| @include media-breakpoint-down(md) { | |||
| .tab-content { | |||
| .card-body { | |||
| padding: 1.25rem; | |||
| } | |||
| } | |||
| .color-scheme-btn { | |||
| padding: 0.375rem 0.75rem; | |||
| font-size: 0.875rem; | |||
| } | |||
| } | |||
| @ -0,0 +1,245 @@ | |||
| // Timeline page styles | |||
| .timeline { | |||
| position: relative; | |||
| padding-left: 2rem; | |||
| &::before { | |||
| content: ''; | |||
| position: absolute; | |||
| left: 0; | |||
| top: 0; | |||
| bottom: 0; | |||
| width: 2px; | |||
| background-color: $gray-300; | |||
| } | |||
| } | |||
| .timeline-date { | |||
| margin: 2rem 0 1rem; | |||
| position: relative; | |||
| h6 { | |||
| background-color: $white; | |||
| display: inline-block; | |||
| padding-right: 1rem; | |||
| margin-bottom: 0; | |||
| margin-left: -2rem; | |||
| font-weight: 600; | |||
| color: $gray-600; | |||
| } | |||
| } | |||
| .timeline-item { | |||
| position: relative; | |||
| display: flex; | |||
| margin-bottom: 2rem; | |||
| &.new-item { | |||
| opacity: 0; | |||
| transform: translateY(20px); | |||
| &.animate-in { | |||
| animation: fadeInUp 0.5s ease forwards; | |||
| } | |||
| } | |||
| } | |||
| .timeline-marker { | |||
| position: absolute; | |||
| left: -2.5rem; | |||
| top: 0.5rem; | |||
| width: 1rem; | |||
| height: 1rem; | |||
| border-radius: 50%; | |||
| z-index: 1; | |||
| &::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| width: 0.5rem; | |||
| height: 0.5rem; | |||
| background-color: $white; | |||
| border-radius: 50%; | |||
| } | |||
| i { | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| font-size: 0.5rem; | |||
| } | |||
| } | |||
| .timeline-content { | |||
| flex: 1; | |||
| background-color: $white; | |||
| border: 1px solid $gray-200; | |||
| border-radius: 0.5rem; | |||
| padding: 1.25rem; | |||
| box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); | |||
| transition: all 0.3s ease; | |||
| &:hover { | |||
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | |||
| transform: translateY(-2px); | |||
| } | |||
| } | |||
| .timeline-header { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 0.75rem; | |||
| img { | |||
| object-fit: cover; | |||
| } | |||
| h6 { | |||
| margin: 0; | |||
| font-size: 0.9375rem; | |||
| small { | |||
| font-weight: normal; | |||
| } | |||
| } | |||
| .dropdown { | |||
| .btn { | |||
| padding: 0.25rem 0.5rem; | |||
| border: none; | |||
| background-color: transparent; | |||
| &:hover { | |||
| background-color: $gray-100; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .timeline-body { | |||
| font-size: 0.875rem; | |||
| p { | |||
| line-height: 1.6; | |||
| } | |||
| .card { | |||
| border: none; | |||
| margin-top: 0.5rem; | |||
| .card-body { | |||
| padding: 1rem; | |||
| } | |||
| } | |||
| .progress { | |||
| margin: 0.5rem 0; | |||
| } | |||
| .btn-sm { | |||
| font-size: 0.8125rem; | |||
| padding: 0.25rem 0.75rem; | |||
| } | |||
| } | |||
| // Activity type colors | |||
| .activity-filters { | |||
| .form-check-label { | |||
| display: flex; | |||
| align-items: center; | |||
| font-size: 0.875rem; | |||
| i { | |||
| width: 1.25rem; | |||
| } | |||
| } | |||
| } | |||
| // Reply input | |||
| .reply-input { | |||
| .input-group { | |||
| .form-control { | |||
| font-size: 0.875rem; | |||
| } | |||
| .btn { | |||
| font-size: 0.875rem; | |||
| } | |||
| } | |||
| } | |||
| // Animations | |||
| @keyframes fadeInUp { | |||
| to { | |||
| opacity: 1; | |||
| transform: translateY(0); | |||
| } | |||
| } | |||
| // Mobile responsiveness | |||
| @include media-breakpoint-down(md) { | |||
| .timeline { | |||
| padding-left: 1.5rem; | |||
| &::before { | |||
| left: 0; | |||
| } | |||
| } | |||
| .timeline-marker { | |||
| left: -1.75rem; | |||
| width: 0.875rem; | |||
| height: 0.875rem; | |||
| &::after { | |||
| width: 0.375rem; | |||
| height: 0.375rem; | |||
| } | |||
| } | |||
| .timeline-content { | |||
| padding: 1rem; | |||
| } | |||
| .timeline-header { | |||
| flex-wrap: wrap; | |||
| img { | |||
| width: 28px; | |||
| height: 28px; | |||
| } | |||
| h6 { | |||
| font-size: 0.875rem; | |||
| } | |||
| .dropdown { | |||
| margin-left: auto; | |||
| } | |||
| } | |||
| .timeline-date h6 { | |||
| margin-left: -1.5rem; | |||
| font-size: 0.875rem; | |||
| } | |||
| } | |||
| @include media-breakpoint-down(sm) { | |||
| .timeline { | |||
| padding-left: 1rem; | |||
| } | |||
| .timeline-marker { | |||
| left: -1.25rem; | |||
| width: 0.75rem; | |||
| height: 0.75rem; | |||
| } | |||
| .timeline-date h6 { | |||
| margin-left: -1rem; | |||
| } | |||
| } | |||
| @ -0,0 +1,136 @@ | |||
| // User Management page styles | |||
| .icon-circle { | |||
| width: 48px; | |||
| height: 48px; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| border-radius: 50%; | |||
| font-size: 1.25rem; | |||
| &.bg-primary-light { | |||
| background-color: rgba($primary, 0.1); | |||
| } | |||
| &.bg-success-light { | |||
| background-color: rgba($success, 0.1); | |||
| } | |||
| &.bg-info-light { | |||
| background-color: rgba($info, 0.1); | |||
| } | |||
| &.bg-danger-light { | |||
| background-color: rgba($danger, 0.1); | |||
| } | |||
| } | |||
| // User table styles | |||
| #userTable { | |||
| tbody tr { | |||
| transition: all 0.2s ease; | |||
| &.table-active { | |||
| background-color: rgba($primary, 0.05); | |||
| } | |||
| &:hover { | |||
| background-color: $gray-100; | |||
| } | |||
| } | |||
| .form-check-input { | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| // Bulk actions | |||
| .bulk-actions { | |||
| background-color: $light; | |||
| padding: 1rem; | |||
| border-radius: 0.375rem; | |||
| margin-top: 1rem; | |||
| animation: slideUp 0.3s ease; | |||
| } | |||
| @keyframes slideUp { | |||
| from { | |||
| opacity: 0; | |||
| transform: translateY(10px); | |||
| } | |||
| to { | |||
| opacity: 1; | |||
| transform: translateY(0); | |||
| } | |||
| } | |||
| // DataTables customization | |||
| .dataTables_wrapper { | |||
| .dataTables_length { | |||
| label { | |||
| font-weight: normal; | |||
| color: $text-muted; | |||
| } | |||
| select { | |||
| @extend .form-select; | |||
| width: auto; | |||
| display: inline-block; | |||
| margin: 0 0.5rem; | |||
| } | |||
| } | |||
| .dataTables_info { | |||
| color: $text-muted; | |||
| font-size: 0.875rem; | |||
| } | |||
| .dataTables_paginate { | |||
| .pagination { | |||
| margin: 0; | |||
| } | |||
| .page-link { | |||
| padding: 0.375rem 0.75rem; | |||
| font-size: 0.875rem; | |||
| &:hover { | |||
| background-color: $gray-100; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // Responsive adjustments | |||
| @include media-breakpoint-down(lg) { | |||
| .bulk-actions { | |||
| .btn { | |||
| font-size: 0.875rem; | |||
| padding: 0.375rem 0.75rem; | |||
| } | |||
| } | |||
| } | |||
| @include media-breakpoint-down(md) { | |||
| .icon-circle { | |||
| width: 40px; | |||
| height: 40px; | |||
| font-size: 1rem; | |||
| } | |||
| .bulk-actions { | |||
| .d-flex { | |||
| flex-direction: column; | |||
| align-items: stretch !important; | |||
| span { | |||
| margin-bottom: 0.5rem; | |||
| text-align: center; | |||
| } | |||
| .btn { | |||
| margin: 0.25rem 0 !important; | |||
| } | |||
| } | |||
| } | |||
| } | |||