chet novoe
This commit is contained in:
@@ -4,6 +4,10 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.p-inputnumber-input{
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
/*#app {*/
|
||||
/* !*max-width: 1280px;*!*/
|
||||
/* !*margin: 0 auto;*!*/
|
||||
|
||||
28
src/components/LoadingView.vue
Normal file
28
src/components/LoadingView.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import ProgressSpinner from "primevue/progressspinner";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative w-full h-screen">
|
||||
<!-- Полупрозрачный белый фон -->
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-white opacity-50 z-0"></div>
|
||||
|
||||
<!-- Спиннер поверх -->
|
||||
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center z-50">
|
||||
<ProgressSpinner
|
||||
style="width: 50px; height: 50px;"
|
||||
strokeWidth="8"
|
||||
fill="transparent"
|
||||
animationDuration=".5s"
|
||||
aria-label="Custom ProgressSpinner"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,28 +1,29 @@
|
||||
<template>
|
||||
<div class="px-4 bg-gray-100 h-full ">
|
||||
<LoadingView v-if="loading"/>
|
||||
<div v-else class="px-4 bg-gray-100 h-full ">
|
||||
<!-- Заголовок -->
|
||||
<h2 class="text-4xl mb-6 font-bold">Monthly Budgets</h2>
|
||||
|
||||
<!-- Плитка с бюджетами -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Будущие и текущие бюджеты -->
|
||||
<div v-for="budget in upcomingBudgets" :key="budget.id" class="p-4 shadow-lg rounded-lg bg-white">
|
||||
<div v-for="budget in budgetInfos" :key="budget.budget.id" class="p-4 shadow-lg rounded-lg bg-white" :class="budget.budget.dateTo < new Date() ? 'bg-gray-100 opacity-60' : ''">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="text-xl font-bold mb-2">{{ budget.month }}</div>
|
||||
<router-link to="/budgets/1">
|
||||
<Button icon="pi pi-arrow-circle-right" rounded text size="large"/>
|
||||
<div class="text-xl font-bold mb-2">{{ budget.budget.name }}</div>
|
||||
<router-link :to="'/budgets/'+budget.budget.id">
|
||||
<i class="pi pi-arrow-circle-right text-green-500" style="font-size: 1.5rem;"/>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="text-sm text-gray-600 mb-4">
|
||||
{{ budget.startDate }} - {{ budget.endDate }}
|
||||
{{ formatDate(budget.budget.dateFrom) }} - {{ formatDate(budget.budget.dateTo) }}
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm">Total Income: <span class="font-bold">{{ budget.totalIncome }}</span></div>
|
||||
<div class="text-sm">Total Expenses: <span class="font-bold">{{ budget.totalExpenses }}</span></div>
|
||||
<div class="text-sm">Planned Expenses: <span class="font-bold">{{ budget.plannedExpenses }}</span></div>
|
||||
<div class="text-sm">Total Income: <span class="font-bold">{{ formatAmount(budget.totalIncomes) }} ₽</span></div>
|
||||
<div class="text-sm">Total Expenses: <span class="font-bold">{{ formatAmount(budget.totalExpenses) }} ₽</span></div>
|
||||
<div class="text-sm">Planned Expenses: <span class="font-bold">{{ formatAmount(budget.totalExpenses) }} ₽</span></div>
|
||||
<div class="text-sm flex items-center">
|
||||
Unplanned Expenses:
|
||||
<span class="ml-2 font-bold">{{ budget.remainingForUnplanned }}</span>
|
||||
<span class="ml-2 font-bold">{{ formatAmount(budget.totalIncomes - budget.totalExpenses) }} ₽</span>
|
||||
<!-- Прогресс бар -->
|
||||
<ProgressBar :value="budget.unplannedProgress" class="ml-4 w-full"/>
|
||||
</div>
|
||||
@@ -51,17 +52,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {ref} from 'vue';
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref} from 'vue';
|
||||
import ProgressBar from 'primevue/progressbar';
|
||||
import Button from "primevue/button";
|
||||
import {BudgetInfo} from "@/models/Budget";
|
||||
import {getBudgetInfos} from "@/services/budgetsService";
|
||||
import {formatAmount, formatDate} from "@/utils/utils";
|
||||
import LoadingView from "@/components/LoadingView.vue";
|
||||
|
||||
const loading = ref(false)
|
||||
const budgetInfos = ref<BudgetInfo[]>([])
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ProgressBar,
|
||||
Button
|
||||
},
|
||||
setup() {
|
||||
const upcomingBudgets = ref([
|
||||
{
|
||||
id: 1,
|
||||
@@ -86,7 +87,6 @@ export default {
|
||||
unplannedProgress: 50,
|
||||
},
|
||||
]);
|
||||
|
||||
const pastBudgets = ref([
|
||||
{
|
||||
id: 3,
|
||||
@@ -112,12 +112,12 @@ export default {
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
upcomingBudgets,
|
||||
pastBudgets,
|
||||
};
|
||||
},
|
||||
};
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
budgetInfos.value = await getBudgetInfos()
|
||||
console.log(budgetInfos.value)
|
||||
loading.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
<template>
|
||||
<Toast/>
|
||||
<div v-if="loading" class="relative w-full h-screen">
|
||||
<!-- Полупрозрачный белый фон -->
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-white opacity-50 z-0"></div>
|
||||
|
||||
<!-- Спиннер поверх -->
|
||||
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center z-50">
|
||||
<ProgressSpinner
|
||||
style="width: 50px; height: 50px;"
|
||||
strokeWidth="8"
|
||||
fill="transparent"
|
||||
animationDuration=".5s"
|
||||
aria-label="Custom ProgressSpinner"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<LoadingView v-if="loading"/>
|
||||
|
||||
<div v-else class="px-4 bg-gray-100 h-full ">
|
||||
<div v-if="updateLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center z-50">
|
||||
@@ -216,6 +202,7 @@ import UnplannedCategoryView from "@/components/budgets/UnplannedCategoryView.vu
|
||||
import {TransactionType} from "@/models/Transaction";
|
||||
import Toast from "primevue/toast";
|
||||
import Button from "primevue/button";
|
||||
import LoadingView from "@/components/LoadingView.vue";
|
||||
|
||||
const loading = ref(true);
|
||||
const updateLoading = ref(false);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "@/services/transactionService";
|
||||
import {getCategories, getCategoryTypes} from "@/services/categoryService";
|
||||
import {useToast} from "primevue/usetoast";
|
||||
import LoadingView from "@/components/LoadingView.vue";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -192,9 +193,7 @@ onMounted(async () => {
|
||||
<Drawer :visible="visible" :header="isEditing ? 'Edit Transaction' : 'Create Transaction'" :showCloseIcon="false"
|
||||
position="right" @hide="closeDrawer"
|
||||
class="!w-128 ">
|
||||
<div v-if="loading">
|
||||
Loading...
|
||||
</div>
|
||||
<LoadingView v-if="loading"/>
|
||||
<div v-else class=" grid gap-4 w-full ">
|
||||
|
||||
<div class="relative w-full justify-center justify-items-center ">
|
||||
@@ -253,11 +252,11 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4">
|
||||
<div class=" ">
|
||||
<div class="flex flex-row gap-4">
|
||||
|
||||
<FloatLabel variant="on" class="w-10">
|
||||
<InputNumber class="!w-10"
|
||||
|
||||
<FloatLabel variant="on" class="">
|
||||
<InputNumber class=""
|
||||
:invalid="!editedTransaction.amount"
|
||||
:minFractionDigits="0"
|
||||
id="amount"
|
||||
@@ -267,12 +266,12 @@ onMounted(async () => {
|
||||
locale="ru-RU"
|
||||
|
||||
/>
|
||||
<label for="amount" class="!w-10">Amount</label>
|
||||
<label for="amount" class="">Amount</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<!-- Comment Input -->
|
||||
<div class="field col-12 col-span-3">
|
||||
<FloatLabel variant="on" class="l">
|
||||
|
||||
<FloatLabel variant="on" class="w-full">
|
||||
<label for="comment">Comment</label>
|
||||
<InputText class="w-full"
|
||||
:invalid="!editedTransaction.comment"
|
||||
@@ -281,7 +280,7 @@ onMounted(async () => {
|
||||
|
||||
/>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Date Picker -->
|
||||
@@ -309,7 +308,7 @@ onMounted(async () => {
|
||||
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="field col-12 flex justify-content-end gap-4">
|
||||
<div class="fixed col-12 bottom-6 flex justify-content-end gap-4">
|
||||
|
||||
<Button label="Save" icon="pi pi-check" class="p-button-success"
|
||||
@click="isEditing ? updateTransaction() : createTransaction()"/>
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useRouter } from 'vue-router';
|
||||
|
||||
// Создаем экземпляр axios
|
||||
const api = axios.create({
|
||||
baseURL: 'https://luminic.space/api/v1',
|
||||
// baseURL: 'https://luminic.space/api/v1',
|
||||
baseURL: 'http://localhost:8000/api/v1',
|
||||
});
|
||||
|
||||
// Устанавливаем токен из localStorage при каждом запуске
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
import apiClient from '@/services/axiosSetup';
|
||||
import {BudgetCategory} from "@/models/Budget";
|
||||
import {Budget, BudgetCategory} from "@/models/Budget";
|
||||
// Импортируете настроенный экземпляр axios
|
||||
|
||||
export const getBudgetInfos = async () => {
|
||||
console.log('getBudgetInfos');
|
||||
let response = await apiClient.get('/budgets/');
|
||||
let budgetInfos = response.data;
|
||||
budgetInfos.forEach((budgetInfo: Budget) => {
|
||||
budgetInfo.budget.dateFrom = new Date(budgetInfo.budget.dateFrom);
|
||||
budgetInfo.budget.dateTo = new Date(budgetInfo.budget.dateTo);
|
||||
budgetInfo.plannedExpenses.forEach(e => {
|
||||
e.date = new Date(e.date)
|
||||
})
|
||||
|
||||
budgetInfo.plannedIncomes.forEach(e => {
|
||||
e.date = new Date(e.date)
|
||||
})
|
||||
|
||||
budgetInfo.transactions.forEach(e => {
|
||||
e.date = new Date(e.date)
|
||||
})
|
||||
})
|
||||
return budgetInfos
|
||||
}
|
||||
|
||||
export const getBudgetInfo = async (budget_id: number) => {
|
||||
console.log('getBudgetInfo');
|
||||
let budgetInfo = await apiClient.get('/budgets/' + budget_id);
|
||||
|
||||
Reference in New Issue
Block a user