desktop version supports rtl

This commit is contained in:
MaysWind
2025-08-18 00:45:26 +08:00
parent 4eff3a337f
commit c00770201b
57 changed files with 502 additions and 371 deletions

View File

@@ -76,8 +76,13 @@
"vue-tsc": "^2.2.10" "vue-tsc": "^2.2.10"
}, },
"browserslist": [ "browserslist": [
"> 1%", "last 5 Chrome versions",
"last 2 versions", "last 5 Firefox versions",
"last 5 Safari versions",
"last 5 Edge versions",
"last 5 ChromeAndroid versions",
"last 5 iOS versions",
"not IE <= 11",
"not dead" "not dead"
] ]
} }

View File

@@ -1,5 +1,8 @@
module.exports = { module.exports = {
plugins: { plugins: {
'postcss-preset-env': {}, 'autoprefixer': {
}, logical: false
},
'postcss-preset-env': {},
},
}; };

View File

@@ -7,7 +7,7 @@
<v-tooltip activator="parent">{{ tt('Click to close') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Click to close') }}</v-tooltip>
<div class="d-inline-flex"> <div class="d-inline-flex">
<img alt="logo" class="notification-logo" :src="APPLICATION_LOGO_PATH" /> <img alt="logo" class="notification-logo" :src="APPLICATION_LOGO_PATH" />
<span class="ml-2">{{ tt('global.app.title') }}</span> <span class="ms-2">{{ tt('global.app.title') }}</span>
</div> </div>
<div> <div>
{{ currentNotificationContent }} {{ currentNotificationContent }}

View File

@@ -307,8 +307,8 @@ init(props.length, props.modelValue);
.pin-code-input input { .pin-code-input input {
text-align: center; text-align: center;
padding-left: 10px; padding-inline-start: 10px;
padding-right: 10px; padding-inline-end: 10px;
width: 100%; width: 100%;
height: var(--ebk-pin-code-input-height) !important; height: var(--ebk-pin-code-input-height) !important;
} }

View File

@@ -13,6 +13,7 @@ import { type CommonAccountBalanceTrendsChartProps, useAccountBalanceTrendsChart
import { useUserStore } from '@/stores/user.ts'; import { useUserStore } from '@/stores/user.ts';
import type { NameValue } from '@/core/base.ts'; import type { NameValue } from '@/core/base.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue } from '@/core/color.ts'; import type { ColorValue } from '@/core/color.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
import { AccountBalanceTrendChartType } from '@/core/statistics.ts'; import { AccountBalanceTrendChartType } from '@/core/statistics.ts';
@@ -47,11 +48,12 @@ interface AccountBalanceTrendsChartDataItem {
const props = defineProps<DesktopAccountBalanceTrendsChartProps>(); const props = defineProps<DesktopAccountBalanceTrendsChartProps>();
const theme = useTheme(); const theme = useTheme();
const { tt, formatAmountToLocalizedNumeralsWithCurrency } = useI18n(); const { tt, getCurrentLanguageTextDirection, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const { allDataItems, allDisplayDateRanges } = useAccountBalanceTrendsChartBase(props); const { allDataItems, allDisplayDateRanges } = useAccountBalanceTrendsChartBase(props);
const userStore = useUserStore(); const userStore = useUserStore();
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark); const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const allSeries = computed<AccountBalanceTrendsChartDataItem[]>(() => { const allSeries = computed<AccountBalanceTrendsChartDataItem[]>(() => {
@@ -203,7 +205,7 @@ const chartOptions = computed<object>(() => {
for (let i = 0; i < displayItems.length; i++) { for (let i = 0; i < displayItems.length; i++) {
tooltip += `<div><span class="chart-pointer" style="background-color: #${DEFAULT_CHART_COLORS[i]}"></span>` tooltip += `<div><span class="chart-pointer" style="background-color: #${DEFAULT_CHART_COLORS[i]}"></span>`
+ `<span>${displayItems[i].name}</span><span style="margin-left: 20px; float: right">${displayItems[i].value}</span><br/>` + `<span>${displayItems[i].name}</span><span class="ms-5" style="float: inline-end">${displayItems[i].value}</span><br/>`
+ `</div>`; + `</div>`;
} }
@@ -214,7 +216,7 @@ const chartOptions = computed<object>(() => {
return `${params[0].name}<br/>` return `${params[0].name}<br/>`
+ '<div><span class="chart-pointer" style="background-color: #' + DEFAULT_CHART_COLORS[0] + '"></span>' + '<div><span class="chart-pointer" style="background-color: #' + DEFAULT_CHART_COLORS[0] + '"></span>'
+ `<span>${props.legendName}</span><span style="margin-left: 20px; float: right">${value}</span><br/>` + `<span>${props.legendName}</span><span class="ms-5" style="float: inline-end">${value}</span><br/>`
+ '</div>'; + '</div>';
} }
} }
@@ -226,7 +228,8 @@ const chartOptions = computed<object>(() => {
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: allDisplayDateRanges.value data: allDisplayDateRanges.value,
inverse: textDirection.value === TextDirection.RTL
} }
], ],
yAxis: [ yAxis: [

View File

@@ -13,7 +13,7 @@
<div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div> <div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div>
<v-tooltip :text="tt('Enter formula mode')"> <v-tooltip :text="tt('Enter formula mode')">
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-icon class="ml-2" :icon="mdiCalculatorVariantOutline" <v-icon class="ms-2" :icon="mdiCalculatorVariantOutline"
@keydown.enter="enterFormulaMode" @keydown.space="enterFormulaMode" @click="enterFormulaMode" @keydown.enter="enterFormulaMode" @keydown.space="enterFormulaMode" @click="enterFormulaMode"
v-bind="props" v-if="enableFormula && !formulaMode"></v-icon> v-bind="props" v-if="enableFormula && !formulaMode"></v-icon>
</template> </template>
@@ -34,14 +34,14 @@
<div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div> <div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div>
<v-tooltip :text="tt('Calculate formula result')"> <v-tooltip :text="tt('Calculate formula result')">
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-icon class="ml-2" color="primary" :icon="mdiCheck" <v-icon class="ms-2" color="primary" :icon="mdiCheck"
@click="calculateFormula" v-bind="props" @click="calculateFormula" v-bind="props"
v-if="formulaMode"></v-icon> v-if="formulaMode"></v-icon>
</template> </template>
</v-tooltip> </v-tooltip>
<v-tooltip :text="tt('Exit formula mode')"> <v-tooltip :text="tt('Exit formula mode')">
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-icon class="ml-2" color="secondary" :icon="mdiClose" <v-icon class="ms-2" color="secondary" :icon="mdiClose"
@click="exitFormulaMode" v-bind="props" @click="exitFormulaMode" v-bind="props"
v-if="formulaMode"></v-icon> v-if="formulaMode"></v-icon>
</template> </template>
@@ -379,6 +379,6 @@ watch(currentValue, (newValue) => {
} }
.text-field-with-colored-label.has-pretend-text .v-field__input { .text-field-with-colored-label.has-pretend-text .v-field__input {
padding-left: 0.5rem; padding-inline-start: 0.5rem;
} }
</style> </style>

View File

@@ -16,7 +16,7 @@
</template> </template>
<template #no-data> <template #no-data>
<div class="color-select-dropdown" ref="dropdownMenu"> <div class="color-select-dropdown px-2" ref="dropdownMenu">
<div class="color-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }" <div class="color-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`" :style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
:key="idx" v-for="(row, idx) in allColorRows"> :key="idx" v-for="(row, idx) in allColorRows">
@@ -26,7 +26,9 @@
:icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)" :icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)"
v-if="!modelValue || modelValue !== colorInfo.color" /> v-if="!modelValue || modelValue !== colorInfo.color" />
<v-badge class="right-bottom-icon" color="primary" <v-badge class="right-bottom-icon" color="primary"
location="bottom right" offset-x="8" offset-y="8" :icon="mdiCheck" offset-x="8" offset-y="8"
:location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
:icon="mdiCheck"
v-if="modelValue && modelValue === colorInfo.color"> v-if="modelValue && modelValue === colorInfo.color">
<v-icon class="ma-2" size="28" :icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)" /> <v-icon class="ma-2" size="28" :icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)" />
</v-badge> </v-badge>
@@ -41,6 +43,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, useTemplateRef, nextTick } from 'vue'; import { ref, computed, useTemplateRef, nextTick } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue, ColorInfo } from '@/core/color.ts'; import type { ColorValue, ColorInfo } from '@/core/color.ts';
import { DEFAULT_ICON_COLOR } from '@/consts/color.ts'; import { DEFAULT_ICON_COLOR } from '@/consts/color.ts';
import { arrayContainsFieldValue } from '@/lib/common.ts'; import { arrayContainsFieldValue } from '@/lib/common.ts';
@@ -64,9 +69,12 @@ const emit = defineEmits<{
(e: 'update:modelValue', value: ColorValue): void; (e: 'update:modelValue', value: ColorValue): void;
}>(); }>();
const { getCurrentLanguageTextDirection } = useI18n();
const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu'); const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu');
const itemPerRow = ref<number>(props.columnCount || 7); const itemPerRow = ref<number>(props.columnCount || 7);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const allColorRows = computed<ColorInfo[][]>(() => getColorsInRows(props.allColorInfos, itemPerRow.value)); const allColorRows = computed<ColorInfo[][]>(() => getColorsInRows(props.allColorInfos, itemPerRow.value));
const color = computed<ColorValue>({ const color = computed<ColorValue>({
@@ -98,11 +106,6 @@ function onMenuStateChanged(state: boolean): void {
</script> </script>
<style> <style>
.color-select-dropdown {
padding-left: 8px;
padding-right: 8px;
}
.color-select-dropdown .color-item { .color-select-dropdown .color-item {
display: grid; display: grid;
} }

View File

@@ -350,10 +350,10 @@ function onKeyDown(type: string, e: KeyboardEvent): void {
} }
.date-time-select-time-picker-container .v-autocomplete.v-input--density-compact .v-field__append-inner .v-autocomplete__menu-icon { .date-time-select-time-picker-container .v-autocomplete.v-input--density-compact .v-field__append-inner .v-autocomplete__menu-icon {
margin-left: 0; margin-inline-start: 0;
} }
.date-time-select-time-picker-container .v-autocomplete .v-field--appended { .date-time-select-time-picker-container .v-autocomplete .v-field--appended {
padding-right: 8px; padding-inline-end: 8px;
} }
</style> </style>

View File

@@ -16,7 +16,7 @@
</template> </template>
<template #no-data> <template #no-data>
<div class="icon-select-dropdown" ref="dropdownMenu"> <div class="icon-select-dropdown px-2" ref="dropdownMenu">
<div class="icon-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }" <div class="icon-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`" :style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
:key="idx" v-for="(row, idx) in allIconRows"> :key="idx" v-for="(row, idx) in allIconRows">
@@ -24,7 +24,9 @@
<div class="cursor-pointer" @click="icon = iconInfo.id"> <div class="cursor-pointer" @click="icon = iconInfo.id">
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" v-if="!modelValue || modelValue !== iconInfo.id" /> <ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" v-if="!modelValue || modelValue !== iconInfo.id" />
<v-badge class="right-bottom-icon" color="primary" <v-badge class="right-bottom-icon" color="primary"
location="bottom right" offset-x="8" offset-y="10" :icon="mdiCheck" offset-x="8" offset-y="10"
:location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
:icon="mdiCheck"
v-if="modelValue && modelValue === iconInfo.id"> v-if="modelValue && modelValue === iconInfo.id">
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" /> <ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" />
</v-badge> </v-badge>
@@ -39,6 +41,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, useTemplateRef, nextTick } from 'vue'; import { ref, computed, useTemplateRef, nextTick } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue } from '@/core/color.ts'; import type { ColorValue } from '@/core/color.ts';
import type { IconInfo, IconInfoWithId } from '@/core/icon.ts'; import type { IconInfo, IconInfoWithId } from '@/core/icon.ts';
import { arrayContainsFieldValue } from '@/lib/common.ts'; import { arrayContainsFieldValue } from '@/lib/common.ts';
@@ -63,9 +68,12 @@ const emit = defineEmits<{
(e: 'update:modelValue', value: string): void; (e: 'update:modelValue', value: string): void;
}>(); }>();
const { getCurrentLanguageTextDirection } = useI18n();
const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu'); const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu');
const itemPerRow = ref<number>(props.columnCount || 7); const itemPerRow = ref<number>(props.columnCount || 7);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const allIconRows = computed<IconInfoWithId[][]>(() => getIconsInRows(props.allIconInfos, itemPerRow.value)); const allIconRows = computed<IconInfoWithId[][]>(() => getIconsInRows(props.allIconInfos, itemPerRow.value));
const icon = computed<string>({ const icon = computed<string>({
@@ -89,11 +97,6 @@ function onMenuStateChanged(state: boolean): void {
</script> </script>
<style> <style>
.icon-select-dropdown {
padding-left: 8px;
padding-right: 8px;
}
.icon-select-dropdown .icon-item { .icon-select-dropdown .icon-item {
display: grid; display: grid;
} }

View File

@@ -2,9 +2,9 @@
<i class="item-icon" :class="classes" :style="style" v-if="!hiddenStatus"> <i class="item-icon" :class="classes" :style="style" v-if="!hiddenStatus">
<slot></slot> <slot></slot>
</i> </i>
<v-badge class="right-bottom-icon" color="secondary" <v-badge class="right-bottom-icon" color="secondary" offset-y="4"
location="bottom right" offset-y="4" :icon="mdiEyeOffOutline" :location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
v-if="hiddenStatus"> :icon="mdiEyeOffOutline" v-if="hiddenStatus">
<i class="item-icon" :class="classes" :style="style"> <i class="item-icon" :class="classes" :style="style">
<slot></slot> <slot></slot>
</i> </i>
@@ -15,6 +15,10 @@
import { computed } from 'vue'; import { computed } from 'vue';
import { type CommonIconProps, useItemIconBase } from '@/components/base/ItemIconBase.ts'; import { type CommonIconProps, useItemIconBase } from '@/components/base/ItemIconBase.ts';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import { import {
mdiEyeOffOutline mdiEyeOffOutline
} from '@mdi/js'; } from '@mdi/js';
@@ -25,8 +29,12 @@ interface DesktopItemIconProps extends CommonIconProps {
} }
const props = defineProps<DesktopItemIconProps>(); const props = defineProps<DesktopItemIconProps>();
const { getCurrentLanguageTextDirection } = useI18n();
const { style, getAccountIcon, getCategoryIcon } = useItemIconBase(props); const { style, getAccountIcon, getCategoryIcon } = useItemIconBase(props);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const classes = computed<string>(() => { const classes = computed<string>(() => {
let allClasses = props.class ? (props.class + ' ') : ''; let allClasses = props.class ? (props.class + ' ') : '';

View File

@@ -14,6 +14,7 @@ import { type CommonMonthlyTrendsChartProps, type MonthlyTrendsBarChartClickEven
import { useUserStore } from '@/stores/user.ts'; import { useUserStore } from '@/stores/user.ts';
import { TextDirection } from '@/core/text.ts';
import { type Year1BasedMonth, DateRangeScene } from '@/core/datetime.ts'; import { type Year1BasedMonth, DateRangeScene } from '@/core/datetime.ts';
import type { ColorValue } from '@/core/color.ts'; import type { ColorValue } from '@/core/color.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
@@ -71,7 +72,9 @@ const emit = defineEmits<{
const theme = useTheme(); const theme = useTheme();
const { tt, const {
tt,
getCurrentLanguageTextDirection,
formatUnixTimeToShortYear, formatUnixTimeToShortYear,
formatYearQuarter, formatYearQuarter,
formatUnixTimeToShortYearMonth, formatUnixTimeToShortYearMonth,
@@ -86,6 +89,7 @@ const userStore = useUserStore();
const selectedLegends = ref<Record<string, boolean>>({}); const selectedLegends = ref<Record<string, boolean>>({});
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark); const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const itemsMap = computed<Record<string, Record<string, unknown>>>(() => { const itemsMap = computed<Record<string, Record<string, unknown>>>(() => {
@@ -327,7 +331,7 @@ const chartOptions = computed<object>(() => {
if (displayItems.length === 1 || item.totalAmount !== 0) { if (displayItems.length === 1 || item.totalAmount !== 0) {
const value = formatAmountToLocalizedNumeralsWithCurrency(item.totalAmount, props.defaultCurrency); const value = formatAmountToLocalizedNumeralsWithCurrency(item.totalAmount, props.defaultCurrency);
tooltip += '<div><span class="chart-pointer" style="background-color: ' + item.color + '"></span>'; tooltip += '<div><span class="chart-pointer" style="background-color: ' + item.color + '"></span>';
tooltip += `<span>${item.name}</span><span style="margin-left: 20px; float: right">${value}</span><br/>`; tooltip += `<span>${item.name}</span><span class="ms-5" style="float: inline-end">${value}</span><br/>`;
tooltip += '</div>'; tooltip += '</div>';
} }
} }
@@ -336,7 +340,7 @@ const chartOptions = computed<object>(() => {
const displayTotalAmount = formatAmountToLocalizedNumeralsWithCurrency(totalAmount, props.defaultCurrency); const displayTotalAmount = formatAmountToLocalizedNumeralsWithCurrency(totalAmount, props.defaultCurrency);
tooltip = '<div style="border-bottom: ' + (isDarkMode.value ? '#eee' : '#333') + ' dashed 1px">' tooltip = '<div style="border-bottom: ' + (isDarkMode.value ? '#eee' : '#333') + ' dashed 1px">'
+ '<span class="chart-pointer" style="background-color: ' + (isDarkMode.value ? '#eee' : '#333') + '"></span>' + '<span class="chart-pointer" style="background-color: ' + (isDarkMode.value ? '#eee' : '#333') + '"></span>'
+ `<span>${tt('Total Amount')}</span><span style="margin-left: 20px; float: right">${displayTotalAmount}</span><br/>` + `<span>${tt('Total Amount')}</span><span class="ms-5" style="float: inline-end">${displayTotalAmount}</span><br/>`
+ '</div>' + tooltip; + '</div>' + tooltip;
} }
@@ -365,7 +369,8 @@ const chartOptions = computed<object>(() => {
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: allDisplayDateRanges.value data: allDisplayDateRanges.value,
inverse: textDirection.value === TextDirection.RTL
} }
], ],
yAxis: [ yAxis: [

View File

@@ -143,13 +143,13 @@ const chartOptions = computed<object>(() => {
let tooltip = `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`; let tooltip = `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`;
if (name) { if (name) {
tooltip += `<span>${name}</span><br/>`; tooltip += `<div class="d-inline-flex">${name}</div><br/>`;
} }
if (props.showValue) { if (props.showValue) {
tooltip += `<span>${value} (${percent})</span>`; tooltip += `<div class="d-inline-flex"><span>${value}</span><span class="ms-1">(${percent})</span></div>`;
} else { } else {
tooltip += `<span>${percent}</span>`; tooltip += `<div class="d-inline-flex">${percent}</div>`;
} }
tooltip += '</div>'; tooltip += '</div>';

View File

@@ -34,7 +34,7 @@
:class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(weekDay.type) }" :class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(weekDay.type) }"
v-for="weekDay in allWeekDays"> v-for="weekDay in allWeekDays">
<template #prepend="{ isActive }"> <template #prepend="{ isActive }">
<v-checkbox density="compact" class="mr-1" :model-value="isActive"></v-checkbox> <v-checkbox density="compact" class="me-1" :model-value="isActive"></v-checkbox>
</template> </template>
</v-list-item> </v-list-item>
</v-list> </v-list>
@@ -44,7 +44,7 @@
:class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(monthDay.day) }" :class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(monthDay.day) }"
v-for="monthDay in allAvailableMonthDays"> v-for="monthDay in allAvailableMonthDays">
<template #prepend="{ isActive }"> <template #prepend="{ isActive }">
<v-checkbox density="compact" class="mr-1" :model-value="isActive"></v-checkbox> <v-checkbox density="compact" class="me-1" :model-value="isActive"></v-checkbox>
</template> </template>
</v-list-item> </v-list-item>
</v-list> </v-list>

View File

@@ -14,12 +14,12 @@
<template #selection> <template #selection>
<div class="d-flex align-center text-truncate cursor-pointer"> <div class="d-flex align-center text-truncate cursor-pointer">
<span class="text-truncate" v-if="customSelectionPrimaryText">{{ customSelectionPrimaryText }}</span> <span class="text-truncate" v-if="customSelectionPrimaryText">{{ customSelectionPrimaryText }}</span>
<v-icon class="disabled" :icon="mdiChevronRight" size="23" v-if="customSelectionPrimaryText && customSelectionSecondaryText" /> <v-icon class="icon-with-direction disabled" :icon="mdiChevronRight" size="23" v-if="customSelectionPrimaryText && customSelectionSecondaryText" />
<span class="text-truncate" v-if="customSelectionPrimaryText && customSelectionSecondaryText">{{ customSelectionSecondaryText }}</span> <span class="text-truncate" v-if="customSelectionPrimaryText && customSelectionSecondaryText">{{ customSelectionSecondaryText }}</span>
<span class="text-truncate" v-if="!customSelectionPrimaryText && !selectedPrimaryItem && !selectedSecondaryItem">{{ noSelectionText }}</span> <span class="text-truncate" v-if="!customSelectionPrimaryText && !selectedPrimaryItem && !selectedSecondaryItem">{{ noSelectionText }}</span>
<span class="text-truncate" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem">{{ selectionPrimaryItemText }}</span> <span class="text-truncate" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem">{{ selectionPrimaryItemText }}</span>
<v-icon class="disabled" :icon="mdiChevronRight" size="23" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem && selectedSecondaryItem" /> <v-icon class="icon-with-direction disabled" :icon="mdiChevronRight" size="23" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem && selectedSecondaryItem" />
<ItemIcon class="mr-2" icon-type="account" size="21.5px" <ItemIcon class="me-2" icon-type="account" size="21.5px"
:icon-id="selectedSecondaryItem && secondaryIconField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryIconField] : null" :icon-id="selectedSecondaryItem && secondaryIconField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryIconField] : null"
:color="selectedSecondaryItem && secondaryColorField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryColorField] : null" :color="selectedSecondaryItem && secondaryColorField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryColorField] : null"
v-if="!customSelectionPrimaryText && selectedSecondaryItem && showSelectionSecondaryIcon" /> v-if="!customSelectionPrimaryText && selectedSecondaryItem && showSelectionSecondaryIcon" />
@@ -47,7 +47,7 @@
v-for="item in filteredItems" v-for="item in filteredItems"
@click="onPrimaryItemClicked(item)"> @click="onPrimaryItemClicked(item)">
<template #prepend> <template #prepend>
<ItemIcon class="mr-2" :icon-type="primaryIconType" <ItemIcon class="me-2" :icon-type="primaryIconType"
:icon-id="primaryIconField ? (item as Record<string, unknown>)[primaryIconField] : undefined" :color="primaryColorField ? (item as Record<string, unknown>)[primaryColorField] : undefined"></ItemIcon> :icon-id="primaryIconField ? (item as Record<string, unknown>)[primaryIconField] : undefined" :color="primaryColorField ? (item as Record<string, unknown>)[primaryColorField] : undefined"></ItemIcon>
</template> </template>
<template #title> <template #title>
@@ -66,7 +66,7 @@
v-for="subItem in filteredSubItems" v-for="subItem in filteredSubItems"
@click="onSecondaryItemClicked(subItem)"> @click="onSecondaryItemClicked(subItem)">
<template #prepend> <template #prepend>
<ItemIcon class="mr-2" :icon-type="secondaryIconType" <ItemIcon class="me-2" :icon-type="secondaryIconType"
:icon-id="secondaryIconField ? subItem[secondaryIconField] : undefined" :color="secondaryColorField ? subItem[secondaryColorField] : undefined"></ItemIcon> :icon-id="secondaryIconField ? subItem[secondaryIconField] : undefined" :color="secondaryColorField ? subItem[secondaryColorField] : undefined"></ItemIcon>
</template> </template>
<template #title> <template #title>

4
src/core/text.ts Normal file
View File

@@ -0,0 +1,4 @@
export enum TextDirection {
LTR = 'ltr',
RTL = 'rtl'
}

View File

@@ -72,7 +72,7 @@ import draggable from 'vuedraggable';
import router from '@/router/desktop.ts'; import router from '@/router/desktop.ts';
import { DecimalSeparator } from '@/core/numeral.ts'; import { DecimalSeparator } from '@/core/numeral.ts';
import { getI18nOptions } from '@/locales/helpers.ts'; import { getI18nOptions, getRtlLocales } from '@/locales/helpers.ts';
import PinCodeInput from '@/components/common/PinCodeInput.vue'; import PinCodeInput from '@/components/common/PinCodeInput.vue';
import MapView from '@/components/common/MapView.vue'; import MapView from '@/components/common/MapView.vue';
@@ -433,6 +433,7 @@ const vuetify = createVuetify({
} }
}, },
locale: { locale: {
rtl: getRtlLocales(),
adapter: ((i18nGlobal: Composer) => { adapter: ((i18nGlobal: Composer) => {
const instance: LocaleInstance = { const instance: LocaleInstance = {
name: 'ezBookkeeping i18n', name: 'ezBookkeeping i18n',

View File

@@ -4,7 +4,16 @@ import 'moment-timezone/moment-timezone-utils';
import type { PartialRecord, NameValue, TypeAndName, TypeAndDisplayName, LocalizedSwitchOption } from '@/core/base.ts'; import type { PartialRecord, NameValue, TypeAndName, TypeAndDisplayName, LocalizedSwitchOption } from '@/core/base.ts';
import { type LanguageInfo, type LanguageOption, ALL_LANGUAGES, DEFAULT_LANGUAGE } from '@/locales/index.ts'; import {
type LanguageInfo,
type LanguageOption,
ALL_LANGUAGES,
DEFAULT_LANGUAGE
} from '@/locales/index.ts';
import {
TextDirection
} from '@/core/text.ts';
import { import {
type DateFormat, type DateFormat,
@@ -222,6 +231,24 @@ export function getI18nOptions(): object {
}; };
} }
export function getRtlLocales(): Record<string, boolean> {
const rtlLocales: Record<string, boolean> = {};
for (const languageKey in ALL_LANGUAGES) {
if (!Object.prototype.hasOwnProperty.call(ALL_LANGUAGES, languageKey)) {
continue;
}
const languageInfo = ALL_LANGUAGES[languageKey];
if (languageInfo.textDirection === TextDirection.RTL) {
rtlLocales[languageKey] = true;
}
}
return rtlLocales;
}
export function useI18n() { export function useI18n() {
const { t, locale } = useVueI18n(); const { t, locale } = useVueI18n();
@@ -721,6 +748,11 @@ export function useI18n() {
return currentLanguageInfo.displayName; return currentLanguageInfo.displayName;
} }
function getCurrentLanguageTextDirection(): TextDirection {
const currentLanguageInfo = getCurrentLanguageInfo();
return currentLanguageInfo.textDirection;
}
function getDefaultCurrency(): string { function getDefaultCurrency(): string {
return t('default.currency'); return t('default.currency');
} }
@@ -1894,7 +1926,9 @@ export function useI18n() {
logger.info(`No specified language, use browser default language ${languageKey}`); logger.info(`No specified language, use browser default language ${languageKey}`);
} }
if (!getLanguageInfo(languageKey)) { const languageInfo = getLanguageInfo(languageKey);
if (!languageInfo) {
languageKey = getDefaultLanguage(); languageKey = getDefaultLanguage();
logger.warn(`Not found language ${languageKey}, use browser default language ${languageKey}`); logger.warn(`Not found language ${languageKey}, use browser default language ${languageKey}`);
} }
@@ -1925,6 +1959,12 @@ export function useI18n() {
services.setLocale(languageKey); services.setLocale(languageKey);
document.querySelector('html')?.setAttribute('lang', languageKey); document.querySelector('html')?.setAttribute('lang', languageKey);
if (languageInfo && languageInfo.textDirection === TextDirection.LTR) {
document.querySelector('html')?.removeAttribute('dir');
} else if (languageInfo && languageInfo.textDirection === TextDirection.RTL) {
document.querySelector('html')?.setAttribute('dir', 'rtl');
}
const defaultCurrency = getDefaultCurrency(); const defaultCurrency = getDefaultCurrency();
const defaultFirstDayOfWeekName = getDefaultFirstDayOfWeek(); const defaultFirstDayOfWeekName = getDefaultFirstDayOfWeek();
let defaultFirstDayOfWeek = WeekDay.DefaultFirstDay.type; let defaultFirstDayOfWeek = WeekDay.DefaultFirstDay.type;
@@ -1933,10 +1973,12 @@ export function useI18n() {
defaultFirstDayOfWeek = (WeekDay.parse(defaultFirstDayOfWeekName) as WeekDay).type; defaultFirstDayOfWeek = (WeekDay.parse(defaultFirstDayOfWeekName) as WeekDay).type;
} }
return { const localeDefaultSettings: LocaleDefaultSettings = {
currency: defaultCurrency, currency: defaultCurrency,
firstDayOfWeek: defaultFirstDayOfWeek firstDayOfWeek: defaultFirstDayOfWeek
}; };
return localeDefaultSettings;
} }
function setTimeZone(timezone: string): void { function setTimeZone(timezone: string): void {
@@ -1987,6 +2029,7 @@ export function useI18n() {
getCurrentLanguageTag, getCurrentLanguageTag,
getCurrentLanguageInfo, getCurrentLanguageInfo,
getCurrentLanguageDisplayName, getCurrentLanguageDisplayName,
getCurrentLanguageTextDirection,
// get localization default type // get localization default type
getDefaultCurrency, getDefaultCurrency,
getDefaultFirstDayOfWeek, getDefaultFirstDayOfWeek,

View File

@@ -11,12 +11,14 @@ import zhHans from './zh_Hans.json';
import zhHant from './zh_Hant.json'; import zhHant from './zh_Hant.json';
import ptBR from './pt_BR.json'; import ptBR from './pt_BR.json';
import { TextDirection } from '@/core/text.ts';
export interface LanguageInfo { export interface LanguageInfo {
readonly name: string; readonly name: string;
readonly displayName: string; readonly displayName: string;
readonly alternativeLanguageTag: string; readonly alternativeLanguageTag: string;
readonly aliases?: string[]; readonly aliases?: string[];
readonly textDirection: TextDirection;
readonly content: object; readonly content: object;
} }
@@ -34,60 +36,70 @@ export const ALL_LANGUAGES: Record<string, LanguageInfo> = {
name: 'German', name: 'German',
displayName: 'Deutsch', displayName: 'Deutsch',
alternativeLanguageTag: 'de-DE', alternativeLanguageTag: 'de-DE',
textDirection: TextDirection.LTR,
content: de content: de
}, },
'en': { 'en': {
name: 'English', name: 'English',
displayName: 'English', displayName: 'English',
alternativeLanguageTag: 'en-US', alternativeLanguageTag: 'en-US',
textDirection: TextDirection.LTR,
content: en content: en
}, },
'es': { 'es': {
name: 'Spanish', name: 'Spanish',
displayName: 'Español', displayName: 'Español',
alternativeLanguageTag: 'es-ES', alternativeLanguageTag: 'es-ES',
textDirection: TextDirection.LTR,
content: es content: es
}, },
'it': { 'it': {
name: 'Italian', name: 'Italian',
displayName: 'Italiano', displayName: 'Italiano',
alternativeLanguageTag: 'it-IT', alternativeLanguageTag: 'it-IT',
textDirection: TextDirection.LTR,
content: it content: it
}, },
'ja': { 'ja': {
name: 'Japanese', name: 'Japanese',
displayName: '日本語', displayName: '日本語',
alternativeLanguageTag: 'ja-JP', alternativeLanguageTag: 'ja-JP',
textDirection: TextDirection.LTR,
content: ja content: ja
}, },
'nl': { 'nl': {
name: 'Dutch', name: 'Dutch',
displayName: 'Nederlands', displayName: 'Nederlands',
alternativeLanguageTag: 'nl-NL', alternativeLanguageTag: 'nl-NL',
textDirection: TextDirection.LTR,
content: nl content: nl
}, },
'pt-BR': { 'pt-BR': {
name: 'Portuguese (Brazil)', name: 'Portuguese (Brazil)',
displayName: 'Português (Brasil)', displayName: 'Português (Brasil)',
alternativeLanguageTag: 'pt-BR', alternativeLanguageTag: 'pt-BR',
textDirection: TextDirection.LTR,
content: ptBR content: ptBR
}, },
'ru': { 'ru': {
name: 'Russian', name: 'Russian',
displayName: 'Русский', displayName: 'Русский',
alternativeLanguageTag: 'ru-RU', alternativeLanguageTag: 'ru-RU',
textDirection: TextDirection.LTR,
content: ru content: ru
}, },
'uk': { 'uk': {
name: 'Ukrainian', name: 'Ukrainian',
displayName: 'Українська', displayName: 'Українська',
alternativeLanguageTag: 'uk-UA', alternativeLanguageTag: 'uk-UA',
textDirection: TextDirection.LTR,
content: uk content: uk
}, },
'vi': { 'vi': {
name: 'Vietnamese', name: 'Vietnamese',
displayName: 'Tiếng Việt', displayName: 'Tiếng Việt',
alternativeLanguageTag: 'vi-VN', alternativeLanguageTag: 'vi-VN',
textDirection: TextDirection.LTR,
content: vi content: vi
}, },
'zh-Hans': { 'zh-Hans': {
@@ -95,6 +107,7 @@ export const ALL_LANGUAGES: Record<string, LanguageInfo> = {
displayName: '中文 (简体)', displayName: '中文 (简体)',
alternativeLanguageTag: 'zh-CN', alternativeLanguageTag: 'zh-CN',
aliases: ['zh-CHS', 'zh-CN', 'zh-SG'], aliases: ['zh-CHS', 'zh-CN', 'zh-SG'],
textDirection: TextDirection.LTR,
content: zhHans content: zhHans
}, },
'zh-Hant': { 'zh-Hant': {
@@ -102,6 +115,7 @@ export const ALL_LANGUAGES: Record<string, LanguageInfo> = {
displayName: '中文 (繁體)', displayName: '中文 (繁體)',
alternativeLanguageTag: 'zh-TW', alternativeLanguageTag: 'zh-TW',
aliases: ['zh-CHT', 'zh-TW', 'zh-HK', 'zh-MO'], aliases: ['zh-CHT', 'zh-TW', 'zh-HK', 'zh-MO'],
textDirection: TextDirection.LTR,
content: zhHant content: zhHant
}, },
}; };

View File

@@ -40,8 +40,8 @@ input[type=number] {
} }
@media (min-width: 600px) { @media (min-width: 600px) {
.text-right-sm { .text-end-sm {
text-align: right !important; text-align: end !important;
} }
} }
@@ -131,7 +131,7 @@ input[type=number] {
.chart-pointer { .chart-pointer {
display: inline-block; display: inline-block;
margin-right: 5px; margin-inline-end: 5px;
border-radius: 10px; border-radius: 10px;
width: 10px; width: 10px;
height: 10px; height: 10px;
@@ -142,7 +142,11 @@ input[type=number] {
} }
.bidirectional-switch.v-input--horizontal .v-input__prepend { .bidirectional-switch.v-input--horizontal .v-input__prepend {
margin-right: 10px; /* same as the padding-left of `.v-switch .v-label` */ margin-inline-end: 10px; /* same as the padding-left of `.v-switch .v-label` */
}
html[dir="rtl"] .bidirectional-switch {
transform: scaleX(-1);
} }
.code-container { .code-container {
@@ -276,6 +280,20 @@ input[type=number] {
margin: 0; margin: 0;
} }
html[dir="rtl"] .v-btn.button-icon-with-direction > .v-btn__content > .v-icon > .v-icon__svg,
html[dir="rtl"] .v-btn.button-icon-with-direction > .v-btn__prepend > .v-icon > .v-icon__svg,
html[dir="rtl"] .v-btn.button-icon-with-direction > .v-btn__append > .v-icon > .v-icon__svg {
transform: scaleX(-1);
}
html[dir="rtl"] .v-icon.icon-with-direction > .v-icon__svg {
transform: scaleX(-1);
}
html[dir="rtl"] .v-img.img-with-direction > img {
transform: scaleX(-1);
}
/** Common class for replacing the default style of Materio **/ /** Common class for replacing the default style of Materio **/
.v-application, .v-application,
.text-body-1, .text-body-1,
@@ -415,3 +433,7 @@ body .v-btn-group {
.dp__main .dp__calendar .dp__calendar_item > .dp__cell_inner { .dp__main .dp__calendar .dp__calendar_item > .dp__cell_inner {
width: 100%; width: 100%;
} }
html[dir="rtl"] .dp__main .dp__btn.dp--arrow-btn-nav {
transform: scaleX(-1);
}

View File

@@ -6,7 +6,7 @@
<div class="d-flex align-center"> <div class="d-flex align-center">
<span>{{ tt('global.app.title') }}</span> <span>{{ tt('global.app.title') }}</span>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" @click="refreshBrowserCache" class="ms-2" :icon="true" @click="refreshBrowserCache"
v-if="!clientVersionMatchServerVersion"> v-if="!clientVersionMatchServerVersion">
<v-icon :icon="mdiWebRefresh" size="24" /> <v-icon :icon="mdiWebRefresh" size="24" />
<v-tooltip activator="parent">{{ tt('Refresh Browser Cache') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh Browser Cache') }}</v-tooltip>

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people4.svg"/> <v-img class="img-with-direction" max-width="600px" src="img/desktop/people4.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column"> <v-col cols="12" md="4" class="auth-card d-flex flex-column">
@@ -45,14 +45,14 @@
<v-col cols="12"> <v-col cols="12">
<v-btn block type="submit" :disabled="!email || requesting" @click="requestResetPassword"> <v-btn block type="submit" :disabled="!email || requesting" @click="requestResetPassword">
{{ tt('Send Reset Link') }} {{ tt('Send Reset Link') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="requesting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="requesting"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<router-link class="d-flex align-center justify-center" to="/login" <router-link class="d-flex align-center justify-center" to="/login"
:class="{ 'disabled': requesting }"> :class="{ 'disabled': requesting }">
<v-icon :icon="mdiChevronLeft"/> <v-icon class="icon-with-direction" :icon="mdiChevronLeft"/>
<span>{{ tt('Back to login page') }}</span> <span>{{ tt('Back to login page') }}</span>
</router-link> </router-link>
</v-col> </v-col>
@@ -99,12 +99,13 @@ import { useI18n } from '@/locales/helpers.ts';
import { useRootStore } from '@/stores/index.ts'; import { useRootStore } from '@/stores/index.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { getClientDisplayVersion } from '@/lib/version.ts'; import { getClientDisplayVersion } from '@/lib/version.ts';
import { import {
mdiChevronLeft, mdiChevronLeft
} from '@mdi/js'; } from '@mdi/js';
type SnackBarType = InstanceType<typeof SnackBar>; type SnackBarType = InstanceType<typeof SnackBar>;

View File

@@ -10,7 +10,7 @@
<span style="font-size: 1rem">{{ tt('Expense') }}</span> <span style="font-size: 1rem">{{ tt('Expense') }}</span>
</div> </div>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loadingOverview" @click="reload(true)"> class="ms-2" :icon="true" :loading="loadingOverview" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -24,19 +24,21 @@
<h4 class="text-2xl font-weight-medium text-primary"> <h4 class="text-2xl font-weight-medium text-primary">
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayExpenseAmount(transactionOverview.thisMonth) : '-' }}</span> <span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayExpenseAmount(transactionOverview.thisMonth) : '-' }}</span>
<v-skeleton-loader class="d-inline-block skeleton-no-margin mt-3 pb-1" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader> <v-skeleton-loader class="d-inline-block skeleton-no-margin mt-3 pb-1" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
<v-btn class="ml-1" density="compact" color="default" variant="text" <v-btn class="ms-1" density="compact" color="default" variant="text"
:icon="true" @click="showAmountInHomePage = !showAmountInHomePage"> :icon="true" @click="showAmountInHomePage = !showAmountInHomePage">
<v-icon :icon="showAmountInHomePage ? mdiEyeOffOutline : mdiEyeOutline" size="20" /> <v-icon :icon="showAmountInHomePage ? mdiEyeOffOutline : mdiEyeOutline" size="20" />
</v-btn> </v-btn>
</h4> </h4>
<div class="mt-1 mb-3"> <div class="mt-1 mb-3">
<span class="mr-2">{{ tt('Monthly income') }}</span> <span class="me-2">{{ tt('Monthly income') }}</span>
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayIncomeAmount(transactionOverview.thisMonth) : '-' }}</span> <span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayIncomeAmount(transactionOverview.thisMonth) : '-' }}</span>
<v-skeleton-loader class="d-inline-block skeleton-no-margin mt-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader> <v-skeleton-loader class="d-inline-block skeleton-no-margin mt-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
</div> </div>
<v-btn size="small" to="/transaction/list?dateType=7">{{ tt('View Details') }}</v-btn> <v-btn size="small" to="/transaction/list?dateType=7">{{ tt('View Details') }}</v-btn>
<v-img class="overview-card-background" src="img/desktop/card-background.png"/> <v-img class="overview-card-background img-with-direction"
<v-img class="overview-card-background-image" width="116px" src="img/desktop/document.svg"/> src="img/desktop/card-background.png"/>
<v-img class="overview-card-background-image img-with-direction"
width="116px" src="img/desktop/document.svg"/>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-col> </v-col>

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people1.svg"/> <v-img class="img-with-direction" max-width="600px" src="img/desktop/people1.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column"> <v-col cols="12" md="4" class="auth-card d-flex flex-column">
@@ -102,12 +102,12 @@
<v-btn block :disabled="inputIsEmpty || logining || verifying" <v-btn block :disabled="inputIsEmpty || logining || verifying"
@click="login" v-if="!show2faInput"> @click="login" v-if="!show2faInput">
{{ tt('Log In') }} {{ tt('Log In') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="logining"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="logining"></v-progress-circular>
</v-btn> </v-btn>
<v-btn block :disabled="twoFAInputIsEmpty || logining || verifying" <v-btn block :disabled="twoFAInputIsEmpty || logining || verifying"
@click="verify" v-else-if="show2faInput"> @click="verify" v-else-if="show2faInput">
{{ tt('Continue') }} {{ tt('Continue') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="verifying"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="verifying"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
@@ -165,9 +165,10 @@ import { useLoginPageBase } from '@/views/base/LoginPageBase.ts';
import { useRootStore } from '@/stores/index.ts'; import { useRootStore } from '@/stores/index.ts';
import { ThemeType } from '@/core/theme.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts'; import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { KnownErrorCode } from '@/consts/api.ts'; import { KnownErrorCode } from '@/consts/api.ts';
import { ThemeType } from '@/core/theme.ts';
import { isUserRegistrationEnabled, isUserForgetPasswordEnabled, isUserVerifyEmailEnabled } from '@/lib/server_settings.ts'; import { isUserRegistrationEnabled, isUserForgetPasswordEnabled, isUserVerifyEmailEnabled } from '@/lib/server_settings.ts';
import { import {

View File

@@ -31,7 +31,7 @@
<v-icon class="nav-item-icon" :icon="mdiListBoxOutline"/> <v-icon class="nav-item-icon" :icon="mdiListBoxOutline"/>
<span class="nav-item-title d-inline-block">{{ tt('Transaction Details') }}</span> <span class="nav-item-title d-inline-block">{{ tt('Transaction Details') }}</span>
<v-btn density="compact" color="secondary" variant="text" size="22" <v-btn density="compact" color="secondary" variant="text" size="22"
class="ml-1" :icon="true" v-if="showAddTransactionButtonInDesktopNavbar" class="ms-1" :icon="true" v-if="showAddTransactionButtonInDesktopNavbar"
@click="showAddDialogInTransactionListPage"> @click="showAddDialogInTransactionListPage">
<v-icon :icon="mdiPlusCircle" size="22" /> <v-icon :icon="mdiPlusCircle" size="22" />
<v-tooltip activator="parent">{{ tt('Add Transaction') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Add Transaction') }}</v-tooltip>
@@ -109,7 +109,7 @@
<div class="layout-navbar navbar-blur"> <div class="layout-navbar navbar-blur">
<div class="navbar-content-container"> <div class="navbar-content-container">
<div class="d-flex h-100 align-center"> <div class="d-flex h-100 align-center">
<v-btn class="ms-n3 mr-2 d-lg-none" color="default" variant="text" <v-btn class="ms-n3 me-2 d-lg-none" color="default" variant="text"
:icon="true" @click="showVerticalOverlayMenu = true"> :icon="true" @click="showVerticalOverlayMenu = true">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
@@ -152,7 +152,7 @@
</v-avatar> </v-avatar>
</v-list-item-action> </v-list-item-action>
</template> </template>
<v-list-item-title class="ml-2 font-weight-semibold"> <v-list-item-title class="ms-2 font-weight-semibold">
{{ currentNickName }} {{ currentNickName }}
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people4.svg"/> <v-img class="img-with-direction" max-width="600px" src="img/desktop/people4.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column"> <v-col cols="12" md="4" class="auth-card d-flex flex-column">
@@ -70,14 +70,14 @@
<v-col cols="12"> <v-col cols="12">
<v-btn block :disabled="!email || !newPassword || !confirmPassword || updating" @click="resetPassword"> <v-btn block :disabled="!email || !newPassword || !confirmPassword || updating" @click="resetPassword">
{{ tt('Update Password') }} {{ tt('Update Password') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="updating"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="updating"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<router-link class="d-flex align-center justify-center" to="/login" <router-link class="d-flex align-center justify-center" to="/login"
:class="{ 'disabled': updating }"> :class="{ 'disabled': updating }">
<v-icon :icon="mdiChevronLeft"/> <v-icon class="icon-with-direction" :icon="mdiChevronLeft"/>
<span>{{ tt('Back to login page') }}</span> <span>{{ tt('Back to login page') }}</span>
</router-link> </router-link>
</v-col> </v-col>
@@ -128,8 +128,9 @@ import { useI18n } from '@/locales/helpers.ts';
import { useRootStore } from '@/stores/index.ts'; import { useRootStore } from '@/stores/index.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { getClientDisplayVersion } from '@/lib/version.ts'; import { getClientDisplayVersion } from '@/lib/version.ts';
import { import {

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="4" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="4" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="320px" src="img/desktop/people2.svg"/> <v-img class="img-with-direction" max-width="320px" src="img/desktop/people2.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="8" class="auth-card d-flex align-center justify-center pa-10"> <v-col cols="12" md="8" class="auth-card d-flex align-center justify-center pa-10">
@@ -28,7 +28,7 @@
<h4 class="text-h4 mb-1">{{ tt('Basic Information') }}</h4> <h4 class="text-h4 mb-1">{{ tt('Basic Information') }}</h4>
<p class="text-sm mt-2 mb-5"> <p class="text-sm mt-2 mb-5">
<span>{{ tt('Already have an account?') }}</span> <span>{{ tt('Already have an account?') }}</span>
<router-link class="ml-1" to="/login">{{ tt('Click here to log in') }}</router-link> <router-link class="ms-1" to="/login">{{ tt('Click here to log in') }}</router-link>
</p> </p>
<v-row> <v-row>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
@@ -129,7 +129,7 @@
:label="tt('Use Preset Transaction Categories')" :label="tt('Use Preset Transaction Categories')"
v-model="usePresetCategories"/> v-model="usePresetCategories"/>
</v-col> </v-col>
<v-col cols="12" sm="6" class="text-right-sm"> <v-col cols="12" sm="6" class="text-end-sm">
<language-select-button :disabled="submitting || navigateToHomePage" <language-select-button :disabled="submitting || navigateToHomePage"
:use-model-value="true" v-model="currentLocale" /> :use-model-value="true" v-model="currentLocale" />
</v-col> </v-col>
@@ -144,7 +144,7 @@
<v-expansion-panel :key="idx" v-for="(category, idx) in categories"> <v-expansion-panel :key="idx" v-for="(category, idx) in categories">
<v-expansion-panel-title class="py-0"> <v-expansion-panel-title class="py-0">
<ItemIcon icon-type="category" :icon-id="category.icon" :color="category.color"></ItemIcon> <ItemIcon icon-type="category" :icon-id="category.icon" :color="category.color"></ItemIcon>
<span class="ml-3">{{ category.name }}</span> <span class="ms-3">{{ category.name }}</span>
</v-expansion-panel-title> </v-expansion-panel-title>
<v-expansion-panel-text v-if="category.subCategories.length"> <v-expansion-panel-text v-if="category.subCategories.length">
<v-list rounded density="comfortable" class="pa-0"> <v-list rounded density="comfortable" class="pa-0">
@@ -154,7 +154,7 @@
<template #prepend> <template #prepend>
<ItemIcon icon-type="category" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon> <ItemIcon icon-type="category" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon>
</template> </template>
<span class="ml-3">{{ subCategory.name }}</span> <span class="ms-3">{{ subCategory.name }}</span>
</v-list-item> </v-list-item>
<v-divider v-if="subIdx !== category.subCategories.length - 1"/> <v-divider v-if="subIdx !== category.subCategories.length - 1"/>
</template> </template>
@@ -175,11 +175,12 @@
</v-window> </v-window>
<div class="d-flex justify-sm-space-between gap-4 flex-wrap justify-center mt-5"> <div class="d-flex justify-sm-space-between gap-4 flex-wrap justify-center mt-5">
<v-btn :color="(currentStep === 'basicSetting' || currentStep === 'finalResult') ? 'default' : 'primary'" <v-btn class="button-icon-with-direction"
:color="(currentStep === 'basicSetting' || currentStep === 'finalResult') ? 'default' : 'primary'"
:disabled="currentStep === 'basicSetting' || currentStep === 'finalResult' || submitting || navigateToHomePage" :disabled="currentStep === 'basicSetting' || currentStep === 'finalResult' || submitting || navigateToHomePage"
:prepend-icon="mdiArrowLeft" :prepend-icon="mdiArrowLeft"
@click="switchToPreviousTab">{{ tt('Previous') }}</v-btn> @click="switchToPreviousTab">{{ tt('Previous') }}</v-btn>
<v-btn color="primary" <v-btn class="button-icon-with-direction" color="primary"
:disabled="submitting || navigateToHomePage" :disabled="submitting || navigateToHomePage"
:append-icon="mdiArrowRight" :append-icon="mdiArrowRight"
@click="switchToNextTab" @click="switchToNextTab"
@@ -190,9 +191,10 @@
@click="submit" @click="submit"
v-if="currentStep === 'presetCategories'"> v-if="currentStep === 'presetCategories'">
{{ tt('Submit') }} {{ tt('Submit') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :append-icon="mdiArrowRight" <v-btn class="button-icon-with-direction"
:append-icon="mdiArrowRight"
@click="navigateToLogin" @click="navigateToLogin"
v-if="currentStep === 'finalResult'">{{ tt('Continue') }}</v-btn> v-if="currentStep === 'finalResult'">{{ tt('Continue') }}</v-btn>
</div> </div>
@@ -385,6 +387,7 @@ function onSnackbarShowStateChanged(newValue: boolean): void {
<style> <style>
.signup-preset-categories .v-expansion-panel-text__wrapper { .signup-preset-categories .v-expansion-panel-text__wrapper {
padding: 0 0 0 20px; padding: 0 0 0 0;
padding-inline-start: 20px;
} }
</style> </style>

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="600px" src="img/desktop/people3.svg"/> <v-img class="img-with-direction" max-width="600px" src="img/desktop/people3.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column"> <v-col cols="12" md="4" class="auth-card d-flex flex-column">
@@ -47,7 +47,7 @@
<v-btn block variant="tonal" :disabled="verifyingByWebAuthn" <v-btn block variant="tonal" :disabled="verifyingByWebAuthn"
@click="unlockByWebAuthn"> @click="unlockByWebAuthn">
{{ tt('Unlock with WebAuthn') }} {{ tt('Unlock with WebAuthn') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="verifyingByWebAuthn"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="verifyingByWebAuthn"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
@@ -105,8 +105,9 @@ import { useUnlockPageBase } from '@/views/base/UnlockPageBase.ts';
import { useSettingsStore } from '@/stores/setting.ts'; import { useSettingsStore } from '@/stores/setting.ts';
import { useUserStore } from '@/stores/user.ts'; import { useUserStore } from '@/stores/user.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { import {
isWebAuthnSupported, isWebAuthnSupported,
verifyWebAuthnCredential verifyWebAuthnCredential

View File

@@ -9,13 +9,13 @@
<v-row no-gutters class="auth-wrapper"> <v-row no-gutters class="auth-wrapper">
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative"> <v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
<div class="d-flex auth-img-footer" v-if="!isDarkMode"> <div class="d-flex auth-img-footer" v-if="!isDarkMode">
<v-img src="img/desktop/background.svg"/> <v-img class="img-with-direction" src="img/desktop/background.svg"/>
</div> </div>
<div class="d-flex auth-img-footer" v-if="isDarkMode"> <div class="d-flex auth-img-footer" v-if="isDarkMode">
<v-img src="img/desktop/background-dark.svg"/> <v-img class="img-with-direction" src="img/desktop/background-dark.svg"/>
</div> </div>
<div class="d-flex align-center justify-center w-100 pt-10"> <div class="d-flex align-center justify-center w-100 pt-10">
<v-img max-width="320px" src="img/desktop/people2.svg"/> <v-img class="img-with-direction" max-width="320px" src="img/desktop/people2.svg"/>
</div> </div>
</v-col> </v-col>
<v-col cols="12" md="4" class="auth-card d-flex flex-column"> <v-col cols="12" md="4" class="auth-card d-flex flex-column">
@@ -48,14 +48,14 @@
<v-col cols="12" v-if="!loading && !token && email && isUserVerifyEmailEnabled()"> <v-col cols="12" v-if="!loading && !token && email && isUserVerifyEmailEnabled()">
<v-btn block type="submit" :disabled="loading || resending || !password" @click="resendEmail"> <v-btn block type="submit" :disabled="loading || resending || !password" @click="resendEmail">
{{ tt('Resend Validation Email') }} {{ tt('Resend Validation Email') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="resending"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="resending"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<router-link class="d-flex align-center justify-center" :to="verified ? '/' : '/login'" <router-link class="d-flex align-center justify-center" :to="verified ? '/' : '/login'"
:class="{ 'disabled': loading || resending }"> :class="{ 'disabled': loading || resending }">
<v-icon :icon="mdiChevronLeft"/> <v-icon class="icon-with-direction" :icon="mdiChevronLeft"/>
<span v-if="!verified">{{ tt('Back to login page') }}</span> <span v-if="!verified">{{ tt('Back to login page') }}</span>
<span v-else-if="verified">{{ tt('Back to home page') }}</span> <span v-else-if="verified">{{ tt('Back to home page') }}</span>
</router-link> </router-link>
@@ -106,8 +106,9 @@ import { useI18n } from '@/locales/helpers.ts';
import { useRootStore } from '@/stores/index.ts'; import { useRootStore } from '@/stores/index.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { ThemeType } from '@/core/theme.ts'; import { ThemeType } from '@/core/theme.ts';
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
import { isUserVerifyEmailEnabled } from '@/lib/server_settings.ts'; import { isUserVerifyEmailEnabled } from '@/lib/server_settings.ts';
import { isUserLogined } from '@/lib/userstate.ts'; import { isUserLogined } from '@/lib/userstate.ts';
import { getClientDisplayVersion } from '@/lib/version.ts'; import { getClientDisplayVersion } from '@/lib/version.ts';

View File

@@ -33,13 +33,13 @@
<v-tab class="tab-text-truncate" :key="accountCategory.type" :value="accountCategory.type" <v-tab class="tab-text-truncate" :key="accountCategory.type" :value="accountCategory.type"
v-for="accountCategory in AccountCategory.values()"> v-for="accountCategory in AccountCategory.values()">
<ItemIcon icon-type="account" :icon-id="accountCategory.defaultAccountIconId" /> <ItemIcon icon-type="account" :icon-id="accountCategory.defaultAccountIconId" />
<div class="d-flex flex-column text-truncate ml-2"> <div class="d-flex flex-column text-truncate ms-2">
<small class="text-truncate text-left smaller" v-if="!loading || allAccountCount > 0">{{ accountCategoryTotalBalance(accountCategory) }}</small> <small class="text-truncate text-start smaller" v-if="!loading || allAccountCount > 0">{{ accountCategoryTotalBalance(accountCategory) }}</small>
<small class="text-truncate text-left smaller my-1" v-else-if="loading && allAccountCount <= 0"> <small class="text-truncate text-start smaller my-1" v-else-if="loading && allAccountCount <= 0">
<v-skeleton-loader class="skeleton-no-margin" <v-skeleton-loader class="skeleton-no-margin"
width="100px" height="16" type="text" :loading="true"></v-skeleton-loader> width="100px" height="16" type="text" :loading="true"></v-skeleton-loader>
</small> </small>
<span class="text-truncate text-left">{{ tt(accountCategory.name) }}</span> <span class="text-truncate text-start">{{ tt(accountCategory.name) }}</span>
</div> </div>
</v-tab> </v-tab>
</v-tabs> </v-tabs>
@@ -50,18 +50,18 @@
<v-card variant="flat" min-height="780"> <v-card variant="flat" min-height="780">
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain" <v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
:ripple="false" :icon="true" @click="showNav = !showNav"> :ripple="false" :icon="true" @click="showNav = !showNav">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
<span>{{ tt('Account List') }}</span> <span>{{ tt('Account List') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading" @click="add">{{ tt('Add') }}</v-btn> :disabled="loading" @click="add">{{ tt('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal" <v-btn class="ms-3" color="primary" variant="tonal"
:disabled="loading" @click="saveSortResult" :disabled="loading" @click="saveSortResult"
v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn> v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading" @click="reload(true)"> class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -69,7 +69,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true"> :disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -92,9 +92,9 @@
<v-card-text class="accounts-overview-title text-truncate pt-0"> <v-card-text class="accounts-overview-title text-truncate pt-0">
<span class="accounts-overview-subtitle">{{ activeAccountCategory?.isLiability ? tt('Outstanding Balance') : tt('Balance') }}</span> <span class="accounts-overview-subtitle">{{ activeAccountCategory?.isLiability ? tt('Outstanding Balance') : tt('Balance') }}</span>
<v-skeleton-loader class="skeleton-no-margin ml-3 mb-2" width="120px" type="text" :loading="true" v-if="loading && activeAccountCategory && !hasAccount(activeAccountCategory)"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3 mb-2" width="120px" type="text" :loading="true" v-if="loading && activeAccountCategory && !hasAccount(activeAccountCategory)"></v-skeleton-loader>
<span class="accounts-overview-amount ml-3" v-else-if="!loading || !activeAccountCategory || hasAccount(activeAccountCategory)">{{ activeAccountCategoryTotalBalance }}</span> <span class="accounts-overview-amount ms-3" v-else-if="!loading || !activeAccountCategory || hasAccount(activeAccountCategory)">{{ activeAccountCategoryTotalBalance }}</span>
<v-btn class="ml-2" density="compact" color="default" variant="text" <v-btn class="ms-2" density="compact" color="default" variant="text"
:icon="true" :disabled="loading" :icon="true" :disabled="loading"
@click="showAccountBalance = !showAccountBalance"> @click="showAccountBalance = !showAccountBalance">
<v-icon :icon="showAccountBalance ? mdiEyeOffOutline : mdiEyeOutline" size="20" /> <v-icon :icon="showAccountBalance ? mdiEyeOffOutline : mdiEyeOutline" size="20" />
@@ -102,13 +102,13 @@
</v-btn> </v-btn>
</v-card-text> </v-card-text>
<v-row class="pl-6 pr-6 pr-md-8" v-if="loading && activeAccountCategory && !hasAccount(activeAccountCategory)"> <v-row class="ps-6 pe-6 pe-md-8" v-if="loading && activeAccountCategory && !hasAccount(activeAccountCategory)">
<v-col cols="12"> <v-col cols="12">
<v-card border class="card-title-with-bg account-card mb-8 h-auto"> <v-card border class="card-title-with-bg account-card mb-8 h-auto">
<template #title> <template #title>
<div class="account-title d-flex align-center"> <div class="account-title d-flex align-center">
<v-icon class="disabled mr-0" size="28px" :icon="mdiSquareRounded" /> <v-icon class="disabled me-0" size="28px" :icon="mdiSquareRounded" />
<span class="account-name text-truncate ml-2"> <span class="account-name text-truncate ms-2">
<v-skeleton-loader class="skeleton-no-margin my-1" <v-skeleton-loader class="skeleton-no-margin my-1"
width="120px" type="text" :loading="true"></v-skeleton-loader> width="120px" type="text" :loading="true"></v-skeleton-loader>
</span> </span>
@@ -126,7 +126,7 @@
{{ tt('Transaction List') }} {{ tt('Transaction List') }}
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<span class="account-balance ml-2"> <span class="account-balance ms-2">
<v-skeleton-loader class="skeleton-no-margin" <v-skeleton-loader class="skeleton-no-margin"
width="100px" type="text" :loading="true"></v-skeleton-loader> width="100px" type="text" :loading="true"></v-skeleton-loader>
</span> </span>
@@ -136,13 +136,13 @@
</v-col> </v-col>
</v-row> </v-row>
<v-row class="pl-5 pr-2 pr-md-4" v-if="!loading && activeAccountCategory && !hasAccount(activeAccountCategory)"> <v-row class="ps-5 pe-2 pe-md-4" v-if="!loading && activeAccountCategory && !hasAccount(activeAccountCategory)">
<v-col cols="12"> <v-col cols="12">
{{ tt('No available account') }} {{ tt('No available account') }}
</v-col> </v-col>
</v-row> </v-row>
<v-row class="pl-6 pr-6 pr-md-8"> <v-row class="ps-6 pe-6 pe-md-8">
<v-col cols="12"> <v-col cols="12">
<draggable-list <draggable-list
class="list-group" class="list-group"
@@ -161,8 +161,8 @@
<div class="account-title d-flex align-baseline"> <div class="account-title d-flex align-baseline">
<ItemIcon size="1.5rem" icon-type="account" :icon-id="element.icon" <ItemIcon size="1.5rem" icon-type="account" :icon-id="element.icon"
:color="element.color" :hidden-status="element.hidden" /> :color="element.color" :hidden-status="element.hidden" />
<span class="account-name text-truncate ml-2">{{ element.name }}</span> <span class="account-name text-truncate ms-2">{{ element.name }}</span>
<small class="account-currency text-truncate ml-2"> <small class="account-currency text-truncate ms-2">
{{ accountCurrency(element) }} {{ accountCurrency(element) }}
</small> </small>
<v-spacer/> <v-spacer/>
@@ -192,7 +192,7 @@
v-show="showHidden || !subAccount.hidden"> v-show="showHidden || !subAccount.hidden">
<ItemIcon size="1.5rem" icon-type="account" :icon-id="subAccount.icon" <ItemIcon size="1.5rem" icon-type="account" :icon-id="subAccount.icon"
:color="subAccount.color" :hidden-status="subAccount.hidden" /> :color="subAccount.color" :hidden-status="subAccount.hidden" />
<span class="ml-2">{{ subAccount.name }}</span> <span class="ms-2">{{ subAccount.name }}</span>
</v-btn> </v-btn>
</v-btn-toggle> </v-btn-toggle>
</div> </div>
@@ -211,7 +211,7 @@
:to="`/transaction/list?accountIds=${element.getAccountOrSubAccountId(activeSubAccount[element.id])}`"> :to="`/transaction/list?accountIds=${element.getAccountOrSubAccountId(activeSubAccount[element.id])}`">
{{ tt('Transaction List') }} {{ tt('Transaction List') }}
</v-btn> </v-btn>
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text" <v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:disabled="loading" :prepend-icon="mdiInvoiceListOutline" :disabled="loading" :prepend-icon="mdiInvoiceListOutline"
@click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]))" @click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]))"
v-if="element.type === AccountType.SingleAccount.type || element.getSubAccount(activeSubAccount[element.id])"> v-if="element.type === AccountType.SingleAccount.type || element.getSubAccount(activeSubAccount[element.id])">
@@ -225,7 +225,7 @@
<v-list-item-title class="cursor-pointer" <v-list-item-title class="cursor-pointer"
@click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]), dateRange.type)"> @click="showReconciliationStatementCustomDateRangeDialog(element.getAccountOrSubAccount(activeSubAccount[element.id]), dateRange.type)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm ml-3">{{ dateRange.displayName }}</span> <span class="text-sm ms-3">{{ dateRange.displayName }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -233,7 +233,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text" <v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:disabled="loading" :disabled="loading"
:prepend-icon="element.isAccountOrSubAccountHidden(activeSubAccount[element.id]) ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.isAccountOrSubAccountHidden(activeSubAccount[element.id]) ? mdiEyeOutline : mdiEyeOffOutline"
@@ -241,14 +241,14 @@
@click="hide(element, element.getAccountOrSubAccount(activeSubAccount[element.id]), !element.isAccountOrSubAccountHidden(activeSubAccount[element.id]))"> @click="hide(element, element.getAccountOrSubAccount(activeSubAccount[element.id]), !element.isAccountOrSubAccountHidden(activeSubAccount[element.id]))">
{{ element.isAccountOrSubAccountHidden(activeSubAccount[element.id]) ? tt('Show') : tt('Hide') }} {{ element.isAccountOrSubAccountHidden(activeSubAccount[element.id]) ? tt('Show') : tt('Hide') }}
</v-btn> </v-btn>
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text" <v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:disabled="loading" :prepend-icon="mdiPencilOutline" :disabled="loading" :prepend-icon="mdiPencilOutline"
v-if="!activeSubAccount[element.id] || element.getSubAccount(activeSubAccount[element.id])" v-if="!activeSubAccount[element.id] || element.getSubAccount(activeSubAccount[element.id])"
@click="edit(element)"> @click="edit(element)">
{{ tt('Edit') }} {{ tt('Edit') }}
</v-btn> </v-btn>
<v-btn class="px-2 ml-1" density="comfortable" color="default" variant="text" <v-btn class="px-2 ms-1" density="comfortable" color="default" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:disabled="loading" :prepend-icon="mdiDeleteOutline" :disabled="loading" :prepend-icon="mdiDeleteOutline"
v-if="!activeSubAccount[element.id] || element.getSubAccount(activeSubAccount[element.id])" v-if="!activeSubAccount[element.id] || element.getSubAccount(activeSubAccount[element.id])"
@@ -256,7 +256,7 @@
{{ tt('Delete') }} {{ tt('Delete') }}
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<span class="account-balance ml-2">{{ accountBalance(element, activeSubAccount[element.id]) }}</span> <span class="account-balance ms-2">{{ accountBalance(element, activeSubAccount[element.id]) }}</span>
</div> </div>
</v-card-text> </v-card-text>
</v-card> </v-card>

View File

@@ -5,9 +5,9 @@
<div class="d-flex align-center justify-center"> <div class="d-flex align-center justify-center">
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" :icon="true" <v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true"
:disabled="loading || submitting || account.type !== AccountType.MultiSubAccounts.type"> :disabled="loading || submitting || account.type !== AccountType.MultiSubAccounts.type">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -29,7 +29,7 @@
<template v-if="account.type === AccountType.MultiSubAccounts.type"> <template v-if="account.type === AccountType.MultiSubAccounts.type">
<v-tab :key="idx" :value="idx" v-for="(subAccount, idx) in subAccounts"> <v-tab :key="idx" :value="idx" v-for="(subAccount, idx) in subAccounts">
<span>{{ tt('Sub Account') + ' #' + (idx + 1) }}</span> <span>{{ tt('Sub Account') + ' #' + (idx + 1) }}</span>
<v-btn class="ml-2" color="error" size="24" variant="text" <v-btn class="ms-2" color="error" size="24" variant="text"
:icon="mdiDeleteOutline" :icon="mdiDeleteOutline"
@click="removeSubAccount(subAccount)"></v-btn> @click="removeSubAccount(subAccount)"></v-btn>
</v-tab> </v-tab>
@@ -38,7 +38,7 @@
</div> </div>
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" <v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container"
:class="{ 'ml-md-5': account.type === AccountType.MultiSubAccounts.type }" :class="{ 'ms-md-5': account.type === AccountType.MultiSubAccounts.type }"
v-model="activeTab"> v-model="activeTab">
<v-window-item value="account"> <v-window-item value="account">
<v-form class="mt-2"> <v-form class="mt-2">
@@ -63,7 +63,7 @@
<ItemIcon icon-type="account" <ItemIcon icon-type="account"
:icon-id="item.raw.defaultAccountIconId" :icon-id="item.raw.defaultAccountIconId"
v-if="item.raw" /> v-if="item.raw" />
<span class="ml-2">{{ item.title }}</span> <span class="ms-2">{{ item.title }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</template> </template>
@@ -174,7 +174,7 @@
<div v-bind="props" class="d-inline-block"> <div v-bind="props" class="d-inline-block">
<v-btn :disabled="inputIsEmpty || loading || submitting" @click="save"> <v-btn :disabled="inputIsEmpty || loading || submitting" @click="save">
{{ tt(saveButtonTitle) }} {{ tt(saveButtonTitle) }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
</div> </div>
</template> </template>

View File

@@ -6,7 +6,7 @@
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt('Reconciliation Statement') }}</h4> <h4 class="text-h4">{{ tt('Reconciliation Statement') }}</h4>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading" @click="reload(true)"> class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -14,7 +14,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading" :icon="true" :disabled="loading"
v-if="showAccountBalanceTrendsCharts"> v-if="showAccountBalanceTrendsCharts">
<v-icon :icon="mdiTuneVertical" /> <v-icon :icon="mdiTuneVertical" />
@@ -42,7 +42,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading"> :icon="true" :disabled="loading">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -94,42 +94,42 @@
<div class="d-flex align-center mb-4"> <div class="d-flex align-center mb-4">
<div class="d-flex align-center text-body-1"> <div class="d-flex align-center text-body-1">
<span class="ml-2">{{ tt('Opening Balance') }}</span> <span class="ms-2">{{ tt('Opening Balance') }}</span>
<span class="text-primary" v-if="loading"> <span class="text-primary" v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-primary ml-2" v-else-if="!loading"> <span class="text-primary ms-2" v-else-if="!loading">
{{ displayOpeningBalance }} {{ displayOpeningBalance }}
</span> </span>
<span class="ml-3">{{ tt('Closing Balance') }}</span> <span class="ms-3">{{ tt('Closing Balance') }}</span>
<span class="text-primary" v-if="loading"> <span class="text-primary" v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-primary ml-2" v-else-if="!loading"> <span class="text-primary ms-2" v-else-if="!loading">
{{ displayClosingBalance }} {{ displayClosingBalance }}
</span> </span>
</div> </div>
<v-spacer/> <v-spacer/>
<div class="d-flex align-center text-body-1"> <div class="d-flex align-center text-body-1">
<span class="ml-2">{{ tt('Total Inflows') }}</span> <span class="ms-2">{{ tt('Total Inflows') }}</span>
<span class="text-income" v-if="loading"> <span class="text-income" v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-income ml-2" v-else-if="!loading"> <span class="text-income ms-2" v-else-if="!loading">
{{ displayTotalInflows }} {{ displayTotalInflows }}
</span> </span>
<span class="ml-3">{{ tt('Total Outflows') }}</span> <span class="ms-3">{{ tt('Total Outflows') }}</span>
<span class="text-expense" v-if="loading"> <span class="text-expense" v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-expense ml-2" v-else-if="!loading"> <span class="text-expense ms-2" v-else-if="!loading">
{{ displayTotalOutflows }} {{ displayTotalOutflows }}
</span> </span>
<span class="ml-3">{{ tt('Net Cash Flow') }}</span> <span class="ms-3">{{ tt('Net Cash Flow') }}</span>
<span class="text-primary" v-if="loading"> <span class="text-primary" v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-primary ml-2" v-else-if="!loading"> <span class="text-primary ms-2" v-else-if="!loading">
{{ displayTotalBalance }} {{ displayTotalBalance }}
</span> </span>
</div> </div>
@@ -151,7 +151,7 @@
> >
<template #item.time="{ item }"> <template #item.time="{ item }">
<span>{{ getDisplayDateTime(item) }}</span> <span>{{ getDisplayDateTime(item) }}</span>
<v-chip class="ml-1" variant="flat" color="secondary" size="x-small" <v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip> v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
</template> </template>
<template #item.type="{ item }"> <template #item.type="{ item }">
@@ -166,23 +166,23 @@
:color="allCategoriesMap[item.categoryId].color" :color="allCategoriesMap[item.categoryId].color"
v-if="allCategoriesMap[item.categoryId] && allCategoriesMap[item.categoryId]?.color"></ItemIcon> v-if="allCategoriesMap[item.categoryId] && allCategoriesMap[item.categoryId]?.color"></ItemIcon>
<v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!allCategoriesMap[item.categoryId] || !allCategoriesMap[item.categoryId]?.color" /> <v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!allCategoriesMap[item.categoryId] || !allCategoriesMap[item.categoryId]?.color" />
<span class="ml-2" v-if="item.type === TransactionType.ModifyBalance"> <span class="ms-2" v-if="item.type === TransactionType.ModifyBalance">
{{ tt('Modify Balance') }} {{ tt('Modify Balance') }}
</span> </span>
<span class="ml-2" v-else-if="item.type !== TransactionType.ModifyBalance && allCategoriesMap[item.categoryId]"> <span class="ms-2" v-else-if="item.type !== TransactionType.ModifyBalance && allCategoriesMap[item.categoryId]">
{{ allCategoriesMap[item.categoryId].name }} {{ allCategoriesMap[item.categoryId].name }}
</span> </span>
</div> </div>
</template> </template>
<template #item.sourceAmount="{ item }"> <template #item.sourceAmount="{ item }">
<span :class="{ 'text-expense': item.type === TransactionType.Expense, 'text-income': item.type === TransactionType.Income }">{{ getDisplaySourceAmount(item) }}</span> <span :class="{ 'text-expense': item.type === TransactionType.Expense, 'text-income': item.type === TransactionType.Income }">{{ getDisplaySourceAmount(item) }}</span>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)"></v-icon>
<span v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)">{{ getDisplayDestinationAmount(item) }}</span> <span v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId && getDisplaySourceAmount(item) !== getDisplayDestinationAmount(item)">{{ getDisplayDestinationAmount(item) }}</span>
</template> </template>
<template #item.sourceAccountId="{ item }"> <template #item.sourceAccountId="{ item }">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span v-if="item.sourceAccountId && allAccountsMap[item.sourceAccountId]">{{ allAccountsMap[item.sourceAccountId].name }}</span> <span v-if="item.sourceAccountId && allAccountsMap[item.sourceAccountId]">{{ allAccountsMap[item.sourceAccountId].name }}</span>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon>
<span v-if="item.type === TransactionType.Transfer && item.destinationAccountId && allAccountsMap[item.destinationAccountId]">{{ allAccountsMap[item.destinationAccountId].name }}</span> <span v-if="item.type === TransactionType.Transfer && item.destinationAccountId && allAccountsMap[item.destinationAccountId]">{{ allAccountsMap[item.destinationAccountId].name }}</span>
</div> </div>
</template> </template>
@@ -197,18 +197,18 @@
</template> </template>
<template #bottom> <template #bottom>
<div class="title-and-toolbar d-flex align-center text-no-wrap mt-2" v-if="loading || (reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length)"> <div class="title-and-toolbar d-flex align-center text-no-wrap mt-2" v-if="loading || (reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length)">
<span class="ml-2">{{ tt('Total Transactions') }}</span> <span class="ms-2">{{ tt('Total Transactions') }}</span>
<span v-if="loading"> <span v-if="loading">
<v-skeleton-loader class="skeleton-no-margin ml-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="ml-2" v-else-if="!loading"> <span class="ms-2" v-else-if="!loading">
{{ formatNumberToLocalizedNumerals(reconciliationStatements?.transactions.length ?? 0) }} {{ formatNumberToLocalizedNumerals(reconciliationStatements?.transactions.length ?? 0) }}
</span> </span>
<v-spacer/> <v-spacer/>
<span v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10"> <span v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10">
{{ tt('Transactions Per Page') }} {{ tt('Transactions Per Page') }}
</span> </span>
<v-select class="ml-2" density="compact" max-width="100" <v-select class="ms-2" density="compact" max-width="100"
item-title="title" item-title="title"
item-value="value" item-value="value"
:disabled="loading" :disabled="loading"

View File

@@ -4,7 +4,7 @@
<v-card> <v-card>
<template #title> <template #title>
<span>{{ tt('Settings Sync') }}</span> <span>{{ tt('Settings Sync') }}</span>
<v-progress-circular indeterminate size="20" class="ml-3" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="20" class="ms-3" v-if="loading"></v-progress-circular>
</template> </template>
<v-card-text class="pb-0"> <v-card-text class="pb-0">
@@ -28,7 +28,7 @@
<div class="w-100"> <div class="w-100">
<span>{{ tt('Synchronized Settings') }}</span> <span>{{ tt('Synchronized Settings') }}</span>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || enabling || disabling" :icon="true"> :disabled="loading || enabling || disabling" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -73,7 +73,7 @@
<v-divider/> <v-divider/>
<v-list rounded density="comfortable" class="pa-0 ml-4"> <v-list rounded density="comfortable" class="pa-0 ms-4">
<template :key="settingItem.settingKey" <template :key="settingItem.settingKey"
v-for="(settingItem, itemIdx) in categorizedItems.items"> v-for="(settingItem, itemIdx) in categorizedItems.items">
<v-divider v-if="itemIdx > 0"/> <v-divider v-if="itemIdx > 0"/>
@@ -85,8 +85,8 @@
@update:model-value="enabledApplicationCloudSettings[settingItem.settingKey] = !!$event"> @update:model-value="enabledApplicationCloudSettings[settingItem.settingKey] = !!$event">
<template #label> <template #label>
<span>{{ tt(settingItem.settingName) }}</span> <span>{{ tt(settingItem.settingName) }}</span>
<v-icon class="ml-2 mr-0" start size="16" :icon="mdiCellphone" v-if="settingItem.mobile"/> <v-icon class="ms-2 me-0" start size="16" :icon="mdiCellphone" v-if="settingItem.mobile"/>
<v-icon class="ml-2 mr-0" start size="16" :icon="mdiMonitor" v-if="settingItem.desktop"/> <v-icon class="ms-2 me-0" start size="16" :icon="mdiMonitor" v-if="settingItem.desktop"/>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>
@@ -105,15 +105,15 @@
<v-col cols="12" class="d-flex flex-wrap gap-4"> <v-col cols="12" class="d-flex flex-wrap gap-4">
<v-btn :disabled="loading || enabling || disabling || !hasEnabledApplicationCloudSettings" v-if="!isEnableCloudSync" @click="enable(false)"> <v-btn :disabled="loading || enabling || disabling || !hasEnabledApplicationCloudSettings" v-if="!isEnableCloudSync" @click="enable(false)">
{{ tt('Enable Settings Sync') }} {{ tt('Enable Settings Sync') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="enabling"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="enabling"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :disabled="loading || enabling || disabling || !hasEnabledApplicationCloudSettings" v-if="isEnableCloudSync" @click="enable(true)"> <v-btn :disabled="loading || enabling || disabling || !hasEnabledApplicationCloudSettings" v-if="isEnableCloudSync" @click="enable(true)">
{{ tt('Update Synchronized Settings') }} {{ tt('Update Synchronized Settings') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="enabling"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="enabling"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :disabled="loading || enabling || disabling" v-if="isEnableCloudSync" @click="disable"> <v-btn :disabled="loading || enabling || disabling" v-if="isEnableCloudSync" @click="disable">
{{ tt('Disable Settings Sync') }} {{ tt('Disable Settings Sync') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="disabling"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="disabling"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -35,18 +35,18 @@
<v-card variant="flat" :min-height="cardMinHeight"> <v-card variant="flat" :min-height="cardMinHeight">
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain" <v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
:ripple="false" :icon="true" @click="showNav = !showNav"> :ripple="false" :icon="true" @click="showNav = !showNav">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
<span>{{ tt('Transaction Categories') }}</span> <span>{{ tt('Transaction Categories') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading || updating" @click="add">{{ tt('Add') }}</v-btn> :disabled="loading || updating" @click="add">{{ tt('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal" <v-btn class="ms-3" color="primary" variant="tonal"
:disabled="loading || updating" @click="saveSortResult" :disabled="loading || updating" @click="saveSortResult"
v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn> v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading || updating" @click="reload(true)"> class="ms-2" :icon="true" :loading="loading || updating" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -54,7 +54,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || updating" :icon="true"> :disabled="loading || updating" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -97,7 +97,7 @@
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<span>{{ tt('No available category') }}</span> <span>{{ tt('No available category') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
@click="showPresetDialog = true" @click="showPresetDialog = true"
v-if="hasSubCategories && noCategory"> v-if="hasSubCategories && noCategory">
{{ tt('Add Default Categories') }} {{ tt('Add Default Categories') }}
@@ -124,14 +124,14 @@
:icon-id="element.icon" :color="element.color" :icon-id="element.icon" :color="element.color"
:hidden-status="element.hidden" /> :hidden-status="element.hidden" />
<div class="d-flex flex-column py-2"> <div class="d-flex flex-column py-2">
<span class="ml-2">{{ element.name }}</span> <span class="ms-2">{{ element.name }}</span>
<span class="transaction-category-comment ml-2">{{ element.comment }}</span> <span class="transaction-category-comment ms-2">{{ element.comment }}</span>
</div> </div>
</div> </div>
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ml-2" color="default" <v-btn class="px-2 ms-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
@@ -163,7 +163,7 @@
</template> </template>
{{ tt('Delete') }} {{ tt('Delete') }}
</v-btn> </v-btn>
<span class="ml-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip>

View File

@@ -4,7 +4,7 @@
<template #title> <template #title>
<div class="d-flex align-center justify-center"> <div class="d-flex align-center justify-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
</div> </div>
</template> </template>
<v-card-text class="pt-0"> <v-card-text class="pt-0">
@@ -35,7 +35,7 @@
<template #item="{ props, item }"> <template #item="{ props, item }">
<v-list-item v-bind="props"> <v-list-item v-bind="props">
<template #prepend> <template #prepend>
<ItemIcon class="mr-2" icon-type="category" <ItemIcon class="me-2" icon-type="category"
:icon-id="item.raw.icon" :color="item.raw.color"></ItemIcon> :icon-id="item.raw.icon" :color="item.raw.color"></ItemIcon>
</template> </template>
<template #title> <template #title>
@@ -84,7 +84,7 @@
<div v-bind="props" class="d-inline-block"> <div v-bind="props" class="d-inline-block">
<v-btn :disabled="inputIsEmpty || loading || submitting" @click="save"> <v-btn :disabled="inputIsEmpty || loading || submitting" @click="save">
{{ tt(saveButtonTitle) }} {{ tt(saveButtonTitle) }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
</div> </div>
</template> </template>

View File

@@ -19,7 +19,7 @@
<v-expansion-panel :key="idx" v-for="(category, idx) in categories"> <v-expansion-panel :key="idx" v-for="(category, idx) in categories">
<v-expansion-panel-title class="py-0"> <v-expansion-panel-title class="py-0">
<ItemIcon icon-type="category" :icon-id="category.icon" :color="category.color"></ItemIcon> <ItemIcon icon-type="category" :icon-id="category.icon" :color="category.color"></ItemIcon>
<span class="ml-3">{{ category.name }}</span> <span class="ms-3">{{ category.name }}</span>
</v-expansion-panel-title> </v-expansion-panel-title>
<v-expansion-panel-text v-if="category.subCategories.length"> <v-expansion-panel-text v-if="category.subCategories.length">
<v-list rounded density="comfortable" class="pa-0"> <v-list rounded density="comfortable" class="pa-0">
@@ -29,7 +29,7 @@
<template #prepend> <template #prepend>
<ItemIcon icon-type="category" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon> <ItemIcon icon-type="category" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon>
</template> </template>
<span class="ml-3">{{ subCategory.name }}</span> <span class="ms-3">{{ subCategory.name }}</span>
</v-list-item> </v-list-item>
<v-divider v-if="subIdx !== category.subCategories.length - 1"/> <v-divider v-if="subIdx !== category.subCategories.length - 1"/>
</template> </template>
@@ -43,7 +43,7 @@
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4"> <div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
<v-btn :disabled="submitting" @click="save"> <v-btn :disabled="submitting" @click="save">
{{ tt('Save') }} {{ tt('Save') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" density="default" variant="tonal" <v-btn color="secondary" density="default" variant="tonal"
:disabled="submitting" @click="showState = false">{{ tt('Cancel') }}</v-btn> :disabled="submitting" @click="showState = false">{{ tt('Cancel') }}</v-btn>
@@ -137,6 +137,7 @@ function save(): void {
<style> <style>
.preset-transaction-categories .v-expansion-panel-text__wrapper { .preset-transaction-categories .v-expansion-panel-text__wrapper {
padding: 0 0 0 20px; padding: 0 0 0 0;
padding-inline-start: 20px;
} }
</style> </style>

View File

@@ -5,7 +5,7 @@
<div class="w-100 text-center"> <div class="w-100 text-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || !hasAnyAvailableAccount" :icon="true"> :disabled="loading || !hasAnyAvailableAccount" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -36,7 +36,7 @@
<div class="d-flex align-center" v-else-if="!dialogMode"> <div class="d-flex align-center" v-else-if="!dialogMode">
<span>{{ tt(title) }}</span> <span>{{ tt(title) }}</span>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true"> :disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -83,7 +83,7 @@
v-for="accountCategory in allCategorizedAccounts" v-for="accountCategory in allCategorizedAccounts"
v-show="showHidden || accountCategory.allVisibleAccountCount > 0"> v-show="showHidden || accountCategory.allVisibleAccountCount > 0">
<v-expansion-panel-title class="expand-panel-title-with-bg py-0"> <v-expansion-panel-title class="expand-panel-title-with-bg py-0">
<span class="ml-3">{{ tt(accountCategory.name) }}</span> <span class="ms-3">{{ tt(accountCategory.name) }}</span>
</v-expansion-panel-title> </v-expansion-panel-title>
<v-expansion-panel-text> <v-expansion-panel-text>
<v-list rounded density="comfortable" class="pa-0"> <v-list rounded density="comfortable" class="pa-0">
@@ -99,7 +99,7 @@
<template #label> <template #label>
<ItemIcon class="d-flex" icon-type="account" :icon-id="account.icon" <ItemIcon class="d-flex" icon-type="account" :icon-id="account.icon"
:color="account.color" :hidden-status="account.hidden"></ItemIcon> :color="account.color" :hidden-status="account.hidden"></ItemIcon>
<span class="ml-3">{{ account.name }}</span> <span class="ms-3">{{ account.name }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>
@@ -107,7 +107,7 @@
<v-divider v-if="(showHidden || !account.hidden) && account.type === AccountType.MultiSubAccounts.type && ((showHidden && accountCategory.allSubAccounts[account.id]) || accountCategory.allVisibleSubAccountCounts[account.id])"/> <v-divider v-if="(showHidden || !account.hidden) && account.type === AccountType.MultiSubAccounts.type && ((showHidden && accountCategory.allSubAccounts[account.id]) || accountCategory.allVisibleSubAccountCounts[account.id])"/>
<v-list rounded density="comfortable" class="pa-0 ml-4" <v-list rounded density="comfortable" class="pa-0 ms-4"
v-if="(showHidden || !account.hidden) && account.type === AccountType.MultiSubAccounts.type && ((showHidden && accountCategory.allSubAccounts[account.id]) || accountCategory.allVisibleSubAccountCounts[account.id])"> v-if="(showHidden || !account.hidden) && account.type === AccountType.MultiSubAccounts.type && ((showHidden && accountCategory.allSubAccounts[account.id]) || accountCategory.allVisibleSubAccountCounts[account.id])">
<template :key="subAccount.id" <template :key="subAccount.id"
v-for="(subAccount, subIdx) in accountCategory.allSubAccounts[account.id]"> v-for="(subAccount, subIdx) in accountCategory.allSubAccounts[account.id]">
@@ -120,7 +120,7 @@
<template #label> <template #label>
<ItemIcon class="d-flex" icon-type="account" :icon-id="subAccount.icon" <ItemIcon class="d-flex" icon-type="account" :icon-id="subAccount.icon"
:color="subAccount.color" :hidden-status="subAccount.hidden"></ItemIcon> :color="subAccount.color" :hidden-status="subAccount.hidden"></ItemIcon>
<span class="ml-3">{{ subAccount.name }}</span> <span class="ms-3">{{ subAccount.name }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>

View File

@@ -5,7 +5,7 @@
<div class="w-100 text-center"> <div class="w-100 text-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || !hasAnyAvailableCategory" :icon="true"> :disabled="loading || !hasAnyAvailableCategory" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -36,7 +36,7 @@
<div class="d-flex align-center" v-else-if="!dialogMode"> <div class="d-flex align-center" v-else-if="!dialogMode">
<span>{{ tt(title) }}</span> <span>{{ tt(title) }}</span>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true"> :disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -78,7 +78,7 @@
class="border" class="border"
v-for="transactionType in allTransactionCategories"> v-for="transactionType in allTransactionCategories">
<v-expansion-panel-title class="expand-panel-title-with-bg py-0"> <v-expansion-panel-title class="expand-panel-title-with-bg py-0">
<span class="ml-3">{{ getCategoryTypeName(transactionType.type) }}</span> <span class="ms-3">{{ getCategoryTypeName(transactionType.type) }}</span>
</v-expansion-panel-title> </v-expansion-panel-title>
<v-expansion-panel-text> <v-expansion-panel-text>
<v-list rounded density="comfortable" class="pa-0"> <v-list rounded density="comfortable" class="pa-0">
@@ -96,7 +96,7 @@
<template #label> <template #label>
<ItemIcon class="d-flex" icon-type="category" :icon-id="category.icon" <ItemIcon class="d-flex" icon-type="category" :icon-id="category.icon"
:color="category.color" :hidden-status="category.hidden"></ItemIcon> :color="category.color" :hidden-status="category.hidden"></ItemIcon>
<span class="ml-3">{{ category.name }}</span> <span class="ms-3">{{ category.name }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>
@@ -104,7 +104,7 @@
<v-divider v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])"/> <v-divider v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])"/>
<v-list rounded density="comfortable" class="pa-0 ml-4" <v-list rounded density="comfortable" class="pa-0 ms-4"
v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])"> v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])">
<template :key="subCategory.id" <template :key="subCategory.id"
v-for="(subCategory, subIdx) in transactionType.allSubCategories[category.id]"> v-for="(subCategory, subIdx) in transactionType.allSubCategories[category.id]">
@@ -117,7 +117,7 @@
<template #label> <template #label>
<ItemIcon class="d-flex" icon-type="category" :icon-id="subCategory.icon" <ItemIcon class="d-flex" icon-type="category" :icon-id="subCategory.icon"
:color="subCategory.color" :hidden-status="subCategory.hidden"></ItemIcon> :color="subCategory.color" :hidden-status="subCategory.hidden"></ItemIcon>
<span class="ml-3">{{ subCategory.name }}</span> <span class="ms-3">{{ subCategory.name }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>

View File

@@ -5,7 +5,7 @@
<div class="w-100 text-center"> <div class="w-100 text-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || !hasAnyAvailableTag" :icon="true"> :disabled="loading || !hasAnyAvailableTag" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -36,7 +36,7 @@
<div class="d-flex align-center" v-else-if="!dialogMode"> <div class="d-flex align-center" v-else-if="!dialogMode">
<span>{{ tt(title) }}</span> <span>{{ tt(title) }}</span>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true"> :disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -90,7 +90,7 @@
<v-expansion-panels class="tag-categories" multiple v-model="expandTagCategories"> <v-expansion-panels class="tag-categories" multiple v-model="expandTagCategories">
<v-expansion-panel class="border" key="default" value="default"> <v-expansion-panel class="border" key="default" value="default">
<v-expansion-panel-title class="expand-panel-title-with-bg py-0"> <v-expansion-panel-title class="expand-panel-title-with-bg py-0">
<span class="ml-3">{{ tt('Tags') }}</span> <span class="ms-3">{{ tt('Tags') }}</span>
</v-expansion-panel-title> </v-expansion-panel-title>
<v-expansion-panel-text> <v-expansion-panel-text>
<v-list rounded density="comfortable" class="pa-0"> <v-list rounded density="comfortable" class="pa-0">
@@ -107,7 +107,7 @@
<v-icon size="24" :icon="mdiPound"/> <v-icon size="24" :icon="mdiPound"/>
</v-badge> </v-badge>
<v-icon size="24" :icon="mdiPound" v-else-if="!transactionTag.hidden"/> <v-icon size="24" :icon="mdiPound" v-else-if="!transactionTag.hidden"/>
<span class="ml-3">{{ transactionTag.name }}</span> <span class="ms-3">{{ transactionTag.name }}</span>
</template> </template>
</v-checkbox> </v-checkbox>
</template> </template>
@@ -269,7 +269,8 @@ init();
} }
.tag-categories .v-expansion-panel-text__wrapper { .tag-categories .v-expansion-panel-text__wrapper {
padding: 0 0 0 20px; padding: 0 0 0 0;
padding-inline-start: 20px;
} }
.tag-categories .v-expansion-panel--active:not(:first-child), .tag-categories .v-expansion-panel--active:not(:first-child),

View File

@@ -39,9 +39,9 @@
v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length"> v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length">
<v-tab class="tab-text-truncate" :key="exchangeRate.currencyCode" :value="exchangeRate.currencyCode" <v-tab class="tab-text-truncate" :key="exchangeRate.currencyCode" :value="exchangeRate.currencyCode"
v-for="exchangeRate in availableExchangeRates"> v-for="exchangeRate in availableExchangeRates">
<div class="text-truncate"> <div class="d-flex w-100">
<span>{{ exchangeRate.currencyDisplayName }}</span> <span class="d-block text-truncate">{{ exchangeRate.currencyDisplayName }}</span>
<small class="smaller ml-1">{{ exchangeRate.currencyCode }}</small> <small class="smaller ms-1">{{ exchangeRate.currencyCode }}</small>
</div> </div>
</v-tab> </v-tab>
</v-tabs> </v-tabs>
@@ -61,16 +61,16 @@
<v-card variant="flat" min-height="680"> <v-card variant="flat" min-height="680">
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain" <v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
:ripple="false" :icon="true" @click="showNav = !showNav"> :ripple="false" :icon="true" @click="showNav = !showNav">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
<span>{{ tt('Exchange Rates Data') }}</span> <span>{{ tt('Exchange Rates Data') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading" @click="update" :disabled="loading" @click="update"
v-if="isUserCustomExchangeRates">{{ tt('Update') }}</v-btn> v-if="isUserCustomExchangeRates">{{ tt('Update') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading" @click="reload(true)"> class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -110,11 +110,11 @@
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span> <span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span>
<span class="text-caption ml-1">{{ exchangeRate.currencyCode }}</span> <span class="text-caption ms-1">{{ exchangeRate.currencyCode }}</span>
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ml-2" color="default" <v-btn class="px-2 ms-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
v-if="exchangeRate.currencyCode !== baseCurrency" v-if="exchangeRate.currencyCode !== baseCurrency"
@@ -134,7 +134,7 @@
</template> </template>
{{ tt('Delete') }} {{ tt('Delete') }}
</v-btn> </v-btn>
<span class="ml-3">{{ getFinalConvertedAmount(exchangeRate, true) }}</span> <span class="ms-3">{{ getFinalConvertedAmount(exchangeRate, true) }}</span>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -51,7 +51,7 @@
<div class="w-100 d-flex justify-center gap-4"> <div class="w-100 d-flex justify-center gap-4">
<v-btn :disabled="submitting || !defaultCurrencyAmount || !currency || !targetCurrencyAmount" @click="confirm"> <v-btn :disabled="submitting || !defaultCurrencyAmount || !currency || !targetCurrencyAmount" @click="confirm">
{{ tt('OK') }} {{ tt('OK') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" variant="tonal" :disabled="submitting" @click="cancel">{{ tt('Cancel') }}</v-btn> <v-btn color="secondary" variant="tonal" :disabled="submitting" @click="cancel">{{ tt('Cancel') }}</v-btn>
</div> </div>

View File

@@ -4,9 +4,9 @@
<v-avatar color="secondary" size="38"> <v-avatar color="secondary" size="38">
<v-icon size="24" :icon="icon" /> <v-icon size="24" :icon="icon" />
</v-avatar> </v-avatar>
<span class="font-weight-bold ml-3">{{ title }}</span> <span class="font-weight-bold ms-3">{{ title }}</span>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" :icon="true"> <v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
<v-list> <v-list>

View File

@@ -34,6 +34,7 @@ import { useI18n } from '@/locales/helpers.ts';
import { useSettingsStore } from '@/stores/setting.ts'; import { useSettingsStore } from '@/stores/setting.ts';
import { useUserStore } from '@/stores/user.ts'; import { useUserStore } from '@/stores/user.ts';
import { TextDirection } from '@/core/text.ts';
import type { HiddenAmount } from '@/core/numeral.ts'; import type { HiddenAmount } from '@/core/numeral.ts';
import { TransactionType } from '@/core/transaction.ts'; import { TransactionType } from '@/core/transaction.ts';
import { DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numeral.ts'; import { DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numeral.ts';
@@ -63,11 +64,12 @@ const emit = defineEmits<{
(e: 'click', event: MonthlyIncomeAndExpenseCardClickEvent): void; (e: 'click', event: MonthlyIncomeAndExpenseCardClickEvent): void;
}>(); }>();
const { tt, getMonthShortName, formatAmountToLocalizedNumeralsWithCurrency } = useI18n(); const { tt, getCurrentLanguageTextDirection, getMonthShortName, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const userStore = useUserStore(); const userStore = useUserStore();
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const showAmountInHomePage = computed<boolean>(() => settingsStore.appSettings.showAmountInHomePage); const showAmountInHomePage = computed<boolean>(() => settingsStore.appSettings.showAmountInHomePage);
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency); const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
const hasAnyData = computed<boolean>(() => { const hasAnyData = computed<boolean>(() => {
@@ -157,21 +159,21 @@ const chartOptions = computed<object>(() => {
return `<table>` + return `<table>` +
`<thead>` + `<thead>` +
`<tr>` + `<tr>` +
`<td colspan="2" class="text-left">${params[0].name}</td>` + `<td colspan="2" class="text-start">${params[0].name}</td>` +
`</tr>` + `</tr>` +
`</thead>` + `</thead>` +
`<tbody>` + `<tbody>` +
( (
incomeAmount !== null ? incomeAmount !== null ?
`<tr>` + `<tr>` +
`<td><span class="overview-monthly-chart-tooltip-indicator bg-income mr-1"></span><span class="mr-4">${tt('Income')}</span></td>` + `<td><span class="overview-monthly-chart-tooltip-indicator bg-income me-1"></span><span class="me-4">${tt('Income')}</span></td>` +
`<td><strong>${incomeAmount}</strong></td>` + `<td><strong>${incomeAmount}</strong></td>` +
`</tr>` : '' `</tr>` : ''
)+ )+
( (
expenseAmount !== null ? expenseAmount !== null ?
`<tr>` + `<tr>` +
`<td><span class="overview-monthly-chart-tooltip-indicator bg-expense mr-1"></span><span class="mr-4">${tt('Expense')}</span></td>` + `<td><span class="overview-monthly-chart-tooltip-indicator bg-expense me-1"></span><span class="me-4">${tt('Expense')}</span></td>` +
`<td><strong>${expenseAmount}</strong></td>` + `<td><strong>${expenseAmount}</strong></td>` +
`</tr>` : '' `</tr>` : ''
) + ) +
@@ -199,6 +201,7 @@ const chartOptions = computed<object>(() => {
{ {
type: 'category', type: 'category',
data: monthNames, data: monthNames,
inverse: textDirection.value === TextDirection.RTL,
axisLine: { axisLine: {
show: false show: false
}, },

View File

@@ -51,13 +51,13 @@
<v-card variant="flat" min-height="680"> <v-card variant="flat" min-height="680">
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain" <v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
:ripple="false" :icon="true" @click="showNav = !showNav"> :ripple="false" :icon="true" @click="showNav = !showNav">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
<span>{{ tt('Statistics & Analysis') }}</span> <span>{{ tt('Statistics & Analysis') }}</span>
<v-btn-group class="ml-4" color="default" density="comfortable" variant="outlined" divided> <v-btn-group class="ms-4" color="default" density="comfortable" variant="outlined" divided>
<v-btn :icon="mdiArrowLeft" <v-btn class="button-icon-with-direction" :icon="mdiArrowLeft"
:disabled="loading || !canShiftDateRange" :disabled="loading || !canShiftDateRange"
@click="shiftDateRange(-1)"/> @click="shiftDateRange(-1)"/>
<v-menu location="bottom"> <v-menu location="bottom">
@@ -83,14 +83,14 @@
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
<v-btn :icon="mdiArrowRight" <v-btn class="button-icon-with-direction" :icon="mdiArrowRight"
:disabled="loading || !canShiftDateRange" :disabled="loading || !canShiftDateRange"
@click="shiftDateRange(1)"/> @click="shiftDateRange(1)"/>
</v-btn-group> </v-btn-group>
<v-menu location="bottom" v-if="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis"> <v-menu location="bottom" v-if="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis">
<template #activator="{ props }"> <template #activator="{ props }">
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:prepend-icon="mdiCalendarRangeOutline" :disabled="loading" :prepend-icon="mdiCalendarRangeOutline" :disabled="loading"
v-bind="props">{{ queryTrendDateAggregationTypeName }}</v-btn> v-bind="props">{{ queryTrendDateAggregationTypeName }}</v-btn>
</template> </template>
@@ -105,7 +105,7 @@
</v-menu> </v-menu>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading" @click="reload(true)"> class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -113,7 +113,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<div class="transaction-keyword-filter ml-2"> <div class="transaction-keyword-filter ms-2">
<v-text-field density="compact" :disabled="loading" <v-text-field density="compact" :disabled="loading"
:prepend-inner-icon="mdiMagnify" :prepend-inner-icon="mdiMagnify"
:append-inner-icon="filterKeyword !== query.keyword ? mdiCheck : undefined" :append-inner-icon="filterKeyword !== query.keyword ? mdiCheck : undefined"
@@ -123,7 +123,7 @@
@keyup.enter="setKeywordFilter(filterKeyword)" @keyup.enter="setKeywordFilter(filterKeyword)"
/> />
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true"> :disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -155,12 +155,12 @@
<v-card-text class="statistics-overview-title pt-0" :class="{ 'disabled': loading }" <v-card-text class="statistics-overview-title pt-0" :class="{ 'disabled': loading }"
v-if="queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && (initing || (categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length))"> v-if="queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && (initing || (categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length))">
<span class="statistics-subtitle">{{ totalAmountName }}</span> <span class="statistics-subtitle">{{ totalAmountName }}</span>
<span class="statistics-overview-amount ml-3" <span class="statistics-overview-amount ms-3"
:class="statisticsTextColor" :class="statisticsTextColor"
v-if="!initing && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length"> v-if="!initing && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
{{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency) }} {{ getDisplayAmount(categoricalAnalysisData.totalAmount, defaultCurrency) }}
</span> </span>
<v-skeleton-loader class="skeleton-no-margin ml-3 mb-2" <v-skeleton-loader class="skeleton-no-margin ms-3 mb-2"
width="120px" type="text" :loading="true" width="120px" type="text" :loading="true"
v-else-if="initing"></v-skeleton-loader> v-else-if="initing"></v-skeleton-loader>
</v-card-text> </v-card-text>
@@ -204,13 +204,13 @@
<v-card-text :class="{ 'readonly': loading }" v-if="queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && query.categoricalChartType === CategoricalChartType.Bar.type"> <v-card-text :class="{ 'readonly': loading }" v-if="queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && query.categoricalChartType === CategoricalChartType.Bar.type">
<v-list rounded lines="two" v-if="initing"> <v-list rounded lines="two" v-if="initing">
<template :key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"> <template :key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]">
<v-list-item class="pl-0"> <v-list-item class="ps-0">
<template #prepend> <template #prepend>
<div> <div>
<v-icon class="disabled mr-0" size="34" :icon="mdiSquareRounded" /> <v-icon class="disabled me-0" size="34" :icon="mdiSquareRounded" />
</div> </div>
</template> </template>
<div class="d-flex flex-column ml-2"> <div class="d-flex flex-column ms-2">
<div class="d-flex"> <div class="d-flex">
<v-skeleton-loader class="skeleton-no-margin my-2" <v-skeleton-loader class="skeleton-no-margin my-2"
width="120px" type="text" :loading="true"></v-skeleton-loader> width="120px" type="text" :loading="true"></v-skeleton-loader>
@@ -226,7 +226,7 @@
<v-list class="py-0" rounded lines="two" v-else-if="!initing && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length"> <v-list class="py-0" rounded lines="two" v-else-if="!initing && categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length">
<template :key="idx" <template :key="idx"
v-for="(item, idx) in categoricalAnalysisData.items"> v-for="(item, idx) in categoricalAnalysisData.items">
<v-list-item class="pl-0" v-if="!item.hidden"> <v-list-item class="ps-0" v-if="!item.hidden">
<template #prepend> <template #prepend>
<router-link class="statistics-list-item" :to="getTransactionItemLinkUrl(item.id)"> <router-link class="statistics-list-item" :to="getTransactionItemLinkUrl(item.id)">
<ItemIcon :icon-type="queryChartDataCategory" size="34px" <ItemIcon :icon-type="queryChartDataCategory" size="34px"
@@ -235,7 +235,7 @@
</router-link> </router-link>
</template> </template>
<router-link class="statistics-list-item" :to="getTransactionItemLinkUrl(item.id)"> <router-link class="statistics-list-item" :to="getTransactionItemLinkUrl(item.id)">
<div class="d-flex flex-column ml-2"> <div class="d-flex flex-column ms-2">
<div class="d-flex"> <div class="d-flex">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<small class="statistics-percent" v-if="item.percent >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '&lt;0.01') }}</small> <small class="statistics-percent" v-if="item.percent >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '&lt;0.01') }}</small>
@@ -1058,7 +1058,7 @@ init(props);
.statistics-list-item .statistics-percent { .statistics-list-item .statistics-percent {
font-size: 0.75rem; font-size: 0.75rem;
opacity: 0.7; opacity: 0.7;
margin-left: 6px; margin-inline-start: 6px;
} }
.statistics-list-item .statistics-amount { .statistics-list-item .statistics-amount {

View File

@@ -6,7 +6,7 @@
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt('Export Results') }}</h4> <h4 class="text-h4">{{ tt('Export Results') }}</h4>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" :icon="true"> <v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
<v-list> <v-list>

View File

@@ -5,13 +5,13 @@
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<span>{{ tt('Transaction Tags') }}</span> <span>{{ tt('Transaction Tags') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading || updating || hasEditingTag" @click="add">{{ tt('Add') }}</v-btn> :disabled="loading || updating || hasEditingTag" @click="add">{{ tt('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal" <v-btn class="ms-3" color="primary" variant="tonal"
:disabled="loading || updating || hasEditingTag" @click="saveSortResult" :disabled="loading || updating || hasEditingTag" @click="saveSortResult"
v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn> v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :disabled="loading || updating || hasEditingTag" class="ms-2" :icon="true" :disabled="loading || updating || hasEditingTag"
:loading="loading" @click="reload"> :loading="loading" @click="reload">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
@@ -20,7 +20,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || updating || hasEditingTag" :icon="true"> :disabled="loading || updating || hasEditingTag" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -86,7 +86,7 @@
<span class="transaction-tag-name">{{ element.name }}</span> <span class="transaction-tag-name">{{ element.name }}</span>
</div> </div>
<v-text-field class="w-100 mr-2" type="text" <v-text-field class="w-100 me-2" type="text"
density="compact" variant="underlined" density="compact" variant="underlined"
:disabled="loading || updating" :disabled="loading || updating"
:placeholder="tt('Tag Title')" :placeholder="tt('Tag Title')"
@@ -106,7 +106,7 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ml-2" color="default" <v-btn class="px-2 ms-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
@@ -163,7 +163,7 @@
v-if="editingTag.id === element.id" @click="cancelSave(editingTag)"> v-if="editingTag.id === element.id" @click="cancelSave(editingTag)">
{{ tt('Cancel') }} {{ tt('Cancel') }}
</v-btn> </v-btn>
<span class="ml-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && !hasEditingTag && availableTagCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && !hasEditingTag && availableTagCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && !hasEditingTag && availableTagCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && !hasEditingTag && availableTagCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip>
@@ -178,7 +178,7 @@
<tr class="text-sm" :class="{ 'even-row': (availableTagCount & 1) === 1}"> <tr class="text-sm" :class="{ 'even-row': (availableTagCount & 1) === 1}">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-text-field class="w-100 mr-2" type="text" color="primary" <v-text-field class="w-100 me-2" type="text" color="primary"
density="compact" variant="underlined" density="compact" variant="underlined"
:disabled="loading || updating" :placeholder="tt('Tag Title')" :disabled="loading || updating" :placeholder="tt('Tag Title')"
v-model="newTag.name" @keyup.enter="save(newTag)"> v-model="newTag.name" @keyup.enter="save(newTag)">
@@ -206,7 +206,7 @@
@click="cancelSave(newTag)"> @click="cancelSave(newTag)">
{{ tt('Cancel') }} {{ tt('Cancel') }}
</v-btn> </v-btn>
<span class="ml-2"> <span class="ms-2">
<v-icon class="disabled" :icon="mdiDrag"/> <v-icon class="disabled" :icon="mdiDrag"/>
</span> </span>
</div> </div>
@@ -474,7 +474,7 @@ transactionTagsStore.loadAllTags({
} }
.transaction-tags-table .v-text-field .v-input__prepend { .transaction-tags-table .v-text-field .v-input__prepend {
margin-right: 0; margin-inline-end: 0;
color: rgba(var(--v-theme-on-surface)); color: rgba(var(--v-theme-on-surface));
} }

View File

@@ -5,13 +5,13 @@
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center"> <div class="title-and-toolbar d-flex align-center">
<span>{{ templateType === TemplateType.Schedule.type ? tt('Scheduled Transactions') : tt('Transaction Templates') }}</span> <span>{{ templateType === TemplateType.Schedule.type ? tt('Scheduled Transactions') : tt('Transaction Templates') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading || updating" @click="add">{{ tt('Add') }}</v-btn> :disabled="loading || updating" @click="add">{{ tt('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal" <v-btn class="ms-3" color="primary" variant="tonal"
:disabled="loading || updating" @click="saveSortResult" :disabled="loading || updating" @click="saveSortResult"
v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn> v-if="displayOrderModified">{{ tt('Save Display Order') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :disabled="loading || updating" class="ms-2" :icon="true" :disabled="loading || updating"
:loading="loading" @click="reload"> :loading="loading" @click="reload">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
@@ -20,7 +20,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || updating" :icon="true"> :disabled="loading || updating" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -89,7 +89,7 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ml-2" color="default" <v-btn class="px-2 ms-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :class="{ 'd-none': loading, 'hover-display': !loading }"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
@@ -124,7 +124,7 @@
</template> </template>
{{ tt('Delete') }} {{ tt('Delete') }}
</v-btn> </v-btn>
<span class="ml-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && availableTemplateCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && availableTemplateCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && availableTemplateCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && availableTemplateCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip>
@@ -387,7 +387,7 @@ init();
} }
.transaction-templates-table .v-text-field .v-input__prepend { .transaction-templates-table .v-text-field .v-input__prepend {
margin-right: 0; margin-inline-end: 0;
color: rgba(var(--v-theme-on-surface)); color: rgba(var(--v-theme-on-surface));
} }

View File

@@ -52,12 +52,12 @@
<v-card variant="flat" min-height="920"> <v-card variant="flat" min-height="920">
<template #title> <template #title>
<div class="title-and-toolbar d-flex align-center text-no-wrap"> <div class="title-and-toolbar d-flex align-center text-no-wrap">
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain" <v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
:ripple="false" :icon="true" @click="showNav = !showNav"> :ripple="false" :icon="true" @click="showNav = !showNav">
<v-icon :icon="mdiMenu" size="24" /> <v-icon :icon="mdiMenu" size="24" />
</v-btn> </v-btn>
<span>{{ tt('Transaction List') }}</span> <span>{{ tt('Transaction List') }}</span>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading || !canAddTransaction" @click="add()"> :disabled="loading || !canAddTransaction" @click="add()">
{{ tt('Add') }} {{ tt('Add') }}
<v-menu activator="parent" :open-on-hover="true" v-if="allTransactionTemplates && allTransactionTemplates.length"> <v-menu activator="parent" :open-on-hover="true" v-if="allTransactionTemplates && allTransactionTemplates.length">
@@ -70,7 +70,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading" @click="importTransaction" :disabled="loading" @click="importTransaction"
v-if="isDataImportingEnabled()"> v-if="isDataImportingEnabled()">
{{ tt('Import') }} {{ tt('Import') }}
@@ -87,7 +87,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-3" color="default" variant="outlined" <v-btn class="ms-3" color="default" variant="outlined"
:disabled="loading || exportingData || !transactions || !transactions.length || transactions.length < 1" v-if="!isDataImportingEnabled() && isDataExportingEnabled()"> :disabled="loading || exportingData || !transactions || !transactions.length || transactions.length < 1" v-if="!isDataImportingEnabled() && isDataExportingEnabled()">
{{ tt('Export') }} {{ tt('Export') }}
<v-menu activator="parent"> <v-menu activator="parent">
@@ -104,7 +104,7 @@
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loading" @click="reload(true, false)"> class="ms-2" :icon="true" :loading="loading" @click="reload(true, false)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -112,7 +112,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<div class="transaction-keyword-filter ml-2"> <div class="transaction-keyword-filter ms-2">
<v-text-field density="compact" :disabled="loading" <v-text-field density="compact" :disabled="loading"
:prepend-inner-icon="mdiMagnify" :prepend-inner-icon="mdiMagnify"
:append-inner-icon="searchKeyword !== query.keyword ? mdiCheck : undefined" :append-inner-icon="searchKeyword !== query.keyword ? mdiCheck : undefined"
@@ -128,36 +128,36 @@
<v-card-text class="pt-0"> <v-card-text class="pt-0">
<div class="transaction-list-datetime-range d-flex align-center"> <div class="transaction-list-datetime-range d-flex align-center">
<span class="text-body-1">{{ tt('Date Range') }}</span> <span class="text-body-1">{{ tt('Date Range') }}</span>
<span class="text-body-1 transaction-list-datetime-range-text ml-2" <span class="text-body-1 transaction-list-datetime-range-text ms-2"
v-if="!query.minTime && !query.maxTime"> v-if="!query.minTime && !query.maxTime">
<span class="text-sm">{{ tt('All') }}</span> <span class="text-sm">{{ tt('All') }}</span>
</span> </span>
<span class="text-body-1 transaction-list-datetime-range-text ml-2" <span class="text-body-1 transaction-list-datetime-range-text ms-2"
v-else-if="query.minTime || query.maxTime"> v-else-if="query.minTime || query.maxTime">
<v-btn class="mr-1" size="x-small" <v-btn class="button-icon-with-direction me-1" size="x-small"
density="compact" color="default" variant="outlined" density="compact" color="default" variant="outlined"
:icon="mdiArrowLeft" :disabled="loading" :icon="mdiArrowLeft" :disabled="loading"
@click="shiftDateRange(query.minTime, query.maxTime, -1)"/> @click="shiftDateRange(query.minTime, query.maxTime, -1)"/>
<span class="text-sm">{{ `${queryMinTime} - ${queryMaxTime}` }}</span> <span class="text-sm">{{ `${queryMinTime} - ${queryMaxTime}` }}</span>
<v-btn class="ml-1" size="x-small" <v-btn class="button-icon-with-direction ms-1" size="x-small"
density="compact" color="default" variant="outlined" density="compact" color="default" variant="outlined"
:icon="mdiArrowRight" :disabled="loading" :icon="mdiArrowRight" :disabled="loading"
@click="shiftDateRange(query.minTime, query.maxTime, 1)"/> @click="shiftDateRange(query.minTime, query.maxTime, 1)"/>
</span> </span>
<v-spacer/> <v-spacer/>
<div class="skeleton-no-margin d-flex align-center" v-if="showTotalAmountInTransactionListPage && currentMonthTotalAmount"> <div class="skeleton-no-margin d-flex align-center" v-if="showTotalAmountInTransactionListPage && currentMonthTotalAmount">
<span class="ml-2 text-subtitle-1">{{ queryAllFilterAccountIdsCount ? tt('Total Inflows') : tt('Total Income') }}</span> <span class="ms-2 text-subtitle-1">{{ queryAllFilterAccountIdsCount ? tt('Total Inflows') : tt('Total Income') }}</span>
<span class="text-income ml-2" v-if="loading"> <span class="text-income ms-2" v-if="loading">
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader> <v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-income ml-2" v-else-if="!loading"> <span class="text-income ms-2" v-else-if="!loading">
{{ currentMonthTotalAmount.income }} {{ currentMonthTotalAmount.income }}
</span> </span>
<span class="text-subtitle-1 ml-3">{{ queryAllFilterAccountIdsCount ? tt('Total Outflows') : tt('Total Expense') }}</span> <span class="text-subtitle-1 ms-3">{{ queryAllFilterAccountIdsCount ? tt('Total Outflows') : tt('Total Expense') }}</span>
<span class="text-expense ml-2" v-if="loading"> <span class="text-expense ms-2" v-if="loading">
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader> <v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
</span> </span>
<span class="text-expense ml-2" v-else-if="!loading"> <span class="text-expense ms-2" v-else-if="!loading">
{{ currentMonthTotalAmount.expense }} {{ currentMonthTotalAmount.expense }}
</span> </span>
</div> </div>
@@ -217,10 +217,10 @@
<v-list-item-title class="cursor-pointer" <v-list-item-title class="cursor-pointer"
@click="changeDateFilter(dateRange.type)"> @click="changeDateFilter(dateRange.type)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm ml-3">{{ dateRange.displayName }}</span> <span class="text-sm ms-3">{{ dateRange.displayName }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
<div class="ml-3 smaller" v-if="((dateRange.isBillingCycle || dateRange.type === DateRange.Custom.type) && query.dateType === dateRange.type) && query.minTime && query.maxTime"> <div class="ms-3 smaller" v-if="((dateRange.isBillingCycle || dateRange.type === DateRange.Custom.type) && query.dateType === dateRange.type) && query.minTime && query.maxTime">
<span>{{ queryMinTime }}</span> <span>{{ queryMinTime }}</span>
<span>&nbsp;-&nbsp;</span> <span>&nbsp;-&nbsp;</span>
<br/> <br/>
@@ -253,7 +253,7 @@
@click="changeCategoryFilter('')"> @click="changeCategoryFilter('')">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiViewGridOutline" /> <v-icon :icon="mdiViewGridOutline" />
<span class="text-sm ml-3">{{ tt('All') }}</span> <span class="text-sm ms-3">{{ tt('All') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -265,7 +265,7 @@
@click="showFilterCategoryDialog = true"> @click="showFilterCategoryDialog = true">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiVectorArrangeBelow" /> <v-icon :icon="mdiVectorArrangeBelow" />
<span class="text-sm ml-3">{{ tt('Multiple Categories') }}</span> <span class="text-sm ms-3">{{ tt('Multiple Categories') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -287,7 +287,7 @@
<v-list-item-title> <v-list-item-title>
<div class="d-flex align-center"> <div class="d-flex align-center">
<ItemIcon icon-type="category" size="24px" :icon-id="category.icon" :color="category.color"></ItemIcon> <ItemIcon icon-type="category" size="24px" :icon-id="category.icon" :color="category.color"></ItemIcon>
<span class="text-sm ml-3">{{ category.name }}</span> <span class="text-sm ms-3">{{ category.name }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -302,7 +302,7 @@
@click="changeCategoryFilter(category.id)"> @click="changeCategoryFilter(category.id)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiViewGridOutline" /> <v-icon :icon="mdiViewGridOutline" />
<span class="text-sm ml-3">{{ tt('All') }}</span> <span class="text-sm ms-3">{{ tt('All') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -319,7 +319,7 @@
@click="changeCategoryFilter(subCategory.id)"> @click="changeCategoryFilter(subCategory.id)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<ItemIcon icon-type="category" size="24px" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon> <ItemIcon icon-type="category" size="24px" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon>
<span class="text-sm ml-3">{{ subCategory.name }}</span> <span class="text-sm ms-3">{{ subCategory.name }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -349,7 +349,7 @@
<v-list-item-title class="cursor-pointer" <v-list-item-title class="cursor-pointer"
@click="changeAmountFilter('')"> @click="changeAmountFilter('')">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm ml-3">{{ tt('All') }}</span> <span class="text-sm ms-3">{{ tt('All') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -362,18 +362,18 @@
<v-list-item-title class="cursor-pointer" <v-list-item-title class="cursor-pointer"
@click="currentAmountFilterType = filterType.type"> @click="currentAmountFilterType = filterType.type">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm ml-3">{{ tt(filterType.name) }}</span> <span class="text-sm ms-3">{{ tt(filterType.name) }}</span>
<span class="text-sm ml-4" v-if="query.amountFilter && query.amountFilter.startsWith(`${filterType.type}:`) && currentAmountFilterType !== filterType.type">{{ queryAmount }}</span> <span class="text-sm ms-4" v-if="query.amountFilter && query.amountFilter.startsWith(`${filterType.type}:`) && currentAmountFilterType !== filterType.type">{{ queryAmount }}</span>
<amount-input class="transaction-amount-filter-value ml-4" density="compact" <amount-input class="transaction-amount-filter-value ms-4" density="compact"
:currency="defaultCurrency" :currency="defaultCurrency"
v-model="currentAmountFilterValue1" v-model="currentAmountFilterValue1"
v-if="currentAmountFilterType === filterType.type"/> v-if="currentAmountFilterType === filterType.type"/>
<span class="ml-2 mr-2" v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2">~</span> <span class="ms-2 me-2" v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2">~</span>
<amount-input class="transaction-amount-filter-value" density="compact" <amount-input class="transaction-amount-filter-value" density="compact"
:currency="defaultCurrency" :currency="defaultCurrency"
v-model="currentAmountFilterValue2" v-model="currentAmountFilterValue2"
v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2"/> v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2"/>
<v-btn class="ml-2" density="compact" color="primary" variant="tonal" <v-btn class="ms-2" density="compact" color="primary" variant="tonal"
@click="changeAmountFilter(filterType.type)" @click="changeAmountFilter(filterType.type)"
v-if="currentAmountFilterType === filterType.type">{{ tt('Apply') }}</v-btn> v-if="currentAmountFilterType === filterType.type">{{ tt('Apply') }}</v-btn>
</div> </div>
@@ -402,7 +402,7 @@
@click="changeAccountFilter('')"> @click="changeAccountFilter('')">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiViewGridOutline" /> <v-icon :icon="mdiViewGridOutline" />
<span class="text-sm ml-3">{{ tt('All') }}</span> <span class="text-sm ms-3">{{ tt('All') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -414,7 +414,7 @@
@click="showFilterAccountDialog = true"> @click="showFilterAccountDialog = true">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiVectorArrangeBelow" /> <v-icon :icon="mdiVectorArrangeBelow" />
<span class="text-sm ml-3">{{ tt('Multiple Accounts') }}</span> <span class="text-sm ms-3">{{ tt('Multiple Accounts') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -430,7 +430,7 @@
@click="changeAccountFilter(account.id)"> @click="changeAccountFilter(account.id)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<ItemIcon icon-type="account" size="24px" :icon-id="account.icon" :color="account.color"></ItemIcon> <ItemIcon icon-type="account" size="24px" :icon-id="account.icon" :color="account.color"></ItemIcon>
<span class="text-sm ml-3">{{ account.name }}</span> <span class="text-sm ms-3">{{ account.name }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -457,7 +457,7 @@
@click="changeTagFilter('')"> @click="changeTagFilter('')">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiViewGridOutline" /> <v-icon :icon="mdiViewGridOutline" />
<span class="text-sm ml-3">{{ tt('All') }}</span> <span class="text-sm ms-3">{{ tt('All') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -468,7 +468,7 @@
@click="changeTagFilter('none')"> @click="changeTagFilter('none')">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiBorderNoneVariant" /> <v-icon :icon="mdiBorderNoneVariant" />
<span class="text-sm ml-3">{{ tt('Without Tags') }}</span> <span class="text-sm ms-3">{{ tt('Without Tags') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -480,7 +480,7 @@
@click="showFilterTagDialog = true"> @click="showFilterTagDialog = true">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :icon="mdiVectorArrangeBelow" /> <v-icon :icon="mdiVectorArrangeBelow" />
<span class="text-sm ml-3">{{ tt('Multiple Tags') }}</span> <span class="text-sm ms-3">{{ tt('Multiple Tags') }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -497,7 +497,7 @@
@click="changeTagFilterType(filterType.type)"> @click="changeTagFilterType(filterType.type)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon size="24" :icon="filterType.icon"/> <v-icon size="24" :icon="filterType.icon"/>
<span class="text-sm ml-3">{{ filterType.displayName }}</span> <span class="text-sm ms-3">{{ filterType.displayName }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -515,7 +515,7 @@
@click="changeTagFilter(transactionTag.id)"> @click="changeTagFilter(transactionTag.id)">
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon size="24" :icon="mdiPound"/> <v-icon size="24" :icon="mdiPound"/>
<span class="text-sm ml-3">{{ transactionTag.name }}</span> <span class="text-sm ms-3">{{ transactionTag.name }}</span>
</div> </div>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
@@ -549,7 +549,7 @@
<td :colspan="showTagInTransactionListPage ? 6 : 5" class="font-weight-bold"> <td :colspan="showTagInTransactionListPage ? 6 : 5" class="font-weight-bold">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span>{{ getDisplayLongDate(transaction) }}</span> <span>{{ getDisplayLongDate(transaction) }}</span>
<v-chip class="ml-1" color="default" size="x-small" <v-chip class="ms-1" color="default" size="x-small"
v-if="transaction.dayOfWeek"> v-if="transaction.dayOfWeek">
{{ getWeekdayLongName(transaction.dayOfWeek) }} {{ getWeekdayLongName(transaction.dayOfWeek) }}
</v-chip> </v-chip>
@@ -572,13 +572,13 @@
:color="transaction.category.color" :color="transaction.category.color"
v-if="transaction.category && transaction.category.color"></ItemIcon> v-if="transaction.category && transaction.category.color"></ItemIcon>
<v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!transaction.category || !transaction.category.color" /> <v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!transaction.category || !transaction.category.color" />
<span class="ml-2" v-if="transaction.type === TransactionType.ModifyBalance"> <span class="ms-2" v-if="transaction.type === TransactionType.ModifyBalance">
{{ tt('Modify Balance') }} {{ tt('Modify Balance') }}
</span> </span>
<span class="ml-2" v-else-if="transaction.type !== TransactionType.ModifyBalance && transaction.category"> <span class="ms-2" v-else-if="transaction.type !== TransactionType.ModifyBalance && transaction.category">
{{ transaction.category.name }} {{ transaction.category.name }}
</span> </span>
<span class="ml-2" v-else-if="transaction.type !== TransactionType.ModifyBalance && !transaction.category"> <span class="ms-2" v-else-if="transaction.type !== TransactionType.ModifyBalance && !transaction.category">
{{ getTransactionTypeName(transaction.type, 'Transaction') }} {{ getTransactionTypeName(transaction.type, 'Transaction') }}
</span> </span>
</div> </div>
@@ -591,7 +591,7 @@
<td class="transaction-table-column-account"> <td class="transaction-table-column-account">
<div class="d-flex align-center"> <div class="d-flex align-center">
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span> <span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="transaction.sourceAccount && transaction.type === TransactionType.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="transaction.sourceAccount && transaction.type === TransactionType.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id"></v-icon>
<span v-if="transaction.sourceAccount && transaction.type === TransactionType.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span> <span v-if="transaction.sourceAccount && transaction.type === TransactionType.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span>
</div> </div>
</td> </td>
@@ -1828,11 +1828,11 @@ init(props);
.transaction-table .transaction-table-column-category .v-btn .v-btn__append, .transaction-table .transaction-table-column-category .v-btn .v-btn__append,
.transaction-table .transaction-table-column-account .v-btn .v-btn__append { .transaction-table .transaction-table-column-account .v-btn .v-btn__append {
margin-left: 0in; margin-inline-start: 0in;
} }
.transaction-table .transaction-table-column-tags .v-chip.transaction-tag { .transaction-table .transaction-table-column-tags .v-chip.transaction-tag {
margin-right: 4px; margin-inline-end: 4px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 2px; margin-bottom: 2px;
} }

View File

@@ -5,9 +5,9 @@
<div class="d-flex align-center justify-center"> <div class="d-flex align-center justify-center">
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt('Import Transactions') }}</h4> <h4 class="text-h4">{{ tt('Import Transactions') }}</h4>
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading || submitting" :icon="true" :disabled="loading || submitting"
v-if="currentStep === 'defineColumn'"> v-if="currentStep === 'defineColumn'">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
@@ -22,7 +22,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading || submitting" :icon="true" :disabled="loading || submitting"
v-if="currentStep === 'checkData'"> v-if="currentStep === 'checkData'">
<v-icon :icon="mdiFilterOutline" /> <v-icon :icon="mdiFilterOutline" />
@@ -110,7 +110,7 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading || submitting" :icon="true" :disabled="loading || submitting"
v-if="currentStep === 'checkData'"> v-if="currentStep === 'checkData'">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
@@ -281,9 +281,9 @@
<v-col cols="12" md="12" class="mb-0 pb-0" v-if="exportFileGuideDocumentUrl"> <v-col cols="12" md="12" class="mb-0 pb-0" v-if="exportFileGuideDocumentUrl">
<a :href="exportFileGuideDocumentUrl" :class="{ 'disabled': submitting }" target="_blank"> <a :href="exportFileGuideDocumentUrl" :class="{ 'disabled': submitting }" target="_blank">
<v-icon :icon="mdiHelpCircleOutline" size="16" /> <v-icon :icon="mdiHelpCircleOutline" size="16" />
<span class="ml-1" v-if="fileType === 'dsv' || fileType === 'dsv_data'">{{ tt('How to import this file?') }}</span> <span class="ms-1" v-if="fileType === 'dsv' || fileType === 'dsv_data'">{{ tt('How to import this file?') }}</span>
<span class="ml-1" v-if="fileType !== 'dsv' && fileType !== 'dsv_data'">{{ tt('How to export this file?') }}</span> <span class="ms-1" v-if="fileType !== 'dsv' && fileType !== 'dsv_data'">{{ tt('How to export this file?') }}</span>
<span class="ml-1" v-if="exportFileGuideDocumentLanguageName">[{{ exportFileGuideDocumentLanguageName }}]</span> <span class="ms-1" v-if="exportFileGuideDocumentLanguageName">[{{ exportFileGuideDocumentLanguageName }}]</span>
</a> </a>
</v-col> </v-col>
</v-row> </v-row>
@@ -332,10 +332,10 @@
<v-btn color="secondary" density="compact" variant="outlined" <v-btn color="secondary" density="compact" variant="outlined"
:append-icon="parsedFileDataColumnMapping.includeHeader ? mdiCheck : mdiClose" :append-icon="parsedFileDataColumnMapping.includeHeader ? mdiCheck : mdiClose"
@click="parsedFileDataColumnMapping.toggleIncludeHeader()">{{ tt('Include Header Line') }}</v-btn> @click="parsedFileDataColumnMapping.toggleIncludeHeader()">{{ tt('Include Header Line') }}</v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
:disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionType) || !parsedFileAllTransactionTypes"> :disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionType) || !parsedFileAllTransactionTypes">
<span>{{ tt('Transaction Type Mapping') }}</span> <span>{{ tt('Transaction Type Mapping') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionType) && parsedFileAllTransactionTypes">({{ getObjectOwnFieldCount(parsedFileValidMappedTransactionTypes) || tt('None') }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionType) && parsedFileAllTransactionTypes">({{ getObjectOwnFieldCount(parsedFileValidMappedTransactionTypes) || tt('None') }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500" <v-menu eager activator="parent" location="bottom" max-height="500"
:close-on-content-click="false"> :close-on-content-click="false">
<v-list class="pa-0"> <v-list class="pa-0">
@@ -363,10 +363,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
:disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTime)"> :disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTime)">
<span>{{ tt('Time Format') }}</span> <span>{{ tt('Time Format') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTime)">({{ parsedFileDataColumnMapping.timeFormat || parsedFileAutoDetectedTimeFormat || tt('Unknown') }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTime)">({{ parsedFileDataColumnMapping.timeFormat || parsedFileAutoDetectedTimeFormat || tt('Unknown') }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500"> <v-menu eager activator="parent" location="bottom" max-height="500">
<v-list> <v-list>
<v-list-item key="auto" <v-list-item key="auto"
@@ -374,8 +374,8 @@
@click="parsedFileDataColumnMapping.timeFormat = ''"> @click="parsedFileDataColumnMapping.timeFormat = ''">
<v-list-item-title class="cursor-pointer"> <v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span> <span>{{ tt('Auto detect') }}</span>
<span class="ml-1" v-if="parsedFileAutoDetectedTimeFormat">({{ parsedFileAutoDetectedTimeFormat }})</span> <span class="ms-1" v-if="parsedFileAutoDetectedTimeFormat">({{ parsedFileAutoDetectedTimeFormat }})</span>
<span class="ml-1" v-if="!parsedFileAutoDetectedTimeFormat">({{ tt('Unknown') }})</span> <span class="ms-1" v-if="!parsedFileAutoDetectedTimeFormat">({{ tt('Unknown') }})</span>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item :key="dateTimeFormat.format" <v-list-item :key="dateTimeFormat.format"
@@ -389,10 +389,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)"> v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">
<span>{{ tt('Timezone Format') }}</span> <span>{{ tt('Timezone Format') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">({{ KnownDateTimezoneFormat.valueOf(parsedFileDataColumnMapping.timezoneFormat || parsedFileAutoDetectedTimezoneFormat || '')?.name || tt('Unknown') }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.TransactionTimezone)">({{ KnownDateTimezoneFormat.valueOf(parsedFileDataColumnMapping.timezoneFormat || parsedFileAutoDetectedTimezoneFormat || '')?.name || tt('Unknown') }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500"> <v-menu eager activator="parent" location="bottom" max-height="500">
<v-list> <v-list>
<v-list-item key="auto" <v-list-item key="auto"
@@ -400,8 +400,8 @@
@click="parsedFileDataColumnMapping.timezoneFormat = ''"> @click="parsedFileDataColumnMapping.timezoneFormat = ''">
<v-list-item-title class="cursor-pointer"> <v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span> <span>{{ tt('Auto detect') }}</span>
<span class="ml-1" v-if="parsedFileAutoDetectedTimezoneFormat && KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')?.name }})</span> <span class="ms-1" v-if="parsedFileAutoDetectedTimezoneFormat && KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')?.name }})</span>
<span class="ml-1" v-if="!parsedFileAutoDetectedTimezoneFormat || !KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ tt('Unknown') }})</span> <span class="ms-1" v-if="!parsedFileAutoDetectedTimezoneFormat || !KnownDateTimezoneFormat.valueOf(parsedFileAutoDetectedTimezoneFormat || '')">({{ tt('Unknown') }})</span>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item :key="timezoneFormat.value" <v-list-item :key="timezoneFormat.value"
@@ -415,10 +415,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
:disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Amount)"> :disabled="!parsedFileDataColumnMapping || !parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Amount)">
<span>{{ tt('Amount Format') }}</span> <span>{{ tt('Amount Format') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Amount)">({{ KnownAmountFormat.valueOf(parsedFileDataColumnMapping.amountFormat || parsedFileAutoDetectedAmountFormat || '')?.format || tt('Unknown') }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Amount)">({{ KnownAmountFormat.valueOf(parsedFileDataColumnMapping.amountFormat || parsedFileAutoDetectedAmountFormat || '')?.format || tt('Unknown') }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500"> <v-menu eager activator="parent" location="bottom" max-height="500">
<v-list> <v-list>
<v-list-item key="auto" <v-list-item key="auto"
@@ -426,8 +426,8 @@
@click="parsedFileDataColumnMapping.amountFormat = ''"> @click="parsedFileDataColumnMapping.amountFormat = ''">
<v-list-item-title class="cursor-pointer"> <v-list-item-title class="cursor-pointer">
<span>{{ tt('Auto detect') }}</span> <span>{{ tt('Auto detect') }}</span>
<span class="ml-1" v-if="parsedFileAutoDetectedAmountFormat && KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')?.format }})</span> <span class="ms-1" v-if="parsedFileAutoDetectedAmountFormat && KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')?.format }})</span>
<span class="ml-1" v-if="!parsedFileAutoDetectedAmountFormat || !KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ tt('Unknown') }})</span> <span class="ms-1" v-if="!parsedFileAutoDetectedAmountFormat || !KnownAmountFormat.valueOf(parsedFileAutoDetectedAmountFormat || '')">({{ tt('Unknown') }})</span>
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item :key="amountFormat.type" <v-list-item :key="amountFormat.type"
@@ -441,10 +441,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.GeographicLocation)"> v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.GeographicLocation)">
<span>{{ tt('Geographic Location Separator') }}</span> <span>{{ tt('Geographic Location Separator') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping.geoLocationOrder">({{ parsedFileDataColumnMapping.formatGeoLocation(tt('Latitude'), tt('Longitude')) }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping.geoLocationOrder">({{ parsedFileDataColumnMapping.formatGeoLocation(tt('Latitude'), tt('Longitude')) }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500" <v-menu eager activator="parent" location="bottom" max-height="500"
:close-on-content-click="false"> :close-on-content-click="false">
<v-list class="pa-0"> <v-list class="pa-0">
@@ -475,10 +475,10 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-btn> </v-btn>
<v-btn class="ml-2" color="secondary" density="compact" variant="outlined" <v-btn class="ms-2" color="secondary" density="compact" variant="outlined"
v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Tags)"> v-if="parsedFileDataColumnMapping && parsedFileDataColumnMapping.isColumnMappingSet(ImportTransactionColumnType.Tags)">
<span>{{ tt('Transaction Tags Separator') }}</span> <span>{{ tt('Transaction Tags Separator') }}</span>
<span class="ml-1" v-if="parsedFileDataColumnMapping.tagSeparator">({{ parsedFileDataColumnMapping.tagSeparator }})</span> <span class="ms-1" v-if="parsedFileDataColumnMapping.tagSeparator">({{ parsedFileDataColumnMapping.tagSeparator }})</span>
<v-menu eager activator="parent" location="bottom" max-height="500"> <v-menu eager activator="parent" location="bottom" max-height="500">
<v-list> <v-list>
<v-list-item :key="separator.value" <v-list-item :key="separator.value"
@@ -494,7 +494,7 @@
</v-btn> </v-btn>
<v-spacer/> <v-spacer/>
<span>{{ tt('Lines Per Page') }}</span> <span>{{ tt('Lines Per Page') }}</span>
<v-select class="ml-2" density="compact" max-width="100" <v-select class="ms-2" density="compact" max-width="100"
item-title="title" item-title="title"
item-value="value" item-value="value"
:disabled="loading || submitting" :disabled="loading || submitting"
@@ -590,7 +590,7 @@
</template> </template>
<template #item.time="{ item }"> <template #item.time="{ item }">
<span>{{ getDisplayDateTime(item) }}</span> <span>{{ getDisplayDateTime(item) }}</span>
<v-chip class="ml-1" variant="flat" color="secondary" size="x-small" <v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip> v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
</template> </template>
<template #item.type="{ value }"> <template #item.type="{ value }">
@@ -607,11 +607,11 @@
:icon-id="allCategoriesMap[item.categoryId].icon" :icon-id="allCategoriesMap[item.categoryId].icon"
:color="allCategoriesMap[item.categoryId].color" :color="allCategoriesMap[item.categoryId].color"
v-if="item.type !== TransactionType.ModifyBalance && item.categoryId && item.categoryId !== '0' && allCategoriesMap[item.categoryId]"></ItemIcon> v-if="item.type !== TransactionType.ModifyBalance && item.categoryId && item.categoryId !== '0' && allCategoriesMap[item.categoryId]"></ItemIcon>
<span class="ml-2" v-if="item.type !== TransactionType.ModifyBalance && item.categoryId && item.categoryId !== '0' && allCategoriesMap[item.categoryId]"> <span class="ms-2" v-if="item.type !== TransactionType.ModifyBalance && item.categoryId && item.categoryId !== '0' && allCategoriesMap[item.categoryId]">
{{ allCategoriesMap[item.categoryId].name }} {{ allCategoriesMap[item.categoryId].name }}
</span> </span>
<div class="text-error font-italic" v-else-if="item.type !== TransactionType.ModifyBalance && (!item.categoryId || item.categoryId === '0' || !allCategoriesMap[item.categoryId])"> <div class="text-error font-italic" v-else-if="item.type !== TransactionType.ModifyBalance && (!item.categoryId || item.categoryId === '0' || !allCategoriesMap[item.categoryId])">
<v-icon class="mr-1" :icon="mdiAlertOutline"/> <v-icon class="me-1" :icon="mdiAlertOutline"/>
<span>{{ item.originalCategoryName }}</span> <span>{{ item.originalCategoryName }}</span>
</div> </div>
</div> </div>
@@ -672,20 +672,20 @@
</template> </template>
<template #item.sourceAmount="{ item }"> <template #item.sourceAmount="{ item }">
<span>{{ getTransactionDisplayAmount(item) }}</span> <span>{{ getTransactionDisplayAmount(item) }}</span>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId"></v-icon>
<span v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId">{{ getTransactionDisplayDestinationAmount(item) }}</span> <span v-if="item.type === TransactionType.Transfer && item.sourceAccountId !== item.destinationAccountId">{{ getTransactionDisplayDestinationAmount(item) }}</span>
</template> </template>
<template #item.actualSourceAccountName="{ item }"> <template #item.actualSourceAccountName="{ item }">
<div class="d-flex align-center" v-if="editingTransaction !== item"> <div class="d-flex align-center" v-if="editingTransaction !== item">
<span v-if="item.sourceAccountId && item.sourceAccountId !== '0' && allAccountsMap[item.sourceAccountId]">{{ allAccountsMap[item.sourceAccountId].name }}</span> <span v-if="item.sourceAccountId && item.sourceAccountId !== '0' && allAccountsMap[item.sourceAccountId]">{{ allAccountsMap[item.sourceAccountId].name }}</span>
<div class="text-error font-italic" v-else> <div class="text-error font-italic" v-else>
<v-icon class="mr-1" :icon="mdiAlertOutline"/> <v-icon class="me-1" :icon="mdiAlertOutline"/>
<span>{{ item.originalSourceAccountName }}</span> <span>{{ item.originalSourceAccountName }}</span>
</div> </div>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon>
<span v-if="item.type === TransactionType.Transfer && item.destinationAccountId && item.destinationAccountId !== '0' && allAccountsMap[item.destinationAccountId]">{{allAccountsMap[item.destinationAccountId].name }}</span> <span v-if="item.type === TransactionType.Transfer && item.destinationAccountId && item.destinationAccountId !== '0' && allAccountsMap[item.destinationAccountId]">{{allAccountsMap[item.destinationAccountId].name }}</span>
<div class="text-error font-italic" v-else-if="item.type === TransactionType.Transfer && (!item.destinationAccountId || item.destinationAccountId === '0' || !allAccountsMap[item.destinationAccountId])"> <div class="text-error font-italic" v-else-if="item.type === TransactionType.Transfer && (!item.destinationAccountId || item.destinationAccountId === '0' || !allAccountsMap[item.destinationAccountId])">
<v-icon class="mr-1" :icon="mdiAlertOutline"/> <v-icon class="me-1" :icon="mdiAlertOutline"/>
<span>{{ item.originalDestinationAccountName }}</span> <span>{{ item.originalDestinationAccountName }}</span>
</div> </div>
</div> </div>
@@ -706,7 +706,7 @@
:items="allVisibleCategorizedAccounts" :items="allVisibleCategorizedAccounts"
v-model="item.sourceAccountId"> v-model="item.sourceAccountId">
</two-column-select> </two-column-select>
<v-icon class="mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon> <v-icon class="icon-with-direction mx-1" size="13" :icon="mdiArrowRight" v-if="item.type === TransactionType.Transfer"></v-icon>
<two-column-select density="compact" variant="plain" <two-column-select density="compact" variant="plain"
primary-key-field="id" primary-value-field="category" primary-key-field="id" primary-value-field="category"
primary-title-field="name" primary-footer-field="displayBalance" primary-title-field="name" primary-footer-field="displayBalance"
@@ -790,7 +790,7 @@
</span> </span>
<v-spacer/> <v-spacer/>
<span>{{ tt('Transactions Per Page') }}</span> <span>{{ tt('Transactions Per Page') }}</span>
<v-select class="ml-2" density="compact" max-width="100" <v-select class="ms-2" density="compact" max-width="100"
item-title="title" item-title="title"
item-value="value" item-value="value"
:disabled="loading || submitting" :disabled="loading || submitting"
@@ -815,17 +815,19 @@
<v-btn color="secondary" variant="tonal" :disabled="loading || submitting" <v-btn color="secondary" variant="tonal" :disabled="loading || submitting"
:prepend-icon="mdiClose" @click="close(false)" :prepend-icon="mdiClose" @click="close(false)"
v-if="currentStep !== 'finalResult'">{{ tt('Cancel') }}</v-btn> v-if="currentStep !== 'finalResult'">{{ tt('Cancel') }}</v-btn>
<v-btn color="primary" :disabled="loading || submitting || (!isImportDataFromTextbox && !importFile) || (isImportDataFromTextbox && !importData)" <v-btn class="button-icon-with-direction" color="primary"
:disabled="loading || submitting || (!isImportDataFromTextbox && !importFile) || (isImportDataFromTextbox && !importData)"
:append-icon="!submitting ? mdiArrowRight : undefined" @click="parseData" :append-icon="!submitting ? mdiArrowRight : undefined" @click="parseData"
v-if="currentStep === 'defineColumn' || currentStep === 'uploadFile'"> v-if="currentStep === 'defineColumn' || currentStep === 'uploadFile'">
{{ tt('Next') }} {{ tt('Next') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="teal" :disabled="submitting || !!editingTransaction || selectedImportTransactionCount < 1 || selectedInvalidTransactionCount > 0" <v-btn class="button-icon-with-direction" color="teal"
:disabled="submitting || !!editingTransaction || selectedImportTransactionCount < 1 || selectedInvalidTransactionCount > 0"
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit" :append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
v-if="currentStep === 'checkData'"> v-if="currentStep === 'checkData'">
{{ (submitting && importProcess > 0 ? tt('format.misc.importingTransactions', { process: formatNumberToLocalizedNumerals(importProcess, 2) }) : tt('Import')) }} {{ (submitting && importProcess > 0 ? tt('format.misc.importingTransactions', { process: formatNumberToLocalizedNumerals(importProcess, 2) }) : tt('Import')) }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" variant="tonal" <v-btn color="secondary" variant="tonal"
:append-icon="mdiCheck" :append-icon="mdiCheck"
@@ -2748,7 +2750,7 @@ defineExpose({
} }
.import-transaction-table .v-chip.transaction-tag { .import-transaction-table .v-chip.transaction-tag {
margin-right: 4px; margin-inline-end: 4px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 2px; margin-bottom: 2px;
} }

View File

@@ -9,7 +9,7 @@
<h4 class="text-h4" v-if="type === 'transferCategory'">{{ tt('Create Nonexistent Transfer Categories') }}</h4> <h4 class="text-h4" v-if="type === 'transferCategory'">{{ tt('Create Nonexistent Transfer Categories') }}</h4>
<h4 class="text-h4" v-if="type === 'tag'">{{ tt('Create Nonexistent Transaction Tags') }}</h4> <h4 class="text-h4" v-if="type === 'tag'">{{ tt('Create Nonexistent Transaction Tags') }}</h4>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="submitting || !invalidItems || !invalidItems.length" :icon="true"> :disabled="submitting || !invalidItems || !invalidItems.length" :icon="true">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -53,7 +53,7 @@
<div class="w-100 d-flex justify-center gap-4"> <div class="w-100 d-flex justify-center gap-4">
<v-btn :disabled="submitting || !selectedNames || !selectedNames.length" @click="confirm"> <v-btn :disabled="submitting || !selectedNames || !selectedNames.length" @click="confirm">
{{ tt('OK') }} {{ tt('OK') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" variant="tonal" :disabled="submitting" @click="cancel">{{ tt('Cancel') }}</v-btn> <v-btn color="secondary" variant="tonal" :disabled="submitting" @click="cancel">{{ tt('Cancel') }}</v-btn>
</div> </div>

View File

@@ -6,7 +6,7 @@
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt('Batch Replace Categories / Accounts / Tags') }}</h4> <h4 class="text-h4">{{ tt('Batch Replace Categories / Accounts / Tags') }}</h4>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :disabled="loading" class="ms-2" :icon="true" :disabled="loading"
:loading="loading" @click="reload"> :loading="loading" @click="reload">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
@@ -15,7 +15,7 @@
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
</v-btn> </v-btn>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" <v-btn density="comfortable" color="default" variant="text" class="ms-2"
:icon="true" :disabled="loading"> :icon="true" :disabled="loading">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent" max-height="500"> <v-menu activator="parent" max-height="500">

View File

@@ -14,7 +14,7 @@
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'account'">{{ tt('Replace Invalid Accounts') }}</h4> <h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'account'">{{ tt('Replace Invalid Accounts') }}</h4>
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'tag'">{{ tt('Replace Invalid Transaction Tags') }}</h4> <h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'tag'">{{ tt('Replace Invalid Transaction Tags') }}</h4>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :disabled="loading" class="ms-2" :icon="true" :disabled="loading"
:loading="loading" @click="reload"> :loading="loading" @click="reload">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>

View File

@@ -5,9 +5,9 @@
<div class="d-flex align-center justify-center"> <div class="d-flex align-center justify-center">
<div class="d-flex w-100 align-center justify-center"> <div class="d-flex w-100 align-center justify-center">
<h4 class="text-h4">{{ tt(title) }}</h4> <h4 class="text-h4">{{ tt(title) }}</h4>
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
</div> </div>
<v-btn density="comfortable" color="default" variant="text" class="ml-2" :icon="true" <v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true"
:disabled="loading || submitting" v-if="mode !== TransactionEditPageMode.View && (activeTab === 'basicInfo' || (activeTab === 'map' && isSupportGetGeoLocationByClick()))"> :disabled="loading || submitting" v-if="mode !== TransactionEditPageMode.View && (activeTab === 'basicInfo' || (activeTab === 'map' && isSupportGetGeoLocationByClick()))">
<v-icon :icon="mdiDotsVertical" /> <v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent"> <v-menu activator="parent">
@@ -80,7 +80,7 @@
</v-tabs> </v-tabs>
</div> </div>
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container ml-md-5" <v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container ms-md-5"
v-model="activeTab"> v-model="activeTab">
<v-window-item value="basicInfo"> <v-window-item value="basicInfo">
<v-form class="mt-2"> <v-form class="mt-2">
@@ -454,7 +454,7 @@
<v-btn :disabled="inputIsEmpty || loading || submitting" <v-btn :disabled="inputIsEmpty || loading || submitting"
v-if="mode !== TransactionEditPageMode.View" @click="save"> v-if="mode !== TransactionEditPageMode.View" @click="save">
{{ tt(saveButtonTitle) }} {{ tt(saveButtonTitle) }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
</div> </div>
</template> </template>
@@ -485,7 +485,7 @@
<v-btn color="error" variant="tonal" :disabled="loading || submitting" <v-btn color="error" variant="tonal" :disabled="loading || submitting"
v-if="mode === TransactionEditPageMode.View && originalTransactionEditable" @click="remove"> v-if="mode === TransactionEditPageMode.View && originalTransactionEditable" @click="remove">
{{ tt('Delete') }} {{ tt('Delete') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="submitting"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" variant="tonal" :disabled="loading || submitting" <v-btn color="secondary" variant="tonal" :disabled="loading || submitting"
@click="cancel">{{ tt(cancelButtonTitle) }}</v-btn> @click="cancel">{{ tt(cancelButtonTitle) }}</v-btn>

View File

@@ -42,7 +42,7 @@
<div ref="buttonContainer" class="w-100 d-flex justify-center gap-4"> <div ref="buttonContainer" class="w-100 d-flex justify-center gap-4">
<v-btn :disabled="generating || !currentPassword" @click="generateToken" v-if="!generatedToken"> <v-btn :disabled="generating || !currentPassword" @click="generateToken" v-if="!generatedToken">
{{ tt('Generate') }} {{ tt('Generate') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="generating"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="generating"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="secondary" variant="tonal" :disabled="generating" <v-btn color="secondary" variant="tonal" :disabled="generating"
@click="cancel" v-if="!generatedToken">{{ tt('Cancel') }}</v-btn> @click="cancel" v-if="!generatedToken">{{ tt('Cancel') }}</v-btn>

View File

@@ -4,7 +4,7 @@
<v-card :class="{ 'disabled': loading || saving }"> <v-card :class="{ 'disabled': loading || saving }">
<template #title> <template #title>
<span>{{ tt('Basic Settings') }}</span> <span>{{ tt('Basic Settings') }}</span>
<v-progress-circular indeterminate size="20" class="ml-3" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="20" class="ms-3" v-if="loading"></v-progress-circular>
</template> </template>
<v-card-text class="d-flex"> <v-card-text class="d-flex">
@@ -38,10 +38,10 @@
<div class="d-flex text-body-1 align-center" style="height: 40px;"> <div class="d-flex text-body-1 align-center" style="height: 40px;">
<span v-if="!loading && emailVerified">{{ tt('Email address is verified') }}</span> <span v-if="!loading && emailVerified">{{ tt('Email address is verified') }}</span>
<span v-if="!loading && !emailVerified">{{ tt('Email address is not verified') }}</span> <span v-if="!loading && !emailVerified">{{ tt('Email address is not verified') }}</span>
<v-btn class="ml-2 px-2" size="small" variant="text" :disabled="loading || resending" <v-btn class="ms-2 px-2" size="small" variant="text" :disabled="loading || resending"
@click="resendVerifyEmail" v-if="isUserVerifyEmailEnabled() && !loading && !emailVerified"> @click="resendVerifyEmail" v-if="isUserVerifyEmailEnabled() && !loading && !emailVerified">
{{ tt('Resend Validation Email') }} {{ tt('Resend Validation Email') }}
<v-progress-circular indeterminate size="18" class="ml-2" v-if="resending"></v-progress-circular> <v-progress-circular indeterminate size="18" class="ms-2" v-if="resending"></v-progress-circular>
</v-btn> </v-btn>
<v-skeleton-loader class="skeleton-no-margin mt-2 mb-1" type="text" style="width: 160px" :loading="true" v-if="loading"></v-skeleton-loader> <v-skeleton-loader class="skeleton-no-margin mt-2 mb-1" type="text" style="width: 160px" :loading="true" v-if="loading"></v-skeleton-loader>
</div> </div>
@@ -351,7 +351,7 @@
<v-card-text class="d-flex flex-wrap gap-4"> <v-card-text class="d-flex flex-wrap gap-4">
<v-btn :disabled="inputIsNotChanged || inputIsInvalid || saving" @click="save"> <v-btn :disabled="inputIsNotChanged || inputIsInvalid || saving" @click="save">
{{ tt('Save Changes') }} {{ tt('Save Changes') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="saving"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="saving"></v-progress-circular>
</v-btn> </v-btn>
<v-btn color="default" variant="tonal" @click="reset"> <v-btn color="default" variant="tonal" @click="reset">

View File

@@ -6,7 +6,7 @@
<div class="d-flex align-center"> <div class="d-flex align-center">
<span>{{ tt('Data Management') }}</span> <span>{{ tt('Data Management') }}</span>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loadingDataStatistics" @click="reloadUserDataStatistics(true)"> class="ms-2" :icon="true" :loading="loadingDataStatistics" @click="reloadUserDataStatistics(true)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>
@@ -91,7 +91,7 @@
<v-btn-group variant="elevated" density="comfortable" color="primary"> <v-btn-group variant="elevated" density="comfortable" color="primary">
<v-btn :disabled="loadingDataStatistics || exportingData || !dataStatistics || !dataStatistics.totalTransactionCount || dataStatistics.totalTransactionCount === '0'"> <v-btn :disabled="loadingDataStatistics || exportingData || !dataStatistics || !dataStatistics.totalTransactionCount || dataStatistics.totalTransactionCount === '0'">
{{ tt('Export Data') }} {{ tt('Export Data') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="exportingData"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="exportingData"></v-progress-circular>
<v-menu activator="parent"> <v-menu activator="parent">
<v-list :disabled="loadingDataStatistics || exportingData || !dataStatistics || !dataStatistics.totalTransactionCount || dataStatistics.totalTransactionCount === '0'"> <v-list :disabled="loadingDataStatistics || exportingData || !dataStatistics || !dataStatistics.totalTransactionCount || dataStatistics.totalTransactionCount === '0'">
<v-list-item @click="exportData('csv')"> <v-list-item @click="exportData('csv')">
@@ -143,7 +143,7 @@
<v-card-text class="d-flex flex-wrap gap-4"> <v-card-text class="d-flex flex-wrap gap-4">
<v-btn color="error" :disabled="loadingDataStatistics || !currentPasswordForClearData || clearingData" @click="clearData"> <v-btn color="error" :disabled="loadingDataStatistics || !currentPasswordForClearData || clearingData" @click="clearData">
{{ tt('Clear User Data') }} {{ tt('Clear User Data') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="clearingData"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="clearingData"></v-progress-circular>
</v-btn> </v-btn>
</v-card-text> </v-card-text>
</v-form> </v-form>

View File

@@ -56,7 +56,7 @@
<v-card-text class="d-flex flex-wrap gap-4"> <v-card-text class="d-flex flex-wrap gap-4">
<v-btn :disabled="!currentPassword || !newPassword || !confirmPassword || updatingPassword" @click="updatePassword"> <v-btn :disabled="!currentPassword || !newPassword || !confirmPassword || updatingPassword" @click="updatePassword">
{{ tt('Save Changes') }} {{ tt('Save Changes') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="updatingPassword"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="updatingPassword"></v-progress-circular>
</v-btn> </v-btn>
</v-card-text> </v-card-text>
</v-form> </v-form>
@@ -68,10 +68,10 @@
<template #title> <template #title>
<div class="d-flex align-center"> <div class="d-flex align-center">
<span>{{ tt('Device & Sessions') }}</span> <span>{{ tt('Device & Sessions') }}</span>
<v-btn class="ml-3" density="compact" color="default" variant="outlined" <v-btn class="ms-3" density="compact" color="default" variant="outlined"
@click="generateMCPToken" v-if="isMCPServerEnabled()">{{ tt('Generate MCP token') }}</v-btn> @click="generateMCPToken" v-if="isMCPServerEnabled()">{{ tt('Generate MCP token') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :loading="loadingSession" @click="reloadSessions(false)"> class="ms-2" :icon="true" :loading="loadingSession" @click="reloadSessions(false)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20"/> <v-progress-circular indeterminate size="20"/>
</template> </template>

View File

@@ -4,7 +4,7 @@
<v-card :class="{ 'disabled': loading }"> <v-card :class="{ 'disabled': loading }">
<template #title> <template #title>
<span>{{ tt('Two-Factor Authentication') }}</span> <span>{{ tt('Two-Factor Authentication') }}</span>
<v-progress-circular indeterminate size="20" class="ml-3" v-if="loading"></v-progress-circular> <v-progress-circular indeterminate size="20" class="ms-3" v-if="loading"></v-progress-circular>
</template> </template>
<v-card-text class="pb-0"> <v-card-text class="pb-0">
@@ -57,19 +57,19 @@
<v-col cols="12" class="d-flex flex-wrap gap-4"> <v-col cols="12" class="d-flex flex-wrap gap-4">
<v-btn :disabled="!currentPassword || loading || disabling " v-if="status === true" @click="disable"> <v-btn :disabled="!currentPassword || loading || disabling " v-if="status === true" @click="disable">
{{ tt('Disable Two-Factor Authentication') }} {{ tt('Disable Two-Factor Authentication') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="disabling"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="disabling"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :disabled="!currentPassword || loading || regenerating" v-if="status === true" @click="regenerateBackupCode()"> <v-btn :disabled="!currentPassword || loading || regenerating" v-if="status === true" @click="regenerateBackupCode()">
{{ tt('Regenerate Backup Codes') }} {{ tt('Regenerate Backup Codes') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="regenerating"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="regenerating"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :disabled="loading || enabling" v-if="status === false && !new2FAQRCode" @click="enable"> <v-btn :disabled="loading || enabling" v-if="status === false && !new2FAQRCode" @click="enable">
{{ tt('Enable Two-Factor Authentication') }} {{ tt('Enable Two-Factor Authentication') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="enabling"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="enabling"></v-progress-circular>
</v-btn> </v-btn>
<v-btn :disabled="!currentPasscode || loading || enableConfirming" v-if="status === false && new2FAQRCode" @click="enableConfirm"> <v-btn :disabled="!currentPasscode || loading || enableConfirming" v-if="status === false && new2FAQRCode" @click="enableConfirm">
{{ tt('Continue') }} {{ tt('Continue') }}
<v-progress-circular indeterminate size="22" class="ml-2" v-if="enableConfirming"></v-progress-circular> <v-progress-circular indeterminate size="22" class="ms-2" v-if="enableConfirming"></v-progress-circular>
</v-btn> </v-btn>
</v-col> </v-col>
</v-row> </v-row>
@@ -82,7 +82,7 @@
<template #title> <template #title>
<span>{{ tt('Backup Code') }}</span> <span>{{ tt('Backup Code') }}</span>
<v-btn density="compact" color="default" variant="text" size="24" <v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" @click="copyBackupCodes"> class="ms-2" :icon="true" @click="copyBackupCodes">
<v-icon :icon="mdiContentCopy" size="20" /> <v-icon :icon="mdiContentCopy" size="20" />
<v-tooltip activator="parent">{{ tt('Copy') }}</v-tooltip> <v-tooltip activator="parent">{{ tt('Copy') }}</v-tooltip>
</v-btn> </v-btn>