diff --git a/jey_theme/public/css/shared.css b/jey_theme/public/css/shared.css index bf8e49e..2fe8c13 100644 --- a/jey_theme/public/css/shared.css +++ b/jey_theme/public/css/shared.css @@ -13,7 +13,8 @@ } /* Hide Organization desktop icon by default */ -.desktop-icon[data-id="Organization"] { +.desktop-icon[data-id="Organization"], +.desktop-icon[data-id="ERPNext"] { display: none !important; } @@ -1375,3 +1376,34 @@ textarea.form-control:focus, border-color: transparent !important; box-shadow: none !important; } + +/* List view — period (Between) filter sized to fit "DD/MM/YYYY to DD/MM/YYYY". */ +.standard-filter-section .frappe-control.jey-period-filter { + flex: 0 0 auto; + min-width: 200px; +} +.standard-filter-section .frappe-control.jey-period-filter input { + min-width: 200px; +} + +/* Grid "Configure Columns" dialog — let rows grow when label wraps. + Frappe hardcodes height:32px inline on each .fields_order row in + grid_row.js, which clips long translated labels (eg. Azerbaijani + "Əsas Tarif (Anbar Ölçü Vahidinə görə)") so the wrapped portion + overflows into the next row and into the help text below. Switch + to flex centering with min-height so rows grow with content. */ +.control-input.fields_order { + height: auto !important; + min-height: 32px; + display: flex; + align-items: center; +} +.control-input.fields_order > .row { + width: 100%; + align-items: center; + margin: 0; +} +.control-input.fields_order > .row > [class*="col-"] { + padding-top: 0 !important; + margin-top: 0 !important; +} diff --git a/jey_theme/public/js/jey_theme.js b/jey_theme/public/js/jey_theme.js index c43a0cd..eaab694 100644 --- a/jey_theme/public/js/jey_theme.js +++ b/jey_theme/public/js/jey_theme.js @@ -1921,6 +1921,215 @@ } })(); +/* ========================================================================== + List View — Period filter (Between) for date fields like posting_date + Replaces the single-date "=" standard filter with a DateRange "Between" + control, or injects one after the ID filter if it's not in standard_filter. + ========================================================================== */ + +(function () { + "use strict"; + + /* Trigger fields: if doctype has any of these as Date/Datetime, we upgrade. + Order matters — first match wins. Extend freely. */ + var TRIGGER_FIELDS = ["posting_date"]; + + function findTriggerDf(listview) { + if (!listview || !listview.meta || !Array.isArray(listview.meta.fields)) return null; + var fields = listview.meta.fields; + for (var i = 0; i < TRIGGER_FIELDS.length; i++) { + var name = TRIGGER_FIELDS[i]; + for (var j = 0; j < fields.length; j++) { + var f = fields[j]; + if (f.fieldname === name && (f.fieldtype === "Date" || f.fieldtype === "Datetime")) { + return f; + } + } + } + return null; + } + + function patchFilterAreaProto(filter_area) { + var proto = Object.getPrototypeOf(filter_area); + if (!proto || proto.__jeyBetweenPatched) return; + proto.__jeyBetweenPatched = true; + + var origSetStandard = proto.set_standard_filter; + proto.set_standard_filter = function (filters) { + if (!filters || filters.length === 0) return origSetStandard.call(this, filters); + + var fields_dict = this.list_view.page.fields_dict || {}; + var handled = []; + var passthrough = []; + + filters.forEach(function (filter) { + var fieldname = filter[1]; + var condition = filter[2]; + var value = filter[3]; + var ctrl = fields_dict[fieldname]; + if ( + condition === "Between" && + ctrl && + ctrl.df && + ctrl.df.fieldtype === "DateRange" + ) { + handled.push({ ctrl: ctrl, value: value }); + } else { + passthrough.push(filter); + } + }); + + var result = origSetStandard.call(this, passthrough); + + if (handled.length) { + result.promise = (result.promise || Promise.resolve()).then(function () { + handled.forEach(function (h) { + h.ctrl.df.match_type = "Between"; + h.ctrl.df.condition = "Between"; + return h.ctrl.set_value(h.value); + }); + }); + } + return result; + }; + } + + function buildDateRangeField(listview, df) { + var page = listview.page; + var $section = page.page_form.find(".standard-filter-section"); + if (!$section.length) $section = page.page_form; + + var existing = page.fields_dict[df.fieldname]; + var $placeholder = null; + + if (existing && existing.$wrapper) { + var $existingWrap = $(existing.$wrapper); + if ($existingWrap.parent().length) { + $placeholder = $(''); + $existingWrap.before($placeholder); + } + $existingWrap.remove(); + delete page.fields_dict[df.fieldname]; + } + + var newField = page.add_field( + { + fieldtype: "DateRange", + label: df.label || "Posting Date", + fieldname: df.fieldname, + condition: "Between", + is_filter: 1, + onchange: function () { + if ( + listview && + listview.filter_area && + typeof listview.filter_area.refresh_list_view === "function" + ) { + listview.filter_area.trigger_refresh = true; + listview.filter_area.refresh_list_view(); + } + }, + }, + $section + ); + + if (!newField) return null; + + newField.df.match_type = "Between"; + newField.df.condition = "Between"; + + var $newWrap = $(newField.$wrapper || newField.wrapper); + $newWrap.addClass("jey-period-filter"); + + if ($placeholder && $placeholder.length) { + $placeholder.replaceWith($newWrap); + } else { + var $idWrap = $section.find('[data-fieldname="name"]').closest(".frappe-control"); + if ($idWrap.length) { + $idWrap.after($newWrap); + } else { + $section.prepend($newWrap); + } + } + + return newField; + } + + function reflectExistingFilter(listview, df, field) { + try { + if ( + !listview.filter_area || + !listview.filter_area.filter_list || + typeof listview.filter_area.filter_list.get_filters !== "function" + ) { + return; + } + var filters = listview.filter_area.filter_list.get_filters() || []; + var match = null; + for (var i = 0; i < filters.length; i++) { + var f = filters[i]; + if (f[1] === df.fieldname && f[2] === "Between") { + match = f; + break; + } + } + if (!match) return; + + var sidebarFilter = listview.filter_area.filter_list.get_filter(df.fieldname); + if (sidebarFilter && typeof sidebarFilter.remove === "function") { + sidebarFilter.remove(); + } + + field.df.match_type = "Between"; + field.df.condition = "Between"; + field.set_value(match[3]); + } catch (e) { + /* noop */ + } + } + + function setup(listview) { + if (!listview || listview.view_name !== "List") return; + if (listview.__jeyPeriodSet) return; + if (!listview.page || !listview.page.page_form) return; + + var df = findTriggerDf(listview); + if (!df) return; + + if (listview.filter_area) patchFilterAreaProto(listview.filter_area); + + var field = buildDateRangeField(listview, df); + if (!field) return; + + reflectExistingFilter(listview, df, field); + + listview.__jeyPeriodSet = true; + } + + function wrapRenderList() { + if (!window.frappe || !frappe.views || !frappe.views.ListView) return false; + var proto = frappe.views.ListView.prototype; + if (proto.__jeyPeriodWrapped) return true; + proto.__jeyPeriodWrapped = true; + + var originalRender = proto.render_list; + proto.render_list = function () { + var result = originalRender.apply(this, arguments); + try { setup(this); } catch (e) { /* noop */ } + return result; + }; + return true; + } + + if (!wrapRenderList()) { + var attempts = 0; + var iv = setInterval(function () { + attempts++; + if (wrapRenderList() || attempts > 100) clearInterval(iv); + }, 200); + } +})(); + /* ========================================================================== List View — Override SVG sprite icons Replaces content in Frappe's SVG sprite sheets so every