mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-12 18:46:40 +07:00
feat: allow deleting default categories once they have no transactions
Раньше стандартные категории нельзя было удалить вообще. Теперь можно — если у категории нет ни одной обычной транзакции, ни строки сплита с ней. Пользовательские категории удаляются как и раньше, без проверок. - delete_category() проверяет Transaction.category_id и TransactionSplit.category_id для стандартных категорий перед удалением. - Кнопка удаления в списке категорий больше не блокируется заранее для стандартных — бэкенд решает, бейдж «стандартная» остался чисто информационным. - При отказе (400) показывается понятное сообщение вместо тихого игнорирования клика.
This commit is contained in:
@@ -49,10 +49,8 @@ function CategoryRow({ category, indented, onEdit, onDelete }: RowProps) {
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("common.delete")}
|
||||
title={category.is_default ? t("category.defaultCannotDelete") : undefined}
|
||||
disabled={category.is_default}
|
||||
onClick={() => onDelete(category)}
|
||||
className="rounded-md p-1.5 text-text-muted hover:bg-surface-2 hover:text-danger disabled:cursor-not-allowed disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-text-muted"
|
||||
className="rounded-md p-1.5 text-text-muted hover:bg-surface-2 hover:text-danger"
|
||||
>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
|
||||
@@ -428,7 +428,7 @@ const ru = {
|
||||
"category.incomeSectionTitle": "Категории доходов",
|
||||
"category.empty": "Категорий пока нет.",
|
||||
"category.defaultBadge": "стандартная",
|
||||
"category.defaultCannotDelete": "Стандартные категории нельзя удалить",
|
||||
"category.defaultDeleteBlocked": "Стандартную категорию можно удалить только после того, как у неё не останется транзакций.",
|
||||
"category.confirmDelete": "Удалить категорию «{{name}}»?\n\nЕё транзакции останутся без категории.",
|
||||
"category.kind.expense": "Расход",
|
||||
"category.kind.income": "Доход",
|
||||
@@ -910,7 +910,7 @@ const en: Record<keyof typeof ru, string> = {
|
||||
"category.incomeSectionTitle": "Income categories",
|
||||
"category.empty": "No categories yet.",
|
||||
"category.defaultBadge": "default",
|
||||
"category.defaultCannotDelete": "Default categories cannot be deleted",
|
||||
"category.defaultDeleteBlocked": "A default category can only be deleted once it has no transactions left.",
|
||||
"category.confirmDelete": "Delete the category “{{name}}”?\n\nIts transactions will be left uncategorized.",
|
||||
"category.kind.expense": "Expense",
|
||||
"category.kind.income": "Income",
|
||||
|
||||
@@ -38,12 +38,15 @@ export function CategoriesPage() {
|
||||
setModalOpen(true);
|
||||
}
|
||||
|
||||
function handleDelete(category: Category) {
|
||||
// Default categories are protected server-side too (400) — the list
|
||||
// already disables the delete button for them, this is just a guard.
|
||||
if (category.is_default) return;
|
||||
if (window.confirm(t("category.confirmDelete", { name: category.name }))) {
|
||||
deleteCategory.mutate(category.id);
|
||||
async function handleDelete(category: Category) {
|
||||
if (!window.confirm(t("category.confirmDelete", { name: category.name }))) return;
|
||||
try {
|
||||
await deleteCategory.mutateAsync(category.id);
|
||||
} catch {
|
||||
// The only way a delete 400s is a default category that still has
|
||||
// transactions pointing at it (api/routes/categories.py) — a custom
|
||||
// category has no such guard and always succeeds.
|
||||
window.alert(t("category.defaultDeleteBlocked"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user