Private
Public Access
1
0

Add edit, delete, and deactivate actions to categories view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-13 18:20:42 -05:00
parent b38865050c
commit 0b4fa68236
@@ -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>