mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-14 06:57:35 +08:00
reduce dialog margins and make the action buttons always at the bottom of the dialog
This commit is contained in:
@@ -269,7 +269,7 @@ const chartOptions = computed<object>(() => {
|
||||
<style scoped>
|
||||
.account-balance-trends-chart-container {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
height: 418px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<v-dialog class="date-range-selection-dialog" width="640" :persistent="!!persistent" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-6">
|
||||
<div class="text-body-1 text-wrap mt-2">
|
||||
<p v-if="hint">{{ hint }}</p>
|
||||
<span v-if="beginDateTime && endDateTime">
|
||||
<span>{{ beginDateTime }}</span>
|
||||
@@ -17,7 +15,7 @@
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<date-time-picker :is-dark-mode="isDarkMode"
|
||||
:enable-time-picker="true"
|
||||
:vertical="true"
|
||||
@@ -26,8 +24,8 @@
|
||||
v-model="dateRange">
|
||||
</date-time-picker>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<v-dialog class="month-range-selection-dialog" width="640" :persistent="!!persistent" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-6">
|
||||
<div class="text-body-1 text-wrap mt-2">
|
||||
<p v-if="hint">{{ hint }}</p>
|
||||
<span v-if="beginDateTime && endDateTime">
|
||||
<span>{{ beginDateTime }}</span>
|
||||
@@ -17,7 +15,7 @@
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
|
||||
<v-row class="match-height">
|
||||
<v-col cols="12" md="6">
|
||||
<month-picker :is-dark-mode="isDarkMode" v-model="dateRange[0]"></month-picker>
|
||||
@@ -27,8 +25,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center mt-2 gap-4">
|
||||
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
<template>
|
||||
<v-dialog class="month-selection-dialog" width="640" :persistent="!!persistent" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4">{{ title }}</h4>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-6">
|
||||
<p v-if="hint">{{ hint }}</p>
|
||||
<div class="text-body-1 text-wrap mt-2" v-if="hint">
|
||||
<p>{{ hint }}</p>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<v-row class="match-height">
|
||||
<v-col>
|
||||
<month-picker :is-dark-mode="isDarkMode" v-model="monthValue"></month-picker>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!monthValue" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="d-flex" :style="`min-width: ${minWidth}px`" v-if="minWidth"></div>
|
||||
<v-slide-group class="slide-group-with-stepper mb-10 hidden-xs" show-arrows>
|
||||
<v-slide-group class="slide-group-with-stepper mb-6 hidden-xs" show-arrows>
|
||||
<v-slide-group-item :key="idx" v-for="(step, idx) in steps">
|
||||
<div class="mx-1"
|
||||
:class="{ 'slide-group-step-active': isStepActive(step), 'slide-group-step-completed': isStepCompleted(idx), 'cursor-pointer': isClickable }"
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<template>
|
||||
<v-dialog width="440" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-dialog width="420" v-model="showState">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Use on Mobile Device') }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4">{{ tt('Use on Mobile Device') }}</h4>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-4">{{ tt('You can scan the QR code below on your mobile device.') }}</div>
|
||||
<div class="text-body-1 text-wrap mt-4">{{ tt('You can scan the QR code below on your mobile device.') }}</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4">
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<div class="w-100 d-flex justify-center">
|
||||
@@ -18,8 +16,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :href="mobileVersionPath">{{ tt('Switch to Mobile Version') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="showState = false">{{ tt('Close') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<v-dialog width="640" :persistent="true" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4 text-error text-wrap">{{ tt('Are you sure you want to clear all transactions?') }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4 text-error text-wrap">{{ tt('Are you sure you want to clear all transactions?') }}</h4>
|
||||
</template>
|
||||
<v-card-text class="pb-2 text-error">{{ tt('format.misc.clearTransactionsInAccountTip', { account: currentAccount?.name }) }}</v-card-text>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<div class="w-100">
|
||||
<v-text-field
|
||||
autocomplete="current-password"
|
||||
@@ -20,8 +18,8 @@
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn color="error" :disabled="!currentPassword || clearingData" @click="confirm">
|
||||
{{ tt('Confirm') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="clearingData"></v-progress-circular>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<v-dialog :width="account.type === AccountType.MultiSubAccounts.type ? 1000 : 800" :persistent="isAccountModified" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-8">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt(title) }}</h4>
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true"
|
||||
:disabled="loading || submitting || account.type !== AccountType.MultiSubAccounts.type">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
@@ -20,7 +21,7 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="d-flex flex-column flex-md-row mt-md-4 pt-0">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
|
||||
<div class="mb-4" v-if="account.type === AccountType.MultiSubAccounts.type">
|
||||
<v-tabs direction="vertical" :disabled="loading || submitting" v-model="currentAccountIndex">
|
||||
<v-tab :value="-1">
|
||||
@@ -167,8 +168,8 @@
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-tooltip :disabled="!inputIsEmpty" :text="inputEmptyProblemMessage ? tt(inputEmptyProblemMessage) : ''">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props" class="d-inline-block">
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<v-dialog width="640" :persistent="true" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4 text-wrap">{{ tt('Are you sure you want to move all transactions?') }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4 text-wrap">{{ tt('Are you sure you want to move all transactions?') }}</h4>
|
||||
</template>
|
||||
<v-card-text>{{ tt('format.misc.moveTransactionsInAccountTip', { fromAccount: fromAccount?.name, toAccount: displayToAccountName }) }}</v-card-text>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<two-column-select primary-key-field="id" primary-value-field="category"
|
||||
@@ -39,8 +37,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!fromAccount || !toAccountId || fromAccount?.id === toAccountId || !toAccountName || !isToAccountNameValid || moving" @click="confirm">
|
||||
{{ tt('Confirm') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="moving"></v-progress-circular>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<v-dialog :min-height="400" :persistent="loading" v-model="showState">
|
||||
<v-card class="pa-6 pa-sm-10 pa-md-12">
|
||||
<v-dialog :persistent="loading" v-model="showState">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<div class="d-flex flex-wrap w-100 align-center">
|
||||
<h4 class="text-h4">{{ tt('Reconciliation Statement') }}</h4>
|
||||
<v-btn density="compact" color="default" variant="text" size="24"
|
||||
class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
|
||||
@@ -13,6 +13,15 @@
|
||||
<v-icon :icon="mdiRefresh" size="24" />
|
||||
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
||||
</v-btn>
|
||||
<v-switch class="bidirectional-switch ms-2 pt-1" color="secondary"
|
||||
:disabled="loading"
|
||||
:label="tt('Account Balance Trends')"
|
||||
v-model="showAccountBalanceTrendsCharts"
|
||||
@click="showAccountBalanceTrendsCharts = !showAccountBalanceTrendsCharts">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Transaction List') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</div>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
|
||||
:icon="true" :disabled="loading"
|
||||
@@ -67,185 +76,179 @@
|
||||
</template>
|
||||
|
||||
<template #subtitle>
|
||||
<div class="text-body-1 text-center text-wrap mt-2" v-if="!startTime && !endTime">
|
||||
<div class="text-body-1 text-wrap mt-2" v-if="!startTime && !endTime">
|
||||
<span>{{ tt('All') }}</span>
|
||||
</div>
|
||||
<div class="text-body-1 text-center text-wrap mt-2" v-if="startTime || endTime">
|
||||
<div class="text-body-1 text-wrap mt-2" v-if="startTime || endTime">
|
||||
<span>{{ displayStartDateTime }}</span>
|
||||
<span> - </span>
|
||||
<span>{{ displayEndDateTime }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-card-text class="py-0 w-100 d-flex justify-center mt-n4">
|
||||
<v-switch class="bidirectional-switch" color="secondary"
|
||||
:label="tt('Account Balance Trends')"
|
||||
v-model="showAccountBalanceTrendsCharts"
|
||||
@click="showAccountBalanceTrendsCharts = !showAccountBalanceTrendsCharts">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Transaction List') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-card-text>
|
||||
|
||||
<div class="d-flex align-center mb-4">
|
||||
<div class="d-flex align-center text-body-1">
|
||||
<span class="ms-2">{{ tt('Opening Balance') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayOpeningBalance }}
|
||||
</span>
|
||||
<span class="ms-3">{{ tt('Closing Balance') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayClosingBalance }}
|
||||
</span>
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<div class="d-flex align-center text-body-1">
|
||||
<span class="ms-2">{{ tt('Total Inflows') }}</span>
|
||||
<span class="text-income" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-income ms-2" v-else-if="!loading">
|
||||
{{ displayTotalInflows }}
|
||||
</span>
|
||||
<span class="ms-3">{{ tt('Total Outflows') }}</span>
|
||||
<span class="text-expense" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-expense ms-2" v-else-if="!loading">
|
||||
{{ displayTotalOutflows }}
|
||||
</span>
|
||||
<span class="ms-3">{{ tt('Net Cash Flow') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayTotalBalance }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-data-table
|
||||
fixed-header
|
||||
fixed-footer
|
||||
multi-sort
|
||||
density="compact"
|
||||
item-value="index"
|
||||
:class="{ 'reconciliation-statement-table': true, 'disabled': loading }"
|
||||
:headers="dataTableHeaders"
|
||||
:items="reconciliationStatements?.transactions ?? []"
|
||||
:hover="true"
|
||||
:no-data-text="loading ? '' : tt('No transaction data')"
|
||||
v-model:items-per-page="countPerPage"
|
||||
v-model:page="currentPage"
|
||||
v-if="!showAccountBalanceTrendsCharts"
|
||||
>
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip label variant="outlined" size="x-small"
|
||||
:class="{ 'text-income' : item.type === TransactionType.Income, 'text-expense': item.type === TransactionType.Expense }"
|
||||
:color="getTransactionTypeColor(item)">{{ getDisplayTransactionType(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.categoryName="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<ItemIcon size="24px" icon-type="category"
|
||||
:icon-id="item.category?.icon ?? ''"
|
||||
:color="item.category?.color ?? ''"
|
||||
v-if="item.category && item.category?.color"></ItemIcon>
|
||||
<v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!item.category || !item.category?.color" />
|
||||
<span class="ms-2" v-if="item.type === TransactionType.ModifyBalance">
|
||||
{{ tt('Modify Balance') }}
|
||||
</span>
|
||||
<span class="ms-2" v-else-if="item.type !== TransactionType.ModifyBalance && item.category">
|
||||
{{ item.category?.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.sourceAmount="{ item }">
|
||||
<span :class="{ 'text-expense': item.type === TransactionType.Expense, 'text-income': item.type === TransactionType.Income }">{{ getDisplaySourceAmount(item) }}</span>
|
||||
<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>
|
||||
</template>
|
||||
<template #item.sourceAccountName="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<span v-if="item.sourceAccount">{{ item.sourceAccount?.name }}</span>
|
||||
<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.destinationAccount">{{ item.destinationAccount?.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.accountBalance="{ item }">
|
||||
<span>{{ getDisplayAccountBalance(item) }}</span>
|
||||
</template>
|
||||
<template #item.operation="{ item }">
|
||||
<v-btn density="compact" variant="text" color="default" :disabled="loading || item.type === TransactionType.ModifyBalance"
|
||||
@click="showTransaction(item)">
|
||||
{{ tt('View') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<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)">
|
||||
<span class="ms-2">{{ tt('Total Transactions') }}</span>
|
||||
<span v-if="loading">
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center mb-4">
|
||||
<div class="d-flex align-center text-body-1">
|
||||
<span>{{ tt('Opening Balance') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="ms-2" v-else-if="!loading">
|
||||
{{ formatNumberToLocalizedNumerals(reconciliationStatements?.transactions.length ?? 0) }}
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayOpeningBalance }}
|
||||
</span>
|
||||
<v-spacer/>
|
||||
<span v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10">
|
||||
{{ tt('Transactions Per Page') }}
|
||||
<span class="ms-3">{{ tt('Closing Balance') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayClosingBalance }}
|
||||
</span>
|
||||
<v-select class="ms-2" density="compact" max-width="100"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:disabled="loading"
|
||||
:items="reconciliationStatementsTablePageOptions"
|
||||
v-model="countPerPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10"
|
||||
/>
|
||||
<pagination-buttons density="compact"
|
||||
:disabled="loading"
|
||||
:totalPageCount="totalPageCount"
|
||||
v-model="currentPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10">
|
||||
</pagination-buttons>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-spacer/>
|
||||
<div class="d-flex align-center text-body-1">
|
||||
<span class="ms-2">{{ tt('Total Inflows') }}</span>
|
||||
<span class="text-income" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-income ms-2" v-else-if="!loading">
|
||||
{{ displayTotalInflows }}
|
||||
</span>
|
||||
<span class="ms-3">{{ tt('Total Outflows') }}</span>
|
||||
<span class="text-expense" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-expense ms-2" v-else-if="!loading">
|
||||
{{ displayTotalOutflows }}
|
||||
</span>
|
||||
<span class="ms-3">{{ tt('Net Cash Flow') }}</span>
|
||||
<span class="text-primary" v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="text-primary ms-2" v-else-if="!loading">
|
||||
{{ displayTotalBalance }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<account-balance-trends-chart
|
||||
:type="chartType"
|
||||
:date-aggregation-type="chartDataDateAggregationType"
|
||||
:fiscal-year-start="fiscalYearStart"
|
||||
:items="[]"
|
||||
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||
:account="currentAccount"
|
||||
:skeleton="true"
|
||||
v-if="showAccountBalanceTrendsCharts && loading"
|
||||
/>
|
||||
<v-data-table
|
||||
fixed-header
|
||||
fixed-footer
|
||||
multi-sort
|
||||
density="compact"
|
||||
item-value="index"
|
||||
:class="{ 'reconciliation-statement-table': true, 'disabled': loading }"
|
||||
:height="dataTableHeight"
|
||||
:headers="dataTableHeaders"
|
||||
:items="reconciliationStatements?.transactions ?? []"
|
||||
:hover="true"
|
||||
:no-data-text="loading ? '' : tt('No transaction data')"
|
||||
v-model:items-per-page="countPerPage"
|
||||
v-model:page="currentPage"
|
||||
v-if="!showAccountBalanceTrendsCharts"
|
||||
>
|
||||
<template #item.time="{ item }">
|
||||
<span>{{ getDisplayDateTime(item) }}</span>
|
||||
<v-chip class="ms-1" variant="flat" color="secondary" size="x-small"
|
||||
v-if="item.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.type="{ item }">
|
||||
<v-chip label variant="outlined" size="x-small"
|
||||
:class="{ 'text-income' : item.type === TransactionType.Income, 'text-expense': item.type === TransactionType.Expense }"
|
||||
:color="getTransactionTypeColor(item)">{{ getDisplayTransactionType(item) }}</v-chip>
|
||||
</template>
|
||||
<template #item.categoryName="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<ItemIcon size="24px" icon-type="category"
|
||||
:icon-id="item.category?.icon ?? ''"
|
||||
:color="item.category?.color ?? ''"
|
||||
v-if="item.category && item.category?.color"></ItemIcon>
|
||||
<v-icon size="24" :icon="mdiPencilBoxOutline" v-else-if="!item.category || !item.category?.color" />
|
||||
<span class="ms-2" v-if="item.type === TransactionType.ModifyBalance">
|
||||
{{ tt('Modify Balance') }}
|
||||
</span>
|
||||
<span class="ms-2" v-else-if="item.type !== TransactionType.ModifyBalance && item.category">
|
||||
{{ item.category?.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.sourceAmount="{ item }">
|
||||
<span :class="{ 'text-expense': item.type === TransactionType.Expense, 'text-income': item.type === TransactionType.Income }">{{ getDisplaySourceAmount(item) }}</span>
|
||||
<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>
|
||||
</template>
|
||||
<template #item.sourceAccountName="{ item }">
|
||||
<div class="d-flex align-center">
|
||||
<span v-if="item.sourceAccount">{{ item.sourceAccount?.name }}</span>
|
||||
<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.destinationAccount">{{ item.destinationAccount?.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #item.accountBalance="{ item }">
|
||||
<span>{{ getDisplayAccountBalance(item) }}</span>
|
||||
</template>
|
||||
<template #item.operation="{ item }">
|
||||
<v-btn density="compact" variant="text" color="default" :disabled="loading || item.type === TransactionType.ModifyBalance"
|
||||
@click="showTransaction(item)">
|
||||
{{ tt('View') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<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)">
|
||||
<span class="ms-2">{{ tt('Total Transactions') }}</span>
|
||||
<span v-if="loading">
|
||||
<v-skeleton-loader class="skeleton-no-margin ms-3" type="text" style="width: 80px" :loading="true"></v-skeleton-loader>
|
||||
</span>
|
||||
<span class="ms-2" v-else-if="!loading">
|
||||
{{ formatNumberToLocalizedNumerals(reconciliationStatements?.transactions.length ?? 0) }}
|
||||
</span>
|
||||
<v-spacer/>
|
||||
<span v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10">
|
||||
{{ tt('Transactions Per Page') }}
|
||||
</span>
|
||||
<v-select class="ms-2" density="compact" max-width="100"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:disabled="loading"
|
||||
:items="reconciliationStatementsTablePageOptions"
|
||||
v-model="countPerPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10"
|
||||
/>
|
||||
<pagination-buttons density="compact"
|
||||
:disabled="loading"
|
||||
:totalPageCount="totalPageCount"
|
||||
v-model="currentPage"
|
||||
v-if="reconciliationStatements && reconciliationStatements.transactions && reconciliationStatements.transactions.length > 10">
|
||||
</pagination-buttons>
|
||||
</div>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<account-balance-trends-chart
|
||||
:type="chartType"
|
||||
:date-aggregation-type="chartDataDateAggregationType"
|
||||
:fiscal-year-start="fiscalYearStart"
|
||||
:items="reconciliationStatements?.transactions"
|
||||
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||
:account="currentAccount"
|
||||
v-if="showAccountBalanceTrendsCharts && !loading"
|
||||
/>
|
||||
<div class="d-flex">
|
||||
<account-balance-trends-chart
|
||||
:type="chartType"
|
||||
:date-aggregation-type="chartDataDateAggregationType"
|
||||
:fiscal-year-start="fiscalYearStart"
|
||||
:items="[]"
|
||||
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||
:account="currentAccount"
|
||||
:skeleton="true"
|
||||
v-if="showAccountBalanceTrendsCharts && loading"
|
||||
/>
|
||||
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<account-balance-trends-chart
|
||||
:type="chartType"
|
||||
:date-aggregation-type="chartDataDateAggregationType"
|
||||
:fiscal-year-start="fiscalYearStart"
|
||||
:items="reconciliationStatements?.transactions"
|
||||
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||
:account="currentAccount"
|
||||
v-if="showAccountBalanceTrendsCharts && !loading"
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn color="secondary" variant="tonal"
|
||||
:disabled="loading" @click="close">{{ tt('Close') }}</v-btn>
|
||||
</div>
|
||||
@@ -389,6 +392,14 @@ const totalPageCount = computed<number>(() => {
|
||||
return Math.ceil(count / countPerPage.value);
|
||||
});
|
||||
|
||||
const dataTableHeight = computed<number | undefined>(() => {
|
||||
if (countPerPage.value <= 10 || !reconciliationStatements.value?.transactions || reconciliationStatements.value?.transactions?.length <= 10) {
|
||||
return undefined;
|
||||
} else {
|
||||
return 380;
|
||||
}
|
||||
});
|
||||
|
||||
const dataTableHeaders = computed<object[]>(() => {
|
||||
const headers: object[] = [];
|
||||
const accountBalanceName = isCurrentLiabilityAccount.value ? 'Account Outstanding Balance' : 'Account Balance';
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<v-dialog width="800" :persistent="isCategoryModified" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-8">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt(title) }}</h4>
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="pt-0">
|
||||
<v-form class="mt-md-6">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
|
||||
<v-form class="w-100 mt-2">
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-text-field
|
||||
@@ -77,8 +77,8 @@
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-tooltip :disabled="!inputIsEmpty" :text="inputEmptyProblemMessage ? tt(inputEmptyProblemMessage) : ''">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props" class="d-inline-block">
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<v-dialog width="800" :persistent="submitting" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-8">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt('Default Categories') }}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="preset-transaction-categories mt-sm-2 mt-md-4 pt-0">
|
||||
<v-card-text class="preset-transaction-categories flex-grow-1 overflow-y-auto">
|
||||
<template :key="categoryType" v-for="(categories, categoryType) in allPresetCategories">
|
||||
<div class="d-flex align-center mb-1">
|
||||
<h4>{{ getCategoryTypeName(parseInt(categoryType)) }}</h4>
|
||||
@@ -39,8 +39,8 @@
|
||||
</v-expansion-panels>
|
||||
</template>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="submitting" @click="save">
|
||||
{{ tt('Save') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-card :class="{ 'pa-2 pa-sm-4 pa-md-8': dialogMode }">
|
||||
<v-card :class="{ 'pa-sm-1 pa-md-2': dialogMode }">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center" v-if="dialogMode">
|
||||
<div class="w-100 text-center">
|
||||
@@ -83,11 +83,11 @@
|
||||
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
||||
</div>
|
||||
|
||||
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-if="!loading && !hasAnyVisibleAccount">
|
||||
<v-card-text v-if="!loading && !hasAnyVisibleAccount">
|
||||
<span class="text-body-1">{{ tt('No available account') }}</span>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-else-if="!loading && hasAnyVisibleAccount">
|
||||
<v-card-text :class="{ 'flex-grow-1 overflow-y-auto': dialogMode }" v-else-if="!loading && hasAnyVisibleAccount">
|
||||
<v-expansion-panels class="account-categories" multiple v-model="expandAccountCategories">
|
||||
<v-expansion-panel :key="accountCategory.category"
|
||||
:value="accountCategory.category"
|
||||
@@ -147,7 +147,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible" v-if="dialogMode">
|
||||
<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 flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!hasAnyVisibleAccount" @click="save">{{ tt(applyText) }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-card :class="{ 'pa-2 pa-sm-4 pa-md-8': dialogMode }">
|
||||
<v-card :class="{ 'pa-sm-1 pa-md-2': dialogMode }">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center" v-if="dialogMode">
|
||||
<div class="w-100 text-center">
|
||||
@@ -81,7 +81,7 @@
|
||||
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
||||
</div>
|
||||
|
||||
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-else-if="!loading">
|
||||
<v-card-text :class="{ 'flex-grow-1 overflow-y-auto': dialogMode }" v-else-if="!loading">
|
||||
<v-expansion-panels class="category-types" multiple v-model="expandCategoryTypes">
|
||||
<v-expansion-panel :key="transactionType.type"
|
||||
:value="transactionType.type"
|
||||
@@ -142,7 +142,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible" v-if="dialogMode">
|
||||
<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 flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!hasAnyVisibleCategory" @click="save">{{ tt(applyText) }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<template>
|
||||
<v-card :class="{ 'pa-2 pa-sm-4 pa-md-8': dialogMode }">
|
||||
<v-card :class="{ 'pa-sm-1 pa-md-2': dialogMode }">
|
||||
<template #title>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<div :class="{ 'text-h4': dialogMode }">
|
||||
<div :class="{ 'text-h4': dialogMode, 'text-wrap': true }">
|
||||
{{ tt(title) }}
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" class="d-flex align-center">
|
||||
<v-text-field eager ref="filterInput" density="compact"
|
||||
<v-spacer v-if="!dialogMode"/>
|
||||
<v-text-field density="compact" :disabled="loading || !hasAnyAvailableTag"
|
||||
:prepend-inner-icon="mdiMagnify"
|
||||
:placeholder="tt('Find tag')"
|
||||
v-model="filterContent"></v-text-field>
|
||||
v-model="filterContent"
|
||||
v-if="dialogMode"></v-text-field>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
|
||||
:disabled="loading || !hasAnyAvailableTag" :icon="true">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
@@ -48,11 +50,11 @@
|
||||
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
||||
</div>
|
||||
|
||||
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-if="!loading && !hasAnyVisibleTag">
|
||||
<v-card-text v-if="!loading && !hasAnyVisibleTag">
|
||||
<span class="text-body-1">{{ tt('No available tag') }}</span>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-else-if="!loading && hasAnyVisibleTag">
|
||||
<v-card-text :class="{ 'flex-grow-1 overflow-y-auto': dialogMode }" v-else-if="!loading && hasAnyVisibleTag">
|
||||
<div class="mb-4" v-if="includeTagsCount > 1">
|
||||
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
|
||||
mandatory="force" divided
|
||||
@@ -111,7 +113,7 @@
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible" v-if="dialogMode">
|
||||
<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 flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!hasAnyAvailableTag" @click="save">{{ tt(applyText) }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<v-dialog width="800" :persistent="submitting || (!!defaultCurrencyAmount && defaultCurrencyAmount !== 1) || currency !== defaultCurrency || (!!targetCurrencyAmount && targetCurrencyAmount !== 1)" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Update User Custom Exchange Rate') }}</h4>
|
||||
</div>
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt('Update User Custom Exchange Rate') }}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="d-flex flex-column flex-md-row mt-2">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<number-input :disabled="submitting"
|
||||
@@ -26,7 +24,7 @@
|
||||
:placeholder="tt('Currency')"
|
||||
v-model="defaultCurrency" />
|
||||
</v-col>
|
||||
<v-col cols="12" class="text-center my-2">
|
||||
<v-col cols="12" class="text-center">
|
||||
<v-icon :icon="mdiSwapVertical" size="24" />
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
@@ -47,8 +45,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="submitting || !defaultCurrencyAmount || !currency || !targetCurrencyAmount" @click="confirm">
|
||||
{{ tt('OK') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<template>
|
||||
<v-dialog width="1000" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Export Results') }}</h4>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Export Results') }}</h4>
|
||||
<v-spacer/>
|
||||
<v-switch class="bidirectional-switch ms-2 pt-1" color="secondary"
|
||||
:label="tt('Raw Data')"
|
||||
v-model="showRawData"
|
||||
@click="showRawData = !showRawData">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Table') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2" :icon="true">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
<v-menu activator="parent">
|
||||
@@ -29,24 +36,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-card-text class="py-0 w-100 d-flex justify-center">
|
||||
<v-switch class="bidirectional-switch" color="secondary"
|
||||
:label="tt('Raw Data')"
|
||||
v-model="showRawData"
|
||||
@click="showRawData = !showRawData">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Table') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto" style="height: 485px">
|
||||
<v-data-table
|
||||
fixed-header
|
||||
fixed-footer
|
||||
multi-sort
|
||||
density="compact"
|
||||
height="360"
|
||||
:headers="dataTableHeaders"
|
||||
:items="dataTableItems"
|
||||
:hover="true"
|
||||
@@ -55,14 +50,14 @@
|
||||
:no-data-text="tt('No data')"
|
||||
v-if="!showRawData"
|
||||
></v-data-table>
|
||||
<div class="w-100 code-container" v-if="showRawData">
|
||||
<v-textarea class="w-100 always-cursor-text" style="height: 360px" :readonly="true"
|
||||
<div class="w-100 h-100 code-container" v-if="showRawData">
|
||||
<v-textarea class="w-100 h-100 always-cursor-text" :readonly="true"
|
||||
:value="exportedData"></v-textarea>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div ref="buttonContainer" class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div ref="buttonContainer" class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn-group variant="tonal" density="comfortable">
|
||||
<v-btn color="primary" :disabled="!exportedData" @click="copy">{{ tt('Copy') }}</v-btn>
|
||||
<v-btn density="compact" color="primary" :disabled="!exportedData" :icon="true">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<v-dialog :persistent="!!persistent" v-model="showState">
|
||||
<v-card class="pa-6 pa-sm-10 pa-md-12">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<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">
|
||||
<h4 class="text-h4">{{ tt('Import Transactions') }}</h4>
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
|
||||
</div>
|
||||
@@ -79,182 +79,185 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="mt-4 cursor-default">
|
||||
<steps-bar min-width="700" :clickable="false" :steps="allSteps" :current-step="currentStep" />
|
||||
</div>
|
||||
<v-card-text>
|
||||
<div class="cursor-default">
|
||||
<steps-bar min-width="700" :clickable="false" :steps="allSteps" :current-step="currentStep" />
|
||||
</div>
|
||||
|
||||
<v-window class="disable-tab-transition" v-model="currentStep">
|
||||
<v-window-item value="uploadFile">
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<two-column-select primary-key-field="displayCategoryName"
|
||||
primary-value-field="displayCategoryName"
|
||||
primary-title-field="displayCategoryName"
|
||||
primary-sub-items-field="fileTypes"
|
||||
secondary-key-field="type"
|
||||
secondary-value-field="type"
|
||||
secondary-title-field="displayName"
|
||||
:disabled="submitting"
|
||||
:enable-filter="true"
|
||||
:filter-placeholder="tt('Find file type')"
|
||||
:filter-no-items-text="tt('No available file type')"
|
||||
:label="tt('File Type')"
|
||||
:placeholder="tt('File Type')"
|
||||
:items="allSupportedImportFileCategoryAndTypes"
|
||||
:auto-update-menu-position="true"
|
||||
v-model="fileType">
|
||||
</two-column-select>
|
||||
</v-col>
|
||||
<v-window class="disable-tab-transition" v-model="currentStep">
|
||||
<v-window-item value="uploadFile">
|
||||
<v-row class="pt-2">
|
||||
<v-col cols="12" md="12">
|
||||
<two-column-select primary-key-field="displayCategoryName"
|
||||
primary-value-field="displayCategoryName"
|
||||
primary-title-field="displayCategoryName"
|
||||
primary-sub-items-field="fileTypes"
|
||||
secondary-key-field="type"
|
||||
secondary-value-field="type"
|
||||
secondary-title-field="displayName"
|
||||
:disabled="submitting"
|
||||
:enable-filter="true"
|
||||
:filter-placeholder="tt('Find file type')"
|
||||
:filter-no-items-text="tt('No available file type')"
|
||||
:label="tt('File Type')"
|
||||
:placeholder="tt('File Type')"
|
||||
:items="allSupportedImportFileCategoryAndTypes"
|
||||
:auto-update-menu-position="true"
|
||||
v-model="fileType">
|
||||
</two-column-select>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="allFileSubTypes">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="type"
|
||||
:disabled="submitting"
|
||||
:label="tt('Format')"
|
||||
:placeholder="tt('Format')"
|
||||
:items="allFileSubTypes"
|
||||
v-model="fileSubType"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12" v-if="allFileSubTypes">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="type"
|
||||
:disabled="submitting"
|
||||
:label="tt('Format')"
|
||||
:placeholder="tt('Format')"
|
||||
:items="allFileSubTypes"
|
||||
v-model="fileSubType"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="!isImportDataFromTextbox && allSupportedEncodings">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="encoding"
|
||||
:disabled="submitting"
|
||||
:label="tt('File Encoding')"
|
||||
:placeholder="tt('File Encoding')"
|
||||
:items="allSupportedEncodings"
|
||||
v-model="fileEncoding"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12" v-if="!isImportDataFromTextbox && allSupportedEncodings">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="encoding"
|
||||
:disabled="submitting"
|
||||
:label="tt('File Encoding')"
|
||||
:placeholder="tt('File Encoding')"
|
||||
:items="allSupportedEncodings"
|
||||
v-model="fileEncoding"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="fileType === 'dsv' || fileType === 'dsv_data'">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="type"
|
||||
:disabled="submitting"
|
||||
:label="tt('Handling Method')"
|
||||
:placeholder="tt('Handling Method')"
|
||||
:items="[
|
||||
{ displayName: tt('Column Mapping'), type: ImportDSVProcessMethod.ColumnMapping },
|
||||
{ displayName: tt('Custom Script'), type: ImportDSVProcessMethod.CustomScript }
|
||||
]"
|
||||
v-model="processDSVMethod"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12" v-if="fileType === 'dsv' || fileType === 'dsv_data'">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="type"
|
||||
:disabled="submitting"
|
||||
:label="tt('Handling Method')"
|
||||
:placeholder="tt('Handling Method')"
|
||||
:items="[
|
||||
{ displayName: tt('Column Mapping'), type: ImportDSVProcessMethod.ColumnMapping },
|
||||
{ displayName: tt('Custom Script'), type: ImportDSVProcessMethod.CustomScript }
|
||||
]"
|
||||
v-model="processDSVMethod"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="supportedAdditionalOptions">
|
||||
<v-select
|
||||
:disabled="submitting"
|
||||
:label="tt('Additional Options')"
|
||||
:placeholder="tt('Additional Options')"
|
||||
v-model="fileType"
|
||||
v-model:menu="additionalOptionsMenuState"
|
||||
>
|
||||
<template #selection>
|
||||
<span class="cursor-pointer">{{ displaySelectedAdditionalOptions }}</span>
|
||||
</template>
|
||||
<v-col cols="12" md="12" v-if="supportedAdditionalOptions">
|
||||
<v-select
|
||||
:disabled="submitting"
|
||||
:label="tt('Additional Options')"
|
||||
:placeholder="tt('Additional Options')"
|
||||
v-model="fileType"
|
||||
v-model:menu="additionalOptionsMenuState"
|
||||
>
|
||||
<template #selection>
|
||||
<span class="cursor-pointer">{{ displaySelectedAdditionalOptions }}</span>
|
||||
</template>
|
||||
|
||||
<template #no-data>
|
||||
<v-list class="py-0">
|
||||
<template v-for="item in allSupportedAdditionalOptions">
|
||||
<v-list-item :key="item.key"
|
||||
:append-icon="importAdditionalOptions[item.key] ? mdiCheck : undefined"
|
||||
@click="importAdditionalOptions[item.key] = !importAdditionalOptions[item.key]"
|
||||
v-if="isDefined(supportedAdditionalOptions[item.key])">{{ tt(item.name) }}</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
<template #no-data>
|
||||
<v-list class="py-0">
|
||||
<template v-for="item in allSupportedAdditionalOptions">
|
||||
<v-list-item :key="item.key"
|
||||
:append-icon="importAdditionalOptions[item.key] ? mdiCheck : undefined"
|
||||
@click="importAdditionalOptions[item.key] = !importAdditionalOptions[item.key]"
|
||||
v-if="isDefined(supportedAdditionalOptions[item.key])">{{ tt(item.name) }}</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="!isImportDataFromTextbox">
|
||||
<v-text-field
|
||||
readonly
|
||||
persistent-placeholder
|
||||
type="text"
|
||||
class="always-cursor-pointer"
|
||||
:disabled="submitting"
|
||||
:label="tt('Data File')"
|
||||
:placeholder="tt('format.misc.clickToSelectedFile', { extensions: supportedImportFileExtensions })"
|
||||
v-model="fileName"
|
||||
@click="showOpenFileDialog"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12" v-if="!isImportDataFromTextbox">
|
||||
<v-text-field
|
||||
readonly
|
||||
persistent-placeholder
|
||||
type="text"
|
||||
class="always-cursor-pointer"
|
||||
:disabled="submitting"
|
||||
:label="tt('Data File')"
|
||||
:placeholder="tt('format.misc.clickToSelectedFile', { extensions: supportedImportFileExtensions })"
|
||||
v-model="fileName"
|
||||
@click="showOpenFileDialog"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" v-if="isImportDataFromTextbox">
|
||||
<v-textarea
|
||||
type="text"
|
||||
persistent-placeholder
|
||||
rows="5"
|
||||
:disabled="submitting"
|
||||
:placeholder="tt('Data to import')"
|
||||
v-model="importData"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12" v-if="isImportDataFromTextbox">
|
||||
<v-textarea
|
||||
type="text"
|
||||
persistent-placeholder
|
||||
rows="5"
|
||||
:disabled="submitting"
|
||||
:placeholder="tt('Data to import')"
|
||||
v-model="importData"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="12" class="mb-0 pb-0" v-if="exportFileGuideDocumentUrl">
|
||||
<a :href="exportFileGuideDocumentUrl" :class="{ 'disabled': submitting }" target="_blank">
|
||||
<v-icon :icon="mdiHelpCircleOutline" size="16" />
|
||||
<span class="ms-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 export this file?') }}</span>
|
||||
<span class="ms-1" v-if="exportFileGuideDocumentLanguageName">[{{ exportFileGuideDocumentLanguageName }}]</span>
|
||||
</a>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
<v-window-item value="defineColumn">
|
||||
<import-transaction-define-column-tab
|
||||
ref="importTransactionDefineColumnTab"
|
||||
:parsed-file-data="parsedFileData"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="executeCustomScript">
|
||||
<import-transaction-execute-custom-script-tab
|
||||
ref="importTransactionExecuteCustomScriptTab"
|
||||
:parsed-file-data="parsedFileData"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="checkData">
|
||||
<import-transaction-check-data-tab
|
||||
ref="importTransactionCheckDataTab"
|
||||
:import-transactions="importTransactions"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="finalResult">
|
||||
<h4 class="text-h4 mb-1">{{ tt('Data Import Completed') }}</h4>
|
||||
<p class="my-5">{{ tt('format.misc.importTransactionResult', { count: getDisplayCount(importedCount || 0) }) }}</p>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
|
||||
<div class="d-flex justify-sm-space-between gap-4 flex-wrap justify-center mt-10">
|
||||
<v-btn color="secondary" variant="tonal" :disabled="loading || submitting"
|
||||
:prepend-icon="mdiClose" @click="close(false)"
|
||||
v-if="currentStep !== 'finalResult'">{{ tt('Cancel') }}</v-btn>
|
||||
<v-btn class="button-icon-with-direction" color="primary"
|
||||
:disabled="loading || submitting || (!isImportDataFromTextbox && !importFile) || (isImportDataFromTextbox && !importData) || (!isImportDataFromTextbox && allSupportedEncodings && fileEncoding === 'auto' && !autoDetectedFileEncoding)"
|
||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="parseData"
|
||||
v-if="currentStep === 'defineColumn' || currentStep === 'executeCustomScript' || currentStep === 'uploadFile'">
|
||||
{{ tt('Next') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn class="button-icon-with-direction" color="teal"
|
||||
:disabled="submitting || importTransactionCheckDataTab?.isEditing || !importTransactionCheckDataTab?.canImport"
|
||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
|
||||
v-if="currentStep === 'checkData'">
|
||||
{{ (submitting && importProcess > 0 ? tt('format.misc.importingTransactions', { process: formatNumberToLocalizedNumerals(importProcess, 2) }) : tt('Import')) }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" variant="tonal"
|
||||
:append-icon="mdiCheck"
|
||||
@click="close(true)"
|
||||
v-if="currentStep === 'finalResult'">{{ tt('Close') }}</v-btn>
|
||||
</div>
|
||||
<v-col cols="12" md="12" v-if="exportFileGuideDocumentUrl">
|
||||
<a :href="exportFileGuideDocumentUrl" :class="{ 'disabled': submitting }" target="_blank">
|
||||
<v-icon :icon="mdiHelpCircleOutline" size="16" />
|
||||
<span class="ms-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 export this file?') }}</span>
|
||||
<span class="ms-1" v-if="exportFileGuideDocumentLanguageName">[{{ exportFileGuideDocumentLanguageName }}]</span>
|
||||
</a>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
<v-window-item value="defineColumn">
|
||||
<import-transaction-define-column-tab
|
||||
ref="importTransactionDefineColumnTab"
|
||||
:parsed-file-data="parsedFileData"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="executeCustomScript">
|
||||
<import-transaction-execute-custom-script-tab
|
||||
ref="importTransactionExecuteCustomScriptTab"
|
||||
:parsed-file-data="parsedFileData"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="checkData">
|
||||
<import-transaction-check-data-tab
|
||||
ref="importTransactionCheckDataTab"
|
||||
:import-transactions="importTransactions"
|
||||
:disabled="loading || submitting"
|
||||
/>
|
||||
</v-window-item>
|
||||
<v-window-item value="finalResult">
|
||||
<h4 class="text-h4 mb-1">{{ tt('Data Import Completed') }}</h4>
|
||||
<p class="my-5">{{ tt('format.misc.importTransactionResult', { count: getDisplayCount(importedCount || 0) }) }}</p>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<div class="d-flex justify-center justify-sm-space-between flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn color="secondary" variant="tonal" :disabled="loading || submitting"
|
||||
:prepend-icon="mdiClose" @click="close(false)"
|
||||
v-if="currentStep !== 'finalResult'">{{ tt('Cancel') }}</v-btn>
|
||||
<v-btn class="button-icon-with-direction" color="primary"
|
||||
:disabled="loading || submitting || (!isImportDataFromTextbox && !importFile) || (isImportDataFromTextbox && !importData) || (!isImportDataFromTextbox && allSupportedEncodings && fileEncoding === 'auto' && !autoDetectedFileEncoding)"
|
||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="parseData"
|
||||
v-if="currentStep === 'defineColumn' || currentStep === 'executeCustomScript' || currentStep === 'uploadFile'">
|
||||
{{ tt('Next') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn class="button-icon-with-direction" color="teal"
|
||||
:disabled="submitting || importTransactionCheckDataTab?.isEditing || !importTransactionCheckDataTab?.canImport"
|
||||
:append-icon="!submitting ? mdiArrowRight : undefined" @click="submit"
|
||||
v-if="currentStep === 'checkData'">
|
||||
{{ (submitting && importProcess > 0 ? tt('format.misc.importingTransactions', { process: formatNumberToLocalizedNumerals(importProcess, 2) }) : tt('Import')) }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" variant="tonal"
|
||||
:append-icon="mdiCheck"
|
||||
@click="close(true)"
|
||||
v-if="currentStep === 'finalResult'">{{ tt('Close') }}</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<v-dialog width="600" :persistent="submitting || !!selectedNames.length" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<h4 class="text-h4" v-if="type === 'expenseCategory'">{{ tt('Create Nonexistent Expense Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'incomeCategory'">{{ tt('Create Nonexistent Income 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>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap">
|
||||
<h4 class="text-h4 text-wrap" v-if="type === 'expenseCategory'">{{ tt('Create Nonexistent Expense Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="type === 'incomeCategory'">{{ tt('Create Nonexistent Income Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="type === 'transferCategory'">{{ tt('Create Nonexistent Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="type === 'tag'">{{ tt('Create Nonexistent Transaction Tags') }}</h4>
|
||||
<v-spacer/>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
|
||||
:disabled="submitting || !invalidItems || !invalidItems.length" :icon="true">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
@@ -31,12 +30,12 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-col cols="12" class="px-0">
|
||||
<v-list class="py-0" density="compact" select-strategy="classic"
|
||||
:disabled="submitting" v-model:selected="selectedNames">
|
||||
<v-list-item class="py-0"
|
||||
<v-list-item class="mx-1 px-2 py-0"
|
||||
:key="item.value" :value="item.name" :title="item.name"
|
||||
v-for="item in invalidItems">
|
||||
<template #prepend="{ isActive }">
|
||||
@@ -50,8 +49,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="submitting || !selectedNames || !selectedNames.length" @click="confirm">
|
||||
{{ tt('OK') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="submitting"></v-progress-circular>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<v-dialog width="1000" :persistent="loading || !!rules.length || !!newRule.targetId" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex 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>
|
||||
<div class="d-flex flex-wrap align-center justify-center">
|
||||
<div class="d-flex flex-wrap align-center">
|
||||
<h4 class="text-h4 text-wrap">{{ tt('Batch Replace Categories / Accounts / Tags') }}</h4>
|
||||
<v-btn density="compact" color="default" variant="text" size="24"
|
||||
class="ms-2" :icon="true" :disabled="loading"
|
||||
:loading="loading" @click="reload">
|
||||
@@ -15,6 +15,7 @@
|
||||
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
|
||||
:icon="true" :disabled="loading">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
@@ -31,10 +32,10 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-table fixed-header fixed-footer height="400" striped="even">
|
||||
<v-table density="comfortable" fixed-header fixed-footer height="400" striped="even">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">{{ tt('Type') }}</th>
|
||||
@@ -57,7 +58,7 @@
|
||||
<tfoot>
|
||||
<tr style="background-color: rgb(var(--v-theme-surface))">
|
||||
<td>
|
||||
<v-select class="w-100" density="compact" variant="outlined"
|
||||
<v-select class="w-100" density="compact" variant="underlined"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:disabled="loading"
|
||||
@@ -88,7 +89,7 @@
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<v-autocomplete class="w-100" density="compact" variant="outlined"
|
||||
<v-autocomplete class="w-100" density="compact" variant="underlined"
|
||||
item-title="name" item-value="value" persistent-placeholder
|
||||
:disabled="loading" :items="sourceItems"
|
||||
:no-data-text="noSourceItemText"
|
||||
@@ -96,7 +97,7 @@
|
||||
</v-autocomplete>
|
||||
</td>
|
||||
<td>
|
||||
<two-column-select density="compact" variant="outlined"
|
||||
<two-column-select density="compact" variant="underlined"
|
||||
primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
||||
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
||||
primary-hidden-field="hidden" primary-sub-items-field="subCategories"
|
||||
@@ -112,7 +113,7 @@
|
||||
v-model="newRule.targetId"
|
||||
v-if="newRule.dataType === 'expenseCategory'">
|
||||
</two-column-select>
|
||||
<two-column-select density="compact" variant="outlined"
|
||||
<two-column-select density="compact" variant="underlined"
|
||||
primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
||||
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
||||
primary-hidden-field="hidden" primary-sub-items-field="subCategories"
|
||||
@@ -128,7 +129,7 @@
|
||||
v-model="newRule.targetId"
|
||||
v-if="newRule.dataType === 'incomeCategory'">
|
||||
</two-column-select>
|
||||
<two-column-select density="compact" variant="outlined"
|
||||
<two-column-select density="compact" variant="underlined"
|
||||
primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
||||
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
||||
primary-hidden-field="hidden" primary-sub-items-field="subCategories"
|
||||
@@ -144,7 +145,7 @@
|
||||
v-model="newRule.targetId"
|
||||
v-if="newRule.dataType === 'transferCategory'">
|
||||
</two-column-select>
|
||||
<two-column-select density="compact" variant="outlined"
|
||||
<two-column-select density="compact" variant="underlined"
|
||||
primary-key-field="id" primary-value-field="category"
|
||||
primary-title-field="name" primary-footer-field="displayBalance"
|
||||
primary-icon-field="icon" primary-icon-type="account"
|
||||
@@ -160,7 +161,7 @@
|
||||
v-model="newRule.targetId"
|
||||
v-if="newRule.dataType === 'account'">
|
||||
</two-column-select>
|
||||
<v-autocomplete density="compact" variant="outlined"
|
||||
<v-autocomplete density="compact" variant="underlined"
|
||||
item-title="name" item-value="id"
|
||||
persistent-placeholder chips
|
||||
:disabled="loading" :items="allTags"
|
||||
@@ -196,8 +197,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="loading" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" :disabled="loading" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<v-dialog width="600" :persistent="loading || (mode === 'replaceInvalidItems' && !!sourceItem) || !!targetItem" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'expenseCategory'">{{ tt('Batch Replace Selected Expense Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'incomeCategory'">{{ tt('Batch Replace Selected Income Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'transferCategory'">{{ tt('Batch Replace Selected Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'account'">{{ tt('Batch Replace Selected Accounts') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'destinationAccount'">{{ tt('Batch Replace Selected Destination Accounts') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'tag'">{{ tt('Batch Replace Selected Transaction Tags') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchAdd' && type === 'tag'">{{ tt('Batch Add Transaction Tags') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'expenseCategory'">{{ tt('Replace Invalid Expense Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'incomeCategory'">{{ tt('Replace Invalid Income Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'transferCategory'">{{ tt('Replace Invalid Transfer Categories') }}</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>
|
||||
<div class="d-flex flex-wrap align-center">
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'expenseCategory'">{{ tt('Batch Replace Selected Expense Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'incomeCategory'">{{ tt('Batch Replace Selected Income Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'transferCategory'">{{ tt('Batch Replace Selected Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'account'">{{ tt('Batch Replace Selected Accounts') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'destinationAccount'">{{ tt('Batch Replace Selected Destination Accounts') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchReplace' && type === 'tag'">{{ tt('Batch Replace Selected Transaction Tags') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'batchAdd' && type === 'tag'">{{ tt('Batch Add Transaction Tags') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'replaceInvalidItems' && type === 'expenseCategory'">{{ tt('Replace Invalid Expense Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'replaceInvalidItems' && type === 'incomeCategory'">{{ tt('Replace Invalid Income Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'replaceInvalidItems' && type === 'transferCategory'">{{ tt('Replace Invalid Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'replaceInvalidItems' && type === 'account'">{{ tt('Replace Invalid Accounts') }}</h4>
|
||||
<h4 class="text-h4 text-wrap" v-if="mode === 'replaceInvalidItems' && type === 'tag'">{{ tt('Replace Invalid Transaction Tags') }}</h4>
|
||||
<v-btn density="compact" color="default" variant="text" size="24"
|
||||
class="ms-2" :icon="true" :disabled="loading"
|
||||
:loading="loading" @click="reload">
|
||||
@@ -26,7 +26,7 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory'">
|
||||
<v-card-text class="w-100 d-flex justify-center" v-if="type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory'">
|
||||
<v-row>
|
||||
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||
<v-autocomplete
|
||||
@@ -96,7 +96,7 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'account' || type === 'destinationAccount'">
|
||||
<v-card-text class="w-100 d-flex justify-center" v-if="type === 'account' || type === 'destinationAccount'">
|
||||
<v-row>
|
||||
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||
<v-autocomplete
|
||||
@@ -131,7 +131,7 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'tag'">
|
||||
<v-card-text class="w-100 d-flex justify-center" v-if="type === 'tag'">
|
||||
<v-row>
|
||||
<v-col cols="12" v-if="mode === 'batchReplace'">
|
||||
<v-autocomplete
|
||||
@@ -196,8 +196,8 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="loading || ((mode === 'replaceInvalidItems' || (mode === 'batchReplace' && type === 'tag')) && !sourceItem && sourceItem !== '') || (!targetItem && targetItem !== '' && !removeTag)" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" :disabled="loading" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -328,14 +328,14 @@
|
||||
</v-data-table>
|
||||
|
||||
<v-dialog width="640" v-model="showCustomAmountFilterDialog">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt('Filter Amount') }}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<div class="ms-2 me-2 d-flex flex-column justify-center" v-if="currentAmountFilterType">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<div class="me-2 d-flex flex-column justify-center" v-if="currentAmountFilterType">
|
||||
{{ tt(currentAmountFilterType.name) }}
|
||||
</div>
|
||||
<amount-input :currency="defaultCurrency"
|
||||
@@ -347,8 +347,8 @@
|
||||
v-model="currentAmountFilterValue2"
|
||||
v-if="currentAmountFilterType && currentAmountFilterType.paramCount === 2"/>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn @click="showCustomAmountFilterDialog = false; filters.amount = currentAmountFilterType?.toTextualFilter(currentAmountFilterValue1, currentAmountFilterValue2) ?? null">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="showCustomAmountFilterDialog = false">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
@@ -357,13 +357,13 @@
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog width="640" v-model="showCustomDescriptionDialog">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt('Filter Description') }}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<v-text-field
|
||||
type="text"
|
||||
persistent-placeholder
|
||||
@@ -372,8 +372,8 @@
|
||||
v-model="currentDescriptionFilterValue"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="!currentDescriptionFilterValue" @click="showCustomDescriptionDialog = false; filters.description = currentDescriptionFilterValue">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="showCustomDescriptionDialog = false; currentDescriptionFilterValue = ''">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
@@ -449,9 +449,9 @@ import {
|
||||
mdiPencilOutline,
|
||||
mdiAlertOutline,
|
||||
mdiPound,
|
||||
mdiFindReplace,
|
||||
mdiTextBoxEditOutline,
|
||||
mdiShapePlusOutline,
|
||||
mdiTransfer,
|
||||
mdiPencilBoxMultipleOutline,
|
||||
mdiNumericPositive1,
|
||||
mdiNumericNegative1
|
||||
} from '@mdi/js';
|
||||
@@ -749,85 +749,85 @@ const filterMenus = computed<ImportTransactionCheckDataMenuGroup[]>(() => [
|
||||
|
||||
const toolMenus = computed<ImportTransactionCheckDataMenu[]>(() => [
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Categories / Accounts / Tags'),
|
||||
disabled: isEditing.value,
|
||||
onClick: showReplaceAllTypesDialog
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Expense Categories'),
|
||||
disabled: isEditing.value || selectedExpenseTransactionCount.value < 1,
|
||||
divider: true,
|
||||
onClick: () => showBatchReplaceDialog('expenseCategory')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Income Categories'),
|
||||
disabled: isEditing.value || selectedIncomeTransactionCount.value < 1,
|
||||
onClick: () => showBatchReplaceDialog('incomeCategory')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Transfer Categories'),
|
||||
disabled: isEditing.value || selectedTransferTransactionCount.value < 1,
|
||||
onClick: () => showBatchReplaceDialog('transferCategory')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Accounts'),
|
||||
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
|
||||
onClick: () => showBatchReplaceDialog('account')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Destination Accounts'),
|
||||
disabled: isEditing.value || selectedTransferTransactionCount.value < 1,
|
||||
onClick: () => showBatchReplaceDialog('destinationAccount')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Replace Selected Transaction Tags'),
|
||||
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
|
||||
onClick: () => showBatchReplaceDialog('tag', allOriginalTransactionTagNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Batch Add Transaction Tags'),
|
||||
disabled: isEditing.value || selectedImportTransactionCount.value < 1,
|
||||
onClick: () => showBatchAddDialog('tag')
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Replace Invalid Expense Categories'),
|
||||
disabled: isEditing.value || !allInvalidExpenseCategoryNames.value || allInvalidExpenseCategoryNames.value.length < 1,
|
||||
divider: true,
|
||||
onClick: () => showReplaceInvalidItemDialog('expenseCategory', allInvalidExpenseCategoryNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Replace Invalid Income Categories'),
|
||||
disabled: isEditing.value || !allInvalidIncomeCategoryNames.value || allInvalidIncomeCategoryNames.value.length < 1,
|
||||
onClick: () => showReplaceInvalidItemDialog('incomeCategory', allInvalidIncomeCategoryNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Replace Invalid Transfer Categories'),
|
||||
disabled: isEditing.value || !allInvalidTransferCategoryNames.value || allInvalidTransferCategoryNames.value.length < 1,
|
||||
onClick: () => showReplaceInvalidItemDialog('transferCategory', allInvalidTransferCategoryNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Replace Invalid Accounts'),
|
||||
disabled: isEditing.value || !allInvalidAccountNames.value || allInvalidAccountNames.value.length < 1,
|
||||
onClick: () => showReplaceInvalidItemDialog('account', allInvalidAccountNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
prependIcon: mdiTextBoxEditOutline,
|
||||
title: tt('Replace Invalid Transaction Tags'),
|
||||
disabled: isEditing.value || !allInvalidTransactionTagNames.value || allInvalidTransactionTagNames.value.length < 1,
|
||||
onClick: () => showReplaceInvalidItemDialog('tag', allInvalidTransactionTagNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiFindReplace,
|
||||
title: tt('Batch Replace Categories / Accounts / Tags'),
|
||||
disabled: isEditing.value,
|
||||
divider: true,
|
||||
onClick: showReplaceAllTypesDialog
|
||||
},
|
||||
{
|
||||
prependIcon: mdiShapePlusOutline,
|
||||
title: tt('Create Nonexistent Expense Categories'),
|
||||
@@ -854,38 +854,38 @@ const toolMenus = computed<ImportTransactionCheckDataMenu[]>(() => [
|
||||
onClick: () => showBatchCreateInvalidItemDialog('tag', allInvalidTransactionTagNames.value)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Expense Transaction to Income Transaction'),
|
||||
disabled: isEditing.value || selectedExpenseTransactionCount.value < 1,
|
||||
divider: true,
|
||||
onClick: () => convertTransactionType(TransactionType.Expense, TransactionType.Income)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Expense Transaction to Transfer Transaction'),
|
||||
disabled: isEditing.value || selectedExpenseTransactionCount.value < 1,
|
||||
onClick: () => convertTransactionType(TransactionType.Expense, TransactionType.Transfer)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Income Transaction to Expense Transaction'),
|
||||
disabled: isEditing.value || selectedIncomeTransactionCount.value < 1,
|
||||
onClick: () => convertTransactionType(TransactionType.Income, TransactionType.Expense)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Income Transaction to Transfer Transaction'),
|
||||
disabled: isEditing.value || selectedIncomeTransactionCount.value < 1,
|
||||
onClick: () => convertTransactionType(TransactionType.Income, TransactionType.Transfer)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Transfer Transaction to Expense Transaction'),
|
||||
disabled: isEditing.value || selectedTransferTransactionCount.value < 1,
|
||||
onClick: () => convertTransactionType(TransactionType.Transfer, TransactionType.Expense)
|
||||
},
|
||||
{
|
||||
prependIcon: mdiTransfer,
|
||||
prependIcon: mdiPencilBoxMultipleOutline,
|
||||
title: tt('Batch Convert Transfer Transaction to Income Transaction'),
|
||||
disabled: isEditing.value || selectedTransferTransactionCount.value < 1,
|
||||
onClick: () => convertTransactionType(TransactionType.Transfer, TransactionType.Income)
|
||||
@@ -909,7 +909,7 @@ const importTransactionsTableHeight = computed<number | undefined>(() => {
|
||||
if (countPerPage.value <= 10 || !props.importTransactions || props.importTransactions.length <= 10) {
|
||||
return undefined;
|
||||
} else {
|
||||
return 400;
|
||||
return 380;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<template>
|
||||
<v-dialog width="800" :persistent="loading || recognizing || !!imageFile" v-model="showState" @paste="onPaste">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('AI Image Recognition') }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4">{{ tt('AI Image Recognition') }}</h4>
|
||||
</template>
|
||||
|
||||
<v-card-text class="d-flex justify-center w-100 my-md-4 pt-0">
|
||||
<div class="w-100 border position-relative"
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto" style="height: 480px">
|
||||
<div class="w-100 h-100 border position-relative"
|
||||
@dragenter.prevent="onDragEnter"
|
||||
@dragover.prevent
|
||||
@dragleave.prevent="onDragLeave"
|
||||
@drop.prevent="onDrop">
|
||||
<div class="d-flex w-100 fill-height justify-center align-center justify-content-center text-center px-4"
|
||||
:class="{ 'dropzone': true, 'dropzone-dragover': isDragOver }" style="height: 480px">
|
||||
<div class="d-flex w-100 h-100 justify-center align-center justify-content-center text-center px-4"
|
||||
:class="{ 'dropzone': true, 'dropzone-dragover': isDragOver }">
|
||||
<div class="d-inline-flex flex-column" v-if="!loading && !imageFile && !isDragOver">
|
||||
<h3 :class="{ 'pa-2': true, 'bg-grey-200': !isDarkMode, 'bg-grey-100': isDarkMode }">{{ tt('You can drag and drop, paste or click to select a receipt or transaction image') }}</h3>
|
||||
<span :class="{ 'pa-2': true, 'bg-grey-200': !isDarkMode, 'bg-grey-100': isDarkMode }">{{ tt('Uploaded image and personal data will be sent to the large language model, please be aware of potential privacy risks.') }}</span>
|
||||
@@ -23,17 +21,17 @@
|
||||
<h3 class="pa-2" v-else-if="loading">{{ tt('Loading image...') }}</h3>
|
||||
<h3 :class="{ 'pa-2': true, 'bg-grey-200': !isDarkMode, 'bg-grey-100': isDarkMode }" v-else-if="recognizing">{{ tt('AI can make mistakes. Check important info.') }}</h3>
|
||||
</div>
|
||||
<v-img height="480px" :class="{ 'cursor-pointer': !loading && !recognizing && !isDragOver }"
|
||||
<v-img :class="{ 'cursor-pointer': !loading && !recognizing && !isDragOver, 'h-100': true }"
|
||||
:src="imageSrc" @click="showOpenImageDialog">
|
||||
<template #placeholder>
|
||||
<div :class="{ 'w-100 fill-height': true, 'bg-grey-200': !isDarkMode, 'bg-grey-100': isDarkMode }"></div>
|
||||
<div :class="{ 'w-100 h-100': true, 'bg-grey-200': !isDarkMode, 'bg-grey-100': isDarkMode }"></div>
|
||||
</template>
|
||||
</v-img>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div ref="buttonContainer" class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="loading || recognizing || !imageFile" @click="recognize">
|
||||
{{ tt('Recognize') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="recognizing"></v-progress-circular>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<v-dialog width="1000" :persistent="isTransactionModified" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-8">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex w-100 align-center justify-center">
|
||||
<div class="d-flex align-center">
|
||||
<h4 class="text-h4">{{ tt(title) }}</h4>
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="loading"></v-progress-circular>
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<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()))">
|
||||
<v-icon :icon="mdiDotsVertical" />
|
||||
@@ -49,7 +50,7 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<v-card-text class="d-flex flex-column flex-md-row mt-md-4 pt-0">
|
||||
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
|
||||
<div class="mb-4">
|
||||
<v-tabs class="v-tabs-pill" direction="vertical" :class="{ 'readonly': type === TransactionEditPageType.Transaction && mode !== TransactionEditPageMode.Add }"
|
||||
:disabled="loading || submitting" v-model="transaction.type">
|
||||
@@ -448,8 +449,8 @@
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-tooltip :disabled="!inputIsEmpty" :text="inputEmptyProblemMessage ? tt(inputEmptyProblemMessage) : ''">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props" class="d-inline-block">
|
||||
@@ -1260,48 +1261,36 @@ defineExpose({
|
||||
|
||||
@media (min-height: 630px) {
|
||||
.transaction-edit-map-view {
|
||||
height: 300px;
|
||||
height: 390px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.transaction-pictures {
|
||||
min-height: 300px;
|
||||
min-height: 414px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 700px) {
|
||||
.transaction-edit-map-view {
|
||||
height: 350px;
|
||||
height: 460px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.transaction-pictures {
|
||||
min-height: 350px;
|
||||
min-height: 484px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 800px) {
|
||||
@media (min-height: 780px) {
|
||||
.transaction-edit-map-view {
|
||||
height: 450px;
|
||||
height: 538px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.transaction-pictures {
|
||||
min-height: 450px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-height: 900px) {
|
||||
.transaction-edit-map-view {
|
||||
height: 550px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.transaction-pictures {
|
||||
min-height: 550px;
|
||||
min-height: 562px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<template>
|
||||
<v-dialog width="640" :persistent="true" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<h4 class="text-h4 text-error text-wrap">{{ tt('Are you sure you want to unlink this login method?') }}</h4>
|
||||
</div>
|
||||
<h4 class="text-h4 text-error text-wrap">{{ tt('Are you sure you want to unlink this login method?') }}</h4>
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<div class="w-100">
|
||||
<v-text-field
|
||||
autocomplete="current-password"
|
||||
@@ -19,8 +17,8 @@
|
||||
/>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn color="error" :disabled="!currentPassword || unlinking" @click="confirm">
|
||||
{{ tt('Confirm') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="unlinking"></v-progress-circular>
|
||||
|
||||
@@ -1,56 +1,64 @@
|
||||
<template>
|
||||
<v-dialog width="800" :persistent="true" v-model="showState">
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<div class="d-flex flex-wrap align-center justify-center">
|
||||
<h4 class="text-h4">{{ tt('Generate Token') }}</h4>
|
||||
<v-spacer/>
|
||||
<v-tooltip :text="tt('Token Type')">
|
||||
<template #activator="{ props }">
|
||||
<div v-bind="props" class="d-inline-block">
|
||||
<v-switch class="bidirectional-switch ms-2 pt-1" color="secondary"
|
||||
:disabled="generating || !isAPITokenEnabled() || !isMCPServerEnabled()"
|
||||
:label="tt('MCP Token')"
|
||||
:model-value="tokenType === 'mcp'"
|
||||
@click="tokenType = tokenType === 'api' ? 'mcp' : 'api'"
|
||||
v-if="!generatedToken">
|
||||
<template #prepend>
|
||||
<span>{{ tt('API Token') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</div>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<v-switch class="bidirectional-switch ms-2 pt-1" color="secondary"
|
||||
:label="tt('Example')"
|
||||
v-model="showAPIExample"
|
||||
@click="showAPIExample = !showAPIExample"
|
||||
v-if="tokenType === 'api' && generatedToken && serverUrl">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Token') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch class="bidirectional-switch ms-2 pt-1" color="secondary"
|
||||
:label="tt('Configuration')"
|
||||
v-model="showMCPConfiguration"
|
||||
@click="showMCPConfiguration = !showMCPConfiguration"
|
||||
v-if="tokenType === 'mcp' && generatedToken && serverUrl">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Token') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-card-text class="py-0 w-100 d-flex justify-center" v-if="tokenType === 'api' && generatedToken && serverUrl">
|
||||
<v-switch class="bidirectional-switch" color="secondary"
|
||||
:label="tt('Example')"
|
||||
v-model="showAPIExample"
|
||||
@click="showAPIExample = !showAPIExample">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Token') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-card-text v-if="(tokenExpirationTime === 0 || (tokenExpirationTime < 0 && tokenCustomExpirationTime === 0)) || tokenType === 'mcp'">
|
||||
<v-alert type="warning" variant="tonal">
|
||||
<span v-if="tokenExpirationTime === 0 || (tokenExpirationTime < 0 && tokenCustomExpirationTime === 0)">{{ tt('Your token does not expire, please keep it secure.') }}</span>
|
||||
<span v-if="tokenType === 'mcp'">{{ tt('When connecting to third-party apps, be aware that they and any large language models they use can access your private data.') }}</span>
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="py-0 w-100 d-flex justify-center" v-if="tokenType === 'mcp' && generatedToken && serverUrl">
|
||||
<v-switch class="bidirectional-switch" color="secondary"
|
||||
:label="tt('Configuration')"
|
||||
v-model="showMCPConfiguration"
|
||||
@click="showMCPConfiguration = !showMCPConfiguration">
|
||||
<template #prepend>
|
||||
<span>{{ tt('Token') }}</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="mt-md-4 w-100 d-flex justify-center">
|
||||
<div class="w-100" v-if="!generatedToken">
|
||||
<v-row>
|
||||
<v-col cols="12" md="12">
|
||||
<v-select
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:disabled="generating"
|
||||
:label="tt('Token Type')"
|
||||
:placeholder="tt('Token Type')"
|
||||
:items="tokenTypeOptions"
|
||||
v-model="tokenType"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" :md="tokenExpirationTime >= 0 ? 12 : 6">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="value"
|
||||
:disabled="generating"
|
||||
:label="tt('Expiration Time')"
|
||||
:placeholder="tt('Expiration Time')"
|
||||
:items="[
|
||||
<v-card-text class="mt-md-2 pt-0" v-if="!generatedToken">
|
||||
<v-row>
|
||||
<v-col cols="12" :md="tokenExpirationTime >= 0 ? 12 : 6">
|
||||
<v-select
|
||||
item-title="displayName"
|
||||
item-value="value"
|
||||
:disabled="generating"
|
||||
:label="tt('Expiration Time')"
|
||||
:placeholder="tt('Expiration Time')"
|
||||
:items="[
|
||||
{ displayName: tt('No Expiration'), value: 0 },
|
||||
{ displayName: tt('format.misc.nHour', { n: formatNumberToLocalizedNumerals(1) }), value: 3600 },
|
||||
{ displayName: tt('format.misc.nDays', { n: formatNumberToLocalizedNumerals(1) }), value: 86400 },
|
||||
@@ -61,56 +69,50 @@
|
||||
{ displayName: tt('format.misc.nDays', { n: formatNumberToLocalizedNumerals(365) }), value: 31536000 },
|
||||
{ displayName: tt('Custom'), value: -1 }
|
||||
]"
|
||||
v-model="tokenExpirationTime"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" v-if="tokenExpirationTime < 0">
|
||||
<number-input
|
||||
:persistent-placeholder="true"
|
||||
:disabled="generating"
|
||||
:label="tt('Custom Expiration Time (Seconds)')"
|
||||
:placeholder="tt('Custom Expiration Time (Seconds)')"
|
||||
:max-decimal-count="0"
|
||||
:min-value="0"
|
||||
:max-value="4294967295"
|
||||
v-model="tokenCustomExpirationTime"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12">
|
||||
<v-text-field
|
||||
autocomplete="current-password"
|
||||
type="password"
|
||||
persistent-placeholder
|
||||
:autofocus="true"
|
||||
:disabled="generating"
|
||||
:label="tt('Current Password')"
|
||||
:placeholder="tt('Current Password')"
|
||||
v-model="currentPassword"
|
||||
@keyup.enter="generateToken"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
v-model="tokenExpirationTime"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" v-if="tokenExpirationTime < 0">
|
||||
<number-input
|
||||
:persistent-placeholder="true"
|
||||
:disabled="generating"
|
||||
:label="tt('Custom Expiration Time (Seconds)')"
|
||||
:placeholder="tt('Custom Expiration Time (Seconds)')"
|
||||
:max-decimal-count="0"
|
||||
:min-value="0"
|
||||
:max-value="4294967295"
|
||||
v-model="tokenCustomExpirationTime"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" md="12">
|
||||
<v-text-field
|
||||
autocomplete="current-password"
|
||||
type="password"
|
||||
persistent-placeholder
|
||||
:autofocus="true"
|
||||
:disabled="generating"
|
||||
:label="tt('Current Password')"
|
||||
:placeholder="tt('Current Password')"
|
||||
v-model="currentPassword"
|
||||
@keyup.enter="generateToken"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<div class="w-100 code-container" v-if="generatedToken">
|
||||
<v-textarea class="w-100 always-cursor-text" :readonly="true"
|
||||
:rows="4" :value="generatedToken" v-if="(tokenType === 'api' && (!showAPIExample || !serverUrl)) || (tokenType === 'mcp' && (!showMCPConfiguration || !serverUrl))" />
|
||||
<v-textarea class="w-100 always-cursor-text" :readonly="true"
|
||||
:rows="5" :value="apiExample" v-if="tokenType === 'api' && showAPIExample && serverUrl" />
|
||||
<v-textarea class="w-100 always-cursor-text" :readonly="true"
|
||||
:rows="15" :value="mcpServerConfiguration" v-if="tokenType === 'mcp' && showMCPConfiguration && serverUrl" />
|
||||
<v-card-text class="flex-grow-1 overflow-y-auto" :style="codeContainerStyle" v-if="generatedToken">
|
||||
<div class="w-100 h-100 code-container">
|
||||
<v-textarea class="w-100 h-100 always-cursor-text" :readonly="true"
|
||||
:value="generatedToken" v-if="(tokenType === 'api' && (!showAPIExample || !serverUrl)) || (tokenType === 'mcp' && (!showMCPConfiguration || !serverUrl))" />
|
||||
<v-textarea class="w-100 h-100 always-cursor-text" :readonly="true"
|
||||
:value="apiExample" v-if="tokenType === 'api' && showAPIExample && serverUrl" />
|
||||
<v-textarea class="w-100 h-100 always-cursor-text" :readonly="true"
|
||||
:value="mcpServerConfiguration" v-if="tokenType === 'mcp' && showMCPConfiguration && serverUrl" />
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="w-100" v-if="(tokenExpirationTime === 0 || (tokenExpirationTime < 0 && tokenCustomExpirationTime === 0)) || tokenType === 'mcp'">
|
||||
<v-alert type="warning" variant="tonal">
|
||||
<span v-if="tokenExpirationTime === 0 || (tokenExpirationTime < 0 && tokenCustomExpirationTime === 0)">{{ tt('Your token does not expire, please keep it secure.') }}</span>
|
||||
<span v-if="tokenType === 'mcp'">{{ tt('When connecting to third-party apps, be aware that they and any large language models they use can access your private data.') }}</span>
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div ref="buttonContainer" class="w-100 d-flex justify-center gap-4">
|
||||
<v-card-text>
|
||||
<div ref="buttonContainer" class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn :disabled="generating || !currentPassword" @click="generateToken" v-if="!generatedToken">
|
||||
{{ tt('Generate') }}
|
||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="generating"></v-progress-circular>
|
||||
@@ -136,7 +138,6 @@ import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useTokensStore } from '@/stores/token.ts';
|
||||
|
||||
import { type NameValue } from '@/core/base.ts';
|
||||
import { type TokenGenerateAPIResponse, type TokenGenerateMCPResponse } from '@/models/token.ts';
|
||||
|
||||
import { isAPITokenEnabled, isMCPServerEnabled } from '@/lib/server_settings.ts';
|
||||
@@ -165,18 +166,14 @@ const showMCPConfiguration = ref<boolean>(false);
|
||||
const serverUrl = ref<string>('');
|
||||
const generatedToken = ref<string>('');
|
||||
|
||||
const tokenTypeOptions = computed<NameValue[]>(() => {
|
||||
const options: NameValue[] = [];
|
||||
|
||||
if (isAPITokenEnabled()) {
|
||||
options.push({ name: tt('API Token'), value: 'api' });
|
||||
const codeContainerStyle = computed<string>(() => {
|
||||
if (tokenType.value === 'api' && showAPIExample.value && serverUrl.value) {
|
||||
return 'height: 160px';
|
||||
} else if (tokenType.value === 'mcp' && showMCPConfiguration.value && serverUrl.value) {
|
||||
return 'height: 390px';
|
||||
} else {
|
||||
return 'height: 160px';
|
||||
}
|
||||
|
||||
if (isMCPServerEnabled()) {
|
||||
options.push({ name: tt('MCP Token'), value: 'mcp' });
|
||||
}
|
||||
|
||||
return options;
|
||||
});
|
||||
|
||||
const apiExample = computed<string>(() => {
|
||||
|
||||
Reference in New Issue
Block a user