insights explorer supports sub condition

This commit is contained in:
MaysWind
2026-03-16 02:07:36 +08:00
parent 302d118ae0
commit dcee067aea
22 changed files with 378 additions and 88 deletions
+12 -2
View File
@@ -4,17 +4,27 @@ import { DateRange } from '@/core/datetime.ts';
export enum TransactionExplorerConditionRelation {
First = 'first',
And = 'and',
Or = 'or'
Or = 'or',
AndSub = 'and(',
OrSub = 'or(',
SubEnd = ')'
}
export type TransactionExplorerSubConditionStartRelation = '(';
export const TransactionExplorerSubConditionStartRelationPlaceholder: TransactionExplorerSubConditionStartRelation = '(';
export const TransactionExplorerConditionRelationPriority: Record<TransactionExplorerConditionRelation, number> = {
[TransactionExplorerConditionRelation.First]: 0,
[TransactionExplorerConditionRelation.Or]: 1,
[TransactionExplorerConditionRelation.And]: 2
[TransactionExplorerConditionRelation.And]: 2,
[TransactionExplorerConditionRelation.AndSub]: 0,
[TransactionExplorerConditionRelation.OrSub]: 0,
[TransactionExplorerConditionRelation.SubEnd]: 0
};
export enum TransactionExplorerConditionFieldType {
Undefined = 'undefined',
TransactionType = 'transactionType',
TransactionCategory = 'transactionCategory',
SourceAccount = 'sourceAccount',
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Heute",
"Yesterday": "Gestern",
"Recent 7 days": "Letzte 7 Tage",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Today",
"Yesterday": "Yesterday",
"Recent 7 days": "Recent 7 days",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Hoy",
"Yesterday": "Ayer",
"Recent 7 days": "Últimos 7 días",
@@ -1750,6 +1752,7 @@
"Remove Query": "Eliminar Consulta",
"Modify Query Name": "Modificar el Nombre de la Consulta",
"Add Condition": "Añadir Condición",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Eliminar Condición",
"No conditions defined. All transactions will match.": "Sin condiciones definidas. Todas las transacciones coincidirán.",
"Unable to retrieve explorer list": "No se puede recuperar la lista de exploraciones",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Aujourd'hui",
"Yesterday": "Hier",
"Recent 7 days": "7 derniers jours",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Oggi",
"Yesterday": "Ieri",
"Recent 7 days": "Ultimi 7 giorni",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "今日",
"Yesterday": "昨日",
"Recent 7 days": "直近7日間",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "ಇಂದು",
"Yesterday": "ನಿನ್ನೆ",
"Recent 7 days": "ಇತ್ತೀಚಿನ 7 ದಿನಗಳು",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "오늘",
"Yesterday": "어제",
"Recent 7 days": "최근 7일",
@@ -1750,6 +1752,7 @@
"Remove Query": "쿼리 제거",
"Modify Query Name": "쿼리 이름 수정",
"Add Condition": "조건 추가",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "조건 제거",
"No conditions defined. All transactions will match.": "조건이 정의되지 않았습니다. 모든 거래가 일치합니다.",
"Unable to retrieve explorer list": "탐색기 목록을 가져올 수 없습니다",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Vandaag",
"Yesterday": "Gisteren",
"Recent 7 days": "Afgelopen 7 dagen",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Hoje",
"Yesterday": "Ontem",
"Recent 7 days": "Últimos 7 dias",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Сегодня",
"Yesterday": "Вчера",
"Recent 7 days": "Последние 7 дней",
@@ -1750,6 +1752,7 @@
"Remove Query": "Удалить запрос",
"Modify Query Name": "Изменить название запроса",
"Add Condition": "Добавить условие",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Удалить условие",
"No conditions defined. All transactions will match.": "Нет условий. Подходят все транзакции.",
"Unable to retrieve explorer list": "Не возможно получить список исследований",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "KJER",
"AND": "IN",
"OR": "ALI",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Danes",
"Yesterday": "Včeraj",
"Recent 7 days": "Zadnjih 7 dni",
@@ -1750,6 +1752,7 @@
"Remove Query": "Odstrani poizvedbo",
"Modify Query Name": "Spremeni ime poizvedbe",
"Add Condition": "Dodaj pogoj",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Odstrani pogoj",
"No conditions defined. All transactions will match.": "Ni določenih pogojev. Prikazane bodo vse transakcije.",
"Unable to retrieve explorer list": "Seznama raziskovanj ni mogoče pridobiti",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "எங்கே",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "இன்று",
"Yesterday": "நேற்று",
"Recent 7 days": "சமீபத்திய 7 நாட்கள்",
@@ -1750,6 +1752,7 @@
"Remove Query": "வினவல் நீக்கு",
"Modify Query Name": "வினவல் பெயரை மாற்று",
"Add Condition": "நிபந்தனை சேர்",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "நிபந்தனை நீக்கு",
"No conditions defined. All transactions will match.": "நிபந்தனைகள் வரையறுக்கப்படவில்லை. அனைத்து பரிவர்த்தனைகளும் பொருந்தும்.",
"Unable to retrieve explorer list": "ஆய்வுக்கருவி பட்டியல் பெற முடியவில்லை",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "วันนี้",
"Yesterday": "เมื่อวาน",
"Recent 7 days": "7 วันที่ผ่านมา",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Bugün",
"Yesterday": "Dün",
"Recent 7 days": "Son 7 gün",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Сьогодні",
"Yesterday": "Вчора",
"Recent 7 days": "Останні 7 днів",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "WHERE",
"AND": "AND",
"OR": "OR",
"AND SUB": "AND SUB",
"OR SUB": "OR SUB",
"Today": "Hôm nay",
"Yesterday": "Hôm qua",
"Recent 7 days": "7 ngày gần đây",
@@ -1750,6 +1752,7 @@
"Remove Query": "Remove Query",
"Modify Query Name": "Modify Query Name",
"Add Condition": "Add Condition",
"Add Sub Condition": "Add Sub Condition",
"Remove Condition": "Remove Condition",
"No conditions defined. All transactions will match.": "No conditions defined. All transactions will match.",
"Unable to retrieve explorer list": "Unable to retrieve explorer list",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "条件",
"AND": "与",
"OR": "或",
"AND SUB": "与子条件",
"OR SUB": "或子条件",
"Today": "今天",
"Yesterday": "昨天",
"Recent 7 days": "最近7天",
@@ -1750,6 +1752,7 @@
"Remove Query": "移除查询",
"Modify Query Name": "修改查询名称",
"Add Condition": "添加条件",
"Add Sub Condition": "添加子条件",
"Remove Condition": "移除条件",
"No conditions defined. All transactions will match.": "没有定义条件。所有交易都会匹配。",
"Unable to retrieve explorer list": "无法获取探索列表",
+3
View File
@@ -1517,6 +1517,8 @@
"WHERE": "條件",
"AND": "且",
"OR": "或",
"AND SUB": "且子條件",
"OR SUB": "或子條件",
"Today": "今天",
"Yesterday": "昨天",
"Recent 7 days": "最近7天",
@@ -1750,6 +1752,7 @@
"Remove Query": "移除查詢",
"Modify Query Name": "修改查詢名稱",
"Add Condition": "新增條件",
"Add Sub Condition": "新增子條件",
"Remove Condition": "移除條件",
"No conditions defined. All transactions will match.": "沒有定義條件。所有交易都符合。",
"Unable to retrieve explorer list": "無法取得探索清單",
+126 -9
View File
@@ -4,7 +4,9 @@ import { AccountType } from '@/core/account.ts';
import { TransactionType } from '@/core/transaction.ts';
import { ChartSortingType } from '@/core/statistics.ts';
import {
type TransactionExplorerSubConditionStartRelation,
TransactionExplorerConditionRelation,
TransactionExplorerSubConditionStartRelationPlaceholder,
TransactionExplorerConditionRelationPriority,
TransactionExplorerConditionFieldType,
TransactionExplorerConditionField,
@@ -339,6 +341,10 @@ export class TransactionExplorerQuery {
);
}
public addSubConditionEnd(): TransactionExplorerConditionWithRelation {
return new TransactionExplorerConditionWithRelation(new TransactionExplorerUndefinedCondition(), TransactionExplorerConditionRelation.SubEnd);
}
public match(transaction: TransactionInsightDataItem): boolean {
if (!this.conditions || this.conditions.length < 1) {
return true;
@@ -434,7 +440,7 @@ export class TransactionExplorerQuery {
return finalTokens;
}
const operatorStack: TransactionExplorerConditionRelation[] = [];
const operatorStack: (TransactionExplorerConditionRelation | TransactionExplorerSubConditionStartRelation)[] = [];
const firstCondition = this.conditions[0] as TransactionExplorerConditionWithRelation;
if (firstCondition.relation !== TransactionExplorerConditionRelation.First) {
@@ -452,10 +458,52 @@ export class TransactionExplorerQuery {
throw new Error('only the first condition can have relation "first"');
}
const currentOperator = item.relation;
if (item.relation === TransactionExplorerConditionRelation.SubEnd) {
while (operatorStack.length > 0) {
const topOperator = operatorStack.pop();
if (topOperator === TransactionExplorerSubConditionStartRelationPlaceholder) {
break;
}
const isAndOrOperator = topOperator === TransactionExplorerConditionRelation.And || topOperator === TransactionExplorerConditionRelation.Or;
if (isAndOrOperator) {
finalTokens.push(topOperator);
} else {
throw new Error('invalid operator in stack');
}
}
} else { // And, Or, AndSub, OrSub
let currentOperator: TransactionExplorerConditionRelation.And | TransactionExplorerConditionRelation.Or;
let startNewSubCondition = false;
switch (item.relation) {
case TransactionExplorerConditionRelation.AndSub:
currentOperator = TransactionExplorerConditionRelation.And;
startNewSubCondition = true;
break;
case TransactionExplorerConditionRelation.OrSub:
currentOperator = TransactionExplorerConditionRelation.Or;
startNewSubCondition = true;
break;
case TransactionExplorerConditionRelation.And:
currentOperator = item.relation;
break;
case TransactionExplorerConditionRelation.Or:
currentOperator = item.relation;
break;
default:
throw new Error('invalid operator in stack');
}
while (operatorStack.length > 0) {
const topOperator = operatorStack[operatorStack.length - 1];
if (topOperator === TransactionExplorerSubConditionStartRelationPlaceholder) {
break;
}
const isAndOrOperator = topOperator === TransactionExplorerConditionRelation.And || topOperator === TransactionExplorerConditionRelation.Or;
if (isAndOrOperator && TransactionExplorerConditionRelationPriority[topOperator] >= TransactionExplorerConditionRelationPriority[currentOperator]) {
@@ -467,8 +515,14 @@ export class TransactionExplorerQuery {
}
operatorStack.push(currentOperator);
if (startNewSubCondition) {
operatorStack.push(TransactionExplorerSubConditionStartRelationPlaceholder);
}
finalTokens.push(item.condition);
}
}
while (operatorStack.length > 0) {
const topOperator = operatorStack.pop();
@@ -483,6 +537,25 @@ export class TransactionExplorerQuery {
return finalTokens;
}
public getConditionNestingDepths(): number[] {
const depths: number[] = [];
let depth = 0;
for (const item of this.conditions) {
if (item.relation === TransactionExplorerConditionRelation.SubEnd) {
depth--;
depths.push(depth);
} else if (item.relation === TransactionExplorerConditionRelation.AndSub || item.relation === TransactionExplorerConditionRelation.OrSub) {
depth++;
depths.push(depth);
} else {
depths.push(depth);
}
}
return depths;
}
public clone(newId: string): TransactionExplorerQuery {
const clonedConditions: TransactionExplorerConditionWithRelation[] = [];
@@ -527,6 +600,7 @@ export class TransactionExplorerQuery {
const id: string = idFieldValue;
const name: string = nameFieldValue;
const conditions: TransactionExplorerConditionWithRelation[] = [];
let conditionDepth = 0;
for (const [item, index] of itemAndIndex(conditionsFieldValue)) {
const condition = TransactionExplorerConditionWithRelation.parse(item);
@@ -541,9 +615,20 @@ export class TransactionExplorerQuery {
return null;
}
if (condition.relation === TransactionExplorerConditionRelation.AndSub ||
condition.relation === TransactionExplorerConditionRelation.OrSub) {
conditionDepth++;
} else if (condition.relation === TransactionExplorerConditionRelation.SubEnd) {
conditionDepth--;
}
conditions.push(condition);
}
if (conditionDepth !== 0) {
return null; // unbalanced parentheses
}
return new TransactionExplorerQuery(id, name, conditions);
}
}
@@ -609,6 +694,12 @@ export class TransactionExplorerConditionWithRelation {
}
public toJsonObject(): unknown {
if (this.relation === TransactionExplorerConditionRelation.SubEnd) {
return {
relation: this.relation
};
}
return {
condition: {
field: this.condition.field,
@@ -620,12 +711,21 @@ export class TransactionExplorerConditionWithRelation {
}
public static parse(data: unknown): TransactionExplorerConditionWithRelation | null {
if (typeof data !== 'object' || !data || !('condition' in data) || !('relation' in data)) {
if (typeof data !== 'object' || !data || !('relation' in data)) {
return null;
}
const relation = data['relation'];
let condition: TransactionExplorerCondition | null = null;
if (relation === TransactionExplorerConditionRelation.First ||
relation === TransactionExplorerConditionRelation.And || relation === TransactionExplorerConditionRelation.Or ||
relation === TransactionExplorerConditionRelation.AndSub || relation === TransactionExplorerConditionRelation.OrSub) {
if (!('condition' in data)) {
return null;
}
const conditionObject = data['condition'];
const relation = data['relation'];
if (typeof conditionObject !== 'object' || !conditionObject || !('field' in conditionObject) || !('operator' in conditionObject) || !('value' in conditionObject) || typeof relation !== 'string') {
return null;
@@ -635,8 +735,6 @@ export class TransactionExplorerConditionWithRelation {
const conditionOperator = conditionObject['operator'] as TransactionExplorerConditionOperatorType;
const conditionValue = conditionObject['value'];
let condition: TransactionExplorerCondition | null = null;
switch (conditionField) {
case TransactionExplorerConditionField.TransactionType.value:
if (conditionOperator === TransactionExplorerConditionOperatorType.In && Array.isArray(conditionValue)) {
@@ -691,12 +789,13 @@ export class TransactionExplorerConditionWithRelation {
default:
break;
}
if (condition === null) {
} else if (relation === TransactionExplorerConditionRelation.SubEnd) {
condition = new TransactionExplorerUndefinedCondition();
} else {
return null;
}
if (relation !== TransactionExplorerConditionRelation.First && relation !== TransactionExplorerConditionRelation.And && relation !== TransactionExplorerConditionRelation.Or) {
if (condition === null) {
return null;
}
@@ -714,6 +813,24 @@ export interface TransactionExplorerCondition<T = TransactionExplorerConditionFi
toExpression(allCategoriesMap: Record<string, TransactionCategory>, allAccountsMap: Record<string, Account>, allTagsMap: Record<string, TransactionTag>): string;
}
export class TransactionExplorerUndefinedCondition implements TransactionExplorerCondition {
public readonly field = TransactionExplorerConditionFieldType.Undefined;
public readonly operator = TransactionExplorerConditionOperatorType.Equals;
public value = '';
public getValueForStore(): string {
return this.value;
}
public match(transaction: TransactionInsightDataItem): boolean {
return !!transaction;
}
public toExpression(): string {
return '';
}
}
export class TransactionExplorerTransactionTypeCondition implements TransactionExplorerCondition<TransactionExplorerConditionFieldType.TransactionType, number[]> {
public static readonly supportedOperators: PartialRecord<TransactionExplorerConditionOperatorType, true> = {
[TransactionExplorerConditionOperatorType.In]: true
@@ -98,11 +98,13 @@
<div v-else-if="element.conditions && element.conditions.length > 0 && !showExpression[element.id]">
<div :key="conditionIndex" v-for="(conditionWithRelation, conditionIndex) in element.conditions">
<div class="d-flex overflow-x-auto align-center gap-2 mb-4">
<div class="d-flex overflow-x-auto align-center gap-2 mb-4"
:style="getConditionStyle(element, conditionIndex)"
v-if="conditionWithRelation.relation !== TransactionExplorerConditionRelation.SubEnd">
<v-select
disabled
class="flex-0-0"
width="120px"
width="140px"
density="compact"
item-title="displayName"
item-value="value"
@@ -113,16 +115,19 @@
<v-select
class="flex-0-0"
width="120px"
width="140px"
density="compact"
item-title="displayName"
item-value="value"
:disabled="loading || disabled || !!editingQuery"
:items="[
{ value: TransactionExplorerConditionRelation.And, displayName: tt('AND') },
{ value: TransactionExplorerConditionRelation.Or, displayName: tt('OR') }
{ value: TransactionExplorerConditionRelation.Or, displayName: tt('OR') },
{ value: TransactionExplorerConditionRelation.AndSub, displayName: tt('AND SUB') },
{ value: TransactionExplorerConditionRelation.OrSub, displayName: tt('OR SUB') }
]"
v-model="conditionWithRelation.relation"
:model-value="conditionWithRelation.relation"
@update:model-value="updateItemRelation(element, conditionIndex, $event)"
v-else-if="conditionIndex >= 1"
/>
@@ -335,6 +340,16 @@
<v-tooltip activator="parent">{{ tt('Remove Condition') }}</v-tooltip>
</v-btn>
</div>
<div class="d-flex overflow-x-auto align-center gap-2 mb-4"
:style="getConditionStyle(element, conditionIndex)"
v-if="conditionWithRelation.relation === TransactionExplorerConditionRelation.SubEnd">
<v-btn class="px-2" density="comfortable" color="primary" variant="text" size="small"
:prepend-icon="mdiPlus"
:disabled="loading || disabled || !!editingQuery"
@click="addSubCondition(element, conditionIndex)">
{{ tt('Add Sub Condition') }}
</v-btn>
</div>
</div>
</div>
<div v-else-if="element.conditions && element.conditions.length > 0 && showExpression[element.id]">
@@ -541,6 +556,25 @@ function getFilteredTransactionCategoriesDisplayContent(filterTransactionCategor
return joinMultiText(selectedCategoryNames);
}
function getConditionStyle(query: TransactionExplorerQuery, conditionIndex: number): Record<string, string> {
const style: Record<string, string> = {};
const depths = query.getConditionNestingDepths();
const item = query.conditions[conditionIndex];
let depth = 0;
if (item && item.relation === TransactionExplorerConditionRelation.SubEnd) {
depth = depths[conditionIndex - 1] ?? 0;
} else {
depth = depths[conditionIndex] ?? 0;
}
if (depth > 0) {
style['margin-inline-start'] = (depth * 1.5) + 'rem';
}
return style;
}
function addQuery(): void {
queries.value.push(TransactionExplorerQuery.create(generateRandomUUID()));
}
@@ -584,16 +618,88 @@ function addCondition(query: TransactionExplorerQuery): void {
query.conditions.push(newCondition);
}
function addSubCondition(query: TransactionExplorerQuery, subEndIndex: number): void {
const newCondition = query.addNewCondition(TransactionExplorerConditionField.TransactionType, false);
query.conditions.splice(subEndIndex, 0, newCondition);
}
function removeCondition(query: TransactionExplorerQuery, conditionIndex: number): void {
const item = query.conditions[conditionIndex];
if (!item || item.relation === TransactionExplorerConditionRelation.SubEnd) {
return;
}
query.conditions.splice(conditionIndex, 1);
if (conditionIndex === 0 && query.conditions.length > 0) {
const newFirstCondition = query.conditions[0];
if (newFirstCondition) {
if (newFirstCondition.relation === TransactionExplorerConditionRelation.AndSub || newFirstCondition.relation === TransactionExplorerConditionRelation.OrSub) {
removeSubCondition(query, conditionIndex + 1);
}
newFirstCondition.relation = TransactionExplorerConditionRelation.First;
}
}
const oldStartSubCondition = item.relation === TransactionExplorerConditionRelation.AndSub || item.relation === TransactionExplorerConditionRelation.OrSub;
if (oldStartSubCondition && conditionIndex < query.conditions.length && query.conditions[conditionIndex]) {
const nextItem = query.conditions[conditionIndex];
if (nextItem.relation === TransactionExplorerConditionRelation.SubEnd) {
query.conditions.splice(conditionIndex, 1);
} else if (nextItem.relation === TransactionExplorerConditionRelation.AndSub || nextItem.relation === TransactionExplorerConditionRelation.OrSub) {
nextItem.relation = item.relation;
removeSubCondition(query, conditionIndex);
} else {
nextItem.relation = item.relation;
}
}
}
function removeSubCondition(query: TransactionExplorerQuery, conditionIndex: number): void {
let depth = 1;
for (let i = conditionIndex + 1; i < query.conditions.length; i++) {
const currentCondition = query.conditions[i];
if (!currentCondition) {
continue;
}
if (currentCondition.relation === TransactionExplorerConditionRelation.AndSub || currentCondition.relation === TransactionExplorerConditionRelation.OrSub) {
depth++;
} else if (currentCondition.relation === TransactionExplorerConditionRelation.SubEnd) {
depth--;
if (depth === 0) {
query.conditions.splice(i, 1);
break;
}
}
}
}
function updateItemRelation(query: TransactionExplorerQuery, conditionIndex: number, value: TransactionExplorerConditionRelation): void {
const item = query.conditions[conditionIndex];
if (!item || item.relation === TransactionExplorerConditionRelation.SubEnd) {
return;
}
const oldStartSubCondition = item.relation === TransactionExplorerConditionRelation.AndSub || item.relation === TransactionExplorerConditionRelation.OrSub;
const newStartSubCondition = value === TransactionExplorerConditionRelation.AndSub || value === TransactionExplorerConditionRelation.OrSub;
item.relation = value;
if (!oldStartSubCondition && newStartSubCondition) {
query.conditions.splice(conditionIndex + 1, 0, query.addSubConditionEnd());
} else if (oldStartSubCondition && !newStartSubCondition) {
removeSubCondition(query, conditionIndex);
}
}
function updateConditionField(query: TransactionExplorerQuery, conditionIndex: number, newField: TransactionExplorerConditionField | undefined): void {