feat(list,grid): period filter for date columns + dialog row fix

- list-view: replace single-date "=" standard filter with a DateRange
  "Between" control when the doctype carries posting_date as Date/Datetime.
  If posting_date isn't in standard_filter, inject the period control after
  the ID filter so it's always reachable.
- shared.css: size the period filter to fit "DD/MM/YYYY to DD/MM/YYYY"
  without being squashed by the surrounding flex layout.
- shared.css: let rows in the grid "Configure Columns" dialog grow with
  content. 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 the help text. Switch to flex centering with
  min-height; reset inline padding-tops that were calibrated for fixed
  32px rows.
- shared.css: hide the ERPNext desktop icon by default (same treatment as
  Organization).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-05-25 17:31:33 +00:00
parent c145f584bd
commit a1e3fe96ed
2 changed files with 242 additions and 1 deletions

View File

@ -13,7 +13,8 @@
} }
/* Hide Organization desktop icon by default */ /* Hide Organization desktop icon by default */
.desktop-icon[data-id="Organization"] { .desktop-icon[data-id="Organization"],
.desktop-icon[data-id="ERPNext"] {
display: none !important; display: none !important;
} }
@ -1375,3 +1376,34 @@ textarea.form-control:focus,
border-color: transparent !important; border-color: transparent !important;
box-shadow: none !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;
}

View File

@ -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 = $('<span class="jey-period-anchor" style="display:none"></span>');
$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 List View Override SVG sprite icons
Replaces <symbol> content in Frappe's SVG sprite sheets so every Replaces <symbol> content in Frappe's SVG sprite sheets so every