106 lines
3.0 KiB
Vue
106 lines
3.0 KiB
Vue
<script setup lang="ts">
|
|
import { getSelectedProfile } from '@/api/profile'
|
|
import {
|
|
TransactionApiClient,
|
|
type TransactionCategory,
|
|
type TransactionCategoryTree,
|
|
} from '@/api/transaction'
|
|
import AppButton from '@/components/common/AppButton.vue'
|
|
import AppPage from '@/components/common/AppPage.vue'
|
|
import ButtonBar from '@/components/common/ButtonBar.vue'
|
|
import CategoryDisplayItem from '@/components/CategoryDisplayItem.vue'
|
|
import EditCategoryModal from '@/components/EditCategoryModal.vue'
|
|
import { showConfirm } from '@/util/alert'
|
|
import { hideLoader, showLoader } from '@/util/loader'
|
|
import { nextTick, onMounted, ref, useTemplateRef, type Ref } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
const route = useRoute()
|
|
|
|
const editCategoryModal = useTemplateRef('editCategoryModal')
|
|
|
|
const categories: Ref<TransactionCategoryTree[]> = ref([])
|
|
const editedCategory: Ref<TransactionCategory | undefined> = ref(undefined)
|
|
|
|
onMounted(async () => {
|
|
await loadCategories()
|
|
})
|
|
|
|
async function loadCategories() {
|
|
const api = new TransactionApiClient(getSelectedProfile(route))
|
|
categories.value = await api.getCategories()
|
|
}
|
|
|
|
async function editCategory(categoryId: number) {
|
|
try {
|
|
const api = new TransactionApiClient(getSelectedProfile(route))
|
|
editedCategory.value = await api.getCategory(categoryId)
|
|
await nextTick()
|
|
const result = await editCategoryModal.value?.show()
|
|
if (result === 'saved') {
|
|
await loadCategories()
|
|
}
|
|
} catch (err) {
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
async function deleteCategory(categoryId: number) {
|
|
const result = await showConfirm(
|
|
'Are you sure you want to delete this category? It will be removed from all transactions. All sub-categories will also be removed.',
|
|
)
|
|
if (result) {
|
|
try {
|
|
showLoader()
|
|
const api = new TransactionApiClient(getSelectedProfile(route))
|
|
await api.deleteCategory(categoryId)
|
|
await loadCategories()
|
|
} catch (err) {
|
|
console.error(err)
|
|
} finally {
|
|
hideLoader()
|
|
}
|
|
}
|
|
}
|
|
|
|
async function addCategory() {
|
|
editedCategory.value = undefined
|
|
await nextTick()
|
|
const result = await editCategoryModal.value?.show()
|
|
if (result === 'saved') {
|
|
await loadCategories()
|
|
}
|
|
}
|
|
</script>
|
|
<template>
|
|
<AppPage title="Categories">
|
|
<p>
|
|
Categories are used to group related transactions for your own organization, as well as
|
|
analytics. Categories are structured hierarchically, where each category could have zero or
|
|
more sub-categories.
|
|
</p>
|
|
<div>
|
|
<CategoryDisplayItem
|
|
v-for="category in categories"
|
|
:key="category.id"
|
|
:category="category"
|
|
:editable="true"
|
|
@edited="editCategory"
|
|
@deleted="deleteCategory"
|
|
/>
|
|
</div>
|
|
<ButtonBar>
|
|
<AppButton
|
|
icon="plus"
|
|
@click="addCategory()"
|
|
>Add Category</AppButton
|
|
>
|
|
</ButtonBar>
|
|
|
|
<EditCategoryModal
|
|
ref="editCategoryModal"
|
|
:category="editedCategory"
|
|
/>
|
|
</AppPage>
|
|
</template>
|