Files
luminic-front/src/components/settings/recurrent/CreateRecurrentModal.vue
Vladimir Voronin c5257376a3 init
2024-10-24 17:35:29 +03:00

234 lines
9.1 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.
<template>
<Dialog :visible="show" modal :header="isEditing ? 'Edit Recurrent Payment' : 'Create Recurrent Payment'"
:closable="true" class="!w-1/3">
<div v-if="loading">
Loading...
</div>
<div v-else class="p-fluid flex flex-col gap-6 w-full py-6 items-start">
<!-- Название -->
<FloatLabel class="w-full">
<label for="paymentName">Payment Name</label>
<InputText v-model="name" id="paymentName" class="!w-full"/>
</FloatLabel>
<!-- Категория -->
<div class="relative w-full justify-center justify-items-center ">
<div class="flex flex-col justify-items-center gap-2">
<SelectButton v-model="selectedCategoryType" :options="categoryTypes" optionLabel="name"
aria-labelledby="basic"
@change="categoryTypeChanged(selectedCategoryType.code)" class="justify-center"/>
<button class="border border-gray-300 rounded-lg w-full z-50"
@click="isCategorySelectorOpened = !isCategorySelectorOpened">
<div class="flex flex-row items-center pe-4 py-2 gap-4">
<div class="flex flex-row justify-between w-full px-4">
<p class="text-6xl font-bold text-gray-700 dark:text-gray-400">{{ selectedCategory.icon }}</p>
<div class="flex flex-col items-start justify-items-start justify-around w-full">
<p class="font-bold text-start">{{ selectedCategory.name }}</p>
<p class="font-light line-clamp-1 items-start text-start">{{ selectedCategory.description }}</p>
</div>
</div>
<div>
<span :class="{'rotate-90': isCategorySelectorOpened}"
class="pi pi-angle-right transition-transform duration-300 text-5xl"/>
</div>
</div>
</button>
</div>
<!-- Анимированное открытие списка категорий -->
<div v-show="isCategorySelectorOpened"
class="absolute left-0 right-0 top-full overflow-hidden z-50 border-b-4 border-x rounded-b-lg bg-white shadow-lg transition-all duration-500"
:class="{ 'max-h-0': !isCategorySelectorOpened, 'max-h-[500px]': isCategorySelectorOpened }">
<div class="grid grid-cols-2 mt-2">
<button v-for="category in selectedCategoryType.code == 'EXPENSE' ? expenseCategories : incomeCategories"
:key="category.id" class="border rounded-lg mx-2 mb-2"
@click="selectCategory(category)">
<div class="flex flex-row justify-between w-full px-2">
<p class="text-4xl font-bold text-gray-700 dark:text-gray-400">{{ category.icon }}</p>
<div class="flex flex-col items-start justify-items-start justify-around w-full">
<p class="font-bold text-start">{{ category.name }}</p>
<p class="font-light line-clamp-1 text-start">{{ category.description }}</p>
</div>
</div>
</button>
</div>
</div>
</div>
<!-- Описание -->
<FloatLabel class="w-full">
<label for="description">Description</label>
<Textarea v-model="description" id="description" rows="3"/>
</FloatLabel>
<!-- Дата повторения (выпадающий список) -->
<div class="w-full relative">
<button class="border border-gray-300 rounded-lg w-full z-50"
@click="isDaySelectorOpened = !isDaySelectorOpened">
<div class="flex flex-row items-center pe-4 py-2 gap-4">
<div class="flex flex-row justify-between w-full px-4">
<p class="font-bold">Повторять каждый {{ repeatDay || 'N' }} день месяца</p>
</div>
<div>
<span :class="{'rotate-90': isDaySelectorOpened}"
class="pi pi-angle-right transition-transform duration-300 text-5xl"/>
</div>
</div>
</button>
<!-- Анимированное открытие списка дней -->
<div v-show="isDaySelectorOpened"
class="absolute left-0 right-0 top-full overflow-hidden z-50 border-b-4 border-x rounded-b-lg bg-white shadow-lg transition-all duration-500"
:class="{ 'max-h-0': !isDaySelectorOpened, 'max-h-[500px]': isDaySelectorOpened }">
<div class="grid grid-cols-7 p-2">
<button v-for="day in days" :key="day" class=" border"
@click="selectDay(day)">
{{ day }}
</button>
</div>
</div>
</div>
<!-- Сумма -->
<InputGroup class="w-full">
<InputGroupAddon></InputGroupAddon>
<InputNumber v-model="amount" placeholder="Amount"/>
<InputGroupAddon>.00</InputGroupAddon>
</InputGroup>
<!-- Кнопки -->
<div class="flex justify-content-end gap-2 mt-4">
<Button label="Save" icon="pi pi-check" @click="savePayment" class="p-button-success"/>
<Button label="Cancel" icon="pi pi-times" @click="closeModal" class="p-button-secondary"/>
</div>
</div>
</Dialog>
</template>
<script setup lang="ts">
import {ref, watch, computed, defineEmits} from 'vue';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import InputNumber from 'primevue/inputnumber';
import Textarea from "primevue/textarea";
import Dialog from 'primevue/dialog';
import FloatLabel from 'primevue/floatlabel';
import InputGroup from 'primevue/inputgroup';
import InputGroupAddon from "primevue/inputgroupaddon";
import SelectButton from 'primevue/selectbutton';
import {saveRecurrentPayment} from "@/services/recurrentService";
const props = defineProps({
show: Boolean, // Показать/скрыть модальное окно
expenseCategories: Array, // Внешние данные для списка категорий
incomeCategories: Array, // Внешние данные для списка категорий
categoryTypes: Array,
payment: Object | null // Для редактирования существующего платежа
})
const emits = defineEmits(["open-edit", "save-payment", "close-modal"]);
const loading = ref(false)
// Поля для формы
const name = ref('');
const selectedCategoryType = ref(props.payment ? props.payment.type : props.categoryTypes[0]);
const selectedCategory = ref(selectedCategoryType.code == 'EXPESE' ? props.expenseCategories[0] : props.incomeCategories[0]);
const categoryTypeChanged = (code) => {
selectedCategory.value = code == "EXPENSE" ? props.expenseCategories[0] : props.incomeCategories[0];
}
const description = ref('');
const repeatDay = ref<number | null>(null);
const amount = ref<number | null>(null);
// Открытие/закрытие списка категорий
const isCategorySelectorOpened = ref(false);
const isDaySelectorOpened = ref(false);
// Список дней (131)
const days = Array.from({length: 31}, (_, i) => i + 1);
// Выбор дня
const selectDay = (day: number) => {
repeatDay.value = day;
isDaySelectorOpened.value = false;
};
// Выбор категории
const selectCategory = (category) => {
isCategorySelectorOpened.value = false;
selectedCategory.value = category;
};
// Определение, редактируем ли мы существующий платеж
const isEditing = computed(() => !!props.payment);
// Слушаем изменения, если редактируем существующий платеж
watch(() => props.payment, (newPayment) => {
if (newPayment) {
name.value = newPayment.name;
selectedCategory.value = newPayment.category;
description.value = newPayment.description;
repeatDay.value = newPayment.repeatDay;
amount.value = newPayment.amount;
} else {
resetForm();
}
});
// Функция для сохранения платежа
const savePayment = async () => {
loading.value = true;
const paymentData = {
name: name.value,
category: selectedCategory.value,
description: description.value,
atDay: repeatDay.value,
amount: amount.value
};
if (isEditing.value && props.payment) {
paymentData.id = props.payment.id; // Если редактируем, сохраняем ID
}
try {
await saveRecurrentPayment(paymentData)
loading.value = false
resetForm();
} catch (error) {
console.error('Error saving payment:', error);
}
emits('save-payment', paymentData);
resetForm();
closeModal();
};
// Закрытие окна и сброс формы
const closeModal = () => {
emits('close-modal');
resetForm();
};
const resetForm = () => {
name.value = '';
selectedCategory.value = props.expenseCategories[0];
description.value = '';
repeatDay.value = null;
amount.value = null;
};
</script>
<style scoped>
/* Плавная анимация поворота */
.rotate-90 {
transform: rotate(90deg);
}
/* Для анимации открытия высоты */
.overflow-hidden {
overflow: hidden;
}
</style>