Merge pull request #1183 from suhailanzar/fix-filter

fix: improve docs filtering & Added status wise Filtering
This commit is contained in:
Able k Saju
2025-07-11 10:09:50 +05:30
committed by GitHub
6 changed files with 234 additions and 55 deletions
+2 -4
View File
@@ -696,11 +696,9 @@ export class Payment extends Transactional {
},
referenceType: {
formula: () => {
if (this.referenceType) {
return;
}
return this.for![0].referenceType;
return this.referenceType || undefined;
},
dependsOn: ['for'],
},
taxes: { formula: async () => await this.getTaxSummary() },
};
+10
View File
@@ -333,6 +333,11 @@ export function getTransactionStatusColumn(): ColumnConfig {
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
metadata: {
status,
color,
label,
},
};
},
};
@@ -707,6 +712,11 @@ export function getDocStatusListColumn(): ColumnConfig {
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
metadata: {
status,
color,
label,
},
};
},
};
+152 -23
View File
@@ -1,8 +1,11 @@
<template>
<Popover
ref="filterPopover"
v-if="fields.length"
placement="bottom-end"
@close="emitFilterChange"
:close-on-click-outside="true"
:close-on-click-content="false"
>
<template #target="{ togglePopover }">
<Button :icon="true" @click="togglePopover()">
@@ -54,7 +57,7 @@
name="x"
class="w-4 h-4 cursor-pointer"
:button="true"
@click="removeFilter(filter)"
@click="removeFilter(i)"
/>
</span>
<span class="group-hover:hidden">
@@ -67,15 +70,17 @@
class="w-24"
:df="{
label: t`Field`,
placeholder: t`Field`,
placeholder: t`field`,
fieldname: 'fieldname',
fieldtype: 'Select',
options: fieldOptions,
}"
:value="filter.fieldname"
:close-drop-down="false"
@mousedown.stop
@click.stop
@change="(value) => updateNewFilters(i, 'fieldname', value)"
/>
<Select
:border="true"
size="small"
@@ -85,12 +90,15 @@
placeholder: t`Condition`,
fieldname: 'condition',
fieldtype: 'Select',
options: conditions,
options: conditionsForDropdown,
}"
:value="filter.condition"
:close-drop-down="false"
@mousedown.stop
@click.stop
@change="(value) => updateNewFilters(i, 'condition', value)"
/>
<Data
:border="true"
size="small"
@@ -103,6 +111,8 @@
}"
:value="String(filter.value)"
:close-drop-down="false"
@mousedown.stop
@click.stop
@change="(value) => updateNewFilters(i, 'value', value)"
/>
</div>
@@ -114,6 +124,7 @@
}}</span>
</template>
</div>
<div class="flex justify-between border-t dark:border-gray-800">
<div
class="
text-base
@@ -128,17 +139,59 @@
hover:bg-gray-100
dark:hover:bg-gray-875
"
@click="addNewFilter"
@click.stop="addNewFilter"
>
<feather-icon name="plus" class="w-4 h-4" />
<span class="ms-2">{{ t`Add a filter` }}</span>
</div>
<div class="flex">
<div
v-if="filters.length"
class="
text-base
p-2
flex
items-center
text-gray-600
dark:text-gray-500
cursor-pointer
hover:bg-gray-100
dark:hover:bg-gray-875
"
@click="clearAllFilters"
>
<feather-icon name="trash-2" class="w-4 h-4" />
<span class="ms-2">{{ t`Clear` }}</span>
</div>
<div
v-if="filters.length"
@click="applyFilters"
class="
text-base
border-t
dark:border-gray-800
p-2
flex
items-center
text-gray-600
dark:text-gray-500
cursor-pointer
hover:bg-gray-100
dark:hover:bg-gray-875
"
>
<feather-icon name="search" class="w-4 h-4" />
<span class="ml-2 text-sm">{{ t`Apply` }}</span>
</div>
</div>
</div>
</div>
</template>
</Popover>
</template>
<script lang="ts">
import { t } from 'fyo';
import { Field, FieldTypeEnum } from 'schemas/types';
import { fyo } from 'src/initFyo';
import { getRandomString } from 'utils';
@@ -149,6 +202,7 @@ import Select from './Controls/Select.vue';
import Icon from './Icon.vue';
import Popover from './Popover.vue';
import { QueryFilter } from 'utils/db/types';
import { t } from 'fyo';
const conditions = [
{ label: t`Is`, value: '=' },
@@ -161,7 +215,7 @@ const conditions = [
{ label: t`Is Not Empty`, value: 'is not null' },
] as const;
type Condition = typeof conditions[number]['value'];
type Condition = typeof conditions[number]['label'];
type Filter = {
fieldname: string;
@@ -194,8 +248,13 @@ export default defineComponent({
FieldTypeEnum.Attachment,
FieldTypeEnum.AttachImage,
];
const listViewSettings =
fyo.models[this.schemaName]?.getListViewSettings?.(fyo);
const statusField = listViewSettings?.columns?.[1] as any;
const fields = fyo.schemaMap[this.schemaName]?.fields ?? [];
return fields.filter((f) => {
const filteredFields = fields.filter((f) => {
if (f.filter) {
return true;
}
@@ -210,16 +269,41 @@ export default defineComponent({
return true;
});
if (statusField && statusField.fieldname) {
const statusFieldExists = filteredFields.some(
(field) => field.fieldname === statusField.fieldname
);
if (!statusFieldExists) {
const originalStatusField = fields.find(
(field) => field.fieldname === statusField.fieldname
);
if (originalStatusField) {
filteredFields.unshift(originalStatusField);
} else {
filteredFields.unshift(statusField);
}
}
}
return filteredFields;
},
fieldOptions(): { label: string; value: string }[] {
return this.fields.map((df) => ({
label: df.label,
label: df.fieldname,
value: df.fieldname,
}));
},
conditions(): { label: string; value: string }[] {
return [...conditions];
},
conditionsForDropdown(): { label: string; value: string }[] {
return conditions.map((c) => ({
label: c.label,
value: c.label,
}));
},
explicitFilters(): Filter[] {
return this.filters.filter((f) => !f.implicit);
},
@@ -234,11 +318,19 @@ export default defineComponent({
return this.t`${this.activeFilterCount} filters applied`;
},
},
created() {
this.addNewFilter();
},
methods: {
getRandomString,
getConditionLabel(value: string): string {
const condition = conditions.find((c) => c.value === value);
return condition ? condition.label : value;
},
getConditionValue(label: string): string {
const condition = conditions.find((c) => c.label === label);
return condition ? condition.value : label;
},
addNewFilter(): void {
const df = this.fields[0];
if (!df) {
@@ -249,24 +341,52 @@ export default defineComponent({
},
addFilter(
fieldname: string,
condition: Condition,
condition: string,
value: Filter['value'],
implicit?: boolean
): void {
this.filters.push({ fieldname, condition, value, implicit: !!implicit });
this.newFilters.push({
const displayCondition = this.getConditionLabel(condition);
const newFilter = {
fieldname,
condition,
condition: displayCondition,
value,
implicit: !!implicit,
});
};
this.filters.push(newFilter);
this.newFilters.push(newFilter);
},
removeFilter(filter: Filter): void {
this.filters = this.filters.filter((f) => f !== filter);
applyFilters() {
this.emitFilterChange();
},
updateNewFilters(index: number, key: keyof Filter, value: Filter['value']) {
removeFilter(index: number): void {
this.filters.splice(index, 1);
this.newFilters.splice(index, 1);
},
clearAllFilters(): void {
this.filters = [];
this.newFilters = [];
this.$emit('change', {});
},
updateNewFilters<K extends keyof Filter>(
index: number,
key: K,
value: Filter[K]
) {
if (key === 'condition') {
const displayCondition = this.getConditionLabel(value as string);
this.newFilters![index][key] = displayCondition as Filter[K];
this.filters[index][key] = displayCondition as Filter[K];
} else {
this.newFilters![index][key] = value;
this.filters[index][key] = value;
}
},
setFilter(filters: QueryFilter, implicit?: boolean): void {
this.filters = [];
this.newFilters = [];
@@ -289,22 +409,31 @@ export default defineComponent({
this.emitFilterChange();
},
emitFilterChange(): void {
const filters: Record<string, [Condition, Filter['value']]> = {};
for (const { condition, value, fieldname } of this.newFilters) {
if (value === '' && condition) {
if (value === '' || value === null || value === undefined) {
continue;
}
filters[fieldname] = [condition, value];
const sqlCondition = this.getConditionValue(condition);
if (fieldname === 'numberSeries') {
filters['name'] = [sqlCondition, value];
} else {
filters[fieldname] = [sqlCondition, value];
}
}
this.$emit('change', filters);
this.filters = [...this.newFilters];
if (this.newFilters.length) {
this.filters = this.filters.filter(
(filter) => filter.condition && filter.value && filter.fieldname
);
this.filters.push(this.newFilters[this.newFilters.length - 1]);
}
+33 -6
View File
@@ -71,6 +71,7 @@
}"
:row="(row as RenderData)"
:column="column"
@status-found="handleStatusFound"
/>
</Row>
</div>
@@ -115,7 +116,7 @@ import Row from 'src/components/Row.vue';
import { fyo } from 'src/initFyo';
import { isNumeric } from 'src/utils';
import { QueryFilter } from 'utils/db/types';
import { PropType, defineComponent } from 'vue';
import { PropType, defineComponent, toRaw } from 'vue';
import ListCell from './ListCell.vue';
export default defineComponent({
@@ -144,6 +145,7 @@ export default defineComponent({
data: [] as RenderData[],
pageStart: 0,
pageEnd: 0,
statusMap: {} as Record<string, string>,
};
},
computed: {
@@ -186,6 +188,9 @@ export default defineComponent({
this.setUpdateListeners();
},
methods: {
handleStatusFound({ rowId, status }: { rowId: string; status: string }) {
this.statusMap[rowId] = status;
},
isNumeric,
setPageIndices({ start, end }: { start: number; end: number }) {
this.pageStart = start;
@@ -210,12 +215,20 @@ export default defineComponent({
fyo.doc.observer.on(`rename:${this.schemaName}`, listener);
},
async updateData(filters?: Record<string, unknown>) {
if (!filters) {
filters = { ...this.filters };
const baseFilters = cloneDeep(toRaw(this.filters));
filters = cloneDeep({ ...baseFilters, ...filters });
let statusFilter: [string, string] | undefined;
if ('status' in filters) {
statusFilter = filters['status'] as [string, string];
}
// Unproxy the filters
filters = cloneDeep(filters);
const isStatusFilter =
Array.isArray(statusFilter) && statusFilter[0] === 'like';
if (isStatusFilter) {
delete filters['status'];
}
const orderBy = ['created'];
if (fyo.db.fieldMap[this.schemaName]['date']) {
@@ -228,7 +241,21 @@ export default defineComponent({
orderBy,
});
this.data = tableData.map((d) => ({
let filteredData = tableData;
if (isStatusFilter && statusFilter?.[1]) {
const lowercaseStatus = String(statusFilter[1]).toLowerCase();
const matchedNames = Object.entries(this.statusMap)
.filter((entry) => entry[1].toLowerCase() === lowercaseStatus)
.map((entry) => entry[0]);
filteredData = tableData.filter((row) =>
matchedNames.includes(String(row.name))
);
}
this.data = filteredData.map((d) => ({
...d,
schema: fyo.schemaMap[this.schemaName],
})) as RenderData[];
+19
View File
@@ -27,6 +27,7 @@ export default defineComponent({
row: { type: Object as PropType<RenderData>, required: true },
column: { type: Object as PropType<Column>, required: true },
},
emits: ['status-found'],
computed: {
columnValue(): string {
const column = this.column;
@@ -51,5 +52,23 @@ export default defineComponent({
return isNumeric(this.column.fieldtype) ? 'justify-end' : '';
},
},
mounted() {
const { render } = this.column as ColumnConfig;
if (render) {
const result = render(this.row) as {
template: string;
metadata?: { status: string; color: string; label: string };
};
if (result?.metadata) {
this.$emit('status-found', {
rowId: this.row.name || this.row.id,
fieldname: this.column.fieldname,
status: result.metadata.status,
label: result.metadata.label,
});
}
}
},
});
</script>
-4
View File
@@ -115,10 +115,6 @@ export default defineComponent({
},
},
activated() {
if (typeof this.filters === 'object') {
this.filterDropdown?.setFilter(this.filters, true);
}
this.listConfig = getListConfig(this.schemaName);
docsPathRef.value =
docsPathMap[this.schemaName] ?? docsPathMap.Entries ?? '';