Skip to content

Commit

Permalink
[IMP] spreadsheet_oca: Allow to edit filters
Browse files Browse the repository at this point in the history
  • Loading branch information
etobella committed Apr 7, 2023
1 parent 7260a52 commit a008924
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 10 deletions.
1 change: 1 addition & 0 deletions spreadsheet_oca/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
],
"spreadsheet.o_spreadsheet": [
"spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml",
"spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js",
"spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_renderer.esm.js",
"spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_controlpanel.esm.js",
"spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet_action.esm.js",
Expand Down
175 changes: 175 additions & 0 deletions spreadsheet_oca/static/src/spreadsheet/bundle/filter.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/** @odoo-module **/

import {Component, onWillStart, useState} from "@odoo/owl";
import {_lt, _t} from "web.core";

import {FilterValue} from "@spreadsheet/global_filters/components/filter_value/filter_value";
import {ModelFieldSelector} from "@web/core/model_field_selector/model_field_selector";
import {ModelSelector} from "@web/core/model_selector/model_selector";
import {RELATIVE_DATE_RANGE_TYPES} from "@spreadsheet/helpers/constants";
import {globalFiltersFieldMatchers} from "@spreadsheet/global_filters/plugins/global_filters_core_plugin";
import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended";
import {useService} from "@web/core/utils/hooks";

const {topbarMenuRegistry} = spreadsheet.registries;
const uuidGenerator = new spreadsheet.helpers.UuidGenerator();

topbarMenuRegistry.add("file", {name: _t("File"), sequence: 10});
topbarMenuRegistry.addChild("filters", ["file"], {
name: _t("Filters"),
sequence: 70,
action: (env) => env.openSidePanel("FilterPanel", {}),
});
topbarMenuRegistry.addChild("save", ["file"], {
name: _t("Save"),
// Description: "Ctrl+S", // This is not working, so removing it from the view for now...
sequence: 10,
action: (env) => env.saveSpreadsheet(),
});

const {sidePanelRegistry} = spreadsheet.registries;

export class FilterPanel extends Component {
onEditFilter(filter) {
this.env.openSidePanel("EditFilterPanel", {filter});
}
onAddFilter(type) {
this.env.openSidePanel("EditFilterPanel", {filter: {type: type}});
}
}

FilterPanel.template = "spreadsheet_oca.FilterPanel";
FilterPanel.components = {
FilterValue,
};

sidePanelRegistry.add("FilterPanel", {
title: "Filters",
Body: FilterPanel,
});

export class EditFilterPanel extends Component {
setup() {
this.filterId = this.props.filter;
this.orm = useService("orm");
this.state = useState({
label: this.props.filter.label,
type: this.props.filter.type,
defaultValue: this.props.filter.defaultValue,
defaultsToCurrentPeriod: this.props.filter.defaultsToCurrentPeriod,
rangeType: this.props.filter.rangeType || "year",
modelName: {technical: this.props.filter.modelName, label: null},
objects: {},
});
this.relativeDateRangeTypes = RELATIVE_DATE_RANGE_TYPES;
onWillStart(this.willStart.bind(this));
}
async willStart() {
if (this.state.modelName.technical !== undefined) {
const modelLabel = await this.orm.call("ir.model", "display_name_for", [
[this.state.modelName.technical],
]);
this.state.modelName.label = modelLabel[0] && modelLabel[0].display_name;
}
var ModelFields = [];
for (var [objectType, objectClass] of Object.entries(
globalFiltersFieldMatchers
)) {
for (const objectId of objectClass.geIds()) {
var fields = objectClass.getFields(objectId);
this.state.objects[objectType + "_" + objectId] = {
id: objectType + "_" + objectId,
objectId: objectId,
name: objectClass.getDisplayName(objectId),
tag: await objectClass.getTag(objectId),
fieldMatch:
objectClass.getFieldMatching(objectId, this.props.filter.id) ||
{},
fields: fields,
model: objectClass.getModel(objectId),
};
ModelFields.push(fields);
}
}
console.log(this.state.objects);
this.models = [
...new Set(
ModelFields.map((field_items) => Object.values(field_items))
.flat()
.filter((field) => field.relation)
.map((field) => field.relation)
),
];
}
get dateRangeTypes() {
return [
{type: "year", description: _t("Year")},
{type: "quarter", description: _t("Quarter")},
{type: "month", description: _t("Month")},
{type: "relative", description: _t("Relative Period")},
];
}
get dateOffset() {
return [
{value: 0, name: ""},
{value: -1, name: _lt("Previous")},
{value: -2, name: _lt("Before Previous")},
{value: 1, name: _lt("Next")},
{value: 2, name: _lt("After next")},
];
}
onChangeFieldMatchOffset(object, ev) {
this.state.objects[object.id].fieldMatch.offset = parseInt(ev.target.value, 10);
}
onModelSelected(ev) {
this.state.modelName.technical = ev.technical;
this.state.modelName.label = ev.label;
}
onDateRangeChange(ev) {
this.state.rangeType = ev.target.value;
}
onSave() {
const action = this.props.filter.id
? "EDIT_GLOBAL_FILTER"
: "ADD_GLOBAL_FILTER";
this.env.openSidePanel("FilterPanel", {});

var filter = {
id: this.props.filter.id || uuidGenerator.uuidv4(),
type: this.state.type,
label: this.state.label,
defaultValue: this.state.defaultValue,
rangeType: this.state.rangeType,
defaultsToCurrentPeriod: this.state.defaultsToCurrentPeriod,
modelName: this.state.modelName.technical,
};
var filterMatching = {};
Object.values(this.state.objects).forEach((object) => {
filterMatching[object.type] = filterMatching[object.type] || {};
filterMatching[object.type][object.id] = {...object.fieldMatch};
});
this.env.model.dispatch(action, {id: filter.id, filter, ...filterMatching});

this.env.openSidePanel("FilterPanel", {});
}
onCancel() {
this.env.openSidePanel("FilterPanel", {});
}
onRemove() {
if (this.props.filter.id) {
this.env.model.dispatch("REMOVE_GLOBAL_FILTER", {id: this.props.filter.id});
}
this.env.openSidePanel("FilterPanel", {});
}
onFieldMatchUpdate(object, name) {
this.state.objects[object.id].fieldMatch.chain = name.chain;
}
}

EditFilterPanel.template = "spreadsheet_oca.EditFilterPanel";
EditFilterPanel.components = {ModelSelector, ModelFieldSelector};

sidePanelRegistry.add("EditFilterPanel", {
title: "Edit Filter",
Body: EditFilterPanel,
});
157 changes: 157 additions & 0 deletions spreadsheet_oca/static/src/spreadsheet/bundle/spreadsheet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,164 @@
/>
</div>
</t>
<t t-name="spreadsheet_oca.FilterPanel" owl="1">
<div
t-foreach="env.model.getters.getGlobalFilters()"
t-as="filter"
t-key="filter.id"
class="o_spreadsheet_oca_filter"
>
<div class="spreadsheet_oca_filter_label">
<span t-esc="filter.label" />
</div>
<div class="spreadsheet_oca_filter_value">
<FilterValue filter="filter" model="env.model" />
<i
class="fa fa-cog btn btn-link text-muted spreadsheet_oca_filter_value_edit"
t-on-click="() =&gt; this.onEditFilter(filter)"
/>
</div>
</div>
<div class="o-sidePanelButtons">
<button
t-on-click="() => this.onAddFilter('date')"
class="btn"
>Add date</button>
<button
t-on-click="() => this.onAddFilter('relation')"
class="btn"
>Add relation</button>
<button
t-on-click="() => this.onAddFilter('text')"
class="btn"
>Add text</button>
</div>
</t>
<t t-name="spreadsheet_oca.EditFilterPanel" owl="1">
<div class="o_spreadsheet_oca_filter">
<div class="spreadsheet_oca_filter_label">
<span>Label</span>
</div>
<div class="spreadsheet_oca_filter_value">
<input type="text" t-model="state.label" class="o_input" />
</div>
</div>
<div class="o_spreadsheet_oca_filter" t-if="state.type == 'date'">
<div class="spreadsheet_oca_filter_label">Time range</div>
<div class="spreadsheet_oca_filter_value">
<select
t-model="state.rangeType"
t-on-change="onDateRangeChange"
class="o_input"
>
<t t-foreach="dateRangeTypes" t-as="range" t-key="range.type">
<option
t-att-selected="state.rangeType === range.type ? 1 : 0"
t-att-value="range.type"
>
<t t-esc="range.description" />
</option>
</t>
</select>
</div>
</div>
<div class="o_spreadsheet_oca_filter" t-if="state.type !== 'relation'">
<div class="spreadsheet_oca_filter_label">Default value</div>
<div
t-if="state.type === 'date' and state.rangeType !== 'relative'"
class="spreadsheet_oca_filter_value"
>
<input
type="checkbox"
class="me-2"
id="default_to_current_period"
t-att-checked="state.defaultsToCurrentPeriod"
/>
<label
for="default_to_current_period"
> Automatically filter on the current period </label>
</div>
<div
t-if="state.type === 'date'and state.rangeType === 'relative'"
class="spreadsheet_oca_filter_value"
>
<select t-model="state.defaultValue" class="o_input">
<option value="" />
<t
t-foreach="relativeDateRangeTypes"
t-as="range"
t-key="range.type"
>
<option
t-att-selected="range.type === state.defaultValue ? 1 : 0"
t-att-value="range.type"
>
<t t-esc="range.description" />
</option>
</t>
</select>
</div>
<input
t-if="state.type === 'text'"
type="text"
class="o_input o_global_filter_default_value"
t-model="state.defaultValue"
/>
</div>

<div class="o_spreadsheet_oca_filter" t-if="state.type === 'relation'">
<div class="spreadsheet_oca_filter_label">Related model</div>
<div class="spreadsheet_oca_filter_value">
<ModelSelector
value="state.modelName.label"
models="models"
onModelSelected.bind="onModelSelected"
/>
</div>
</div>
<div
class="o_spreadsheet_oca_filter"
t-foreach="Object.values(state.objects)"
t-as="object"
t-key="object.id"
t-if="object.model"
>
<div class="spreadsheet_oca_filter_label">
<span t-esc="object.name" /> - <span t-esc="object.tag" />
</div>
<div class="spreadsheet_oca_filter_value">
<ModelFieldSelector
fieldName="object.fieldMatch.chain || ''"
resModel="object.model"
readonly="false"
isDebugMode="!!env.debug"
update="(name) => this.onFieldMatchUpdate(object, name)"
/>
</div>

<div class="spreadsheet_oca_filter_value" t-if="state.type === 'date'">
<select
t-model="object.fieldMatch.offset"
t-on-change="(ev) => this.onChangeFieldMatchOffset(object, ev)"
class="o_input"
>
<t t-foreach="dateOffset" t-as="offset" t-key="offset.value">
<option
t-att-selected="offset.value === object.fieldMatch.offset ? 1 : 0"
t-att-value="offset.value"
>
<t t-esc="offset.name" />
</option>
</t>
</select>
</div>
</div>
<div class="o-sidePanelButtons">
<button t-on-click="onRemove" class="btn btn-danger">Remove</button>
<button t-on-click="onCancel" class="btn btn-warning">Cancel</button>
<button t-on-click="onSave" class="btn btn-primary">Save</button>
</div>
</t>
<t t-name="spreadsheet_oca.SpreadsheetRenderer" owl="1">
<Spreadsheet model="spreadsheet_model" />
<Dialog
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
/** @odoo-module **/

import {_lt, _t} from "web.core";
import {Component} from "@odoo/owl";
import {ControlPanel} from "@web/search/control_panel/control_panel";
import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended";

const {useState} = owl;
const {topbarMenuRegistry} = spreadsheet.registries;

topbarMenuRegistry.add("file", {name: _t("File"), sequence: 10});
topbarMenuRegistry.addChild("save", ["file"], {
name: _lt("Save"),
// Description: "Ctrl+S", // This is not working, so removing it from the view for now...
sequence: 10,
action: (env) => env.saveSpreadsheet(),
});
export class SpreadsheetName extends Component {
setup() {
this.state = useState({
Expand Down
24 changes: 24 additions & 0 deletions spreadsheet_oca/static/src/spreadsheet/spreadsheet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,28 @@
.o_spreadsheet_oca_name_warning {
font-size: 0.8em;
}
.o_spreadsheet_oca_filter {
padding: 16px;
.spreadsheet_oca_filter_label {
font-weight: bold;
}
.spreadsheet_oca_filter_value {
display: flex;
.o_field_widget {
width: 100%;
.o_field_tags {
width: 100%;
}
}
.o_field_selector {
width: 100%;
}
.spreadsheet_oca_filter_value_edit {
width: 15px;
margin-left: 1rem;
padding: 0;
cursor: pointer;
}
}
}
}

0 comments on commit a008924

Please sign in to comment.