fixes
This commit is contained in:
@@ -10,18 +10,16 @@
|
|||||||
<Button label="Sub" :class="checkNotif ? 'flex' : '!hidden'" @click="checkSubscribe"/>
|
<Button label="Sub" :class="checkNotif ? 'flex' : '!hidden'" @click="checkSubscribe"/>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
<OverlayView class="w-full sticky invisible lg:visible top-0 z-10"/>
|
<!-- <OverlayView class="w-full sticky invisible lg:visible top-0 z-10"/>-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MenuBar from "./components/MenuBar.vue";
|
import MenuBar from "./components/MenuBar.vue";
|
||||||
import OverlayView from "@/components/OverlayView.vue";
|
|
||||||
import ToolBar from "@/components/ToolBar.vue";
|
import ToolBar from "@/components/ToolBar.vue";
|
||||||
import Button from "primevue/button";
|
import Button from "primevue/button";
|
||||||
import axiosSetup from "@/services/axiosSetup";
|
import {computed, onMounted} from "vue";
|
||||||
import {computed, onMounted, ref} from "vue";
|
|
||||||
import {subscribeUserToPush} from "@/services/pushManager";
|
import {subscribeUserToPush} from "@/services/pushManager";
|
||||||
import apiClient from '@/services/axiosSetup';
|
import apiClient from '@/services/axiosSetup';
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
height: 0.5rem !important;
|
height: 0.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
/*margin-top: 1rem;*/
|
/*margin-top: 1rem;*/
|
||||||
/*height: 12 8px !important*/
|
/*height: 12 8px !important*/
|
||||||
|
|||||||
@@ -42,18 +42,18 @@ import Menubar from "primevue/menubar";
|
|||||||
|
|
||||||
const items = ref([
|
const items = ref([
|
||||||
{
|
{
|
||||||
label: 'Home',
|
label: 'Главная',
|
||||||
icon: 'pi pi-home',
|
icon: 'pi pi-home',
|
||||||
url: '/'
|
url: '/'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Analytics',
|
label: 'Аналитика',
|
||||||
icon: 'pi pi-star',
|
icon: 'pi pi-star',
|
||||||
url: '/analytics'
|
url: '/analytics'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Budgets',
|
label: 'Бюджеты',
|
||||||
icon: 'pi pi-search',
|
icon: 'pi pi-briefcase',
|
||||||
url: '/budgets'
|
url: '/budgets'
|
||||||
// items: [
|
// items: [
|
||||||
// {
|
// {
|
||||||
@@ -93,12 +93,12 @@ const items = ref([
|
|||||||
// ]
|
// ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Transactions',
|
label: 'Транзакции',
|
||||||
icon: "pi pi-star",
|
icon: "pi pi-star",
|
||||||
url: '/transactions'
|
url: '/transactions'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
label: 'Настройки',
|
||||||
icon: 'pi pi-envelope',
|
icon: 'pi pi-envelope',
|
||||||
url: '/settings',
|
url: '/settings',
|
||||||
// badge: 3
|
// badge: 3
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const openDrawer = (selectedTransactionType = null, selectedCategoryType = null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const closeDrawer = () => {
|
const closeDrawer = () => {
|
||||||
drawerOpened.value = false;
|
drawerOpened.value = false;
|
||||||
}
|
}
|
||||||
@@ -76,11 +77,8 @@ onMounted(() => {
|
|||||||
<div v-if="loading">Loding...</div>
|
<div v-if="loading">Loding...</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<TransactionEditDrawer v-if="drawerOpened" :visible="drawerOpened"
|
<TransactionEditDrawer v-if="drawerOpened" :visible="drawerOpened"
|
||||||
|
|
||||||
|
|
||||||
:transaction-type="transactionType"
|
:transaction-type="transactionType"
|
||||||
:category-type="categoryType"
|
:category-type="categoryType"
|
||||||
|
|
||||||
@close-drawer="closeDrawer()"
|
@close-drawer="closeDrawer()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import Button from "primevue/button";
|
|||||||
import InputNumber from "primevue/inputnumber";
|
import InputNumber from "primevue/inputnumber";
|
||||||
import ProgressBar from "primevue/progressbar";
|
import ProgressBar from "primevue/progressbar";
|
||||||
|
|
||||||
import { Category } from "@/models/Category";
|
import {Category} from "@/models/Category";
|
||||||
import { computed, ref, watch } from "vue";
|
import {computed, ref, watch} from "vue";
|
||||||
import { formatAmount } from "@/utils/utils";
|
import {formatAmount} from "@/utils/utils";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
category: {
|
category: {
|
||||||
@@ -29,7 +29,7 @@ const startEditing = () => {
|
|||||||
|
|
||||||
const stopEditing = () => {
|
const stopEditing = () => {
|
||||||
isEditing.value = false;
|
isEditing.value = false;
|
||||||
emits("category-updated", { ...props.category, currentLimit: currentLimit.value });
|
emits("category-updated", {...props.category, currentLimit: currentLimit.value});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Реактивное вычисление отношения затрат к плану
|
// Реактивное вычисление отношения затрат к плану
|
||||||
@@ -54,34 +54,42 @@ watch(
|
|||||||
|
|
||||||
<div class="flex flex-row justify-between w-full">
|
<div class="flex flex-row justify-between w-full">
|
||||||
<div :class="isEditing ? 'w-1/5': ''" class="min-w-1 w-4/6 justify-between ">
|
<div :class="isEditing ? 'w-1/5': ''" class="min-w-1 w-4/6 justify-between ">
|
||||||
<h4 class="text-lg line-clamp-1">{{props.category.category.icon }} {{ props.category.category.name }}</h4>
|
<h4 class="text-lg line-clamp-1">{{ props.category.category.icon }} {{ props.category.category.name }}</h4>
|
||||||
<!-- <p class="text-sm text-gray-500 line-clamp-1 min-w-1 ">{{ editedCategory.category.description }}</p>-->
|
<!-- <p class="text-sm text-gray-500 line-clamp-1 min-w-1 ">{{ editedCategory.category.description }}</p>-->
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row gap-2 justify-end items-center w-4/6 ">
|
<div class="flex flex-row gap-2 justify-end items-center w-4/6 ">
|
||||||
|
|
||||||
<!-- Сумма, которая становится редактируемой при клике -->
|
<!-- Сумма, которая становится редактируемой при клике -->
|
||||||
<button v-if="!isEditing" @click="startEditing"
|
<button v-if="!isEditing" @click="startEditing"
|
||||||
class="text-lg font-bold cursor-pointer w-fit text-end line-clamp-1">
|
class="text-lg font-bold cursor-pointer w-fit text-end line-clamp-1">
|
||||||
<div class="flex flex-row gap-2 items-baseline" >
|
<div class="flex flex-row gap-2 items-baseline">
|
||||||
<p class="font-light text-sm" :class="spentPlannedRatio == 0 ? 'hidden': ''">{{spentPlannedRatio.toFixed(0)}} %</p>
|
<p class="font-light text-sm" :class="spentPlannedRatio == 0 ? 'hidden': ''">
|
||||||
<p class="line-clamp-1 w-fit">{{ formatAmount(props.category.currentSpent) }} /
|
{{ spentPlannedRatio.toFixed(0) }} %</p>
|
||||||
{{ formatAmount(currentLimit) }} ₽</p>
|
<p class="line-clamp-1 w-fit">{{ formatAmount(props.category.currentSpent) }} /
|
||||||
|
{{ formatAmount(currentLimit) }} ₽</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<InputNumber v-else ref="inputRefs" type="text" v-model="props.category.currentLimit"
|
<InputNumber v-else ref="inputRefs" type="text" v-model="props.category.currentLimit"
|
||||||
class="text-lg font-bold border-b-2 border-gray-300 outline-none focus:border-blue-500 w-32 text-right"
|
class="text-lg font-bold border-b-2 border-gray-300 outline-none focus:border-none !w-24 text-right"
|
||||||
:min="props.category.categoryPlannedLimit" :max="900000" :invalid="currentLimit < props.category.categoryPlannedLimit"
|
:min="props.category.categoryPlannedLimit" :max="900000"
|
||||||
|
:invalid="currentLimit < props.category.categoryPlannedLimit"
|
||||||
v-tooltip.top="'Сумма не должна быть ниже суммы запланированных!'"
|
v-tooltip.top="'Сумма не должна быть ниже суммы запланированных!'"
|
||||||
unstyled />
|
unstyled
|
||||||
<Button v-if="isEditing" @click="stopEditing" icon="pi pi-check" severity="success" rounded outlined
|
/>
|
||||||
|
<Button v-if="isEditing" @click="stopEditing" icon="pi pi-check" severity="success" rounded outlined
|
||||||
aria-label="Search"/>
|
aria-label="Search"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<ProgressBar :value="Number(spentPlannedRatio.toFixed(0))" class="w-full" :show-value="false"> </ProgressBar>
|
<ProgressBar :value="Number(spentPlannedRatio.toFixed(0))" class="w-full .your-parent-class"
|
||||||
<!-- <div class="z-50">{{formatAmount(spentPlannedRatio.toFixed(0))}}%</div>-->
|
:pt=" {
|
||||||
|
value: {
|
||||||
|
style: spentPlannedRatio >=100 ?'background: red !important' : '',
|
||||||
|
}
|
||||||
|
}" :color="'red-200'" :show-value="false"></ProgressBar>
|
||||||
|
<!-- <div class="z-50">{{formatAmount(spentPlannedRatio.toFixed(0))}}%</div>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {Transaction} from "@/models/Transaction";
|
|||||||
import TransactionEditDrawer from "@/components/budgets/TransactionEditDrawer.vue";
|
import TransactionEditDrawer from "@/components/budgets/TransactionEditDrawer.vue";
|
||||||
import {Category, CategoryType} from "@/models/Category";
|
import {Category, CategoryType} from "@/models/Category";
|
||||||
import {getCategories, getCategoryTypes} from "@/services/categoryService";
|
import {getCategories, getCategoryTypes} from "@/services/categoryService";
|
||||||
import {updateTransactionRequest} from "@/services/transactionService";
|
import {setTransactionDoneRequest} from "@/services/transactionService";
|
||||||
import {formatAmount, formatDate} from "@/utils/utils";
|
import {formatAmount, formatDate} from "@/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
@@ -20,15 +20,19 @@ const props = defineProps(
|
|||||||
isList: {
|
isList: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
|
},
|
||||||
|
transactions: {
|
||||||
|
type: Array as () => Array<Transaction>,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const emits = defineEmits(['open-drawer', 'transaction-checked', 'transaction-updated'])
|
const emits = defineEmits(['open-drawer', 'transaction-checked', 'transaction-updated', 'delete-transaction'])
|
||||||
|
|
||||||
|
|
||||||
const setIsDoneTrue = async () => {
|
const setIsDoneTrue = async () => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await updateTransactionRequest(props.transaction)
|
await setTransactionDoneRequest(props.transaction)
|
||||||
emits('transaction-checked')
|
emits('transaction-checked')
|
||||||
}, 10);
|
}, 10);
|
||||||
// showedTransaction.value.isDone = !showedTransaction.value.isDone;
|
// showedTransaction.value.isDone = !showedTransaction.value.isDone;
|
||||||
@@ -113,7 +117,8 @@ onMounted(async () => {
|
|||||||
<p :class="transaction.isDone && isPlanned && !props.isList ? 'line-through' : ''" class="font-bold">{{
|
<p :class="transaction.isDone && isPlanned && !props.isList ? 'line-through' : ''" class="font-bold">{{
|
||||||
transaction.comment
|
transaction.comment
|
||||||
}}</p>
|
}}</p>
|
||||||
<p :class="transaction.isDone && isPlanned && !props.isList ? 'line-through' : ''" class="font-light">{{
|
<p :class="transaction.isDone && isPlanned && !props.isList ? 'line-through' : ''" class="font-light">
|
||||||
|
{{ isPlanned ? transaction.category.icon : '' }} {{
|
||||||
transaction.category.name
|
transaction.category.name
|
||||||
}} |
|
}} |
|
||||||
{{ formatDate(transaction.date) }}</p>
|
{{ formatDate(transaction.date) }}</p>
|
||||||
@@ -128,13 +133,18 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<TransactionEditDrawer v-if="drawerOpened" :visible="drawerOpened" :expenseCategories="expenseCategories"
|
|
||||||
:incomeCategories="incomeCategories" :transaction="transaction"
|
|
||||||
:category-types="categoryTypes"
|
|
||||||
@transaction-updated="transactionUpdate"
|
|
||||||
@close-drawer="closeDrawer()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<TransactionEditDrawer v-if="drawerOpened" :visible="drawerOpened" :expenseCategories="expenseCategories"
|
||||||
|
:incomeCategories="incomeCategories" :transaction="transaction"
|
||||||
|
:category-types="categoryTypes"
|
||||||
|
@transaction-updated="transactionUpdate"
|
||||||
|
@delete-transaction="transactionUpdate"
|
||||||
|
@create-transaction="transactionUpdate"
|
||||||
|
@close-drawer="closeDrawer()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
<div class="flex flex-col ">
|
<div class="flex flex-col ">
|
||||||
<!-- {{ budget }}-->
|
<!-- {{ budget }}-->
|
||||||
<h2 class="text-4xl font-bold">Бюджет {{ budget.name }} </h2>
|
<h2 class="text-4xl font-bold">Бюджет {{ budget.name }} </h2>
|
||||||
<!-- <div class="flex flex-row gap-2 text-xl">{{ formatDate(budget.dateFrom) }} - -->
|
<div class="flex flex-row gap-2 text-xl">{{ formatDate(budget.dateFrom) }} -
|
||||||
<!-- {{ formatDate(budget.dateTo) }}-->
|
{{ formatDate(budget.dateTo) }}
|
||||||
<!-- </div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<!-- Аналитика и плановые доходы/расходы -->
|
<!-- Аналитика и плановые доходы/расходы -->
|
||||||
@@ -49,7 +49,8 @@
|
|||||||
<button class="grid grid-cols-2 gap-5 items-center w-full" @click="detailedShowed = !detailedShowed">
|
<button class="grid grid-cols-2 gap-5 items-center w-full" @click="detailedShowed = !detailedShowed">
|
||||||
<div class="flex flex-col items-center font-bold ">
|
<div class="flex flex-col items-center font-bold ">
|
||||||
<h4 class="text-lg font-bold">Поступления</h4>
|
<h4 class="text-lg font-bold">Поступления</h4>
|
||||||
<div class="font-light bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
|
||||||
|
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
||||||
+{{ formatAmount(totalIncomes) }}
|
+{{ formatAmount(totalIncomes) }}
|
||||||
₽
|
₽
|
||||||
</div>
|
</div>
|
||||||
@@ -57,8 +58,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center ">
|
<div class="flex flex-col items-center ">
|
||||||
<h4 class="text-lg font-bold ">Расходы</h4>
|
<h4 class="text-lg font-bold ">Расходы</h4>
|
||||||
<div class="font-light bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center" :class="totalExpenses > totalIncomes ? ' text-red-700' : ''">
|
||||||
-{{ formatAmount(totalExpenses) }}
|
-{{ formatAmount(totalExpenses) }} ({{formatAmount(totalExpenses- totalIncomes)}})
|
||||||
₽
|
₽
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +108,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center ">
|
<div class="flex flex-col items-center ">
|
||||||
<span class="text-sm lg:text-base">Сбережения</span>
|
<span class="text-sm lg:text-base">Сбережения</span>
|
||||||
<div class="font-light bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
<div class="font-light bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center" :class="savingRatio < 30 ? '!font-bold text-red-700' : ''">
|
||||||
{{ savingRatio.toFixed(0) }} %
|
{{ savingRatio.toFixed(0) }} %
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,21 +134,26 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class=" h-full overflow-y-auto gap-4 flex-col row-span-6 hidden lg:flex">
|
<div class=" h-full overflow-y-auto gap-4 flex-col row-span-6 hidden lg:flex">
|
||||||
<div class="flex flex-row ">
|
<div class="flex flex-row gap-4">
|
||||||
<h3 class="text-2xl font-bold">Транзакции</h3>
|
<h3 class="text-2xl font-bold">Транзакции</h3>
|
||||||
|
<button @click="openDrawer('INSTANT', 'EXPENSE')">
|
||||||
|
<!-- <i class="pi pi-plus-circle text-green-500" style="font-size: 1rem;"/>-->
|
||||||
|
<span class="font-light text-sm">+ Добавить</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class=" flex gap-2 overflow-x-auto ">
|
<div class=" flex gap-2 overflow-x-auto ">
|
||||||
|
|
||||||
<button v-for="categorySum in transactionCategoriesSums"
|
<button v-for="categorySum in transactionCategoriesSums"
|
||||||
class="rounded-xl border p-1 bg-white border-gray-300 mb-2 min-w-fit px-2">
|
class="rounded-xl border p-1 bg-white border-gray-300 mb-2 min-w-fit px-2">
|
||||||
<p> <span class="text-sm font-bold">{{ categorySum.category.name }}</span>: {{ categorySum.sum }} ₽</p>
|
<p><span class="text-sm font-bold">{{ categorySum.category.name }}</span>: {{ categorySum.sum }} ₽</p>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 gap-4 max-h-tlist overflow-y-auto pe-2">
|
<div class="grid grid-cols-1 gap-4 max-h-tlist overflow-y-auto pe-2">
|
||||||
<BudgetTransactionView v-for="transaction in transactions" :key="transaction.id"
|
<BudgetTransactionView v-for="transaction in transactions" :key="transaction.id"
|
||||||
:transaction="transaction"
|
:transaction="transaction"
|
||||||
:is-list="true" class=""
|
:is-list="true"
|
||||||
|
@transaction-updated="updateTransactions"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,9 +164,12 @@
|
|||||||
<div class="card p-4 shadow-lg rounded-lg col-span-1 h-fit">
|
<div class="card p-4 shadow-lg rounded-lg col-span-1 h-fit">
|
||||||
<!-- Планируемые доходы -->
|
<!-- Планируемые доходы -->
|
||||||
<div>
|
<div>
|
||||||
<div class="flex flex-row gap-4 items-center">
|
<div class="flex flex-row gap-4 items-center mb-4">
|
||||||
<h3 class="text-xl font-bold text-green-500 mb-4 ">Плановые поступления</h3>
|
<h3 class="text-xl font-bold text-green-500 ">Плановые поступления</h3>
|
||||||
<!-- <Button icon="pi pi-plus" rounded outlined size="small"/>-->
|
<button @click="openDrawer('PLANNED', 'INCOME')">
|
||||||
|
<!-- <i class="pi pi-plus-circle text-green-500" style="font-size: 1rem;"/>-->
|
||||||
|
<span class="font-light text-sm">+ Добавить</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 mb-2">
|
<div class="grid grid-cols-2 mb-2">
|
||||||
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
||||||
@@ -185,7 +194,13 @@
|
|||||||
<div class="card p-4 shadow-lg rounded-lg col-span-1 h-fit">
|
<div class="card p-4 shadow-lg rounded-lg col-span-1 h-fit">
|
||||||
<!-- Планируемые расходы -->
|
<!-- Планируемые расходы -->
|
||||||
<div class>
|
<div class>
|
||||||
<h3 class="text-xl font-bold text-red-500 mb-4">Плановые расходы</h3>
|
<div class="flex flex-row gap-4 items-center mb-4">
|
||||||
|
<h3 class="text-xl font-bold text-red-500">Плановые расходы</h3>
|
||||||
|
<button @click="openDrawer('PLANNED', 'EXPENSE')">
|
||||||
|
<!-- <i class="pi pi-plus-circle text-green-500" style="font-size: 1rem;"/>-->
|
||||||
|
<span class="font-light text-sm">+ Добавить</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 mb-2">
|
<div class="grid grid-cols-2 mb-2">
|
||||||
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
<div class="font-bold bg-gray-100 p-1 rounded-lg box-shadow-inner w-full text-center">
|
||||||
{{ formatAmount(totalPlannedExpenses) }}
|
{{ formatAmount(totalPlannedExpenses) }}
|
||||||
@@ -219,19 +234,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class=" h-full overflow-y-auto gap-4 flex-col row-span-6 lg:hidden ">
|
<div class=" h-full overflow-y-auto gap-4 flex-col row-span-6 lg:hidden ">
|
||||||
<div class="flex flex-row ">
|
<div class="flex flex-row ">
|
||||||
<h3 class="text-2xl font-bold">Транзакций</h3>
|
<h3 class="text-2xl font-bold">Транзакции</h3>
|
||||||
|
<button @click="openDrawer('INSTANT', 'EXPENSE')">
|
||||||
|
<!-- <i class="pi pi-plus-circle text-green-500" style="font-size: 1rem;"/>-->
|
||||||
|
<span class="font-light text-sm">+ Добавить</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class=" flex gap-2">
|
<div class=" flex gap-2">
|
||||||
<button v-for="categorySum in transactionCategoriesSums"
|
<button v-for="categorySum in transactionCategoriesSums"
|
||||||
class="rounded-xl border p-1 bg-white border-gray-300 mb-2 min-w-fit px-2">
|
class="rounded-xl border p-1 bg-white border-gray-300 mb-2 min-w-fit px-2">
|
||||||
<p> <span class="text-sm font-bold">{{ categorySum.category.name }}</span>: {{ categorySum.sum }} ₽</p>
|
<p><span class="text-sm font-bold">{{ categorySum.category.name }}</span>: {{ categorySum.sum }} ₽</p>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 gap-4 max-h-tlist overflow-y-auto pe-2">
|
<div class="grid grid-cols-1 gap-4 max-h-tlist overflow-y-auto pe-2">
|
||||||
<BudgetTransactionView v-for="transaction in transactions" :key="transaction.id"
|
<BudgetTransactionView v-for="transaction in transactions" :key="transaction.id"
|
||||||
:transaction="transaction"
|
:transaction="transaction"
|
||||||
:is-list="true" class=""
|
:is-list="true"
|
||||||
|
@transaction-updated="updateTransactions"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -240,7 +261,18 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TransactionEditDrawer v-if="drawerOpened" :visible="drawerOpened"
|
||||||
|
:transaction-type="transactionType"
|
||||||
|
:category-type="categoryType"
|
||||||
|
:transactions="transactions.slice(0,3)"
|
||||||
|
@transaction-updated="updateTransactions"
|
||||||
|
@delete-transaction="updateTransactions"
|
||||||
|
@create-transaction="updateTransactions"
|
||||||
|
@close-drawer="closeDrawer"
|
||||||
|
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@@ -256,7 +288,7 @@ import {
|
|||||||
} from "@/services/budgetsService";
|
} from "@/services/budgetsService";
|
||||||
import {Budget, BudgetCategory, BudgetInfo} from "@/models/Budget";
|
import {Budget, BudgetCategory, BudgetInfo} from "@/models/Budget";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {formatAmount} from "@/utils/utils";
|
import {formatAmount, formatDate} from "@/utils/utils";
|
||||||
import ProgressBar from "primevue/progressbar";
|
import ProgressBar from "primevue/progressbar";
|
||||||
import ProgressSpinner from "primevue/progressspinner";
|
import ProgressSpinner from "primevue/progressspinner";
|
||||||
import BudgetCategoryView from "@/components/budgets/BudgetCategoryView.vue";
|
import BudgetCategoryView from "@/components/budgets/BudgetCategoryView.vue";
|
||||||
@@ -267,6 +299,7 @@ import LoadingView from "@/components/LoadingView.vue";
|
|||||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||||
import {Chart as ChartJS} from 'chart.js/auto';
|
import {Chart as ChartJS} from 'chart.js/auto';
|
||||||
import SelectButton from "primevue/selectbutton";
|
import SelectButton from "primevue/selectbutton";
|
||||||
|
import TransactionEditDrawer from "@/components/budgets/TransactionEditDrawer.vue";
|
||||||
|
|
||||||
// Зарегистрируем плагин
|
// Зарегистрируем плагин
|
||||||
ChartJS.register(ChartDataLabels);
|
ChartJS.register(ChartDataLabels);
|
||||||
@@ -334,7 +367,26 @@ const totalSaving = computed(() => {
|
|||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
const drawerOpened = ref(false)
|
||||||
|
const transactionType = ref('')
|
||||||
|
const categoryType = ref('')
|
||||||
|
const openDrawer = (selectedTransactionType = null, selectedCategoryType = null) => {
|
||||||
|
if (selectedTransactionType && selectedCategoryType) {
|
||||||
|
transactionType.value = selectedTransactionType;
|
||||||
|
categoryType.value = selectedCategoryType;
|
||||||
|
} else if (selectedTransactionType) {
|
||||||
|
transactionType.value = selectedTransactionType;
|
||||||
|
categoryType.value = 'EXPENSE'
|
||||||
|
}
|
||||||
|
|
||||||
|
drawerOpened.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const closeDrawer = async () => {
|
||||||
|
drawerOpened.value = false;
|
||||||
|
// await updateTransactions()
|
||||||
|
}
|
||||||
|
|
||||||
const dailyRatio = computed(() => {
|
const dailyRatio = computed(() => {
|
||||||
const value = (totalExpenses.value - totalLoans.value - totalSaving.value) / totalExpenses.value
|
const value = (totalExpenses.value - totalLoans.value - totalSaving.value) / totalExpenses.value
|
||||||
@@ -384,15 +436,12 @@ const fetchPlannedExpenses = async () => {
|
|||||||
}
|
}
|
||||||
const transactions = ref<Transaction[]>([])
|
const transactions = ref<Transaction[]>([])
|
||||||
const fetchBudgetTransactions = async () => {
|
const fetchBudgetTransactions = async () => {
|
||||||
|
|
||||||
|
|
||||||
transactions.value = await getBudgetTransactions(route.params.id, 'INSTANT')
|
transactions.value = await getBudgetTransactions(route.params.id, 'INSTANT')
|
||||||
updateLoading.value = false
|
updateLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateTransactions = async () => {
|
const updateTransactions = async () => {
|
||||||
|
await Promise.all([fetchPlannedIncomes(), fetchPlannedExpenses(), fetchBudgetCategories(), fetchBudgetTransactions()])
|
||||||
await Promise.all([fetchPlannedIncomes(), fetchPlannedExpenses(), fetchBudgetCategories(),])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const categories = ref<BudgetCategory[]>([])
|
const categories = ref<BudgetCategory[]>([])
|
||||||
@@ -480,8 +529,9 @@ const incomesByPeriod = computed(() => {
|
|||||||
let incomesUntil25 = 0
|
let incomesUntil25 = 0
|
||||||
let incomesFrom25 = 0
|
let incomesFrom25 = 0
|
||||||
plannedIncomes.value.forEach((i) => {
|
plannedIncomes.value.forEach((i) => {
|
||||||
|
console.log(i.date)
|
||||||
if (i.date <= budget.value?.dateFrom && i.date <= twentyFour.value) {
|
if (i.date >= budget.value?.dateFrom && i.date <= twentyFour.value) {
|
||||||
|
console.log(i.date)
|
||||||
incomesUntil25 += i.amount
|
incomesUntil25 += i.amount
|
||||||
} else {
|
} else {
|
||||||
incomesFrom25 += i.amount
|
incomesFrom25 += i.amount
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ import {
|
|||||||
createTransactionRequest,
|
createTransactionRequest,
|
||||||
getTransactionTypes,
|
getTransactionTypes,
|
||||||
updateTransactionRequest,
|
updateTransactionRequest,
|
||||||
deleteTransactionRequest
|
deleteTransactionRequest, getTransactions
|
||||||
} from "@/services/transactionService";
|
} from "@/services/transactionService";
|
||||||
import {getCategories, getCategoryTypes} from "@/services/categoryService";
|
import {getCategories, getCategoryTypes} from "@/services/categoryService";
|
||||||
import {useToast} from "primevue/usetoast";
|
import {useToast} from "primevue/usetoast";
|
||||||
import LoadingView from "@/components/LoadingView.vue";
|
import LoadingView from "@/components/LoadingView.vue";
|
||||||
|
import BudgetTransactionView from "@/components/budgets/BudgetTransactionView.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -37,6 +38,10 @@ const props = defineProps({
|
|||||||
categoryType: {
|
categoryType: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
transactions: {
|
||||||
|
type: Array as () => Array<Transaction>,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -247,6 +252,7 @@ const closeDrawer = () => emit('close-drawer');
|
|||||||
const keyboardOpen = ref(false);
|
const keyboardOpen = ref(false);
|
||||||
const isMobile = ref(false);
|
const isMobile = ref(false);
|
||||||
const userAgent = ref(null);
|
const userAgent = ref(null);
|
||||||
|
const transactions = ref<Transaction[]>(props.transactions);
|
||||||
// Мониторинг при монтировании
|
// Мониторинг при монтировании
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@@ -254,7 +260,12 @@ onMounted(async () => {
|
|||||||
await fetchCategoriesAndTypes();
|
await fetchCategoriesAndTypes();
|
||||||
|
|
||||||
prepareData();
|
prepareData();
|
||||||
|
if (!transactions.value && !isEditing.value) {
|
||||||
|
console.log()
|
||||||
|
await getTransactions('INSTANT', 'EXPENSE' ).then(transactionsResponse => transactions.value = transactionsResponse.data);
|
||||||
|
transactions.value = transactions.value.slice(0,3)
|
||||||
|
console.log(transactions.value.slice(0,3))
|
||||||
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
const deviceInfo = platform;
|
const deviceInfo = platform;
|
||||||
isMobile.value = deviceInfo.os.family === 'iOS' || deviceInfo.os.family === 'Android';
|
isMobile.value = deviceInfo.os.family === 'iOS' || deviceInfo.os.family === 'Android';
|
||||||
@@ -267,7 +278,7 @@ onMounted(async () => {
|
|||||||
<div class="card flex justify-center h-dvh">
|
<div class="card flex justify-center h-dvh">
|
||||||
|
|
||||||
|
|
||||||
<Drawer :visible="visible" :header="isEditing ? 'Edit Transaction' : 'Create Transaction'" :showCloseIcon="false"
|
<Drawer :visible="visible" :header="isEditing ? 'Изменить транзакцию' : 'Создать транзакцию'" :showCloseIcon="false"
|
||||||
position="right" @hide="closeDrawer"
|
position="right" @hide="closeDrawer"
|
||||||
class="!w-128 ">
|
class="!w-128 ">
|
||||||
<div v-if="result" class="absolute top-0 left-0 w-full h-full flex items-center justify-center z-50">
|
<div v-if="result" class="absolute top-0 left-0 w-full h-full flex items-center justify-center z-50">
|
||||||
@@ -363,13 +374,13 @@ onMounted(async () => {
|
|||||||
@blur="keyboardOpen=false"
|
@blur="keyboardOpen=false"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<label for="amount" class="">Amount</label>
|
<label for="amount" class="">Сумма</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
|
|
||||||
<!-- Comment Input -->
|
<!-- Comment Input -->
|
||||||
|
|
||||||
<FloatLabel variant="on" class="w-full">
|
<FloatLabel variant="on" class="w-full">
|
||||||
<label for="comment">Comment</label>
|
<label for="comment">Комментарий</label>
|
||||||
<InputText class="w-full"
|
<InputText class="w-full"
|
||||||
:invalid="!editedTransaction.comment"
|
:invalid="!editedTransaction.comment"
|
||||||
id="comment"
|
id="comment"
|
||||||
@@ -384,7 +395,7 @@ onMounted(async () => {
|
|||||||
<!-- Date Picker -->
|
<!-- Date Picker -->
|
||||||
<div class="field col-12 gap-0">
|
<div class="field col-12 gap-0">
|
||||||
<FloatLabel variant="on">
|
<FloatLabel variant="on">
|
||||||
<label for="date">Date </label>
|
<label for="date">Дата</label>
|
||||||
|
|
||||||
<DatePicker class="w-full"
|
<DatePicker class="w-full"
|
||||||
inline
|
inline
|
||||||
@@ -401,18 +412,17 @@ onMounted(async () => {
|
|||||||
|
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<BudgetTransactionView v-if="!isEditing && transactions" v-for="transaction in transactions" :is-list="true"
|
||||||
|
:transaction="transaction"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Amount Input -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
{{ keyboardOpen }}
|
|
||||||
<div class="fixed col-12 flex justify-content-end gap-4 bottom-8">
|
<div class="fixed col-12 flex justify-content-end gap-4 bottom-8">
|
||||||
|
|
||||||
<Button label="Save" icon="pi pi-check" class="p-button-success"
|
<Button label="Сохранить" icon="pi pi-check" class="p-button-success"
|
||||||
@click="isEditing ? updateTransaction() : createTransaction()"/>
|
@click="isEditing ? updateTransaction() : createTransaction()"/>
|
||||||
<Button label="Cancel" icon="pi pi-times" class="p-button-secondary " @click="closeDrawer"/>
|
<Button label="Отмена" icon="pi pi-times" class="p-button-secondary " @click="closeDrawer"/>
|
||||||
<Button v-if="isEditing" label="Delete" icon="pi pi-times" class="p-button-success" severity="danger"
|
<Button v-if="isEditing" label="Удалить" icon="pi pi-times" class="p-button-success" severity="danger"
|
||||||
@click="deleteTransaction"/>
|
@click="deleteTransaction"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const getBudgetTransactions = async (budgetId, transactionType, categoryT
|
|||||||
|
|
||||||
let url = `/budgets/${budgetId}/transactions`
|
let url = `/budgets/${budgetId}/transactions`
|
||||||
if (transactionType && !categoryType) {
|
if (transactionType && !categoryType) {
|
||||||
url += '/?type=' + transactionType
|
url += '?type=' + transactionType
|
||||||
}
|
}
|
||||||
if (transactionType && categoryType) {
|
if (transactionType && categoryType) {
|
||||||
url += '/'+transactionType+'/'+categoryType
|
url += '/'+transactionType+'/'+categoryType
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const getCategories = async (type = null) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getCategoryTypes = async () => {
|
export const getCategoryTypes = async () => {
|
||||||
return await apiClient.get('/categories/types/');
|
return await apiClient.get('/categories/types');
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createCategory = async (category: Category) => {
|
export const createCategory = async (category: Category) => {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const createTransactionRequest = async (transaction: Transaction) => {
|
|||||||
|
|
||||||
export const updateTransactionRequest = async (transaction: Transaction) => {
|
export const updateTransactionRequest = async (transaction: Transaction) => {
|
||||||
const id = transaction.id
|
const id = transaction.id
|
||||||
// transaction.date = format(transaction.date, 'yyyy-MM-dd')
|
transaction.date = format(transaction.date, 'yyyy-MM-dd')
|
||||||
const response = await apiClient.put(`/transactions/${id}`, transaction);
|
const response = await apiClient.put(`/transactions/${id}`, transaction);
|
||||||
transaction = response.data
|
transaction = response.data
|
||||||
transaction.date = new Date(transaction.date);
|
transaction.date = new Date(transaction.date);
|
||||||
@@ -44,10 +44,20 @@ export const updateTransactionRequest = async (transaction: Transaction) => {
|
|||||||
return transaction
|
return transaction
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setTransactionDoneRequest = async (transaction: Transaction) => {
|
||||||
|
const id = transaction.id
|
||||||
|
// transaction.date = format(transaction.date, 'yyyy-MM-dd')
|
||||||
|
const response = await apiClient.put(`/transactions/${id}/done`, transaction);
|
||||||
|
transaction = response.data
|
||||||
|
transaction.date = new Date(transaction.date);
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
};
|
||||||
|
|
||||||
export const deleteTransactionRequest = async (id: number) => {
|
export const deleteTransactionRequest = async (id: number) => {
|
||||||
return await apiClient.delete(`/transactions/${id}`);
|
return await apiClient.delete(`/transactions/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTransactionTypes = async () => {
|
export const getTransactionTypes = async () => {
|
||||||
return await apiClient.get('/transactions/types/');
|
return await apiClient.get('/transactions/types');
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user