From cb2ac6e2de80da66370941465dad939f4dcfcf1a Mon Sep 17 00:00:00 2001 From: Diogo Date: Fri, 22 May 2026 11:41:11 -0300 Subject: [PATCH] feat: add new fields and auto priority selection for ocorrences --- .../EC6/Convive/Config/DataInitializer.java | 48 +++++++++---- .../Controller/OcorrenciaController.java | 10 ++- .../Convive/Event/OcorrenciaCriadaEvent.java | 2 + .../Convive/Model/CategoriaOcorrencia.java | 25 +++++++ .../com/EC6/Convive/Model/Ocorrencia.java | 7 ++ .../Repository/OcorrenciaRepository.java | 2 + .../Service/EmailRenderingService.java | 6 ++ .../Convive/Service/OcorrenciaService.java | 11 +++ .../main/resources/static/js/ocorrencias.js | 6 ++ .../resources/static/js/triagemOcorrencias.js | 13 ++++ .../moderador/triagemOcorrencias.html | 23 ++++++- .../templates/morador/ocorrencias.html | 44 +++++++++--- .../NotificationEmailListenerTest.java | 8 ++- .../Service/EmailRenderingServiceTest.java | 8 ++- .../Service/OcorrenciaServiceTest.java | 68 ++++++++++++++++++- 15 files changed, 251 insertions(+), 30 deletions(-) create mode 100644 Convive/src/main/java/com/EC6/Convive/Model/CategoriaOcorrencia.java diff --git a/Convive/src/main/java/com/EC6/Convive/Config/DataInitializer.java b/Convive/src/main/java/com/EC6/Convive/Config/DataInitializer.java index fbef378..53f4c82 100644 --- a/Convive/src/main/java/com/EC6/Convive/Config/DataInitializer.java +++ b/Convive/src/main/java/com/EC6/Convive/Config/DataInitializer.java @@ -113,54 +113,76 @@ private void seedOcorrencias() { LocalDateTime agora = LocalDateTime.now(); salvarOcorrencia(moradores.get(0), + "Vazamento na garagem", + CategoriaOcorrencia.INFRAESTRUTURA, "Vazamento no teto da garagem próximo à vaga 12.", - Prioridade.ALTA, StatusOcorrencia.REGISTRADA, agora.minusHours(3)); + StatusOcorrencia.REGISTRADA, agora.minusHours(3)); salvarOcorrencia(moradores.get(1), + "Lâmpada queimada no corredor", + CategoriaOcorrencia.OUTRO, "Lâmpada queimada no corredor do 2º andar.", - Prioridade.NAO_DEFINIDA, StatusOcorrencia.REGISTRADA, agora.minusDays(1)); + StatusOcorrencia.REGISTRADA, agora.minusDays(1)); salvarOcorrencia(moradores.get(2), + "Barulho excessivo após 23h", + CategoriaOcorrencia.BARULHO, "Barulho excessivo após 23h no apartamento 302.", - Prioridade.MEDIA, StatusOcorrencia.EM_ANALISE, agora.minusDays(2)); + StatusOcorrencia.EM_ANALISE, agora.minusDays(2)); salvarOcorrencia(moradores.get(3), + "Portão da garagem com falha", + CategoriaOcorrencia.INFRAESTRUTURA, "Portão da garagem com falha intermitente.", - Prioridade.ALTA, StatusOcorrencia.EM_ANALISE, agora.minusDays(4)); + StatusOcorrencia.EM_ANALISE, agora.minusDays(4)); salvarOcorrencia(moradores.get(4), + "Animal solto nas áreas comuns", + CategoriaOcorrencia.REGRAS, "Animal solto nas áreas comuns sem coleira.", - Prioridade.BAIXA, StatusOcorrencia.REGISTRADA, agora.minusDays(6)); + StatusOcorrencia.REGISTRADA, agora.minusDays(6)); salvarOcorrencia(moradores.get(0), + "Uso indevido da churrasqueira", + CategoriaOcorrencia.REGRAS, "Uso indevido da churrasqueira fora do horário permitido.", - Prioridade.MEDIA, StatusOcorrencia.RESOLVIDA, agora.minusDays(10)); + StatusOcorrencia.RESOLVIDA, agora.minusDays(10)); salvarOcorrencia(moradores.get(1), + "Encomenda sem identificação", + CategoriaOcorrencia.REGRAS, "Entrega de encomenda sem identificação na portaria.", - Prioridade.BAIXA, StatusOcorrencia.RESOLVIDA, agora.minusDays(15)); + StatusOcorrencia.RESOLVIDA, agora.minusDays(15)); salvarOcorrencia(moradores.get(5), + "Limpeza da piscina", + CategoriaOcorrencia.LIMPEZA, "Reclamação sobre limpeza da piscina.", - Prioridade.NAO_DEFINIDA, StatusOcorrencia.REGISTRADA, agora.minusDays(20)); + StatusOcorrencia.REGISTRADA, agora.minusDays(20)); salvarOcorrencia(moradores.get(2), + "Revisão de multa por ruído", + CategoriaOcorrencia.BARULHO, "Solicitação de revisão de multa por ruído.", - Prioridade.ALTA, StatusOcorrencia.REJEITADA, agora.minusDays(35)); + StatusOcorrencia.REJEITADA, agora.minusDays(35)); salvarOcorrencia(moradores.get(3), + "Infiltração na unidade 408", + CategoriaOcorrencia.INFRAESTRUTURA, "Infiltração na parede da unidade 408.", - Prioridade.MEDIA, StatusOcorrencia.RESOLVIDA, agora.minusDays(40)); + StatusOcorrencia.RESOLVIDA, agora.minusDays(40)); System.out.println("10 ocorrências de teste criadas."); } - private void salvarOcorrencia(Morador morador, String descricao, Prioridade prioridade, - StatusOcorrencia status, LocalDateTime dataRegistro) { + private void salvarOcorrencia(Morador morador, String titulo, CategoriaOcorrencia categoria, + String descricao, StatusOcorrencia status, LocalDateTime dataRegistro) { Ocorrencia ocorrencia = new Ocorrencia(); ocorrencia.setUsuario(morador); + ocorrencia.setTitulo(titulo); + ocorrencia.setCategoria(categoria); ocorrencia.setDescricao(descricao); - ocorrencia.setPrioridade(prioridade); + ocorrencia.setPrioridade(categoria.getPrioridadePadrao()); ocorrencia.setStatus(status); ocorrencia.setDataRegistro(dataRegistro); ocorrencia.setProtocolo(gerarProtocolo()); diff --git a/Convive/src/main/java/com/EC6/Convive/Controller/OcorrenciaController.java b/Convive/src/main/java/com/EC6/Convive/Controller/OcorrenciaController.java index 1329594..e918f1f 100644 --- a/Convive/src/main/java/com/EC6/Convive/Controller/OcorrenciaController.java +++ b/Convive/src/main/java/com/EC6/Convive/Controller/OcorrenciaController.java @@ -70,15 +70,23 @@ public String carregarMais( @PostMapping public String registrarNovaOcorrencia( @AuthenticationPrincipal CustomUserDetails userDetails, + @RequestParam String titulo, + @RequestParam CategoriaOcorrencia categoria, @RequestParam String descricao, @RequestParam(value = "imagemEvidencia", required = false) MultipartFile imagemEvidencia, RedirectAttributes redirectAttributes) { try { + if (titulo == null || titulo.isBlank()) { + redirectAttributes.addFlashAttribute("erroOcorrencia", "O título é obrigatório."); + return "redirect:/morador/ocorrencias"; + } + Usuario usuarioLogado = userDetails.getUsuario(); Ocorrencia novaOcorrencia = new Ocorrencia(); + novaOcorrencia.setTitulo(titulo.trim()); + novaOcorrencia.setCategoria(categoria); novaOcorrencia.setDescricao(descricao); - novaOcorrencia.setPrioridade(Prioridade.NAO_DEFINIDA); novaOcorrencia.setStatus(StatusOcorrencia.REGISTRADA); novaOcorrencia.setUsuario(usuarioLogado); diff --git a/Convive/src/main/java/com/EC6/Convive/Event/OcorrenciaCriadaEvent.java b/Convive/src/main/java/com/EC6/Convive/Event/OcorrenciaCriadaEvent.java index cd3a8ac..9620548 100644 --- a/Convive/src/main/java/com/EC6/Convive/Event/OcorrenciaCriadaEvent.java +++ b/Convive/src/main/java/com/EC6/Convive/Event/OcorrenciaCriadaEvent.java @@ -5,6 +5,8 @@ public record OcorrenciaCriadaEvent( LocalDateTime dataRegistro, String nomeMorador, + String titulo, + String categoria, String descricao ) { } diff --git a/Convive/src/main/java/com/EC6/Convive/Model/CategoriaOcorrencia.java b/Convive/src/main/java/com/EC6/Convive/Model/CategoriaOcorrencia.java new file mode 100644 index 0000000..12f1a63 --- /dev/null +++ b/Convive/src/main/java/com/EC6/Convive/Model/CategoriaOcorrencia.java @@ -0,0 +1,25 @@ +package com.EC6.Convive.Model; + +public enum CategoriaOcorrencia { + BARULHO("Barulho", Prioridade.ALTA), + INFRAESTRUTURA("Infraestrutura", Prioridade.ALTA), + LIMPEZA("Limpeza", Prioridade.MEDIA), + REGRAS("Regras", Prioridade.MEDIA), + OUTRO("Outro", Prioridade.NAO_DEFINIDA); + + private final String rotulo; + private final Prioridade prioridadePadrao; + + CategoriaOcorrencia(String rotulo, Prioridade prioridadePadrao) { + this.rotulo = rotulo; + this.prioridadePadrao = prioridadePadrao; + } + + public String getRotulo() { + return rotulo; + } + + public Prioridade getPrioridadePadrao() { + return prioridadePadrao; + } +} diff --git a/Convive/src/main/java/com/EC6/Convive/Model/Ocorrencia.java b/Convive/src/main/java/com/EC6/Convive/Model/Ocorrencia.java index 9f1465f..23c1332 100644 --- a/Convive/src/main/java/com/EC6/Convive/Model/Ocorrencia.java +++ b/Convive/src/main/java/com/EC6/Convive/Model/Ocorrencia.java @@ -19,6 +19,13 @@ public class Ocorrencia { @GeneratedValue(strategy = GenerationType.UUID) private UUID id; + @Column(name = "titulo", length = 200, nullable = false) + private String titulo; + + @Enumerated(EnumType.STRING) + @Column(name = "categoria", nullable = false) + private CategoriaOcorrencia categoria; + @Column(name = "descricao", length = 10000) private String descricao; diff --git a/Convive/src/main/java/com/EC6/Convive/Repository/OcorrenciaRepository.java b/Convive/src/main/java/com/EC6/Convive/Repository/OcorrenciaRepository.java index 325ab7b..c268b56 100644 --- a/Convive/src/main/java/com/EC6/Convive/Repository/OcorrenciaRepository.java +++ b/Convive/src/main/java/com/EC6/Convive/Repository/OcorrenciaRepository.java @@ -26,6 +26,7 @@ public interface OcorrenciaRepository extends JpaRepository { SELECT o FROM Ocorrencia o WHERE o.usuario.id = :moradorId AND (:busca IS NULL OR LOWER(o.protocolo) LIKE LOWER(CONCAT('%', :busca, '%')) + OR LOWER(o.titulo) LIKE LOWER(CONCAT('%', :busca, '%')) OR LOWER(o.descricao) LIKE LOWER(CONCAT('%', :busca, '%'))) AND (:status IS NULL OR o.status = :status) AND (:prioridade IS NULL OR o.prioridade = :prioridade) @@ -40,6 +41,7 @@ Page findByUsuarioTriagem(@Param("moradorId") UUID moradorId, @Query(""" SELECT o FROM Ocorrencia o WHERE (:busca IS NULL OR LOWER(o.protocolo) LIKE LOWER(CONCAT('%', :busca, '%')) + OR LOWER(o.titulo) LIKE LOWER(CONCAT('%', :busca, '%')) OR LOWER(o.descricao) LIKE LOWER(CONCAT('%', :busca, '%'))) AND (:status IS NULL OR o.status = :status) AND (:prioridade IS NULL OR o.prioridade = :prioridade) diff --git a/Convive/src/main/java/com/EC6/Convive/Service/EmailRenderingService.java b/Convive/src/main/java/com/EC6/Convive/Service/EmailRenderingService.java index 4755992..e31442e 100644 --- a/Convive/src/main/java/com/EC6/Convive/Service/EmailRenderingService.java +++ b/Convive/src/main/java/com/EC6/Convive/Service/EmailRenderingService.java @@ -94,6 +94,8 @@ public RenderedEmail renderOcorrenciaNotification(String recipientName, Ocorrenc context.setVariable("detailRows", List.of( new EmailDetailRow("Data do registro", EmailDateFormatter.format(event.dataRegistro())), new EmailDetailRow("Registrado por", event.nomeMorador()), + new EmailDetailRow("Título", event.titulo()), + new EmailDetailRow("Categoria", event.categoria()), new EmailDetailRow("Descrição", event.descricao()) )); @@ -105,6 +107,8 @@ public RenderedEmail renderOcorrenciaNotification(String recipientName, Ocorrenc Data do registro: %s Registrado por: %s + Título: %s + Categoria: %s Descrição: %s Acesse: %s @@ -115,6 +119,8 @@ public RenderedEmail renderOcorrenciaNotification(String recipientName, Ocorrenc recipientName, EmailDateFormatter.format(event.dataRegistro()), event.nomeMorador(), + event.titulo(), + event.categoria(), event.descricao(), actionUrl ); diff --git a/Convive/src/main/java/com/EC6/Convive/Service/OcorrenciaService.java b/Convive/src/main/java/com/EC6/Convive/Service/OcorrenciaService.java index c17b245..cf06c44 100644 --- a/Convive/src/main/java/com/EC6/Convive/Service/OcorrenciaService.java +++ b/Convive/src/main/java/com/EC6/Convive/Service/OcorrenciaService.java @@ -26,7 +26,16 @@ public class OcorrenciaService { private final OcorrenciaRepository ocorrenciaRepository; private final ApplicationEventPublisher eventPublisher; + public void aplicarPrioridadePadrao(Ocorrencia ocorrencia) { + if (ocorrencia.getCategoria() == null) { + ocorrencia.setPrioridade(Prioridade.NAO_DEFINIDA); + return; + } + ocorrencia.setPrioridade(ocorrencia.getCategoria().getPrioridadePadrao()); + } + public Ocorrencia insert(Ocorrencia ocorrencia) { + aplicarPrioridadePadrao(ocorrencia); ocorrencia.setDataRegistro(LocalDateTime.now()); String anoAtual = String.valueOf(Year.now().getValue()); @@ -50,6 +59,8 @@ public Ocorrencia insert(Ocorrencia ocorrencia) { eventPublisher.publishEvent(new OcorrenciaCriadaEvent( saved.getDataRegistro(), saved.getUsuario().getNome(), + saved.getTitulo(), + saved.getCategoria().getRotulo(), saved.getDescricao() )); diff --git a/Convive/src/main/resources/static/js/ocorrencias.js b/Convive/src/main/resources/static/js/ocorrencias.js index 9df7956..54015bc 100644 --- a/Convive/src/main/resources/static/js/ocorrencias.js +++ b/Convive/src/main/resources/static/js/ocorrencias.js @@ -4,6 +4,8 @@ function abrirConsulta(elemento) { const id = elemento.getAttribute('data-id'); const protocolo = elemento.getAttribute('data-protocolo'); const data = elemento.getAttribute('data-data'); + const titulo = elemento.getAttribute('data-titulo'); + const categoria = elemento.getAttribute('data-categoria'); const prioridade = elemento.getAttribute('data-prioridade'); const status = elemento.getAttribute('data-status').replace('_', ' '); const descricao = elemento.getAttribute('data-descricao'); @@ -11,6 +13,8 @@ function abrirConsulta(elemento) { const campoProtocolo = document.getElementById('detalhe-protocolo'); const campoData = document.getElementById('detalhe-data'); + const campoTitulo = document.getElementById('detalhe-titulo-ocorrencia'); + const campoCategoria = document.getElementById('detalhe-categoria'); const campoPrioridade = document.getElementById('detalhe-prioridade'); const campoStatus = document.getElementById('detalhe-status'); const campoDescricao = document.getElementById('detalhe-descricao'); @@ -20,6 +24,8 @@ function abrirConsulta(elemento) { if (campoProtocolo) campoProtocolo.innerText = '#' + protocolo; if (campoData) campoData.innerText = data; + if (campoTitulo) campoTitulo.innerText = titulo || ''; + if (campoCategoria) campoCategoria.innerText = categoria || ''; if (campoPrioridade) campoPrioridade.innerText = prioridade; if (campoStatus) campoStatus.innerText = status; if (campoDescricao) campoDescricao.innerText = descricao; diff --git a/Convive/src/main/resources/static/js/triagemOcorrencias.js b/Convive/src/main/resources/static/js/triagemOcorrencias.js index f8e3d60..ed71b1f 100644 --- a/Convive/src/main/resources/static/js/triagemOcorrencias.js +++ b/Convive/src/main/resources/static/js/triagemOcorrencias.js @@ -48,6 +48,9 @@ function aplicarEvidencia(urlEvidencia) { function preencherDetalhes(card) { var id = card.getAttribute('data-id'); var protocolo = card.getAttribute('data-protocolo'); + var titulo = card.getAttribute('data-titulo'); + var categoriaRotulo = card.getAttribute('data-categoria-rotulo'); + var categoria = card.getAttribute('data-categoria'); var prioridade = card.getAttribute('data-prioridade'); var status = card.getAttribute('data-status'); var descricao = card.getAttribute('data-descricao'); @@ -60,11 +63,21 @@ function preencherDetalhes(card) { setDetalheField('detalhe-protocolo', 'innerText', protocolo, function (p) { return '#' + p; }); setDetalheField('detalhe-morador', 'innerText', morador); setDetalheField('detalhe-data', 'innerText', data); + setDetalheField('detalhe-titulo', 'innerText', titulo); + setDetalheField('detalhe-categoria', 'innerText', categoriaRotulo); setDetalheField('detalhe-descricao', 'innerText', descricao); setDetalheField('input-resposta', 'value', resposta); setDetalheField('select-prioridade', 'value', prioridade); setDetalheField('select-status', 'value', status); + var mostrarAviso = categoria === 'OUTRO' && prioridade === 'NAO_DEFINIDA'; + ['detalhe-aviso-prioridade', 'detalhe-aviso-prioridade-mobile'].forEach(function (id) { + var aviso = document.getElementById(id); + if (!aviso) return; + if (mostrarAviso) aviso.classList.remove('hidden'); + else aviso.classList.add('hidden'); + }); + aplicarBadgePrioridade(prioridade); aplicarEvidencia(urlEvidencia); } diff --git a/Convive/src/main/resources/templates/moderador/triagemOcorrencias.html b/Convive/src/main/resources/templates/moderador/triagemOcorrencias.html index 540567d..801a164 100644 --- a/Convive/src/main/resources/templates/moderador/triagemOcorrencias.html +++ b/Convive/src/main/resources/templates/moderador/triagemOcorrencias.html @@ -40,7 +40,7 @@

Triagem de Ocorrências

search - +
Prioridade: @@ -92,6 +92,9 @@

Fila de Análise

th:data-protocolo="${ocorrencia.protocolo}" th:data-prioridade="${ocorrencia.prioridade}" th:data-status="${ocorrencia.status}" + th:data-titulo="${ocorrencia.titulo}" + th:data-categoria="${ocorrencia.categoria.name()}" + th:data-categoria-rotulo="${ocorrencia.categoria.rotulo}" th:data-descricao="${ocorrencia.descricao}" th:data-resposta="${ocorrencia.respostaModerador ?: ''}" th:data-data-sort="${#temporals.format(ocorrencia.dataRegistro, 'yyyyMMddHHmmss')}" @@ -108,6 +111,11 @@

Fila de Análise

Data
+
+ Categoria +
+

Título...

Descrição...

@@ -166,6 +174,13 @@

Detalhes da Ocorrência

+
+

Título e Categoria

+

+

+ +
+

Descrição do Problema

@@ -241,6 +256,12 @@
+
+

Título e Categoria

+

+

+ +

Descrição do Problema

diff --git a/Convive/src/main/resources/templates/morador/ocorrencias.html b/Convive/src/main/resources/templates/morador/ocorrencias.html index c1eec12..e87f1c2 100644 --- a/Convive/src/main/resources/templates/morador/ocorrencias.html +++ b/Convive/src/main/resources/templates/morador/ocorrencias.html @@ -52,7 +52,7 @@

Minhas Ocorrências

@@ -90,7 +90,7 @@

Ocorrências Recentes< @@ -115,6 +115,8 @@

Ocorrências Recentes< th:data-data-sort="${#temporals.format(ocorrencia.dataRegistro, 'yyyyMMddHHmmss')}" th:data-prioridade="${ocorrencia.prioridade}" th:data-status="${ocorrencia.status}" + th:data-titulo="${ocorrencia.titulo}" + th:data-categoria="${ocorrencia.categoria.rotulo}" th:data-descricao="${ocorrencia.descricao}" th:data-comentario="${ocorrencia.respostaModerador}" th:data-evidencia="${ocorrencia.urlEvidencia != null ? ocorrencia.urlEvidencia : ''}"> @@ -124,8 +126,9 @@

Ocorrências Recentes<
- Descrição - Descrição do problema + Título + Título + Categoria
@@ -180,6 +183,21 @@