diff --git a/static/css/custom_v2.css b/static/css/custom_v2.css index cf2c6d7..440a5f1 100644 --- a/static/css/custom_v2.css +++ b/static/css/custom_v2.css @@ -26,10 +26,11 @@ h1, h2, h3, h4, h5, h6 { flex-direction: row !important; align-items: center !important; justify-content: space-between !important; + flex-wrap: nowrap !important; /* Ensure they stay on one line */ gap: 2px !important; width: 100% !important; box-sizing: border-box !important; - overflow: hidden !important; /* Prevent wrapping overflow */ + /* overflow: hidden; Removed to allow dropdown list to show */ } /* Hide any stray shortcuts that might have survived JS cleanup */ diff --git a/staticfiles/css/custom_v2.css b/staticfiles/css/custom_v2.css index bf57386..440a5f1 100644 --- a/staticfiles/css/custom_v2.css +++ b/staticfiles/css/custom_v2.css @@ -20,6 +20,63 @@ h1, h2, h3, h4, h5, h6 { font-weight: 700; } +/* --- Masar Date Filter Row (Dynamic Wrapper) --- */ +.masar-date-filter-row { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + justify-content: space-between !important; + flex-wrap: nowrap !important; /* Ensure they stay on one line */ + gap: 2px !important; + width: 100% !important; + box-sizing: border-box !important; + /* overflow: hidden; Removed to allow dropdown list to show */ +} + +/* Hide any stray shortcuts that might have survived JS cleanup */ +.masar-date-filter-row .datetimeshortcuts { + display: none !important; +} + +.masar-date-filter-row select { + width: 32% !important; + min-width: 0 !important; + font-size: 11px !important; + padding: 0 2px !important; + height: 28px !important; + line-height: 1 !important; + box-sizing: border-box !important; + margin: 0 !important; +} + +.masar-date-filter-row input { + width: 33% !important; + min-width: 0 !important; + font-size: 11px !important; + padding: 0 2px !important; + height: 28px !important; + margin: 0 !important; + box-sizing: border-box !important; +} + +/* Specific fix for date inputs to ensure they look clean */ +.masar-date-filter-row input[type="date"] { + -webkit-appearance: none; /* Remove some browser defaults */ + appearance: none; + line-height: 28px; +} +.masar-date-filter-row input[type="date"]::-webkit-inner-spin-button, +.masar-date-filter-row input[type="date"]::-webkit-calendar-picker-indicator { + /* Make the calendar icon smaller and fit */ + width: 12px; + height: 12px; + margin: 0; + padding: 0; + opacity: 0.6; +} + + +/* --- Other Admin Tweaks --- */ .hero-section { background: linear-gradient(135deg, var(--primary-dark) 0%, #2D2D30 100%); padding: 100px 0; @@ -28,18 +85,6 @@ h1, h2, h3, h4, h5, h6 { overflow: hidden; } -.hero-section::before { - content: ''; - position: absolute; - top: -50px; - right: -50px; - width: 200px; - height: 200px; - background: var(--accent-orange); - filter: blur(80px); - opacity: 0.2; -} - .glass-card { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(15px); @@ -65,46 +110,6 @@ h1, h2, h3, h4, h5, h6 { color: white; } -.tracking-input { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - color: white; - border-radius: 12px 0 0 12px; - padding: 15px 20px; -} - -.tracking-input:focus { - background: rgba(255, 255, 255, 0.15); - border-color: var(--accent-orange); - box-shadow: none; - color: white; -} - -.feature-icon { - width: 60px; - height: 60px; - background: var(--accent-orange); - border-radius: 15px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 20px; - color: white; - font-size: 24px; -} - -.parcel-status-badge { - padding: 8px 16px; - border-radius: 50px; - font-size: 14px; - font-weight: 600; -} - -.status-pending { background: #FFE8CC; color: #D9480F; } -.status-picked_up { background: #E3FAFC; color: #0B7285; } -.status-in_transit { background: #E7F5FF; color: #1864AB; } -.status-delivered { background: #EBFBEE; color: #2B8A3E; } - /* Chat Widget */ #masar-chat-widget { position: fixed; @@ -132,10 +137,6 @@ h1, h2, h3, h4, h5, h6 { transition: transform 0.2s; } -#masar-chat-toggle:hover { - transform: scale(1.05); -} - /* RTL Support for Chat */ [dir="rtl"] #masar-chat-widget { right: auto; @@ -146,146 +147,14 @@ h1, h2, h3, h4, h5, h6 { left: 20px; } -.typing-dots span { - display: inline-block; - width: 8px; - height: 8px; - background-color: #adb5bd; - border-radius: 50%; - margin: 0 2px; - animation: typing 1s infinite; -} -.typing-dots span:nth-child(2) { animation-delay: 0.2s; } -.typing-dots span:nth-child(3) { animation-delay: 0.4s; } - -@keyframes typing { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-5px); } -} - -/* --- Admin Panel Customizations --- */ - -/* Fix: Prevent accidental file dialog open when clicking labels for logos/favicons */ -body.model-platformprofile label[for="id_logo"], -body.model-platformprofile label[for="id_favicon"], -body.model-platformprofile label[for="id_admin_panel_logo"] { - pointer-events: none; - cursor: default; -} - -/* Improve Admin Form Spacing */ -.form-row { - padding: 15px 10px; - border-bottom: 1px solid #f0f0f0; -} - -/* --- Fix Admin Search Box & Filter Layout --- */ - -/* Target the search form container in Jazzmin/AdminLTE */ -#changelist-search .input-group { - display: flex !important; - flex-wrap: nowrap !important; - max-width: 300px !important; -} - -/* Make the input field take available space */ -#changelist-search input[type="text"] { - flex-grow: 1 !important; - width: auto !important; -} - -/* Ensure the button stays inline */ -#changelist-search button[type="submit"], -#changelist-search .btn { - white-space: nowrap !important; -} - -/* Force filters to sit nicely in a row */ -.filter-wrapper .form-group, -.filter-wrapper select, -.filter-wrapper .select2-container { - margin-bottom: 0 !important; - display: inline-block !important; - width: auto !important; -} - -/* --- Date Range Filter Styling (Compact & Horizontal) --- */ - -/* The dropdown we injected */ -.admin-date-dropdown { - width: auto !important; - min-width: 120px; - display: inline-block !important; - margin-bottom: 5px; /* Spacing if it wraps */ - margin-left: 5px; /* Spacing for RTL (since we use float/flex) */ -} - -/* The container of inputs (From/To) */ -.admindatefilter .controls.date-filter-controls { - display: inline-flex !important; - align-items: center !important; - flex-wrap: nowrap !important; - gap: 5px; -} - -/* The actual date inputs */ -.admindatefilter .date-input { - width: 90px !important; /* Force small width */ - padding: 0.25rem 0.5rem; - font-size: 0.875rem; -} - -/* Ensure the whole filter block flows nicely if possible */ -.admindatefilter { - display: block; /* Default block in sidebar */ -} - -/* If filters are in a horizontal top bar (Jazzmin tweaks), ensure they flow */ -.filter-wrapper .admindatefilter { - display: inline-block !important; - vertical-align: top; -} - -/* --- Search Box RTL/LTR Border Radius Handling --- */ - -/* LTR (Default) */ -.masar-search-input { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.masar-search-btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -/* RTL Override for Search Box */ -[dir="rtl"] .masar-search-input { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; -} -[dir="rtl"] .masar-search-btn { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} - /* --- Admin Panel RTL Sidebar Override (Agresive) --- */ - -/* We use [dir="rtl"] selector which should be on html tag */ - @media (min-width: 992px) { - /* Main Sidebar */ [dir="rtl"] .main-sidebar { left: auto !important; right: 0 !important; border-right: none !important; border-left: 1px solid rgba(0,0,0,0.1) !important; } - - /* Content Wrapper & Headers */ [dir="rtl"] .content-wrapper, [dir="rtl"] .main-header, [dir="rtl"] .main-footer { @@ -293,22 +162,17 @@ body.model-platformprofile label[for="id_admin_panel_logo"] { margin-right: 250px !important; transition: margin-right .3s ease-in-out !important; } - - /* Collapsed Sidebar State */ [dir="rtl"].sidebar-collapse .main-sidebar { margin-left: 0 !important; - margin-right: 0 !important; /* AdminLTE collapses by width, not margin usually, but ensure position matches */ + margin-right: 0 !important; width: 4.6rem !important; } - [dir="rtl"].sidebar-collapse .content-wrapper, [dir="rtl"].sidebar-collapse .main-header, [dir="rtl"].sidebar-collapse .main-footer { margin-left: 0 !important; margin-right: 4.6rem !important; } - - /* Fix Brand Logo Area */ [dir="rtl"] .brand-link { float: right !important; width: 100% !important; @@ -319,63 +183,41 @@ body.model-platformprofile label[for="id_admin_panel_logo"] { margin-right: 0.8rem !important; margin-left: 0.5rem !important; } - - /* Navbar alignment */ [dir="rtl"] .navbar-nav { - flex-direction: row; /* bootstrap default, but ensure items flow right to left visually */ + flex-direction: row; } [dir="rtl"] .navbar-nav .nav-item { float: right; } - - /* Right side navbar items (user menu etc) should be on left now? - In RTL, "ml-auto" (margin-left: auto) pushes items to the LEFT. - Jazzmin uses ml-auto for the user menu. - In RTL, we want them on the LEFT. - Bootstrap 4 RTL support usually flips ml-auto to mr-auto, - but if we are running LTR bootstrap in RTL mode, ml-auto pushes to Right. - Wait, ml-auto = margin-left: auto. In LTR, this pushes to Right. - In RTL, we want these items on the LEFT. So we need margin-right: auto. - */ - [dir="rtl"] .ml-auto { margin-left: 0 !important; margin-right: auto !important; } - [dir="rtl"] .mr-auto { margin-right: 0 !important; margin-left: auto !important; } } - -/* Sidebar Navigation Items RTL */ [dir="rtl"] .nav-sidebar .nav-item > .nav-link { display: flex !important; - flex-direction: row !important; /* Standard flow: RTL = Start(Right) to End(Left) */ + flex-direction: row !important; align-items: center !important; } - [dir="rtl"] .nav-sidebar .nav-icon { margin-left: 0.5rem !important; margin-right: 0 !important; } - [dir="rtl"] .nav-sidebar .nav-link p { display: inline-block !important; margin-right: 0 !important; text-align: right !important; width: 100% !important; } - -/* Fix sidebar angle icon (arrow) */ [dir="rtl"] .nav-sidebar .nav-icon.fa-angle-left { transform: rotate(180deg); margin-left: 0 !important; - margin-right: auto !important; /* Push to the far left */ + margin-right: auto !important; } - -/* General Utils */ [dir="rtl"] .float-right { float: left !important; } diff --git a/staticfiles/js/admin_date_range_dropdown.js b/staticfiles/js/admin_date_range_dropdown.js index 556d0ec..210290d 100644 --- a/staticfiles/js/admin_date_range_dropdown.js +++ b/staticfiles/js/admin_date_range_dropdown.js @@ -1,119 +1,130 @@ (function($) { - $(document).ready(function() { - // Helper to format date as YYYY-MM-DD - function formatDate(d) { - var year = d.getFullYear(); - var month = ('0' + (d.getMonth() + 1)).slice(-2); - var day = ('0' + d.getDate()).slice(-2); - return year + '-' + month + '-' + day; + // Masar Date Range Filter Layout Fix v4 + // Forces a horizontal layout for the Date Range Filter in Django Admin Sidebar + // v4: Switches to type="date", removes Django's calendar shortcuts to prevent layout breakage. + + function initDateRangeDropdown() { + + // Find all "Greater Than or Equal" inputs (the start date of the range) + var $gteInputs = $('input[name$="__gte"]'); + + if ($gteInputs.length === 0) { + return; // Not found yet } - function initDateRangeDropdown() { - var $gteInputs = $('input[name$="__gte"]'); + $gteInputs.each(function() { + var $gte = $(this); + if ($gte.data('masar-processed')) return; + $gte.data('masar-processed', true); - $gteInputs.each(function() { - var $gte = $(this); - var name = $gte.attr('name'); - var prefix = name.substring(0, name.lastIndexOf('__gte')); - var $lte = $('input[name="' + prefix + '__lte"]'); + var name = $gte.attr('name'); + var prefix = name.substring(0, name.lastIndexOf('__gte')); + var $lte = $('input[name="' + prefix + '__lte"]'); - if ($lte.length === 0) return; + if ($lte.length === 0) return; - var $container = $gte.closest('.admindatefilter'); - if ($container.length === 0) { - $container = $gte.closest('div[data-filter-name], li, .form-row, .card-body, .filter-wrapper'); - } - if ($container.length === 0) $container = $gte.parent(); + // Locate the container + var $parent = $gte.parent(); - if ($container.data('dropdown-init')) return; - $container.data('dropdown-init', true); + // Create custom flex wrapper + var $wrapper = $('
'); + + // Create the Quick Select Dropdown + var $select = $(''); + + // CONVERT INPUTS TO HTML5 DATE + // This gives us a native picker and removes the need for Django's clunky JS shortcuts + $gte.attr('type', 'date').removeClass('vDateField'); + $lte.attr('type', 'date').removeClass('vDateField'); - var $controls = $gte.closest('.controls'); - if ($controls.length === 0) { - $controls = $gte.parent(); - } - $controls.addClass('date-filter-controls'); // Hook for CSS - - // --- Dropdown --- - var $select = $(''); + // --- RESTRUCTURING DOM --- + // 1. Insert wrapper before the start input + $gte.before($wrapper); + + // 2. Move elements into wrapper + $wrapper.append($select); + $wrapper.append($gte); + $wrapper.append($lte); - // --- Inputs Styling --- - $gte.addClass('form-control form-control-sm date-input'); - $lte.addClass('form-control form-control-sm date-input'); + // 3. AGGRESSIVE CLEANUP + // Remove text nodes, BRs, AND Django's calendar shortcuts (.datetimeshortcuts) + // We search the *original parent* for these leftovers. + $parent.contents().filter(function() { + return ( + (this.nodeType === 3 && $.trim($(this).text()) !== '') || // Text + this.tagName === 'BR' || // Line breaks + $(this).hasClass('datetimeshortcuts') // Django Calendar Icons + ); + }).remove(); - // Insert Dropdown - if ($controls.length) { - $controls.before($select); + // Also hide any shortcuts that might be dynamically appended later (via CSS rule or observer) + // But removing the 'vDateField' class above usually prevents Django from initializing them. + + // Logic for Dropdown Changes + function formatDate(d) { + var year = d.getFullYear(); + var month = ('0' + (d.getMonth() + 1)).slice(-2); + var day = ('0' + d.getDate()).slice(-2); + return year + '-' + month + '-' + day; + } + + var gteVal = $gte.val(); + var lteVal = $lte.val(); + if (gteVal || lteVal) { + $select.val('custom'); + } else { + $select.val('any'); + } + + $select.on('change', function() { + var val = $(this).val(); + var today = new Date(); + + if (val === 'custom') { + // Do nothing, let user edit } else { - $gte.before($select); - } - - // Initial State - var gteVal = $gte.val(); - var lteVal = $lte.val(); - - if (gteVal || lteVal) { - $select.val('custom'); - $controls.css('display', 'inline-flex'); // Ensure flex - } else { - $select.val('any'); - $controls.hide(); - } - - // Event Listener - $select.on('change', function() { - var val = $(this).val(); - var today = new Date(); - - if (val === 'custom') { - $controls.css('display', 'inline-flex').hide().fadeIn(); + if (val === 'any') { + $gte.val(''); + $lte.val(''); } else { - if (val === 'any') { - $gte.val(''); - $lte.val(''); - } else { - var startStr = ''; - var endStr = formatDate(today); + var startStr = ''; + var endStr = formatDate(today); - if (val === 'today') { - startStr = formatDate(today); - } else if (val === '7days') { - var past = new Date(); - past.setDate(today.getDate() - 7); - startStr = formatDate(past); - } else if (val === 'month') { - var firstDay = new Date(today.getFullYear(), today.getMonth(), 1); - startStr = formatDate(firstDay); - } else if (val === 'year') { - var firstDay = new Date(today.getFullYear(), 0, 1); - startStr = formatDate(firstDay); - } - - $gte.val(startStr); - $lte.val(endStr); - } - - // Auto-submit - var $form = $gte.closest('form'); - if ($form.length) { - $form.submit(); - } else { - var $btn = $container.find('input[type="submit"], button[type="submit"]'); - if ($btn.length) $btn.click(); + if (val === 'today') { + startStr = formatDate(today); + } else if (val === '7days') { + var past = new Date(); + past.setDate(today.getDate() - 7); + startStr = formatDate(past); + } else if (val === 'month') { + var firstDay = new Date(today.getFullYear(), today.getMonth(), 1); + startStr = formatDate(firstDay); } + $gte.val(startStr); + $lte.val(endStr); } - }); + // Trigger Form Submit + var $form = $gte.closest('form'); + if ($form.length) { + $form.submit(); + } + } }); - } + }); + } + $(document).ready(function() { initDateRangeDropdown(); + // Retry logic for stubborn renderers + setTimeout(initDateRangeDropdown, 200); setTimeout(initDateRangeDropdown, 500); + setTimeout(initDateRangeDropdown, 1000); }); + })(django.jQuery); \ No newline at end of file