mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 17:24:26 +08:00
support drag-and-drop to change query display orders
This commit is contained in:
+35
-27
@@ -28,7 +28,7 @@ export class InsightsExplorerBasicInfo implements InsightsExplorerInfoResponse {
|
|||||||
public name: string;
|
public name: string;
|
||||||
public displayOrder: number;
|
public displayOrder: number;
|
||||||
public hidden: boolean;
|
public hidden: boolean;
|
||||||
public data: Record<string, string | number | string[]> = {};
|
public data: Record<string, string | number | object[]> = {};
|
||||||
|
|
||||||
private constructor(id: string, name: string, displayOrder: number, hidden: boolean) {
|
private constructor(id: string, name: string, displayOrder: number, hidden: boolean) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -98,9 +98,9 @@ export class InsightsExplorer implements InsightsExplorerInfoResponse {
|
|||||||
this.chartSortingType = chartSortingType;
|
this.chartSortingType = chartSortingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get data(): Record<string, string | number | string[]> {
|
public get data(): Record<string, string | number | object[]> {
|
||||||
return {
|
return {
|
||||||
queries: this.queries.map(q => q.toJson()),
|
queries: this.queries.map(q => q.toJsonObject()),
|
||||||
timezoneUsedForDateRange: this.timezoneUsedForDateRange,
|
timezoneUsedForDateRange: this.timezoneUsedForDateRange,
|
||||||
chartType: this.chartType,
|
chartType: this.chartType,
|
||||||
categoryDimension: this.categoryDimension,
|
categoryDimension: this.categoryDimension,
|
||||||
@@ -139,10 +139,10 @@ export class InsightsExplorer implements InsightsExplorerInfoResponse {
|
|||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
if (Array.isArray(data['queries'])) {
|
if (Array.isArray(data['queries'])) {
|
||||||
const textualQueries = data['queries'] as string[];
|
const queryItems = data['queries'] as object[];
|
||||||
|
|
||||||
for (const textualQuery of textualQueries) {
|
for (const queryItem of queryItems) {
|
||||||
const query = TransactionExplorerQuery.parse(textualQuery);
|
const query = TransactionExplorerQuery.parse(queryItem);
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
queries.push(query);
|
queries.push(query);
|
||||||
@@ -190,13 +190,13 @@ export class InsightsExplorer implements InsightsExplorerInfoResponse {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createNewExplorer(name?: string): InsightsExplorer {
|
public static createNewExplorer(newQueryId: string): InsightsExplorer {
|
||||||
return new InsightsExplorer(
|
return new InsightsExplorer(
|
||||||
'',
|
'',
|
||||||
name || '',
|
'',
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
[TransactionExplorerQuery.create()],
|
[TransactionExplorerQuery.create(newQueryId)],
|
||||||
InsightsExplorer.Default.timezoneUsedForDateRange,
|
InsightsExplorer.Default.timezoneUsedForDateRange,
|
||||||
InsightsExplorer.Default.chartType,
|
InsightsExplorer.Default.chartType,
|
||||||
InsightsExplorer.Default.categoryDimension,
|
InsightsExplorer.Default.categoryDimension,
|
||||||
@@ -209,14 +209,14 @@ export class InsightsExplorer implements InsightsExplorerInfoResponse {
|
|||||||
|
|
||||||
export interface InsightsExplorerCreateRequest {
|
export interface InsightsExplorerCreateRequest {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly data: Record<string, string | number | string[]>;
|
readonly data: Record<string, string | number | object[]>;
|
||||||
readonly clientSessionId?: string;
|
readonly clientSessionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InsightsExplorerModifyRequest {
|
export interface InsightsExplorerModifyRequest {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly data: Record<string, string | number | string[]>;
|
readonly data: Record<string, string | number | object[]>;
|
||||||
readonly hidden: boolean;
|
readonly hidden: boolean;
|
||||||
readonly clientSessionId?: string;
|
readonly clientSessionId?: string;
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,7 @@ export interface InsightsExplorerInfoResponse {
|
|||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly displayOrder: number;
|
readonly displayOrder: number;
|
||||||
readonly hidden: boolean;
|
readonly hidden: boolean;
|
||||||
readonly data: Record<string, string | number | string[]>;
|
readonly data: Record<string, string | number | object[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExpressionNode {
|
interface ExpressionNode {
|
||||||
@@ -253,10 +253,12 @@ interface ExpressionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TransactionExplorerQuery {
|
export class TransactionExplorerQuery {
|
||||||
|
public id: string;
|
||||||
public name: string;
|
public name: string;
|
||||||
public conditions: TransactionExplorerConditionWithRelation[];
|
public conditions: TransactionExplorerConditionWithRelation[];
|
||||||
|
|
||||||
private constructor(name: string, conditions: TransactionExplorerConditionWithRelation[]) {
|
private constructor(id: string, name: string, conditions: TransactionExplorerConditionWithRelation[]) {
|
||||||
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.conditions = conditions;
|
this.conditions = conditions;
|
||||||
}
|
}
|
||||||
@@ -450,7 +452,7 @@ export class TransactionExplorerQuery {
|
|||||||
return finalTokens;
|
return finalTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
public clone(): TransactionExplorerQuery {
|
public clone(newId: string): TransactionExplorerQuery {
|
||||||
const clonedConditions: TransactionExplorerConditionWithRelation[] = [];
|
const clonedConditions: TransactionExplorerConditionWithRelation[] = [];
|
||||||
|
|
||||||
for (const condition of this.conditions) {
|
for (const condition of this.conditions) {
|
||||||
@@ -463,29 +465,35 @@ export class TransactionExplorerQuery {
|
|||||||
clonedConditions.push(clonedCondition);
|
clonedConditions.push(clonedCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TransactionExplorerQuery(this.name, clonedConditions);
|
return new TransactionExplorerQuery(newId, this.name, clonedConditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toJson(): string {
|
public toJsonObject(): object {
|
||||||
return JSON.stringify({
|
return {
|
||||||
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
conditions: this.conditions.map(condition => condition.toJsonObject())
|
conditions: this.conditions.map(condition => condition.toJsonObject())
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(): TransactionExplorerQuery {
|
public static create(id: string): TransactionExplorerQuery {
|
||||||
return new TransactionExplorerQuery("", []);
|
return new TransactionExplorerQuery(id, "", []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static parse(json: string): TransactionExplorerQuery | null {
|
public static parse(obj: object): TransactionExplorerQuery | null {
|
||||||
const parsed = JSON.parse(json);
|
if (!('id' in obj) || !('name' in obj) || !('conditions' in obj)) {
|
||||||
const nameFieldValue = parsed['name'] as unknown;
|
|
||||||
const conditionsFieldValue = parsed['conditions'] as unknown;
|
|
||||||
|
|
||||||
if (typeof nameFieldValue !== 'string' || !Array.isArray(conditionsFieldValue)) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const idFieldValue = obj['id'] as unknown;
|
||||||
|
const nameFieldValue = obj['name'] as unknown;
|
||||||
|
const conditionsFieldValue = obj['conditions'] as unknown;
|
||||||
|
|
||||||
|
if (typeof idFieldValue !== 'string' || typeof nameFieldValue !== 'string' || !Array.isArray(conditionsFieldValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id: string = idFieldValue;
|
||||||
const name: string = nameFieldValue;
|
const name: string = nameFieldValue;
|
||||||
const conditions: TransactionExplorerConditionWithRelation[] = [];
|
const conditions: TransactionExplorerConditionWithRelation[] = [];
|
||||||
|
|
||||||
@@ -505,7 +513,7 @@ export class TransactionExplorerQuery {
|
|||||||
conditions.push(condition);
|
conditions.push(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TransactionExplorerQuery(name, conditions);
|
return new TransactionExplorerQuery(id, name, conditions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import {
|
|||||||
getDateRangeByDateType,
|
getDateRangeByDateType,
|
||||||
getFiscalYearFromUnixTime
|
getFiscalYearFromUnixTime
|
||||||
} from '@/lib/datetime.ts';
|
} from '@/lib/datetime.ts';
|
||||||
|
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||||
import services, { type ApiResponsePromise } from '@/lib/services.ts';
|
import services, { type ApiResponsePromise } from '@/lib/services.ts';
|
||||||
import logger from '@/lib/logger.ts';
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
@@ -473,7 +474,7 @@ export const useExplorersStore = defineStore('explorers', () => {
|
|||||||
|
|
||||||
const allInsightsExplorerBasicInfos = ref<InsightsExplorerBasicInfo[]>([]);
|
const allInsightsExplorerBasicInfos = ref<InsightsExplorerBasicInfo[]>([]);
|
||||||
const allInsightsExplorerBasicInfosMap = ref<Record<string, InsightsExplorerBasicInfo>>({});
|
const allInsightsExplorerBasicInfosMap = ref<Record<string, InsightsExplorerBasicInfo>>({});
|
||||||
const currentInsightsExplorer = ref<InsightsExplorer>(InsightsExplorer.createNewExplorer());
|
const currentInsightsExplorer = ref<InsightsExplorer>(InsightsExplorer.createNewExplorer(generateRandomUUID()));
|
||||||
const insightsExplorerListStateInvalid = ref<boolean>(true);
|
const insightsExplorerListStateInvalid = ref<boolean>(true);
|
||||||
|
|
||||||
const allTransactions = computed<TransactionInsightDataItem[]>(() => {
|
const allTransactions = computed<TransactionInsightDataItem[]>(() => {
|
||||||
@@ -743,7 +744,7 @@ export const useExplorersStore = defineStore('explorers', () => {
|
|||||||
transactionExplorerFilter.value.startTime = 0;
|
transactionExplorerFilter.value.startTime = 0;
|
||||||
transactionExplorerFilter.value.endTime = 0;
|
transactionExplorerFilter.value.endTime = 0;
|
||||||
transactionExplorerAllData.value = [];
|
transactionExplorerAllData.value = [];
|
||||||
currentInsightsExplorer.value = InsightsExplorer.createNewExplorer();
|
currentInsightsExplorer.value = InsightsExplorer.createNewExplorer(generateRandomUUID());
|
||||||
transactionExplorerStateInvalid.value = true;
|
transactionExplorerStateInvalid.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,7 +785,7 @@ export const useExplorersStore = defineStore('explorers', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resetQuery) {
|
if (resetQuery) {
|
||||||
currentInsightsExplorer.value = InsightsExplorer.createNewExplorer();
|
currentInsightsExplorer.value = InsightsExplorer.createNewExplorer(generateRandomUUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ function init(initProps: InsightsExplorerProps): void {
|
|||||||
loadExplorer(initProps.initId);
|
loadExplorer(initProps.initId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
explorersStore.updateCurrentInsightsExplorer(InsightsExplorer.createNewExplorer());
|
explorersStore.updateCurrentInsightsExplorer(InsightsExplorer.createNewExplorer(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needReload && !explorersStore.transactionExplorerStateInvalid && !explorersStore.insightsExplorerListStateInvalid) {
|
if (!needReload && !explorersStore.transactionExplorerStateInvalid && !explorersStore.insightsExplorerListStateInvalid) {
|
||||||
@@ -394,7 +394,7 @@ function createNewExplorer(): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
explorersStore.updateCurrentInsightsExplorer(InsightsExplorer.createNewExplorer());
|
explorersStore.updateCurrentInsightsExplorer(InsightsExplorer.createNewExplorer(generateRandomUUID()));
|
||||||
router.push(getFilterLinkUrl());
|
router.push(getFilterLinkUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,340 +10,345 @@
|
|||||||
@click="clearAllQueries">{{ tt('Clear All') }}</v-btn>
|
@click="clearAllQueries">{{ tt('Clear All') }}</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</v-card-subtitle>
|
</v-card-subtitle>
|
||||||
<v-card-text class="pt-0">
|
<v-card-text class="pt-0 pb-0">
|
||||||
<div :key="queryIndex" v-for="(query, queryIndex) in queries">
|
<draggable-list
|
||||||
<v-card border class="card-title-with-bg mt-4">
|
item-key="id"
|
||||||
<v-card-title class="d-flex align-center py-2 px-5">
|
handle=".drag-handle"
|
||||||
<v-icon :icon="mdiTextBoxSearchOutline" size="20" />
|
ghost-class="dragging-item"
|
||||||
<span class="query-name text-subtitle-1 ms-2" v-if="editingQuery !== query">{{ query.name || tt('format.misc.queryIndex', { index: queryIndex + 1 }) }}</span>
|
v-model="queries"
|
||||||
<div class="query-name-edit ms-2" v-if="editingQuery === query">
|
>
|
||||||
<v-text-field autofocus type="text" density="compact" variant="underlined"
|
<template #item="{ element, index }">
|
||||||
:disabled="loading || disabled"
|
<v-card border class="card-title-with-bg mt-4 mb-8">
|
||||||
:placeholder="tt('format.misc.queryIndex', { index: queryIndex + 1 })"
|
<v-card-title class="d-flex align-center py-2 px-5">
|
||||||
v-text-field-auto-width="{ minWidth: 20, maxWidth: 300, auxSpanId: `query-name-aux-span-${queryIndex + 1}` }"
|
<v-icon :icon="mdiTextBoxSearchOutline" size="20" />
|
||||||
v-model="editingQueryName"
|
<span class="query-name text-subtitle-1 ms-2" v-if="editingQuery !== element">{{ element.name || tt('format.misc.queryIndex', { index: index + 1 }) }}</span>
|
||||||
@keyup.esc="cancelUpdateQueryName"
|
<div class="query-name-edit ms-2" v-if="editingQuery === element">
|
||||||
@keyup.enter="updateQueryName(query)" />
|
<v-text-field autofocus type="text" density="compact" variant="underlined"
|
||||||
<span :id="`query-name-aux-span-${queryIndex + 1}`" />
|
:disabled="loading || disabled"
|
||||||
</div>
|
:placeholder="tt('format.misc.queryIndex', { index: index + 1 })"
|
||||||
<v-btn class="ms-2" density="compact" color="primary" variant="text" size="small"
|
v-text-field-auto-width="{ minWidth: 20, maxWidth: 300, auxSpanId: `query-name-aux-span-${index + 1}-${element.id}` }"
|
||||||
:icon="true" :disabled="loading || disabled"
|
v-model="editingQueryName"
|
||||||
@click="updateQueryName(query)"
|
@keyup.esc="cancelUpdateQueryName"
|
||||||
v-if="editingQuery === query">
|
@keyup.enter="updateQueryName(element)" />
|
||||||
<v-icon :icon="mdiCheck" size="18" />
|
<span :id="`query-name-aux-span-${index + 1}-${element.id}`" />
|
||||||
<v-tooltip activator="parent">{{ tt('Update') }}</v-tooltip>
|
</div>
|
||||||
</v-btn>
|
<v-btn class="ms-2" density="compact" color="primary" variant="text" size="small"
|
||||||
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
:icon="true" :disabled="loading || disabled"
|
||||||
:icon="true" :disabled="loading || disabled"
|
@click="updateQueryName(element)"
|
||||||
@click="cancelUpdateQueryName"
|
v-if="editingQuery === element">
|
||||||
v-if="editingQuery === query">
|
<v-icon :icon="mdiCheck" size="18" />
|
||||||
<v-icon :icon="mdiClose" size="18" />
|
<v-tooltip activator="parent">{{ tt('Update') }}</v-tooltip>
|
||||||
<v-tooltip activator="parent">{{ tt('Cancel') }}</v-tooltip>
|
</v-btn>
|
||||||
</v-btn>
|
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
||||||
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
:icon="true" :disabled="loading || disabled"
|
||||||
:icon="true" :disabled="loading || disabled || !!editingQuery"
|
@click="cancelUpdateQueryName"
|
||||||
@click="editingQueryName = query.name; editingQuery = query"
|
v-if="editingQuery === element">
|
||||||
v-if="!editingQuery || editingQuery !== query">
|
<v-icon :icon="mdiClose" size="18" />
|
||||||
<v-icon :icon="mdiPencilOutline" size="18" />
|
<v-tooltip activator="parent">{{ tt('Cancel') }}</v-tooltip>
|
||||||
<v-tooltip activator="parent">{{ tt('Modify Query Name') }}</v-tooltip>
|
</v-btn>
|
||||||
</v-btn>
|
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
||||||
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
:icon="true" :disabled="loading || disabled || !!editingQuery"
|
||||||
:icon="true" :disabled="loading || disabled || !!editingQuery"
|
@click="editingQueryName = element.name; editingQuery = element"
|
||||||
@click="duplicateQuery(query)"
|
v-if="!editingQuery || editingQuery !== element">
|
||||||
v-if="!editingQuery || editingQuery !== query">
|
<v-icon :icon="mdiPencilOutline" size="18" />
|
||||||
<v-icon :icon="mdiContentCopy" size="18" />
|
<v-tooltip activator="parent">{{ tt('Modify Query Name') }}</v-tooltip>
|
||||||
<v-tooltip activator="parent">{{ tt('Duplicate') }}</v-tooltip>
|
</v-btn>
|
||||||
</v-btn>
|
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
||||||
<v-spacer />
|
:icon="true" :disabled="loading || disabled || !!editingQuery"
|
||||||
<v-switch class="bidirectional-switch ms-2" color="secondary"
|
@click="duplicateQuery(element)"
|
||||||
:disabled="loading || disabled || !!editingQuery || !query.conditions || query.conditions.length < 1"
|
v-if="!editingQuery || editingQuery !== element">
|
||||||
:label="tt('Expression')"
|
<v-icon :icon="mdiContentCopy" size="18" />
|
||||||
v-model="showExpression[queryIndex]"
|
<v-tooltip activator="parent">{{ tt('Duplicate') }}</v-tooltip>
|
||||||
@click="showExpression[queryIndex] = !showExpression[queryIndex]">
|
</v-btn>
|
||||||
<template #prepend>
|
<v-spacer />
|
||||||
<span>{{ tt('Editor') }}</span>
|
<v-switch class="bidirectional-switch ms-2" color="secondary"
|
||||||
</template>
|
:disabled="loading || disabled || !!editingQuery || !element.conditions || element.conditions.length < 1"
|
||||||
</v-switch>
|
:label="tt('Expression')"
|
||||||
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
v-model="showExpression[element.id]"
|
||||||
:icon="true" :disabled="loading || disabled || !!editingQuery || queries.length < 1"
|
@click="showExpression[element.id] = !showExpression[element.id]">
|
||||||
@click="removeQuery(queryIndex)">
|
<template #prepend>
|
||||||
<v-icon :icon="mdiClose" size="18" />
|
<span>{{ tt('Editor') }}</span>
|
||||||
<v-tooltip activator="parent">{{ tt('Remove Query') }}</v-tooltip>
|
</template>
|
||||||
</v-btn>
|
</v-switch>
|
||||||
</v-card-title>
|
<v-btn class="ms-2" density="compact" color="default" variant="text" size="small"
|
||||||
|
:icon="true" :disabled="loading || disabled || !!editingQuery || queries.length < 1 || (queries.length === 1 && (!element.conditions || element.conditions.length < 1))"
|
||||||
|
@click="removeQuery(index)">
|
||||||
|
<v-icon :icon="mdiClose" size="18" />
|
||||||
|
<v-tooltip activator="parent">{{ tt('Remove Query') }}</v-tooltip>
|
||||||
|
</v-btn>
|
||||||
|
<span class="ms-2 mb-1">
|
||||||
|
<v-icon :class="!loading && !disabled && !editingQuery && queries.length > 1 ? 'drag-handle' : 'disabled'"
|
||||||
|
:icon="mdiDrag"/>
|
||||||
|
<v-tooltip activator="parent" v-if="!loading && !disabled && !editingQuery && queries.length > 1">{{ tt('Drag to Reorder') }}</v-tooltip>
|
||||||
|
</span>
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
<v-divider />
|
<v-divider />
|
||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<div class="text-center py-4" v-if="!query.conditions || query.conditions.length < 1">
|
<div class="text-center py-4" v-if="!element.conditions || element.conditions.length < 1">
|
||||||
{{ tt('No conditions defined. All transactions will match.') }}
|
{{ tt('No conditions defined. All transactions will match.') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="query.conditions && query.conditions.length > 0 && !showExpression[queryIndex]">
|
<div v-else-if="element.conditions && element.conditions.length > 0 && !showExpression[element.id]">
|
||||||
<div :key="conditionIndex" v-for="(conditionWithRelation, conditionIndex) in query.conditions">
|
<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">
|
||||||
<v-select
|
|
||||||
disabled
|
|
||||||
class="flex-0-0"
|
|
||||||
width="120px"
|
|
||||||
density="compact"
|
|
||||||
item-title="displayName"
|
|
||||||
item-value="value"
|
|
||||||
:items="[{ value: TransactionExplorerConditionRelation.First, displayName: tt('WHERE') }]"
|
|
||||||
:model-value="TransactionExplorerConditionRelation.First"
|
|
||||||
v-if="conditionIndex < 1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-select
|
|
||||||
class="flex-0-0"
|
|
||||||
width="120px"
|
|
||||||
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') }
|
|
||||||
]"
|
|
||||||
v-model="conditionWithRelation.relation"
|
|
||||||
v-else-if="conditionIndex >= 1"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-select
|
|
||||||
class="flex-0-0"
|
|
||||||
density="compact"
|
|
||||||
item-title="name"
|
|
||||||
item-value="value"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
:items="allTransactionExplorerConditionFields"
|
|
||||||
@update:model-value="updateConditionField(queryIndex, conditionIndex, TransactionExplorerConditionField.valueOf($event))"
|
|
||||||
v-model="conditionWithRelation.condition.field"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-select
|
|
||||||
class="flex-0-0"
|
|
||||||
density="compact"
|
|
||||||
item-title="name"
|
|
||||||
item-value="value"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
:items="getAllTransactionExplorerConditionOperators(conditionWithRelation.getSupportedOperators())"
|
|
||||||
v-model="conditionWithRelation.condition.operator"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="d-flex w-100 flex-1-1" style="min-width: 280px;">
|
|
||||||
<v-select
|
<v-select
|
||||||
multiple chips closable-chips
|
disabled
|
||||||
|
class="flex-0-0"
|
||||||
|
width="120px"
|
||||||
density="compact"
|
density="compact"
|
||||||
item-title="displayName"
|
item-title="displayName"
|
||||||
item-value="type"
|
item-value="value"
|
||||||
|
:items="[{ value: TransactionExplorerConditionRelation.First, displayName: tt('WHERE') }]"
|
||||||
|
:model-value="TransactionExplorerConditionRelation.First"
|
||||||
|
v-if="conditionIndex < 1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-select
|
||||||
|
class="flex-0-0"
|
||||||
|
width="120px"
|
||||||
|
density="compact"
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="value"
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
:placeholder="tt('None')"
|
|
||||||
:items="[
|
:items="[
|
||||||
{ type: TransactionType.Expense, displayName: tt('Expense') },
|
{ value: TransactionExplorerConditionRelation.And, displayName: tt('AND') },
|
||||||
{ type: TransactionType.Income, displayName: tt('Income') },
|
{ value: TransactionExplorerConditionRelation.Or, displayName: tt('OR') }
|
||||||
{ type: TransactionType.Transfer, displayName: tt('Transfer') }
|
|
||||||
]"
|
]"
|
||||||
v-model="conditionWithRelation.condition.value"
|
v-model="conditionWithRelation.relation"
|
||||||
v-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionType.value"
|
v-else-if="conditionIndex >= 1"
|
||||||
>
|
/>
|
||||||
<template #item="{ props, item }">
|
|
||||||
<v-list-item :value="item.value" v-bind="props">
|
|
||||||
<template #title>
|
|
||||||
<v-list-item-title>
|
|
||||||
<div class="d-flex align-center">{{ item.title }}</div>
|
|
||||||
</v-list-item-title>
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
</v-select>
|
|
||||||
|
|
||||||
<v-text-field
|
<v-select
|
||||||
class="always-cursor-pointer text-field-truncate"
|
class="flex-0-0"
|
||||||
density="compact"
|
density="compact"
|
||||||
item-title="displayName"
|
item-title="name"
|
||||||
item-value="type"
|
item-value="value"
|
||||||
persistent-placeholder
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
:readonly="true"
|
:items="allTransactionExplorerConditionFields"
|
||||||
:disabled="loading || disabled || !!editingQuery || !hasAnyTransactionCategory"
|
@update:model-value="updateConditionField(element, conditionIndex, TransactionExplorerConditionField.valueOf($event))"
|
||||||
:placeholder="tt('None')"
|
v-model="conditionWithRelation.condition.field"
|
||||||
:model-value="getFilteredTransactionCategoriesDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
|
||||||
@click="currentCondition = conditionWithRelation.condition; showFilterTransactionCategoriesDialog = true"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionCategory.value"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-text-field
|
<v-select
|
||||||
class="always-cursor-pointer text-field-truncate"
|
class="flex-0-0"
|
||||||
density="compact"
|
density="compact"
|
||||||
item-title="displayName"
|
item-title="name"
|
||||||
item-value="type"
|
item-value="value"
|
||||||
persistent-placeholder
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
:readonly="true"
|
:items="getAllTransactionExplorerConditionOperators(conditionWithRelation.getSupportedOperators())"
|
||||||
:disabled="loading || disabled || !!editingQuery || !hasAnyAccount"
|
v-model="conditionWithRelation.condition.operator"
|
||||||
:placeholder="tt('None')"
|
|
||||||
:model-value="getFilteredAccountsDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
|
||||||
@click="currentCondition = conditionWithRelation.condition; showFilterSourceAccountsDialog = true"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.SourceAccount.value"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-text-field
|
<div class="d-flex w-100 flex-1-1" style="min-width: 280px;">
|
||||||
class="always-cursor-pointer text-field-truncate"
|
<v-select
|
||||||
density="compact"
|
multiple chips closable-chips
|
||||||
item-title="displayName"
|
|
||||||
item-value="type"
|
|
||||||
persistent-placeholder
|
|
||||||
:readonly="true"
|
|
||||||
:disabled="loading || disabled || !!editingQuery || !hasAnyAccount"
|
|
||||||
:placeholder="tt('None')"
|
|
||||||
:model-value="getFilteredAccountsDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
|
||||||
@click="currentCondition = conditionWithRelation.condition; showFilterDestinationAccountsDialog = true"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.DestinationAccount.value"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="d-flex w-100 align-center gap-2"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.SourceAmount.value ||
|
|
||||||
conditionWithRelation.condition.field === TransactionExplorerConditionField.DestinationAmount.value">
|
|
||||||
<amount-input density="compact"
|
|
||||||
:currency="defaultCurrency"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
v-model="conditionWithRelation.condition.value[0]"
|
|
||||||
/>
|
|
||||||
<span class="ms-2 me-2"
|
|
||||||
v-if="conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.Between.value ||
|
|
||||||
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.NotBetween.value">~</span>
|
|
||||||
<amount-input density="compact"
|
|
||||||
:currency="defaultCurrency"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
v-model="conditionWithRelation.condition.value[1]"
|
|
||||||
v-if="conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.Between.value ||
|
|
||||||
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.NotBetween.value"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-text-field disabled density="compact"
|
|
||||||
:placeholder="tt('None')"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.GeoLocation.value"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="d-flex w-100" v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionTag.value">
|
|
||||||
<v-text-field
|
|
||||||
disabled
|
|
||||||
persistent-placeholder
|
|
||||||
density="compact"
|
density="compact"
|
||||||
:placeholder="tt('None')"
|
item-title="displayName"
|
||||||
v-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionTag.value &&
|
item-value="type"
|
||||||
(conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsEmpty.value || conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsNotEmpty.value)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-autocomplete
|
|
||||||
density="compact"
|
|
||||||
item-title="name"
|
|
||||||
item-value="id"
|
|
||||||
auto-select-first
|
|
||||||
persistent-placeholder
|
|
||||||
multiple
|
|
||||||
chips
|
|
||||||
closable-chips
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
:placeholder="tt('None')"
|
:placeholder="tt('None')"
|
||||||
:items="allTags"
|
:items="[
|
||||||
|
{ type: TransactionType.Expense, displayName: tt('Expense') },
|
||||||
|
{ type: TransactionType.Income, displayName: tt('Income') },
|
||||||
|
{ type: TransactionType.Transfer, displayName: tt('Transfer') }
|
||||||
|
]"
|
||||||
v-model="conditionWithRelation.condition.value"
|
v-model="conditionWithRelation.condition.value"
|
||||||
v-model:search="tagSearchContent"
|
v-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionType.value"
|
||||||
v-else-if="conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsEmpty.value && conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsNotEmpty.value"
|
|
||||||
>
|
>
|
||||||
<template #chip="{ props, item }">
|
|
||||||
<v-chip :prepend-icon="mdiPound" :text="item.title" v-bind="props"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #item="{ props, item }">
|
<template #item="{ props, item }">
|
||||||
<v-list-item :value="item.value" v-bind="props" v-if="!item.raw.hidden">
|
<v-list-item :value="item.value" v-bind="props">
|
||||||
<template #title>
|
<template #title>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">{{ item.title }}</div>
|
||||||
<v-icon size="20" start :icon="mdiPound"/>
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
</div>
|
|
||||||
</v-list-item-title>
|
|
||||||
</template>
|
|
||||||
</v-list-item>
|
|
||||||
<v-list-item :disabled="true" v-bind="props"
|
|
||||||
v-if="item.raw.hidden && item.raw.name.toLowerCase().indexOf(tagSearchContent.toLowerCase()) >= 0 && isAllFilteredTagHidden">
|
|
||||||
<template #title>
|
|
||||||
<v-list-item-title>
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<v-icon size="20" start :icon="mdiPound"/>
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
</div>
|
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</template>
|
</template>
|
||||||
|
</v-select>
|
||||||
|
|
||||||
<template #no-data>
|
<v-text-field
|
||||||
<v-list class="py-0">
|
class="always-cursor-pointer text-field-truncate"
|
||||||
<v-list-item>{{ tt('No available tag') }}</v-list-item>
|
density="compact"
|
||||||
</v-list>
|
item-title="displayName"
|
||||||
</template>
|
item-value="type"
|
||||||
</v-autocomplete>
|
persistent-placeholder
|
||||||
|
:readonly="true"
|
||||||
|
:disabled="loading || disabled || !!editingQuery || !hasAnyTransactionCategory"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
:model-value="getFilteredTransactionCategoriesDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
||||||
|
@click="currentCondition = conditionWithRelation.condition; showFilterTransactionCategoriesDialog = true"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionCategory.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
class="always-cursor-pointer text-field-truncate"
|
||||||
|
density="compact"
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="type"
|
||||||
|
persistent-placeholder
|
||||||
|
:readonly="true"
|
||||||
|
:disabled="loading || disabled || !!editingQuery || !hasAnyAccount"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
:model-value="getFilteredAccountsDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
||||||
|
@click="currentCondition = conditionWithRelation.condition; showFilterSourceAccountsDialog = true"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.SourceAccount.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-text-field
|
||||||
|
class="always-cursor-pointer text-field-truncate"
|
||||||
|
density="compact"
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="type"
|
||||||
|
persistent-placeholder
|
||||||
|
:readonly="true"
|
||||||
|
:disabled="loading || disabled || !!editingQuery || !hasAnyAccount"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
:model-value="getFilteredAccountsDisplayContent(arrayItemToObjectField(conditionWithRelation.condition.value as string[], true))"
|
||||||
|
@click="currentCondition = conditionWithRelation.condition; showFilterDestinationAccountsDialog = true"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.DestinationAccount.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="d-flex w-100 align-center gap-2"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.SourceAmount.value ||
|
||||||
|
conditionWithRelation.condition.field === TransactionExplorerConditionField.DestinationAmount.value">
|
||||||
|
<amount-input density="compact"
|
||||||
|
:currency="defaultCurrency"
|
||||||
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
|
v-model="conditionWithRelation.condition.value[0]"
|
||||||
|
/>
|
||||||
|
<span class="ms-2 me-2"
|
||||||
|
v-if="conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.Between.value ||
|
||||||
|
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.NotBetween.value">~</span>
|
||||||
|
<amount-input density="compact"
|
||||||
|
:currency="defaultCurrency"
|
||||||
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
|
v-model="conditionWithRelation.condition.value[1]"
|
||||||
|
v-if="conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.Between.value ||
|
||||||
|
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.NotBetween.value"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-text-field disabled density="compact"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.GeoLocation.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="d-flex w-100" v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionTag.value">
|
||||||
|
<v-text-field
|
||||||
|
disabled
|
||||||
|
persistent-placeholder
|
||||||
|
density="compact"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
v-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.TransactionTag.value &&
|
||||||
|
(conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsEmpty.value || conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsNotEmpty.value)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-autocomplete
|
||||||
|
density="compact"
|
||||||
|
item-title="name"
|
||||||
|
item-value="id"
|
||||||
|
auto-select-first
|
||||||
|
persistent-placeholder
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
closable-chips
|
||||||
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
:items="allTags"
|
||||||
|
v-model="conditionWithRelation.condition.value"
|
||||||
|
v-model:search="tagSearchContent"
|
||||||
|
v-else-if="conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsEmpty.value && conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsNotEmpty.value"
|
||||||
|
>
|
||||||
|
<template #chip="{ props, item }">
|
||||||
|
<v-chip :prepend-icon="mdiPound" :text="item.title" v-bind="props"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #item="{ props, item }">
|
||||||
|
<v-list-item :value="item.value" v-bind="props" v-if="!item.raw.hidden">
|
||||||
|
<template #title>
|
||||||
|
<v-list-item-title>
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="20" start :icon="mdiPound"/>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
<v-list-item :disabled="true" v-bind="props"
|
||||||
|
v-if="item.raw.hidden && item.raw.name.toLowerCase().indexOf(tagSearchContent.toLowerCase()) >= 0 && isAllFilteredTagHidden">
|
||||||
|
<template #title>
|
||||||
|
<v-list-item-title>
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="20" start :icon="mdiPound"/>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #no-data>
|
||||||
|
<v-list class="py-0">
|
||||||
|
<v-list-item>{{ tt('No available tag') }}</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</template>
|
||||||
|
</v-autocomplete>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-text-field disabled density="compact"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Pictures.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-text-field disabled density="compact"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Description.value &&
|
||||||
|
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsEmpty.value || conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsNotEmpty.value"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-text-field density="compact"
|
||||||
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
|
:placeholder="tt('None')"
|
||||||
|
v-model="conditionWithRelation.condition.value"
|
||||||
|
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Description.value &&
|
||||||
|
conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsEmpty.value && conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsNotEmpty.value"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-text-field disabled density="compact"
|
<v-btn color="default" density="compact"
|
||||||
:placeholder="tt('None')"
|
variant="text" size="small"
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Pictures.value"
|
:icon="true"
|
||||||
/>
|
:disabled="loading || disabled || !!editingQuery"
|
||||||
|
@click="removeCondition(element, conditionIndex)">
|
||||||
<v-text-field disabled density="compact"
|
<v-icon :icon="mdiClose" size="18" />
|
||||||
:placeholder="tt('None')"
|
<v-tooltip activator="parent">{{ tt('Remove Condition') }}</v-tooltip>
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Description.value &&
|
</v-btn>
|
||||||
conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsEmpty.value || conditionWithRelation.condition.operator === TransactionExplorerConditionOperator.IsNotEmpty.value"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-text-field density="compact"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
:placeholder="tt('None')"
|
|
||||||
v-model="conditionWithRelation.condition.value"
|
|
||||||
v-else-if="conditionWithRelation.condition.field === TransactionExplorerConditionField.Description.value &&
|
|
||||||
conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsEmpty.value && conditionWithRelation.condition.operator !== TransactionExplorerConditionOperator.IsNotEmpty.value"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-btn color="default" density="compact"
|
|
||||||
variant="text" size="small"
|
|
||||||
:icon="true"
|
|
||||||
:disabled="loading || disabled || !!editingQuery"
|
|
||||||
@click="removeCondition(queryIndex, conditionIndex)">
|
|
||||||
<v-icon :icon="mdiClose" size="18" />
|
|
||||||
<v-tooltip activator="parent">{{ tt('Remove Condition') }}</v-tooltip>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-else-if="element.conditions && element.conditions.length > 0 && showExpression[element.id]">
|
||||||
<div v-else-if="query.conditions && query.conditions.length > 0 && showExpression[queryIndex]">
|
<div class="w-100 code-container">
|
||||||
<div class="w-100 code-container">
|
<v-textarea class="w-100 always-cursor-text mb-4" :readonly="true"
|
||||||
<v-textarea class="w-100 always-cursor-text mb-4" :readonly="true"
|
:value="getExpression(element, index)"></v-textarea>
|
||||||
:value="getExpression(queryIndex)"></v-textarea>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-btn class="px-2" density="comfortable" color="primary" variant="text" size="small"
|
<v-btn class="px-2" density="comfortable" color="primary" variant="text" size="small"
|
||||||
:prepend-icon="mdiPlus"
|
:prepend-icon="mdiPlus"
|
||||||
:disabled="loading || disabled || !!editingQuery || showExpression[queryIndex]"
|
:disabled="loading || disabled || !!editingQuery || showExpression[element.id]"
|
||||||
@click="addCondition(queryIndex)">
|
@click="addCondition(element)">
|
||||||
{{ tt('Add Condition') }}
|
{{ tt('Add Condition') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
</template>
|
||||||
<div class="query-group-separator d-flex align-center justify-center my-4"
|
</draggable-list>
|
||||||
v-if="queries.length > 1 && queryIndex < queries.length - 1">
|
|
||||||
<v-chip color="primary" variant="outlined" size="small">
|
|
||||||
{{ tt('or') }}
|
|
||||||
</v-chip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-dialog width="800" v-model="showFilterSourceAccountsDialog">
|
<v-dialog width="800" v-model="showFilterSourceAccountsDialog">
|
||||||
@@ -407,6 +412,8 @@ import {
|
|||||||
arrayItemToObjectField
|
arrayItemToObjectField
|
||||||
} from '@/lib/common.ts';
|
} from '@/lib/common.ts';
|
||||||
|
|
||||||
|
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||||
|
|
||||||
import logger from '@/lib/logger.ts';
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -416,6 +423,7 @@ import {
|
|||||||
mdiContentCopy,
|
mdiContentCopy,
|
||||||
mdiCheck,
|
mdiCheck,
|
||||||
mdiClose,
|
mdiClose,
|
||||||
|
mdiDrag,
|
||||||
mdiPound
|
mdiPound
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
|
|
||||||
@@ -444,7 +452,7 @@ const explorersStore = useExplorersStore();
|
|||||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||||
|
|
||||||
const currentCondition = ref<TransactionExplorerCondition | undefined>(undefined);
|
const currentCondition = ref<TransactionExplorerCondition | undefined>(undefined);
|
||||||
const showExpression = ref<Record<number, boolean>>({});
|
const showExpression = ref<Record<string, boolean>>({});
|
||||||
const showFilterSourceAccountsDialog = ref<boolean>(false);
|
const showFilterSourceAccountsDialog = ref<boolean>(false);
|
||||||
const showFilterDestinationAccountsDialog = ref<boolean>(false);
|
const showFilterDestinationAccountsDialog = ref<boolean>(false);
|
||||||
const showFilterTransactionCategoriesDialog = ref<boolean>(false);
|
const showFilterTransactionCategoriesDialog = ref<boolean>(false);
|
||||||
@@ -452,7 +460,12 @@ const tagSearchContent = ref<string>('');
|
|||||||
const editingQuery = ref<TransactionExplorerQuery | undefined>(undefined);
|
const editingQuery = ref<TransactionExplorerQuery | undefined>(undefined);
|
||||||
const editingQueryName = ref<string>('');
|
const editingQueryName = ref<string>('');
|
||||||
|
|
||||||
const queries = computed<TransactionExplorerQuery[]>(() => explorersStore.currentInsightsExplorer.queries);
|
const queries = computed<TransactionExplorerQuery[]>({
|
||||||
|
get: () => explorersStore.currentInsightsExplorer.queries,
|
||||||
|
set: (value: TransactionExplorerQuery[]) => {
|
||||||
|
explorersStore.currentInsightsExplorer.queries = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
const hasAnyAccount = computed<boolean>(() => accountsStore.allPlainAccounts.length > 0);
|
const hasAnyAccount = computed<boolean>(() => accountsStore.allPlainAccounts.length > 0);
|
||||||
@@ -545,7 +558,7 @@ function getFilteredTransactionCategoriesDisplayContent(filterTransactionCategor
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addQuery(): void {
|
function addQuery(): void {
|
||||||
queries.value.push(TransactionExplorerQuery.create());
|
queries.value.push(TransactionExplorerQuery.create(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateQueryName(query: TransactionExplorerQuery): void {
|
function updateQueryName(query: TransactionExplorerQuery): void {
|
||||||
@@ -560,7 +573,7 @@ function cancelUpdateQueryName(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function duplicateQuery(query: TransactionExplorerQuery): void {
|
function duplicateQuery(query: TransactionExplorerQuery): void {
|
||||||
queries.value.push(query.clone());
|
queries.value.push(query.clone(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeQuery(queryIndex: number): void {
|
function removeQuery(queryIndex: number): void {
|
||||||
@@ -583,33 +596,21 @@ function removeQuery(queryIndex: number): void {
|
|||||||
showExpression.value = newShowExpression;
|
showExpression.value = newShowExpression;
|
||||||
|
|
||||||
if (queries.value.length < 1) {
|
if (queries.value.length < 1) {
|
||||||
queries.value.push(TransactionExplorerQuery.create());
|
queries.value.push(TransactionExplorerQuery.create(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAllQueries(): void {
|
function clearAllQueries(): void {
|
||||||
queries.value.length = 0;
|
queries.value.length = 0;
|
||||||
queries.value.push(TransactionExplorerQuery.create());
|
queries.value.push(TransactionExplorerQuery.create(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCondition(queryIndex: number): void {
|
function addCondition(query: TransactionExplorerQuery): void {
|
||||||
const query = queries.value[queryIndex];
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newCondition = query.addNewCondition(TransactionExplorerConditionField.TransactionType, query.conditions.length < 1);
|
const newCondition = query.addNewCondition(TransactionExplorerConditionField.TransactionType, query.conditions.length < 1);
|
||||||
query.conditions.push(newCondition);
|
query.conditions.push(newCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCondition(queryIndex: number, conditionIndex: number): void {
|
function removeCondition(query: TransactionExplorerQuery, conditionIndex: number): void {
|
||||||
const query = queries.value[queryIndex];
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
query.conditions.splice(conditionIndex, 1);
|
query.conditions.splice(conditionIndex, 1);
|
||||||
|
|
||||||
if (conditionIndex === 0 && query.conditions.length > 0) {
|
if (conditionIndex === 0 && query.conditions.length > 0) {
|
||||||
@@ -621,17 +622,11 @@ function removeCondition(queryIndex: number, conditionIndex: number): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateConditionField(queryIndex: number, conditionIndex: number, newField: TransactionExplorerConditionField | undefined): void {
|
function updateConditionField(query: TransactionExplorerQuery, conditionIndex: number, newField: TransactionExplorerConditionField | undefined): void {
|
||||||
if (!newField) {
|
if (!newField) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = queries.value[queryIndex];
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldConditionWithRelation = query.conditions[conditionIndex];
|
const oldConditionWithRelation = query.conditions[conditionIndex];
|
||||||
|
|
||||||
if (!oldConditionWithRelation) {
|
if (!oldConditionWithRelation) {
|
||||||
@@ -675,10 +670,8 @@ function updateTransactionCategories(changed: boolean, selectedCategoryIds?: str
|
|||||||
showFilterTransactionCategoriesDialog.value = false;
|
showFilterTransactionCategoriesDialog.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExpression(queryIndex: number): string {
|
function getExpression(query: TransactionExplorerQuery, queryIndex: number): string {
|
||||||
const query = queries.value[queryIndex];
|
if (!query.conditions || query.conditions.length < 1) {
|
||||||
|
|
||||||
if (!query || !query.conditions || query.conditions.length < 1) {
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +685,7 @@ function getExpression(queryIndex: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (queries.value.length === 0) {
|
if (queries.value.length === 0) {
|
||||||
queries.value.push(TransactionExplorerQuery.create());
|
queries.value.push(TransactionExplorerQuery.create(generateRandomUUID()));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user