Merge pull request 'update category' (#37) from feature/change-categories into master
Some checks failed
Build and Publish Images / build-and-push (push) Failing after 30s

Reviewed-on: #37
This commit was merged in pull request #37.
This commit is contained in:
2026-05-16 16:30:57 +03:00
3 changed files with 97 additions and 9 deletions

View File

@@ -63,6 +63,8 @@
"addCategory": "Add category", "addCategory": "Add category",
"deleteConfirm": "Delete category?", "deleteConfirm": "Delete category?",
"resetConfirm": "Delete all expenses for this category?", "resetConfirm": "Delete all expenses for this category?",
"editTitle": "Category settings",
"editError": "Error updating category",
"createError": "Error creating category", "createError": "Error creating category",
"deleteError": "Error deleting category", "deleteError": "Error deleting category",
"resetError": "Error resetting expenses" "resetError": "Error resetting expenses"

View File

@@ -63,6 +63,8 @@
"addCategory": "Добавить категорию", "addCategory": "Добавить категорию",
"deleteConfirm": "Удалить категорию?", "deleteConfirm": "Удалить категорию?",
"resetConfirm": "Удалить все траты по этой категории?", "resetConfirm": "Удалить все траты по этой категории?",
"editTitle": "Настройки категории",
"editError": "Ошибка обновления категории",
"createError": "Ошибка создания категории", "createError": "Ошибка создания категории",
"deleteError": "Ошибка удаления категории", "deleteError": "Ошибка удаления категории",
"resetError": "Ошибка сброса трат" "resetError": "Ошибка сброса трат"

View File

@@ -23,6 +23,7 @@ import {
Copy, Copy,
Check, Check,
User, User,
Settings,
} from 'lucide-react'; } from 'lucide-react';
import ShoppingListModal from '../components/ShoppingListModal'; import ShoppingListModal from '../components/ShoppingListModal';
@@ -45,6 +46,10 @@ export default function FamilyView() {
const [expenseAmount, setExpenseAmount] = useState(''); const [expenseAmount, setExpenseAmount] = useState('');
const [expenseDescription, setExpenseDescription] = useState(''); const [expenseDescription, setExpenseDescription] = useState('');
const [showEditCategory, setShowEditCategory] = useState<number | null>(null);
const [editCategoryName, setEditCategoryName] = useState('');
const [editCategoryLimit, setEditCategoryLimit] = useState('');
const [showHistory, setShowHistory] = useState<number | null>(null); const [showHistory, setShowHistory] = useState<number | null>(null);
const [showArchive, setShowArchive] = useState<number | null>(null); const [showArchive, setShowArchive] = useState<number | null>(null);
const [historyData, setHistoryData] = useState<ExpenseHistoryResponse | null>(null); const [historyData, setHistoryData] = useState<ExpenseHistoryResponse | null>(null);
@@ -181,6 +186,29 @@ export default function FamilyView() {
} }
}; };
const handleOpenEditCategory = (category: Category) => {
setEditCategoryName(category.name);
setEditCategoryLimit(parseFloat(category.limit_amount.toString()).toString());
setShowEditCategory(category.id);
setShowAddExpense(null);
};
const handleUpdateCategory = async (categoryId: number) => {
if (!familyId || !editCategoryName || !editCategoryLimit) return;
try {
await categoryApi.update(parseInt(familyId), categoryId, {
name: editCategoryName,
limit_amount: parseFloat(editCategoryLimit),
});
setShowEditCategory(null);
loadCategories();
} catch (err: any) {
const errorMsg = err.response?.data?.message || err.message || t('category.editError');
alert(`${t('category.editError')}: ${errorMsg}`);
}
};
const handleShowHistory = async (categoryId: number) => { const handleShowHistory = async (categoryId: number) => {
if (!familyId) return; if (!familyId) return;
@@ -396,15 +424,24 @@ export default function FamilyView() {
</h2> </h2>
</div> </div>
{showAddExpense !== category.id && ( {showAddExpense !== category.id && showEditCategory !== category.id && (
<button <div className="flex items-center gap-2">
onClick={() => setShowAddExpense(category.id)} <button
className="flex items-center gap-2 px-4 py-2 btn-danger text-white rounded-xl hover:shadow-lg transition-all duration-300 font-semibold whitespace-nowrap text-sm" onClick={() => handleOpenEditCategory(category)}
> className="p-2 bg-gray-200 hover:bg-gray-300 text-gray-600 rounded-xl transition-all duration-300"
<TrendingDown className="w-4 h-4" /> title={t('category.editTitle')}
<span className="hidden sm:inline">{t('category.addExpense')}</span> >
<span className="sm:hidden">{t('category.expense')}</span> <Settings className="w-4 h-4" />
</button> </button>
<button
onClick={() => setShowAddExpense(category.id)}
className="flex items-center gap-2 px-4 py-2 btn-danger text-white rounded-xl hover:shadow-lg transition-all duration-300 font-semibold whitespace-nowrap text-sm"
>
<TrendingDown className="w-4 h-4" />
<span className="hidden sm:inline">{t('category.addExpense')}</span>
<span className="sm:hidden">{t('category.expense')}</span>
</button>
</div>
)} )}
</div> </div>
@@ -511,6 +548,53 @@ export default function FamilyView() {
</div> </div>
)} )}
{showEditCategory === category.id && (
<div className="glass-effect p-6 rounded-2xl border-2 border-gray-200 mt-4">
<h3 className="font-semibold text-gray-800 mb-4 text-center">
{t('category.editTitle')}
</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('category.categoryName')}
</label>
<input
type="text"
value={editCategoryName}
onChange={(e) => setEditCategoryName(e.target.value)}
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all font-medium"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('category.categoryLimit')}
</label>
<input
type="number"
value={editCategoryLimit}
onChange={(e) => setEditCategoryLimit(e.target.value)}
className="w-full px-4 py-3 border-2 border-gray-300 rounded-2xl focus:border-purple-500 focus:ring-2 focus:ring-purple-200 transition-all text-center font-semibold text-lg"
/>
</div>
<div className="flex gap-3">
<button
onClick={() => handleUpdateCategory(category.id)}
className="flex-1 flex items-center justify-center gap-2 px-5 py-3 btn-success text-white rounded-2xl hover:shadow-xl transition-all font-semibold"
>
<Check className="w-5 h-5" />
{t('common.save')}
</button>
<button
onClick={() => setShowEditCategory(null)}
className="px-5 py-3 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-2xl transition-all font-medium"
>
<X className="w-5 h-5" />
</button>
</div>
</div>
</div>
)}
{showHistory === category.id && historyData && ( {showHistory === category.id && historyData && (
<div className="mt-4 glass-effect p-4 rounded-2xl border-2 border-blue-200"> <div className="mt-4 glass-effect p-4 rounded-2xl border-2 border-blue-200">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">