Add edit, delete, and deactivate actions to categories view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<div class="d-flex align-center mb-4">
|
||||
<h1 class="text-h4">Categories</h1>
|
||||
<v-spacer />
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="showDialog = true">Add Category</v-btn>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="openCreateDialog">Add Category</v-btn>
|
||||
</div>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
@@ -11,22 +11,41 @@
|
||||
<template #prepend="{ item }">
|
||||
<v-chip :color="item.type === 'Income' ? 'success' : item.type === 'Transfer' ? 'info' : 'error'" size="x-small">{{ item.type }}</v-chip>
|
||||
</template>
|
||||
<template #append="{ item }">
|
||||
<v-btn icon="mdi-pencil" size="x-small" variant="text" @click.stop="openEditDialog(item)" />
|
||||
<v-btn v-if="!item.isSystem" icon="mdi-delete" size="x-small" variant="text" color="error" @click.stop="openDeleteDialog(item)" />
|
||||
</template>
|
||||
</v-treeview>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-dialog v-model="showDialog" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title>Add Category</v-card-title>
|
||||
<v-card-title>{{ editingCategory ? 'Edit Category' : 'Add Category' }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-text-field v-model="form.name" label="Category Name" />
|
||||
<v-select v-model="form.type" label="Type" :items="['Income','Expense','Transfer']" />
|
||||
<v-select v-model="form.type" label="Type" :items="['Income','Expense','Transfer']" :disabled="!!editingCategory" />
|
||||
<v-select v-model="form.parentId" label="Parent Category" :items="parentOptions" item-title="name" item-value="id" clearable />
|
||||
<v-switch v-if="editingCategory" v-model="form.isActive" label="Active" color="primary" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showDialog = false">Cancel</v-btn>
|
||||
<v-btn color="primary" @click="saveCategory">Create</v-btn>
|
||||
<v-btn color="primary" @click="saveCategory">{{ editingCategory ? 'Save' : 'Create' }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="showDeleteDialog" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title>Delete Category</v-card-title>
|
||||
<v-card-text>
|
||||
Are you sure you want to delete <strong>{{ deleteTarget?.name }}</strong>? This action cannot be undone.
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showDeleteDialog = false">Cancel</v-btn>
|
||||
<v-btn color="error" @click="confirmDelete">Delete</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@@ -41,22 +60,64 @@ import type { CategoryTree, Category } from '@/types'
|
||||
const categoryTree = ref<CategoryTree[]>([])
|
||||
const allCategories = ref<Category[]>([])
|
||||
const showDialog = ref(false)
|
||||
const form = ref({ name: '', type: 'Expense', parentId: null as string | null })
|
||||
const editingCategory = ref<Category | null>(null)
|
||||
const showDeleteDialog = ref(false)
|
||||
const deleteTarget = ref<CategoryTree | null>(null)
|
||||
const form = ref({ name: '', type: 'Expense', parentId: null as string | null, isActive: true })
|
||||
|
||||
const parentOptions = computed(() => allCategories.value.filter(c => !c.parentId))
|
||||
|
||||
onMounted(async () => {
|
||||
const [tree, all] = await Promise.all([categoriesApi.getTree(), categoriesApi.getAll()])
|
||||
categoryTree.value = tree.data
|
||||
allCategories.value = all.data
|
||||
await reloadCategories()
|
||||
})
|
||||
|
||||
async function saveCategory() {
|
||||
await categoriesApi.create(form.value)
|
||||
async function reloadCategories() {
|
||||
const [tree, all] = await Promise.all([categoriesApi.getTree(), categoriesApi.getAll()])
|
||||
categoryTree.value = tree.data
|
||||
allCategories.value = all.data
|
||||
}
|
||||
|
||||
function openCreateDialog() {
|
||||
editingCategory.value = null
|
||||
form.value = { name: '', type: 'Expense', parentId: null, isActive: true }
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
function openEditDialog(item: CategoryTree) {
|
||||
const cat = allCategories.value.find(c => c.id === item.id)
|
||||
if (!cat) return
|
||||
editingCategory.value = cat
|
||||
form.value = { name: cat.name, type: cat.type, parentId: cat.parentId, isActive: cat.isActive }
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
function openDeleteDialog(item: CategoryTree) {
|
||||
deleteTarget.value = item
|
||||
showDeleteDialog.value = true
|
||||
}
|
||||
|
||||
async function saveCategory() {
|
||||
if (editingCategory.value) {
|
||||
await categoriesApi.update(editingCategory.value.id, {
|
||||
name: form.value.name,
|
||||
parentId: form.value.parentId,
|
||||
sortOrder: editingCategory.value.sortOrder,
|
||||
isActive: form.value.isActive,
|
||||
})
|
||||
} else {
|
||||
await categoriesApi.create(form.value)
|
||||
}
|
||||
await reloadCategories()
|
||||
showDialog.value = false
|
||||
form.value = { name: '', type: 'Expense', parentId: null }
|
||||
editingCategory.value = null
|
||||
form.value = { name: '', type: 'Expense', parentId: null, isActive: true }
|
||||
}
|
||||
|
||||
async function confirmDelete() {
|
||||
if (!deleteTarget.value) return
|
||||
await categoriesApi.delete(deleteTarget.value.id)
|
||||
await reloadCategories()
|
||||
showDeleteDialog.value = false
|
||||
deleteTarget.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user