diff --git a/Views/Vehicle/Gas/_GasModal.cshtml b/Views/Vehicle/Gas/_GasModal.cshtml index d28e2d0..d45e6a8 100644 --- a/Views/Vehicle/Gas/_GasModal.cshtml +++ b/Views/Vehicle/Gas/_GasModal.cshtml @@ -56,7 +56,7 @@
-
+
@@ -136,7 +136,7 @@ @await Html.PartialAsync("_ExtraField", Model.GasRecord.ExtraFields)
-
+
diff --git a/wwwroot/css/site.css b/wwwroot/css/site.css index 4789422..cb6fd78 100644 --- a/wwwroot/css/site.css +++ b/wwwroot/css/site.css @@ -98,12 +98,292 @@ html { aspect-ratio: 1/1; border-style: dashed; border-width: 2px; - page-break-after: always; } - .recordSticker { - height: 100%; - page-break-after: always; +} + +/* Mobile-First Modal Improvements */ +@media (max-width: 768px) { + /* Full-screen modal on mobile */ + .modal-dialog { + margin: 0; + width: 100vw; + height: 100vh; + max-width: none; + max-height: none; } + + .modal-content { + height: 100vh; + border-radius: 0; + border: none; + display: flex; + flex-direction: column; + } + + /* Slide-up animation for mobile modals */ + .modal.fade .modal-dialog { + transform: translateY(100%); + transition: transform 0.3s ease-out; + } + + .modal.show .modal-dialog { + transform: translateY(0); + } + + .modal-body { + flex: 1; + overflow-y: auto; + padding: 1rem; + } + + .modal-header { + flex-shrink: 0; + padding: 1rem; + border-bottom: 1px solid #dee2e6; + } + + .modal-footer { + flex-shrink: 0; + padding: 1rem; + border-top: 1px solid #dee2e6; + } + + /* Touch-friendly targets - minimum 44px */ + .btn { + min-height: 44px; + min-width: 44px; + padding: 0.75rem 1rem; + } + + .btn-sm { + min-height: 44px; + min-width: 44px; + padding: 0.5rem 0.75rem; + } + + .btn-close { + width: 44px; + height: 44px; + padding: 0.75rem; + } + + .form-control { + min-height: 44px; + padding: 0.75rem; + font-size: 16px; /* Prevents zoom on iOS */ + } + + .form-select { + min-height: 44px; + padding: 0.75rem; + font-size: 16px; + } + + .form-check-input { + width: 1.5rem; + height: 1.5rem; + margin-top: 0.25rem; + } + + .form-check-label { + min-height: 44px; + display: flex; + align-items: center; + padding-left: 0.5rem; + } + + .input-group-text { + min-height: 44px; + display: flex; + align-items: center; + justify-content: center; + } + + /* Single column layout on mobile */ + .col-md-6 { + flex: 0 0 100% !important; + max-width: 100% !important; + } + + /* Mobile form spacing */ + .form-group { + margin-bottom: 1.5rem; + } + + .form-group label { + margin-bottom: 0.75rem; + font-weight: 500; + } + + .form-group input, + .form-group select, + .form-group textarea { + margin-bottom: 1rem; + } + + /* Simplified header layout on mobile */ + .modal-header .d-flex { + flex-direction: column; + align-items: stretch !important; + } + + .modal-header .modal-title { + margin-bottom: 1rem; + text-align: center; + } + + .modal-header .form-check { + justify-content: center; + margin-bottom: 0.5rem; + } + + .modal-header .btn-close { + position: absolute; + top: 1rem; + right: 1rem; + margin: 0; + } + + /* Mobile-friendly footer buttons */ + .modal-footer { + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + .modal-footer .btn { + width: 100%; + margin: 0; + } + + .modal-footer .btn:first-child { + order: 2; /* Move delete button to bottom */ + } + + .modal-footer .btn:last-child { + order: 1; /* Move primary action to top */ + } + + .modal-footer .btn:nth-child(2) { + order: 3; /* Cancel button in middle */ + } + + /* Bottom sheet pattern for mobile */ + .modal-content { + position: relative; + } + + .modal-content::before { + content: ''; + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + width: 40px; + height: 4px; + background-color: #dee2e6; + border-radius: 2px; + z-index: 1000; + cursor: grab; + } + + .modal-content::before:active { + cursor: grabbing; + } + + /* Touch feedback for interactive elements */ + .btn:active, + .form-control:active, + .form-check-input:active { + transform: scale(0.98); + transition: transform 0.1s ease; + } + + /* Improved focus states for mobile */ + .form-control:focus, + .form-select:focus { + border-color: #86b7fe; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + outline: none; + } + + /* Mobile tag input styling */ + .mobile-tag-input .bootstrap-tagsinput { + border-radius: 6px; + border: 1px solid #ced4da; + } + + .mobile-tag-input .bootstrap-tagsinput:focus-within { + border-color: #86b7fe; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + } + + .mobile-tag-input .tag { + background-color: #0d6efd; + color: white; + border-radius: 4px; + margin: 2px; + padding: 4px 8px; + font-size: 14px; + } + + /* Enhanced mobile breakpoints for better responsiveness */ + .mobile-single-column { + width: 100% !important; + max-width: 100% !important; + flex: 0 0 100% !important; + } + + /* Improved mobile modal animations */ + .modal.fade .modal-dialog { + transform: translateY(100%); + transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + } + + .modal.show .modal-dialog { + transform: translateY(0); + } + + /* Mobile keyboard adjustments */ + .modal-body { + /* Prevent viewport scaling when keyboard appears */ + position: relative; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + + /* Accessibility improvements for mobile */ + .form-label { + font-weight: 600; + color: #212529; + margin-bottom: 0.5rem; + } + + /* Error state improvements for mobile */ + .is-invalid { + border-color: #dc3545 !important; + border-width: 2px !important; + } + + .is-invalid:focus { + box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25) !important; + } + + /* Success state for mobile */ + .is-valid { + border-color: #198754 !important; + border-width: 2px !important; + } + + .is-valid:focus { + box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25) !important; + } +} + +.recordSticker { + height: 100%; + page-break-after: always; +} .stickerNote { height:100%; } diff --git a/wwwroot/js/gasrecord.js b/wwwroot/js/gasrecord.js index 44677f7..83ca779 100644 --- a/wwwroot/js/gasrecord.js +++ b/wwwroot/js/gasrecord.js @@ -1,11 +1,133 @@ +// Mobile detection utility +function isMobileDevice() { + return window.matchMedia("(max-width: 768px)").matches || + /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); +} + +// Initialize form inputs based on device type +function initializeFormInputs() { + if (isMobileDevice()) { + // Convert date input to native HTML5 on mobile + var dateInput = $('#gasRecordDate'); + if (dateInput.length) { + dateInput.attr('type', 'date'); + dateInput.removeClass('datepicker'); // Remove Bootstrap datepicker class + } + + // Default to simple mode on mobile + var modeToggle = $('#fuelEntryModeToggle'); + if (modeToggle.length && !modeToggle.is(':checked')) { + modeToggle.prop('checked', true); + toggleFuelEntryMode(true); + } + + // Initialize mobile-friendly tag selector + initMobileTagSelector($("#gasRecordTag")); + } else { + // Use Bootstrap datepicker on desktop + initDatePicker($('#gasRecordDate')); + + // Use standard tag selector on desktop + initTagSelector($("#gasRecordTag")); + } +} + +// Mobile-optimized tag selector +function initMobileTagSelector(input) { + if (input.length) { + // Initialize with mobile-friendly options + input.tagsinput({ + maxTags: 5, // Limit tags on mobile + trimValue: true, + confirmKeys: [13, 44, 32], // Enter, comma, space + focusClass: 'focus', + freeInput: true + }); + + // Add mobile-specific styling + input.parent().addClass('mobile-tag-input'); + + // Increase touch target size for mobile + input.parent().find('.bootstrap-tagsinput').css({ + 'min-height': '44px', + 'padding': '8px', + 'font-size': '16px' // Prevent zoom on iOS + }); + + // Style the input within tags + input.parent().find('.bootstrap-tagsinput input').css({ + 'font-size': '16px', + 'min-height': '30px' + }); + } +} + +// Swipe to dismiss functionality for mobile modals +function initSwipeToDismiss() { + if (!isMobileDevice()) return; + + var modal = $('#gasRecordModal'); + var modalContent = modal.find('.modal-content'); + var startY = 0; + var currentY = 0; + var isDragging = false; + var threshold = 100; // Minimum swipe distance to dismiss + + // Touch start + modalContent.on('touchstart', function(e) { + startY = e.originalEvent.touches[0].clientY; + isDragging = true; + modalContent.css('transition', 'none'); + }); + + // Touch move + modalContent.on('touchmove', function(e) { + if (!isDragging) return; + + currentY = e.originalEvent.touches[0].clientY; + var deltaY = currentY - startY; + + // Only allow downward swipes + if (deltaY > 0) { + modalContent.css('transform', `translateY(${deltaY}px)`); + } + }); + + // Touch end + modalContent.on('touchend', function(e) { + if (!isDragging) return; + + isDragging = false; + var deltaY = currentY - startY; + + modalContent.css('transition', 'transform 0.3s ease-out'); + + if (deltaY > threshold) { + // Dismiss modal + modalContent.css('transform', 'translateY(100%)'); + setTimeout(function() { + hideAddGasRecordModal(); + modalContent.css('transform', ''); + }, 300); + } else { + // Snap back + modalContent.css('transform', ''); + } + }); +} + function showAddGasRecordModal() { $.get(`/Vehicle/GetAddGasRecordPartialView?vehicleId=${GetVehicleId().vehicleId}`, function (data) { if (data) { $("#gasRecordModalContent").html(data); - //initiate datepicker - initDatePicker($('#gasRecordDate')); - initTagSelector($("#gasRecordTag")); + + // Initialize inputs based on device type + initializeFormInputs(); + $('#gasRecordModal').modal('show'); + + // Initialize swipe to dismiss for mobile + initSwipeToDismiss(); } }); } @@ -25,11 +147,16 @@ function showEditGasRecordModal(gasRecordId, nocache) { $.get(`/Vehicle/GetGasRecordForEditById?gasRecordId=${gasRecordId}`, function (data) { if (data) { $("#gasRecordModalContent").html(data); - //initiate datepicker - initDatePicker($('#gasRecordDate')); - initTagSelector($("#gasRecordTag")); + + // Initialize inputs based on device type + initializeFormInputs(); + $('#gasRecordModal').modal('show'); bindModalInputChanges('gasRecordModal'); + + // Initialize swipe to dismiss for mobile + initSwipeToDismiss(); + $('#gasRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () { if (getGlobalConfig().useMarkDown) { toggleMarkDownOverlay("gasRecordNotes"); @@ -441,10 +568,14 @@ function editMultipleGasRecords(ids) { $.post('/Vehicle/GetGasRecordsEditModal', { recordIds: ids }, function (data) { if (data) { $("#gasRecordModalContent").html(data); - //initiate datepicker - initDatePicker($('#gasRecordDate')); - initTagSelector($("#gasRecordTag")); + + // Initialize inputs based on device type + initializeFormInputs(); + $('#gasRecordModal').modal('show'); + + // Initialize swipe to dismiss for mobile + initSwipeToDismiss(); } }); }