-
-
-
Total Incomes:
-
- +{{ formatAmount(budgetInfo.totalIncomes) }}
- ₽
-
-
-
-
-
Total Expenses:
-
- -{{ formatAmount(budgetInfo.totalExpenses) }}
- ₽
-
-
-
-
Income at 10th:
-
- +{{ formatAmount(budgetInfo.chartData[0][0]) }} ₽
-
-
-
-
Income at 25th:
-
- +{{ formatAmount(budgetInfo.chartData[0][1]) }} ₽
-
-
-
-
Expenses at 10th:
-
- -{{ formatAmount(budgetInfo.chartData[1][0]) }} ₽
-
-
-
-
Expenses at 25th:
-
- -{{ formatAmount(budgetInfo.chartData[1][1]) }} ₽
-
-
-
-
Left for unplanned:
-
- {{ formatAmount(leftForUnplanned) }} ₽
-
-
-
-
-
Current spending:
-
- {{ formatAmount(leftForUnplanned) }} ₽
-
-
+
-
-
-
-
- {{ formatAmount(leftForUnplanned) }} ₽
+
+
+
Transactions List
+
+
+
+ {{ categorySum.category.name }} :
+ {{ categorySum.sum }} ₽
+
+
+
+
-
-
-
-
-
-
Planned Incomes
-
-
-
-
- {{ formatAmount(budgetInfo.totalIncomes) }}
- ₽
-
-
- {{ formatAmount(totalIncomeLeftToGet) }}
- ₽
-
-
-
-
-
-
-
-
-
-
Planned Expenses
-
-
- {{ formatAmount(budgetInfo.totalExpenses) }}
- ₽
-
-
- {{ formatAmount(totalExpenseLeftToSpend) }}
- ₽
-
-
-
-
-
-
-
-
-
Unplanned Categories
-
-
-
-
-
-
-
-
-
Transactions List
-
-
-
- {{ categorySum.category.name }} :
- {{ categorySum.sum }} ₽
-
-
-
-
-
-
-
@@ -188,40 +240,61 @@
@@ -332,13 +665,16 @@ onMounted(async () => {
}
.max-h-tlist {
- max-height: 45dvh; /* Ограничение высоты списка */
+ max-height: 1170px; /* Ограничение высоты списка */
}
-
.box-shadow-inner {
box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.3);
}
+.chart {
+ width: 70%;
+}
+
\ No newline at end of file
diff --git a/src/components/budgets/TransactionEditDrawer.vue b/src/components/budgets/TransactionEditDrawer.vue
index 1b40f42..13ed198 100644
--- a/src/components/budgets/TransactionEditDrawer.vue
+++ b/src/components/budgets/TransactionEditDrawer.vue
@@ -40,10 +40,10 @@ const props = defineProps({
}
});
-const emit = defineEmits(['create-transaction', 'update-transaction', 'delete-transaction', 'close-drawer']);
+const emit = defineEmits(['create-transaction', 'update-transaction', 'delete-transaction', 'close-drawer', 'transaction-updated']);
const toast = useToast();
const categoryTypeChanged = () => {
- console.log(selectedCategoryType.value)
+
editedTransaction.value.category = selectedCategoryType.value.code == "EXPENSE" ? expenseCategories.value[0] : incomeCategories.value[0];
}
@@ -82,11 +82,30 @@ const fetchCategoriesAndTypes = async () => {
categoryTypes.value = categoryTypesResponse.data;
transactionTypes.value = transactionTypesResponse.data;
+ console.log(entireCategories.value)
} catch (error) {
console.error('Error fetching categories and types:', error);
}
};
+const checkForm = () => {
+ const errorMessages = {
+ transactionType: 'Тип транзакции должен быть выбран',
+ category: 'Категория должна быть выбрана',
+ date: 'Дата должна быть выбрана',
+ comment: 'Комментарий должен быть введен',
+ amount: 'Сумма не может быть пустой или 0'
+ };
+
+ if (!editedTransaction.value.transactionType) return showError(errorMessages.transactionType);
+ if (!editedTransaction.value.category) return showError(errorMessages.category);
+ if (!editedTransaction.value.date) return showError(errorMessages.date);
+ if (!editedTransaction.value.comment) return showError(errorMessages.comment);
+ if (!editedTransaction.value.amount || editedTransaction.value.amount === 0) return showError(errorMessages.amount);
+
+ return true;
+};
+
// Инициализация данных
const prepareData = () => {
if (!props.transaction) {
@@ -102,39 +121,84 @@ const prepareData = () => {
}
};
+const result = ref(false)
+const isError = ref(false)
+const resultText = ref('')
+
+const computeResult = (resultState, error) => {
+
+ if (!resultState && error) {
+ result.value = true;
+ isError.value = true
+ resultText.value = `Ошибка: ${error.message}`
+ } else {
+
+ result.value = true;
+ isError.value = false
+ resultText.value = 'Успех!'
+ }
+ setTimeout(() => {
+ result.value = false
+ resultText.value = ''
+ }, 1000)
+}
+
+const showError = (message) => {
+ result.value = true;
+ isError.value = true;
+ resultText.value = message;
+ return false;
+};
// Создание транзакции
const createTransaction = async () => {
- try {
- loading.value = true;
- if (editedTransaction.value.transactionType.code === 'INSTANT') {
- editedTransaction.value.isDone = true;
+ if (checkForm()) {
+ try {
+ loading.value = true;
+ if (editedTransaction.value.transactionType.code === 'INSTANT') {
+ editedTransaction.value.isDone = true;
+ }
+ await createTransactionRequest(editedTransaction.value);
+ toast.add({severity: 'success', summary: 'Transaction created!', detail: 'Транзакция создана!', life: 3000});
+ emit('create-transaction', editedTransaction.value);
+ computeResult(true)
+ resetForm();
+ } catch (error) {
+ computeResult(false, error)
+ console.error('Error creating transaction:', error);
+ } finally {
+ loading.value = false;
}
- await createTransactionRequest(editedTransaction.value);
- toast.add({severity: 'success', summary: 'Transaction created!', detail: 'Транзакция создана!', life: 3000});
- emit('create-transaction', editedTransaction.value);
- resetForm();
- } catch (error) {
- console.error('Error creating transaction:', error);
- } finally {
- loading.value = false;
- console.log(editedTransaction.value)
}
+ setTimeout(() => {
+ result.value = false
+ resultText.value = ''
+ }, 1000)
};
// Обновление транзакции
const updateTransaction = async () => {
- try {
- loading.value = true;
- const response = await updateTransactionRequest(editedTransaction.value);
- editedTransaction.value = response.data;
- toast.add({severity: 'success', summary: 'Transaction updated!', detail: 'Транзакция обновлена!', life: 3000});
- emit('update-transaction', editedTransaction.value);
- } catch (error) {
- console.error('Error updating transaction:', error);
- } finally {
- loading.value = false;
+ if (checkForm()) {
+ try {
+ loading.value = true;
+ const response = await updateTransactionRequest(editedTransaction.value);
+ response.data;
+ // toast.add({severity: 'success', summary: 'Transaction updated!', detail: 'Транзакция обновлена!', life: 3000});
+ emit('update-transaction', editedTransaction.value);
+ emit('transaction-updated');
+ computeResult(true)
+ } catch (error) {
+ computeResult(false, error)
+ console.error('Error updating transaction:', error);
+ } finally {
+ loading.value = false;
+ }
}
+ setTimeout(() => {
+ result.value = false
+ resultText.value = ''
+
+ }, 1000)
};
// Удаление транзакции
@@ -145,7 +209,11 @@ const deleteTransaction = async () => {
toast.add({severity: 'success', summary: 'Transaction deleted!', detail: 'Транзакция удалена!', life: 3000});
emit('delete-transaction', editedTransaction.value);
closeDrawer()
+ computeResult(true)
} catch (error) {
+ computeResult(false, error)
+ toast.add({severity: 'warn', summary: 'Error!', detail: 'Транзакция обновлена!', life: 3000});
+
console.error('Error deleting transaction:', error);
} finally {
loading.value = false;
@@ -162,15 +230,15 @@ const resetForm = () => {
};
const dateErrorMessage = computed(() => {
- console.log('tut')
+
if (editedTransaction.value.transactionType.code != 'PLANNED' && editedTransaction.value.date > new Date()) {
- console.log('tut2')
+
return 'При мгновенных тратах дата должна быть меньше текущей!'
} else if (editedTransaction.value.transactionType.code == 'PLANNED' && editedTransaction.value.date < new Date()) {
- console.log('tu3')
+
return 'При плановых тратах дата должна быть больше текущей!'
} else {
- console.log('tu4')
+
return ''
}
})
@@ -183,12 +251,16 @@ const userAgent = ref(null);
// Мониторинг при монтировании
onMounted(async () => {
loading.value = true;
+
await fetchCategoriesAndTypes();
+
prepareData();
+
loading.value = false;
const deviceInfo = platform;
isMobile.value = deviceInfo.os.family === 'iOS' || deviceInfo.os.family === 'Android';
- console.log(deviceInfo);
+ console.log()
+
})
@@ -196,14 +268,32 @@ onMounted(async () => {
+
+
+
+
+
+
+
+
+
+
- {{userAgent}}
@@ -217,7 +307,7 @@ onMounted(async () => {
aria-labelledby="basic"
@change="categoryTypeChanged" class="justify-center"/>
-
@@ -241,7 +331,7 @@ onMounted(async () => {
{
{{ keyboardOpen }}
-
+
diff --git a/src/components/budgets/UnplannedCategoryView.vue b/src/components/budgets/UnplannedCategoryView.vue
deleted file mode 100644
index 1c06393..0000000
--- a/src/components/budgets/UnplannedCategoryView.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-
-
{{ editedCategory.category.name }}
-
{{ editedCategory.category.description }}
-
-
-
-
-
-
- {{ formatAmount(editedCategory.categorySetting.value) }} ₽
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/components/settings/RecurrentSettingView.vue b/src/components/settings/RecurrentSettingView.vue
index 17815d9..6a27c6d 100644
--- a/src/components/settings/RecurrentSettingView.vue
+++ b/src/components/settings/RecurrentSettingView.vue
@@ -11,7 +11,7 @@ const recurrentPayments = ref
([]);
const fetchRecurrentPayments = async () => {
loading.value = true;
try {
- console.log('loaded')
+
const result = await getRecurrentPayments();
recurrentPayments.value = result.data;
} catch (error) {
diff --git a/src/components/transactions/TransactionList.vue b/src/components/transactions/TransactionList.vue
index a48c665..beebe42 100644
--- a/src/components/transactions/TransactionList.vue
+++ b/src/components/transactions/TransactionList.vue
@@ -21,7 +21,6 @@ const fetchCategories = async () => {
try {
const response = await getTransactions('INSTANT');
transactions.value = response.data
- console.log(transactions.value)
} catch (error) {
console.error('Error fetching categories:', error);
}
@@ -72,7 +71,7 @@ onMounted(async () => {
-
+
diff --git a/src/main.ts b/src/main.ts
index 42595ef..ba495ba 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,11 +8,15 @@ import Aura from '@primevue/themes/aura';
import router from './router';
import Ripple from "primevue/ripple";
import ToastService from 'primevue/toastservice'
+import Tooltip from 'primevue/tooltip';
+
+
const app = createApp(App);
app.use(router);
app.use(ToastService);
app.directive('ripple', Ripple);
+app.directive('tooltip', Tooltip);
app.use(PrimeVue, {
theme: {
preset: Aura
diff --git a/src/plugins/axios.ts b/src/plugins/axios.ts
index 59c1ae6..feec0f8 100644
--- a/src/plugins/axios.ts
+++ b/src/plugins/axios.ts
@@ -3,8 +3,8 @@ import axios from 'axios';
// Создание экземпляра axios с базовым URL
const apiClient = axios.create({
- // baseURL: 'https://luminic.space/api/v1',
- baseURL: 'http://localhost:8000/api/v1',
+ baseURL: 'https://luminic.space/api/v1',
+ // baseURL: 'http://localhost:8000/api/v1',
headers: {
'Content-Type': 'application/json',
diff --git a/src/router/index.ts b/src/router/index.ts
index 61b11e6..c53a3d7 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,4 +1,4 @@
-import {createRouter, createWebHistory, useRoute} from 'vue-router';
+import {createRouter, createWebHistory} from 'vue-router';
import CategoriesList from '@/components/settings/categories/CategoriesList.vue';
import CreateCategoryModal from "@/components/settings/categories/CreateCategoryModal.vue";
import CategoryListItem from "@/components/settings/categories/CategoryListItem.vue"; // Импортируем новый компонент
@@ -10,18 +10,33 @@ import TransactionList from "@/components/transactions/TransactionList.vue";
import LoginView from "@/components/auth/LoginView.vue";
const routes = [
-
- {path: '/', name: 'Budgets main', component: BudgetList, meta: { requiresAuth: true }},
- { path: '/login', component: LoginView },
- {path: '/budgets', name: 'Budgets', component: BudgetList, meta: { requiresAuth: true }},
- {path: '/budgets/:id', name: 'BudgetView', component: BudgetView, meta: { requiresAuth: true }},
- {path: '/transactions/:mode*', name: 'Transaction List', component: TransactionList, meta: { requiresAuth: true }},
+ {path: '/login', component: LoginView},
+ {path: '/', name: 'Budgets main', component: BudgetList, meta: {requiresAuth: true}},
+ {path: '/analytics', name: 'Analytics', component: BudgetList, meta: {requiresAuth: true}},
+ {path: '/budgets', name: 'Budgets', component: BudgetList, meta: {requiresAuth: true}},
+ {path: '/budgets/:id', name: 'BudgetView', component: BudgetView, meta: {requiresAuth: true}},
+ {path: '/transactions/:mode*', name: 'Transaction List', component: TransactionList, meta: {requiresAuth: true}},
// {path: '/transactions/create', name: 'Transaction List', component: TransactionList},
- {path: '/settings/', name: 'Settings', component: SettingsView, meta: { requiresAuth: true }},
- {path: '/settings/categories', name: 'Categories', component: CategoriesList, meta: { requiresAuth: true }},
- {path: '/settings/recurrents', name: 'Recurrent operations list', component: RecurrentList, meta: { requiresAuth: true }},
- {path: '/settings/categories/create', name: "Categories Creation", component: CreateCategoryModal, meta: { requiresAuth: true }},// Добавляем новый маршрут
- {path: '/settings/categories/one', name: "Categories Creation", component: CategoryListItem, meta: { requiresAuth: true }}// Добавляем новый маршрут
+ {path: '/settings/', name: 'Settings', component: SettingsView, meta: {requiresAuth: true}},
+ {path: '/settings/categories', name: 'Categories', component: CategoriesList, meta: {requiresAuth: true}},
+ {
+ path: '/settings/recurrents',
+ name: 'Recurrent operations list',
+ component: RecurrentList,
+ meta: {requiresAuth: true}
+ },
+ {
+ path: '/settings/categories/create',
+ name: "Categories Creation",
+ component: CreateCategoryModal,
+ meta: {requiresAuth: true}
+ },// Добавляем новый маршрут
+ {
+ path: '/settings/categories/one',
+ name: "Categories Creation",
+ component: CategoryListItem,
+ meta: {requiresAuth: true}
+ }// Добавляем новый маршрут
];
const router = createRouter({
@@ -32,11 +47,8 @@ const router = createRouter({
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.meta.requiresAuth && !token) {
- const router= useRoute()
- console.log(to)
- console.log(router.path)
- console.log(router.params)
- next('/login?back='+to.fullPath);
+ // const router = useRoute()
+ next('/login?back=' + to.fullPath);
} else {
next();
}
diff --git a/src/services/budgetsService.ts b/src/services/budgetsService.ts
index e3fceed..d50dc0a 100644
--- a/src/services/budgetsService.ts
+++ b/src/services/budgetsService.ts
@@ -3,7 +3,7 @@ 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) => {
@@ -24,22 +24,43 @@ export const getBudgetInfos = async () => {
return budgetInfos
}
+export const getBudgetTransactions = async (budgetId, transactionType, categoryType) => {
+
+ let url = `/budgets/${budgetId}/transactions`
+ if (transactionType) {
+ url += '/' + transactionType
+ }
+ if (transactionType && categoryType) {
+ url += '/'+categoryType
+ }
+ // if (!categoryType) {
+ // throw new Error('No CategoryType');
+ // }
+ let response = await apiClient.get(url);
+ let transactions = response.data;
+ transactions.forEach(e => {
+ e.date = new Date(e.date)
+ })
+ return transactions
+}
+
+export const getBudgetCategories = async (budgetId) => {
+ let response = await apiClient.get('/budgets/' + budgetId + '/categories/');
+ return response.data;
+}
+
+export const getBudgetCategoriesSums = async (budgetId) => {
+ let response = await apiClient.get('/budgets/' + budgetId + '/categories/_calc_sums');
+ return response.data;
+}
+
export const getBudgetInfo = async (budget_id: number) => {
- console.log('getBudgetInfo');
+
let budgetInfo = await apiClient.get('/budgets/' + budget_id);
budgetInfo = budgetInfo.data;
- 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)
- })
+ budgetInfo.dateFrom = new Date(budgetInfo.dateFrom)
+ budgetInfo.dateTo = new Date(budgetInfo.dateTo)
return budgetInfo
};
diff --git a/src/services/transactionService.ts b/src/services/transactionService.ts
index 6ed01d0..5ace4d1 100644
--- a/src/services/transactionService.ts
+++ b/src/services/transactionService.ts
@@ -36,8 +36,13 @@ export const createTransactionRequest = async (transaction: Transaction) => {
export const updateTransactionRequest = async (transaction: Transaction) => {
const id = transaction.id
- transaction.date = format(transaction.date, 'yyyy-MM-dd')
- return await apiClient.put(`/transactions/${id}`, transaction);
+ // transaction.date = format(transaction.date, 'yyyy-MM-dd')
+ const response = await apiClient.put(`/transactions/${id}`, transaction);
+ transaction = response.data
+ transaction.date = new Date(transaction.date);
+ console.log(transaction.date);
+
+ return transaction
};
export const deleteTransactionRequest = async (id: number) => {
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index c923ad9..93433b0 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -4,7 +4,6 @@ export const formatAmount = (amount: number) => {
export const formatDate = (date) => {
const validDate = typeof date === 'string' ? new Date(date) : date;
-
// Проверяем, является ли validDate корректной датой
if (isNaN(validDate.getTime())) {
return 'Invalid Date'; // Если дата неверная, возвращаем текст ошибки