diff --git a/backend/app/Http/Controllers/Api/CostCenterController.php b/backend/app/Http/Controllers/Api/CostCenterController.php index 8944d2b..48b0a4d 100755 --- a/backend/app/Http/Controllers/Api/CostCenterController.php +++ b/backend/app/Http/Controllers/Api/CostCenterController.php @@ -209,6 +209,49 @@ public function destroy(int $id): JsonResponse ]); } + /** + * Atualizar palavras-chave de um centro de custo (incluindo de sistema) + */ + public function updateKeywords(Request $request, int $id): JsonResponse + { + $costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id); + + $validated = $request->validate([ + 'keywords' => 'required|array', + 'keywords.*' => 'string|max:100', + ]); + + DB::beginTransaction(); + try { + // Remover antigas + $costCenter->keywords()->delete(); + + // Adicionar novas + foreach ($validated['keywords'] as $keyword) { + $costCenter->keywords()->create([ + 'keyword' => trim($keyword), + 'is_case_sensitive' => false, + 'is_active' => true, + ]); + } + + DB::commit(); + + return response()->json([ + 'success' => true, + 'message' => 'Palavras-chave atualizadas com sucesso', + 'data' => $costCenter->fresh()->load('keywords'), + ]); + + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'success' => false, + 'message' => 'Erro ao atualizar palavras-chave: ' . $e->getMessage(), + ], 500); + } + } + /** * Adicionar palavra-chave a um centro de custo */ diff --git a/backend/routes/api.php b/backend/routes/api.php index f67cc48..21871df 100755 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -94,6 +94,7 @@ // ============================================ Route::apiResource('cost-centers', CostCenterController::class); Route::post('cost-centers/{id}/keywords', [CostCenterController::class, 'addKeyword']); + Route::put('cost-centers/{id}/keywords', [CostCenterController::class, 'updateKeywords']); Route::delete('cost-centers/{id}/keywords/{keywordId}', [CostCenterController::class, 'removeKeyword']); Route::post('cost-centers/match', [CostCenterController::class, 'matchByText']); diff --git a/frontend/src/pages/CostCenters.jsx b/frontend/src/pages/CostCenters.jsx index 034ec82..33327ca 100755 --- a/frontend/src/pages/CostCenters.jsx +++ b/frontend/src/pages/CostCenters.jsx @@ -12,9 +12,12 @@ const CostCenters = () => { const [loading, setLoading] = useState(true); const [showModal, setShowModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showKeywordsModal, setShowKeywordsModal] = useState(false); const [selectedItem, setSelectedItem] = useState(null); const [saving, setSaving] = useState(false); const [newKeyword, setNewKeyword] = useState(''); + const [systemKeywords, setSystemKeywords] = useState([]); + const [newSystemKeyword, setNewSystemKeyword] = useState(''); const [openDropdownId, setOpenDropdownId] = useState(null); const dropdownRef = useRef(null); @@ -153,6 +156,58 @@ const CostCenters = () => { } }; + // === Funções para gerenciar keywords de centro de custo de sistema === + const handleOpenKeywordsModal = (item) => { + setSelectedItem(item); + setSystemKeywords(item.keywords?.map(k => k.keyword) || []); + setNewSystemKeyword(''); + setShowKeywordsModal(true); + }; + + const handleCloseKeywordsModal = () => { + setShowKeywordsModal(false); + setSelectedItem(null); + setSystemKeywords([]); + setNewSystemKeyword(''); + }; + + const handleAddSystemKeyword = () => { + const keyword = newSystemKeyword.trim(); + if (keyword && !systemKeywords.includes(keyword)) { + setSystemKeywords(prev => [...prev, keyword]); + setNewSystemKeyword(''); + } + }; + + const handleRemoveSystemKeyword = (keyword) => { + setSystemKeywords(prev => prev.filter(k => k !== keyword)); + }; + + const handleSystemKeywordKeyPress = (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleAddSystemKeyword(); + } + }; + + const handleSaveSystemKeywords = async () => { + if (!selectedItem) return; + + setSaving(true); + try { + const response = await costCenterService.updateKeywords(selectedItem.id, systemKeywords); + if (response.success) { + toast.success(t('costCenters.keywordsUpdated') || 'Palavras-chave atualizadas com sucesso'); + handleCloseKeywordsModal(); + loadCostCenters(); + } + } catch (error) { + toast.error(error.response?.data?.message || t('costCenters.keywordsError') || 'Erro ao atualizar palavras-chave'); + } finally { + setSaving(false); + } + }; + const handleDeleteClick = (item) => { setSelectedItem(item); setShowDeleteModal(true); @@ -267,7 +322,16 @@ const CostCenters = () => { )} - {!item.is_system && ( + {item.is_system ? ( + + ) : (
+
+ +
+

+ + {t('costCenters.keywordsHelp') || 'Palavras-chave são usadas para atribuir automaticamente transações a este centro de custo.'} +

+ +
+
+ setNewSystemKeyword(e.target.value)} + onKeyPress={handleSystemKeywordKeyPress} + placeholder={t('costCenters.keywordPlaceholder') || 'Digite e pressione Enter...'} + /> + +
+ +
+ {systemKeywords.map((keyword, index) => ( + + {keyword} + + + ))} + {systemKeywords.length === 0 && ( + + + {t('costCenters.noKeywords') || 'Nenhuma palavra-chave definida'} + + )} +
+
+
+ +
+ + +
+ + + + )} ); }; diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index ca3c347..57d5476 100755 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -212,6 +212,12 @@ export const costCenterService = { return response.data; }, + // Atualizar todas as palavras-chave (inclui centros de custo de sistema) + updateKeywords: async (id, keywords) => { + const response = await api.put(`/cost-centers/${id}/keywords`, { keywords }); + return response.data; + }, + // Remover palavra-chave removeKeyword: async (id, keywordId) => { const response = await api.delete(`/cost-centers/${id}/keywords/${keywordId}`);