From e85dbe6f104ffe119130f4808f4bdf5985396250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Mon, 10 Aug 2026 11:15:15 -0300 Subject: [PATCH] Add category edit action --- backend/routes/catalogRoutes.js | 11 ++++++- backend/services/catalogService.js | 30 +++++++++++++++++- src/dataService.ts | 15 +++++++++ src/pages/Registrations.tsx | 49 ++++++++++++++++++++++++------ 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/backend/routes/catalogRoutes.js b/backend/routes/catalogRoutes.js index 4c1a62c..b06e7c2 100644 --- a/backend/routes/catalogRoutes.js +++ b/backend/routes/catalogRoutes.js @@ -10,7 +10,8 @@ const { getCatalogSummary, listCategories, listConsumptionReferences, - listProducts + listProducts, + updateCategory } = require('../services/catalogService'); const { importTinyProductCompositionExport } = require('../services/productionOrderService'); @@ -40,6 +41,14 @@ router.post('/catalog/categories', verifyToken, async (req, res, next) => { } }); +router.put('/catalog/categories/:id', verifyToken, async (req, res, next) => { + try { + res.json(await updateCategory(req.params.id, req.body || {})); + } catch (error) { + next(error); + } +}); + router.delete('/catalog/categories/:id', verifyToken, async (req, res, next) => { try { await deleteCategory(req.params.id); diff --git a/backend/services/catalogService.js b/backend/services/catalogService.js index 65d5e83..d206475 100644 --- a/backend/services/catalogService.js +++ b/backend/services/catalogService.js @@ -110,6 +110,33 @@ const createCategory = async ({ name, description }) => { return mapCategory(result.rows[0]); }; +const updateCategory = async (id, { name, description }) => { + const normalizedId = Number(id); + const normalizedName = normalizeText(name); + if (!normalizedId || !normalizedName) { + const error = new Error('Nome da categoria é obrigatório.'); + error.statusCode = 400; + throw error; + } + + const result = await pool.query(` + UPDATE catalog_categories + SET name = $1, + description = $2, + updated_at = CURRENT_TIMESTAMP + WHERE id = $3 + RETURNING id, name, description, created_at, updated_at + `, [normalizedName, normalizeText(description) || null, normalizedId]); + + if (!result.rows.length) { + const error = new Error('Categoria não encontrada.'); + error.statusCode = 404; + throw error; + } + + return mapCategory(result.rows[0]); +}; + const deleteCategory = async (id) => { await pool.query('DELETE FROM catalog_categories WHERE id = $1', [id]); }; @@ -272,5 +299,6 @@ module.exports = { getCatalogSummary, listCategories, listConsumptionReferences, - listProducts + listProducts, + updateCategory }; diff --git a/src/dataService.ts b/src/dataService.ts index fbf4fc7..a4218a2 100644 --- a/src/dataService.ts +++ b/src/dataService.ts @@ -291,6 +291,21 @@ export const saveCatalogCategory = async (payload: CatalogCategoryPayload): Prom return data as CatalogCategory; }; +export const updateCatalogCategory = async (id: number, payload: CatalogCategoryPayload): Promise => { + const response = await authFetch(`/catalog/categories/${id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + const data = await response.json().catch(() => null); + if (!response.ok) { + throw new Error(data?.error || 'Não foi possível atualizar a categoria.'); + } + + return data as CatalogCategory; +}; + export const deleteCatalogCategory = async (id: number): Promise => { const response = await authFetch(`/catalog/categories/${id}`, { method: 'DELETE' }); if (!response.ok) { diff --git a/src/pages/Registrations.tsx b/src/pages/Registrations.tsx index cdfe58b..0fba964 100644 --- a/src/pages/Registrations.tsx +++ b/src/pages/Registrations.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; -import { ClipboardList, Loader2, Package, RefreshCw, Ruler, Save, Tags, Trash2, Upload } from 'lucide-react'; +import { ClipboardList, Loader2, Package, Pencil, RefreshCw, Ruler, Save, Tags, Trash2, Upload } from 'lucide-react'; import { deleteCatalogCategory, deleteCatalogProduct, @@ -9,7 +9,8 @@ import { importProductCompositions, saveCatalogCategory, saveCatalogProduct, - saveConsumptionReference + saveConsumptionReference, + updateCatalogCategory } from '../dataService'; import PaginationControls from '../components/PaginationControls'; import type { CatalogCategory, CatalogProduct, CatalogProductType, CatalogSummary, ConsumptionReference } from '../types'; @@ -124,6 +125,7 @@ const Registrations = () => { const compositionFileInputRef = useRef(null); const [categoryForm, setCategoryForm] = useState({ name: '', description: '' }); + const [editingCategory, setEditingCategory] = useState(null); const [productForm, setProductForm] = useState(defaultProductForm); const [referenceForm, setReferenceForm] = useState({ productId: '', @@ -291,9 +293,14 @@ const Registrations = () => { }; const saveCategory = () => runAction(async () => { - await saveCatalogCategory(categoryForm); + if (editingCategory) { + await updateCatalogCategory(editingCategory.id, categoryForm); + } else { + await saveCatalogCategory(categoryForm); + } setCategoryForm({ name: '', description: '' }); - }, 'Categoria salva.'); + setEditingCategory(null); + }, editingCategory ? 'Categoria atualizada.' : 'Categoria salva.'); const saveProduct = () => runAction(async () => { await saveCatalogProduct({ @@ -386,6 +393,18 @@ const Registrations = () => { `Categoria ${category.name} excluida.` ); + const editCategory = (category: CatalogCategory) => { + setEditingCategory(category); + setCategoryForm({ name: category.name, description: category.description || '' }); + setStatus('idle'); + setFeedback(''); + }; + + const cancelCategoryEdit = () => { + setEditingCategory(null); + setCategoryForm({ name: '', description: '' }); + }; + const removeProduct = (product: CatalogProduct) => runAction( () => deleteCatalogProduct(product.id), `Produto ${product.sku} excluido.` @@ -713,9 +732,14 @@ const Registrations = () => {

{category.name}

{category.description || 'Sem descricao'}

- +
+ + +
))} {!catalog.categories.length &&
Nenhuma categoria cadastrada.
} @@ -736,7 +760,14 @@ const Registrations = () => {
-

Nova categoria

+
+

{editingCategory ? 'Editar categoria' : 'Nova categoria'}

+ {editingCategory && ( + + )} +