diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 0394dbb..c1e90e2 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -3849,3 +3849,347 @@ table td:nth-child(4) { min-height: 200px; } } + +/* ===== COMPREHENSIVE MOBILE IMPROVEMENTS ===== */ +@media (max-width: 768px) { + /* Prevent iOS zoom on input focus */ + input, select, textarea { + font-size: 16px !important; + } + + /* Make sidebar a slide-out drawer */ + .dashboard-sidebar { + position: fixed; + top: 0; + left: -100%; + width: 85%; + max-width: 320px; + height: 100vh; + max-height: 100vh; + z-index: 1000; + background: var(--bg-primary); + transition: left 0.3s ease; + padding: 1rem; + overflow-y: auto; + box-shadow: 4px 0 20px rgba(0, 0, 0, 0.5); + } + + .dashboard-sidebar.mobile-open { + left: 0; + } + + /* Mobile sidebar overlay */ + .mobile-sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + z-index: 999; + } + + .mobile-sidebar-overlay.active { + display: block; + } + + /* Mobile filter toggle button */ + .mobile-filter-toggle { + display: flex !important; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + background: var(--bg-secondary); + border: 2px solid var(--terminal-green); + color: var(--terminal-green); + font-family: var(--font-mono); + font-size: 0.9rem; + cursor: pointer; + margin-bottom: 1rem; + width: 100%; + } + + .mobile-filter-toggle:active { + background: var(--terminal-green); + color: var(--bg-primary); + } + + /* Close button in mobile sidebar */ + .mobile-sidebar-close { + display: block !important; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 44px; + height: 44px; + background: transparent; + border: 2px solid var(--terminal-green); + color: var(--terminal-green); + font-size: 1.5rem; + cursor: pointer; + z-index: 10; + } + + /* Better header on mobile */ + .user-header { + flex-wrap: wrap; + padding: 0.5rem; + gap: 0.5rem; + } + + .header-left { + flex: 1; + min-width: 0; + } + + .header-right { + width: 100%; + justify-content: space-between; + flex-wrap: wrap; + gap: 0.5rem; + } + + .app-title { + font-size: 1rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + /* Compact header buttons */ + .header-btn, .settings-btn, .new-ticket-btn { + padding: 0.5rem 0.75rem; + font-size: 0.85rem; + min-height: 44px; + } + + /* Table wrapper for horizontal scroll */ + .table-wrapper { + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + margin-bottom: 1rem; + } + + .table-wrapper table { + min-width: 600px; + } + + /* Larger touch targets for table rows */ + table td, table th { + padding: 12px 8px; + } + + table td a { + display: inline-block; + padding: 4px 0; + } + + /* Stats widgets - 2 per row */ + .stats-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.5rem; + } + + .stat-card { + padding: 0.75rem; + min-height: 70px; + } + + .stat-value { + font-size: 1.5rem; + } + + .stat-label { + font-size: 0.7rem; + } + + /* Better toolbar on mobile */ + .toolbar { + flex-direction: column; + gap: 0.75rem; + } + + .toolbar-search { + width: 100%; + order: -1; + } + + .toolbar-actions { + width: 100%; + flex-wrap: wrap; + gap: 0.5rem; + } + + .toolbar-actions .btn { + flex: 1; + min-width: 100px; + text-align: center; + } + + /* Full width modals on mobile */ + .modal-overlay { + padding: 0.5rem; + align-items: flex-start; + padding-top: 2rem; + } + + .modal-content { + width: 100% !important; + max-width: 100% !important; + max-height: calc(100vh - 4rem); + margin: 0; + } + + .modal-body { + max-height: calc(100vh - 12rem); + overflow-y: auto; + } + + /* Better form elements */ + .modal-body input, + .modal-body select, + .modal-body textarea { + width: 100%; + min-height: 44px; + padding: 0.75rem; + margin-bottom: 0.75rem; + } + + .modal-footer { + flex-direction: column; + gap: 0.5rem; + } + + .modal-footer button { + width: 100%; + min-height: 48px; + } + + /* Settings modal improvements */ + .settings-content { + width: 100% !important; + max-width: 100% !important; + height: auto; + max-height: 90vh; + } + + /* Better pagination */ + .pagination { + flex-wrap: wrap; + justify-content: center; + gap: 0.25rem; + } + + .pagination button, + .pagination a { + min-width: 44px; + min-height: 44px; + padding: 0.5rem; + } + + /* Admin dropdown improvements */ + .admin-dropdown { + position: static; + } + + .admin-dropdown-content { + position: fixed; + left: 0; + right: 0; + top: auto; + bottom: 0; + width: 100%; + max-height: 50vh; + overflow-y: auto; + border-radius: 0; + } + + .admin-dropdown-content a { + padding: 1rem; + min-height: 48px; + } + + /* Bulk action bar on mobile */ + .bulk-action-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 100; + flex-wrap: wrap; + padding: 0.75rem; + gap: 0.5rem; + max-height: 50vh; + overflow-y: auto; + } + + .bulk-action-bar button { + flex: 1 1 calc(50% - 0.5rem); + min-height: 44px; + } + + /* View toggle more accessible */ + .view-toggle { + margin-bottom: 1rem; + } + + .view-btn { + flex: 1; + min-height: 48px; + } + + /* Better quick actions */ + .quick-actions { + display: flex; + gap: 0.25rem; + } + + .quick-action-btn { + min-width: 36px; + min-height: 36px; + padding: 0.25rem; + } +} + +/* Extra small screens */ +@media (max-width: 480px) { + .app-title { + font-size: 0.9rem; + } + + .stats-row { + grid-template-columns: 1fr; + } + + .stat-card { + padding: 1rem; + } + + /* Hide certain table columns */ + table th:nth-child(4), + table td:nth-child(4), + table th:nth-child(6), + table td:nth-child(6) { + display: none; + } + + .toolbar-actions .btn span { + display: none; + } + + .header-right { + justify-content: flex-end; + } + + .new-ticket-btn span { + display: none; + } + + .new-ticket-btn::before { + content: '+'; + font-size: 1.2rem; + } +} diff --git a/assets/css/ticket.css b/assets/css/ticket.css index 61ca2e5..981f7c1 100644 --- a/assets/css/ticket.css +++ b/assets/css/ticket.css @@ -1612,3 +1612,208 @@ body.dark-mode .editable { margin-left: 0; } } + +/* ===== COMPREHENSIVE TICKET PAGE MOBILE STYLES ===== */ +@media (max-width: 768px) { + /* Prevent iOS zoom on input focus */ + input, select, textarea { + font-size: 16px !important; + } + + /* Better ticket header layout */ + .ticket-header { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + .ticket-header-left { + width: 100%; + } + + .ticket-header-right { + width: 100%; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + } + + .ticket-header-right .btn { + flex: 1; + min-width: 100px; + text-align: center; + min-height: 44px; + } + + /* Stack ticket metadata vertically */ + .ticket-meta-grid { + display: flex; + flex-direction: column; + gap: 0.75rem; + } + + .ticket-meta-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; + border-bottom: 1px solid var(--border-color); + } + + .ticket-meta-label { + font-weight: bold; + min-width: 100px; + } + + .ticket-meta-value { + text-align: right; + flex: 1; + } + + /* Better select dropdowns */ + .ticket-meta-item select { + min-height: 44px; + padding: 0.5rem; + max-width: 60%; + } + + /* Full width title on mobile */ + .ticket-title { + font-size: 1.1rem; + word-break: break-word; + } + + /* Better tabs on mobile */ + .ticket-tabs { + display: flex; + flex-wrap: nowrap; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + gap: 0; + } + + .tab-btn { + flex: 0 0 auto; + min-width: 100px; + padding: 0.75rem 1rem; + white-space: nowrap; + min-height: 48px; + } + + /* Description and comment sections */ + .ticket-description, + .comments-section { + padding: 1rem; + } + + /* Better comment input */ + .comment-form textarea { + min-height: 120px; + font-size: 16px; + } + + .comment-form .btn { + width: 100%; + min-height: 48px; + margin-top: 0.5rem; + } + + /* Markdown toolbar scroll */ + .markdown-toolbar { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + flex-wrap: nowrap; + } + + .markdown-toolbar button { + min-width: 44px; + min-height: 44px; + flex-shrink: 0; + } + + /* Comment styling */ + .comment { + padding: 1rem; + } + + .comment-header { + flex-direction: column; + align-items: flex-start; + gap: 0.25rem; + } + + /* Dependencies list */ + .dependency-item { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + + .dependency-item .btn { + align-self: flex-end; + } + + /* Attachments grid */ + .attachments-grid { + grid-template-columns: 1fr; + } + + .attachment-item { + padding: 1rem; + } + + /* Activity timeline */ + .timeline-item { + padding: 0.75rem; + } + + .timeline-content { + font-size: 0.9rem; + } + + /* Status select */ + #statusSelect { + width: 100%; + min-height: 48px; + } + + /* Back link more prominent */ + .back-link { + display: inline-flex; + align-items: center; + padding: 0.5rem 1rem; + min-height: 44px; + } +} + +/* Extra small screens for ticket page */ +@media (max-width: 480px) { + .ticket-id { + font-size: 0.9rem; + } + + .ticket-title { + font-size: 1rem; + } + + .tab-btn { + font-size: 0.85rem; + padding: 0.5rem 0.75rem; + } + + .ticket-meta-item { + flex-direction: column; + align-items: flex-start; + gap: 0.25rem; + } + + .ticket-meta-value { + text-align: left; + width: 100%; + } + + .ticket-meta-item select { + max-width: 100%; + width: 100%; + } +} diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js index 6ac14ef..cfec4ad 100644 --- a/assets/js/dashboard.js +++ b/assets/js/dashboard.js @@ -27,6 +27,71 @@ function toggleSidebar() { } } +/** + * Mobile sidebar functions + */ +function openMobileSidebar() { + const sidebar = document.getElementById('dashboardSidebar'); + const overlay = document.getElementById('mobileSidebarOverlay'); + if (sidebar) { + sidebar.classList.add('mobile-open'); + } + if (overlay) { + overlay.classList.add('active'); + } + document.body.style.overflow = 'hidden'; +} + +function closeMobileSidebar() { + const sidebar = document.getElementById('dashboardSidebar'); + const overlay = document.getElementById('mobileSidebarOverlay'); + if (sidebar) { + sidebar.classList.remove('mobile-open'); + } + if (overlay) { + overlay.classList.remove('active'); + } + document.body.style.overflow = ''; +} + +// Initialize mobile sidebar elements +function initMobileSidebar() { + const sidebar = document.getElementById('dashboardSidebar'); + const dashboardMain = document.querySelector('.dashboard-main'); + + if (!sidebar || !dashboardMain) return; + + // Create overlay if it doesn't exist + if (!document.getElementById('mobileSidebarOverlay')) { + const overlay = document.createElement('div'); + overlay.id = 'mobileSidebarOverlay'; + overlay.className = 'mobile-sidebar-overlay'; + overlay.onclick = closeMobileSidebar; + document.body.appendChild(overlay); + } + + // Create mobile filter toggle button if it doesn't exist + if (!document.getElementById('mobileFilterToggle')) { + const toggleBtn = document.createElement('button'); + toggleBtn.id = 'mobileFilterToggle'; + toggleBtn.className = 'mobile-filter-toggle'; + toggleBtn.innerHTML = ' Filters & Options'; + toggleBtn.onclick = openMobileSidebar; + toggleBtn.style.display = 'none'; // Hidden by default, shown via CSS media query + dashboardMain.insertBefore(toggleBtn, dashboardMain.firstChild); + } + + // Create close button inside sidebar if it doesn't exist + if (!sidebar.querySelector('.mobile-sidebar-close')) { + const closeBtn = document.createElement('button'); + closeBtn.className = 'mobile-sidebar-close'; + closeBtn.innerHTML = '×'; + closeBtn.onclick = closeMobileSidebar; + closeBtn.style.display = 'none'; // Hidden by default, shown via CSS media query + sidebar.insertBefore(closeBtn, sidebar.firstChild); + } +} + // Restore sidebar state on page load document.addEventListener('DOMContentLoaded', function() { const savedState = localStorage.getItem('sidebarCollapsed'); @@ -40,6 +105,9 @@ document.addEventListener('DOMContentLoaded', function() { // Main initialization document.addEventListener('DOMContentLoaded', function() { + // Initialize mobile sidebar for dashboard + initMobileSidebar(); + // Check if we're on the dashboard page const hasTable = document.querySelector('table'); const isTicketPage = window.location.pathname.includes('/ticket/') || diff --git a/views/DashboardView.php b/views/DashboardView.php index 78a4cf0..0ea65b6 100644 --- a/views/DashboardView.php +++ b/views/DashboardView.php @@ -357,6 +357,7 @@ +
@@ -445,6 +446,7 @@ ?>
+