[IMP] web: search bar: facet descriptions
We improve the facet descriptions of the domain created via the domain
selector. We do this in several ways like it was done for the readonly
mode of the domain selector:
- instead of displaying ids for relational fields, we display the
associated names.
- instead of displaying values for selection fields, we display the
associated labels.
- quotes around strings are not displayed by default. They are if
there is some ambiguity: the presence of a value of another type
like 0, false or an expression makes necessary to put them.
Task ID: 3291990
Part-of: odoo/odoo#121688
This commit is contained in:
@@ -20,7 +20,13 @@ import { FACET_ICONS, FACET_COLORS } from "./utils/misc";
|
||||
|
||||
import { EventBus, toRaw } from "@odoo/owl";
|
||||
import { toDomain, toTree, formatValue, normalizeValue } from "@web/core/domain_tree";
|
||||
import { createVirtualOperators, extractPathsFromDomain } from "@web/core/domain_selector/utils";
|
||||
import {
|
||||
createVirtualOperators,
|
||||
extractIdsFromDomain,
|
||||
extractPathsFromDomain,
|
||||
leafToString,
|
||||
useLoadDisplayNames,
|
||||
} from "@web/core/domain_selector/utils";
|
||||
import { useLoadFieldInfo, useLoadPathDescription } from "@web/core/model_field_selector/utils";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { getOperatorInfo } from "@web/core/domain_selector/domain_selector_operators";
|
||||
@@ -214,11 +220,17 @@ function simplifyTree(tree, isRoot = true) {
|
||||
* @param {boolean} [isSubExpression=true]
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDomainTreeDescription(tree, getDescription, isSubExpression = true) {
|
||||
function getDomainTreeDescription(
|
||||
tree,
|
||||
getFieldDef,
|
||||
getDescription,
|
||||
displayNames,
|
||||
isSubExpression = true
|
||||
) {
|
||||
if (tree.type === "connector") {
|
||||
// we assume that the domain tree is normalized (--> there is at least two children)
|
||||
const childDescriptions = tree.children.map((c) =>
|
||||
getDomainTreeDescription(c, getDescription)
|
||||
getDomainTreeDescription(c, getFieldDef, getDescription, displayNames)
|
||||
);
|
||||
const separator = tree.value === "&" ? _t("and") : _t("or");
|
||||
let description = childDescriptions.join(` ${separator} `);
|
||||
@@ -230,43 +242,28 @@ export function getDomainTreeDescription(tree, getDescription, isSubExpression =
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
const { path, operator, value } = tree;
|
||||
const operatorInfo = getOperatorInfo(operator);
|
||||
|
||||
let description = `${getDescription(path)} ${operatorInfo.label} `;
|
||||
|
||||
if (["set", "not_set"].includes(operatorInfo.key)) {
|
||||
description = description.trim();
|
||||
} else if (["is", "is_not"].includes(operatorInfo.key)) {
|
||||
description += value ? _t("set") : _t("not set");
|
||||
} else {
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
let join;
|
||||
let addParenthesis;
|
||||
switch (operator) {
|
||||
case "between":
|
||||
join = _t("and");
|
||||
addParenthesis = false;
|
||||
break;
|
||||
case "in":
|
||||
case "not in":
|
||||
join = ",";
|
||||
addParenthesis = true;
|
||||
break;
|
||||
default:
|
||||
join = _t("or");
|
||||
addParenthesis = values.length > 1;
|
||||
}
|
||||
const jointedValues = values.map((val) => String(val)).join(` ${join} `);
|
||||
const { negate, operator, path, value } = tree;
|
||||
const fieldDef = getFieldDef(path);
|
||||
const operatorInfo = getOperatorInfo(operator, negate);
|
||||
const { operatorDescription, valueDescription } = leafToString(
|
||||
fieldDef,
|
||||
operatorInfo,
|
||||
value,
|
||||
displayNames[fieldDef?.relation]
|
||||
);
|
||||
let description = `${getDescription(path)} ${operatorDescription} `;
|
||||
if (valueDescription) {
|
||||
const { values, join, addParenthesis } = valueDescription;
|
||||
const jointedValues = values.join(` ${join} `);
|
||||
description += addParenthesis ? `( ${jointedValues} )` : jointedValues;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
export function useGetDomainTreeDescription(fieldService) {
|
||||
function useGetDomainTreeDescription(fieldService, nameService) {
|
||||
const loadFieldInfo = useLoadFieldInfo(fieldService);
|
||||
const loadPathDescription = useLoadPathDescription(fieldService);
|
||||
const loadDisplayNames = useLoadDisplayNames(nameService);
|
||||
return async (resModel, tree) => {
|
||||
tree = simplifyTree(tree);
|
||||
const domain = toDomain(tree);
|
||||
@@ -287,8 +284,16 @@ export function useGetDomainTreeDescription(fieldService) {
|
||||
await Promise.all(promises);
|
||||
const getFieldDef = (path) => pathFieldDefs[formatValue(path)];
|
||||
const getDescription = (path) => pathDescriptions[formatValue(path)];
|
||||
const idsByModel = extractIdsFromDomain(domain, getFieldDef);
|
||||
const treeWithVirtualOperators = createVirtualOperators(tree, getFieldDef);
|
||||
return getDomainTreeDescription(treeWithVirtualOperators, getDescription, false);
|
||||
const displayNames = await loadDisplayNames(idsByModel);
|
||||
return getDomainTreeDescription(
|
||||
treeWithVirtualOperators,
|
||||
getFieldDef,
|
||||
getDescription,
|
||||
displayNames,
|
||||
false
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -310,12 +315,12 @@ export class SearchModel extends EventBus {
|
||||
*/
|
||||
setup(services) {
|
||||
// services
|
||||
const { field: fieldService, orm, user, view } = services;
|
||||
const { field: fieldService, name: nameService, orm, user, view } = services;
|
||||
this.orm = orm;
|
||||
this.userService = user;
|
||||
this.viewService = view;
|
||||
|
||||
this.getDomainTreeDescription = useGetDomainTreeDescription(fieldService);
|
||||
this.getDomainTreeDescription = useGetDomainTreeDescription(fieldService, nameService);
|
||||
|
||||
// used to manage search items related to date/datetime fields
|
||||
this.referenceMoment = DateTime.local();
|
||||
|
||||
@@ -24,6 +24,7 @@ export class WithSearch extends Component {
|
||||
orm: useService("orm"),
|
||||
view: useService("view"),
|
||||
field: useService("field"),
|
||||
name: useService("name"),
|
||||
},
|
||||
this.props.searchModelArgs
|
||||
);
|
||||
|
||||
@@ -1677,7 +1677,7 @@ QUnit.module("Search", (hooks) => {
|
||||
await editInput(target, ".o_domain_debug_input", `[("foo", "in", [uid, 1, "a"])]`);
|
||||
await click(target.querySelector(".modal footer button"));
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [`Foo is in ( uid , 1 , a )`]);
|
||||
assert.deepEqual(getFacetTexts(target), [`Foo is in ( uid , 1 , "a" )`]);
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
["foo", "in", [7, 1, "a"]], // uid = 7
|
||||
]);
|
||||
@@ -1794,5 +1794,49 @@ QUnit.module("Search", (hooks) => {
|
||||
assert.deepEqual(getFacetTexts(target), ["Boolean is not set"]);
|
||||
assert.deepEqual(getDomain(controlPanel), [["boolean", "!=", true]]);
|
||||
});
|
||||
|
||||
QUnit.test("display names in facets", async function (assert) {
|
||||
patchWithCleanup(odoo, { debug: true });
|
||||
serverData.models.partner = {
|
||||
fields: {},
|
||||
records: [
|
||||
{ id: 1, display_name: "John" },
|
||||
{ id: 2, display_name: "David" },
|
||||
],
|
||||
};
|
||||
|
||||
const controlPanel = await makeWithSearch({
|
||||
serverData,
|
||||
resModel: "foo",
|
||||
Component: SearchBar,
|
||||
searchMenuTypes: ["filter"],
|
||||
searchViewId: false,
|
||||
searchViewArch: `<search />`,
|
||||
});
|
||||
await toggleSearchBarMenu(target);
|
||||
await openAddCustomFilterDialog(target);
|
||||
await editInput(
|
||||
target,
|
||||
".o_domain_debug_input",
|
||||
`[("bar", "=", 1 ), ("bar", "in", [2, 5555]), ("bar", "!=", false), ("id", "=", 2)]`
|
||||
);
|
||||
await click(target.querySelector(".modal footer button"));
|
||||
|
||||
assert.deepEqual(getFacetTexts(target), [
|
||||
"Bar = John",
|
||||
"Bar is in ( David , Inaccessible/missing record ID: 5555 )",
|
||||
"Bar is set",
|
||||
"ID = 2",
|
||||
]);
|
||||
assert.deepEqual(getDomain(controlPanel), [
|
||||
"&",
|
||||
["bar", "=", 1],
|
||||
"&",
|
||||
["bar", "in", [2, 5555]],
|
||||
"&",
|
||||
["bar", "!=", false],
|
||||
["id", "=", 2],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user