Files
luminic-front/src/components/transactions/TransactionList.vue
2025-03-03 10:33:14 +03:00

202 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import {computed, onMounted, onUnmounted, ref, watch} from "vue";
import BudgetTransactionView from "@/components/budgets/BudgetTransactionView.vue";
import IconField from "primevue/iconfield";
import InputIcon from "primevue/inputicon";
import InputText from "primevue/inputtext";
import {getTransactions, getTransactionTypes} from "@/services/transactionService";
import {Transaction} from "@/models/Transaction";
import ProgressSpinner from "primevue/progressspinner";
import {getUsers} from "@/services/userService";
import Button from "primevue/button";
import {EventBus} from '@/utils/EventBus.ts';
import {useSpaceStore} from "@/stores/spaceStore";
import router from "@/router";
import {useDrawerStore} from "@/stores/drawerStore";
const loading = ref(false);
const searchText = ref("");
const transactions = ref<Transaction[]>([]);
const limit = 20; // Количество транзакций на одну загрузку
const offset = ref(0); // Начальное смещение
const allLoaded = ref(false); // Флаг для отслеживания окончания данных
const drawerStore = useDrawerStore()
// Функция для получения транзакций с параметрами limit и offset
const fetchTransactions = async (reload) => {
// if (loading.value || allLoaded.value) return; // Останавливаем загрузку, если уже загружается или данные загружены полностью
loading.value = true;
try {
const response = await getTransactions('INSTANT', null, null, selectedUserId.value ? selectedUserId.value : null, null, reload ? offset.value : limit, reload ? 0 : offset.value);
const newTransactions = response.data;
// Проверка на конец данных
if (newTransactions.length < limit) {
allLoaded.value = true; // Если данных меньше limit, значит, достигнут конец
}
// Добавляем новые транзакции к текущему списку
reload ? transactions.value = newTransactions : transactions.value.push(...newTransactions)
!reload ? offset.value += limit : offset.value
} catch (error) {
console.error("Error fetching transactions:", error);
}
loading.value = false;
};
const switchUserFilter = async (user) => {
if (selectedUserId.value == user.id) {
selectedUserId.value = null
} else if (selectedUserId.value == null) {
selectedUserId.value = user.id;
} else {
selectedUserId.value = user.id;
}
await getTransactions('INSTANT', null, null, selectedUserId.value, null, offset.value, 0)
.then(it => transactions.value = it.data)
}
const tgname = computed(() => {
if (window.Telegram.WebApp) {
const tg = window.Telegram.WebApp;
return tg.initDataUnsafe.user;
}
});
// Отфильтрованные транзакции по поисковому запросу
const filteredTransactions = computed(() => {
// Проверяем, есть ли текст поиска
const search = searchText.value.trim().toLowerCase();
if (!search) {
// Если текст поиска пуст, возвращаем все транзакции
return transactions.value;
}
// Проверяем наличие данных
if (!transactions.value || !Array.isArray(transactions.value)) {
console.warn("Transactions is not a valid array");
return [];
}
// Фильтруем транзакции по тексту поиска
return transactions.value.filter(transaction => {
return transaction.comment.toLowerCase().includes(search) ||
transaction.category.name.toLowerCase().includes(search);
});
});
// Обработчик прокрутки для ленивой загрузки
// const handleScroll = () => {
// const bottomReached = window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2000;
// if (bottomReached && !loading.value) {
// fetchTransactions(); // Загружаем следующую страницу
// }
// };
const users = ref([])
const selectedUserId = ref(null)
const fetchUsers = async () => {
users.value = await getUsers();
}
const selectedTransactionType = ref(null)
const spaceStore = useSpaceStore()
const selectedSpace = computed(() => spaceStore.space)
watch(selectedSpace, async (newValue, oldValue) => {
if (newValue != oldValue) {
transactions.value = [];
await fetchTransactions(true)
}
})
const types = ref([])
onMounted(async () => {
EventBus.on('transactions-updated', fetchTransactions, true);
if (selectedSpace.value) {
await fetchTransactions(false); // Первоначальная загрузка данных
}
// await fetchUsers();
await getTransactionTypes().then(it => types.value = it.data);
// window.addEventListener("scroll", handleScroll); // Добавляем обработчик прокрутки
});
onUnmounted(async () => {
EventBus.off('transactions-updated', fetchTransactions);
})
</script>
<template>
<div class="flex flex-col gap-4 p-4 bg-gray-100 h-full ">
<div class="flex flex-row gap-4 items-center">
<h2 class="text-4xl font-bold">Список транзакций </h2>
<Button label="+ Создать" @click="{
drawerStore.setCategoryType('EXPENSE');
drawerStore.setTransactionType('INSTANT');
drawerStore.visible = true
}" size="small"/>
</div>
<div v-if="!selectedSpace" class="flex w-full h-full items-center justify-center">
<p>Сперва нужно выбрать Пространство.
<button class="text-blue-500 hover:underline" @click="router.push('/spaces').then((res) => router.go(0))">
Перейти
</button>
</p>
</div>
<div v-else class="flex flex-col gap-2">
<IconField>
<InputIcon class="pi pi-search"/>
<InputText v-model="searchText" placeholder="поиск"></InputText>
</IconField>
<div class="flex flex-row gap-2">
<!-- <span v-for="user in users">{{user.id}}</span>-->
<button v-for="user in selectedSpace.users" @click="switchUserFilter(user)"
class="rounded-xl border p-1 bg-white border-gray-300 mb-2 min-w-fit px-2"
:class="selectedUserId == user.id ? '!bg-blue-100' : ''">
<p><span class="text-sm font-bold">{{ user.firstName }}</span></p>
</button>
<!-- <button v-for="type in types" 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">{{ type.name }}</span></p>-->
<!-- </button>-->
</div>
<div class="flex flex-col gap-2">
<BudgetTransactionView
v-for="transaction in filteredTransactions"
:key="transaction.id"
:transaction="transaction"
:is-list="true"
@transaction-updated="fetchTransactions(true)"
@delete-transaction="fetchTransactions(true)"
/>
<div v-if="!loading" class="flex items-center justify-center px-2 py-1 mb-5">
<Button @click="fetchTransactions(false)">Загрузить следующие...</Button>
</div>
<!-- Показать спиннер загрузки, если идет загрузка -->
<ProgressSpinner v-if="loading" class="mb-4" style="width: 50px; height: 50px;"
strokeWidth="8"
fill="transparent"
animationDuration=".5s"/>
</div>
</div>
</div>
</template>
<style scoped>
</style>