|
| 1 | +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; |
| 2 | + |
| 3 | +import com.carvalhotechsolutions.mundoanimal.model.Agendamento; |
| 4 | +import com.carvalhotechsolutions.mundoanimal.model.Cliente; |
| 5 | +import com.carvalhotechsolutions.mundoanimal.repositories.AgendamentoRepository; |
| 6 | +import javafx.application.Platform; |
| 7 | +import javafx.beans.property.IntegerProperty; |
| 8 | +import javafx.beans.property.SimpleIntegerProperty; |
| 9 | +import javafx.beans.property.SimpleStringProperty; |
| 10 | +import javafx.collections.FXCollections; |
| 11 | +import javafx.collections.ObservableList; |
| 12 | +import javafx.collections.transformation.FilteredList; |
| 13 | +import javafx.collections.transformation.SortedList; |
| 14 | +import javafx.event.ActionEvent; |
| 15 | +import javafx.fxml.FXML; |
| 16 | +import javafx.fxml.Initializable; |
| 17 | +import javafx.scene.control.*; |
| 18 | +import javafx.scene.control.cell.PropertyValueFactory; |
| 19 | +import javafx.scene.layout.VBox; |
| 20 | + |
| 21 | +import java.net.URL; |
| 22 | +import java.time.LocalDate; |
| 23 | +import java.util.List; |
| 24 | +import java.util.ResourceBundle; |
| 25 | + |
| 26 | +public class HistoricoController implements Initializable { |
| 27 | + private static final int ROW_HEIGHT = 79; // Altura de cada linha em pixels |
| 28 | + |
| 29 | + private static final int HEADER_HEIGHT = 79; // Altura do header em pixels |
| 30 | + |
| 31 | + private IntegerProperty itemsPerPage = new SimpleIntegerProperty(); |
| 32 | + |
| 33 | + @FXML |
| 34 | + private TableColumn<Agendamento, String> dataColumn, donoColumn, petColumn, responsavelColumn, tipoColumn; |
| 35 | + |
| 36 | + @FXML |
| 37 | + private TextField filterField; |
| 38 | + |
| 39 | + @FXML |
| 40 | + private Label numberOfResults; |
| 41 | + |
| 42 | + @FXML |
| 43 | + private TableView<Agendamento> tableView; |
| 44 | + |
| 45 | + @FXML |
| 46 | + private Pagination paginator; |
| 47 | + |
| 48 | + @FXML |
| 49 | + private Button btnFiltro; |
| 50 | + |
| 51 | + @FXML |
| 52 | + private DatePicker dataFinalPicker; |
| 53 | + |
| 54 | + @FXML |
| 55 | + private DatePicker dataInicialPicker; |
| 56 | + |
| 57 | + |
| 58 | + private final AgendamentoRepository agendamentoRepository = new AgendamentoRepository(); |
| 59 | + private ObservableList<Agendamento> agendamentosList = FXCollections.observableArrayList(); |
| 60 | + private FilteredList<Agendamento> filteredData; |
| 61 | + private SortedList<Agendamento> sortedData; |
| 62 | + |
| 63 | + @Override |
| 64 | + public void initialize(URL url, ResourceBundle resourceBundle) { |
| 65 | + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); |
| 66 | + filteredData = new FilteredList<>(agendamentosList, p -> true); // Inicializa antes de atualizar a tabela |
| 67 | + sortedData = new SortedList<>(filteredData); |
| 68 | + sortedData.comparatorProperty().bind(tableView.comparatorProperty()); |
| 69 | + |
| 70 | + // Adicionar listener para mudanças na altura da tabela |
| 71 | + tableView.heightProperty().addListener((obs, oldHeight, newHeight) -> { |
| 72 | + calcularItensPorPagina(newHeight.doubleValue()); |
| 73 | + tableView.refresh(); // Força a atualização da TableView |
| 74 | + // Reconfigura a paginação quando a altura muda |
| 75 | + Platform.runLater(this::configurarPaginacao); |
| 76 | + }); |
| 77 | + |
| 78 | + // Calcula inicial de itens por página |
| 79 | + calcularItensPorPagina(tableView.getHeight()); |
| 80 | + |
| 81 | + // Adiciona listener para o campo de busca |
| 82 | + filterField.focusedProperty().addListener((observable, oldValue, newValue) -> { |
| 83 | + if (newValue) { // Quando o campo recebe foco |
| 84 | + limparFiltroData(); |
| 85 | + } |
| 86 | + }); |
| 87 | + |
| 88 | + configurarColunas(); |
| 89 | + configurarBuscaAgendamentos(); |
| 90 | + atualizarTableView(); |
| 91 | + } |
| 92 | + |
| 93 | + private void calcularItensPorPagina(double alturaTotal) { |
| 94 | + // Subtrai a altura do header da altura total |
| 95 | + double alturaDisponivel = alturaTotal - HEADER_HEIGHT; |
| 96 | + // Calcula quantas linhas cabem na altura disponível |
| 97 | + int numeroLinhas = Math.max(1, (int) Math.floor(alturaDisponivel / ROW_HEIGHT)); |
| 98 | + // Atualiza a propriedade de itens por página |
| 99 | + itemsPerPage.set(numeroLinhas); |
| 100 | + } |
| 101 | + |
| 102 | + private void configurarPaginacao() { |
| 103 | + if (filteredData.isEmpty()) { |
| 104 | + paginator.setPageCount(1); |
| 105 | + paginator.setCurrentPageIndex(0); |
| 106 | + paginator.setDisable(true); |
| 107 | + tableView.setItems(FXCollections.observableArrayList()); |
| 108 | + return; |
| 109 | + } |
| 110 | + |
| 111 | + // Usa o valor dinâmico de itens por página |
| 112 | + int totalPages = (int) Math.ceil((double) filteredData.size() / itemsPerPage.get()); |
| 113 | + paginator.setPageCount(totalPages); |
| 114 | + |
| 115 | + // Se a página atual é maior que o novo número total de páginas, |
| 116 | + // ajusta para a última página válida |
| 117 | + if (paginator.getCurrentPageIndex() >= totalPages) { |
| 118 | + paginator.setCurrentPageIndex(totalPages - 1); |
| 119 | + } |
| 120 | + |
| 121 | + paginator.setDisable(false); |
| 122 | + paginator.currentPageIndexProperty().addListener((obs, oldIndex, newIndex) -> { |
| 123 | + Platform.runLater(() -> { |
| 124 | + atualizarPaginaAtual(newIndex.intValue()); |
| 125 | + tableView.refresh(); // Força a atualização da TableView |
| 126 | + }); |
| 127 | + }); |
| 128 | + |
| 129 | + // Atualiza a visualização inicial |
| 130 | + atualizarPaginaAtual(paginator.getCurrentPageIndex()); |
| 131 | + } |
| 132 | + |
| 133 | + private void atualizarPaginaAtual(int pageIndex) { |
| 134 | + int startIndex = pageIndex * itemsPerPage.get(); |
| 135 | + int endIndex = Math.min(startIndex + itemsPerPage.get(), filteredData.size()); |
| 136 | + |
| 137 | + // Cria uma nova lista com os itens da página atual |
| 138 | + ObservableList<Agendamento> pageItems = FXCollections.observableArrayList( |
| 139 | + filteredData.subList(startIndex, endIndex) |
| 140 | + ); |
| 141 | + |
| 142 | + tableView.setItems(pageItems); |
| 143 | + tableView.refresh(); |
| 144 | + } |
| 145 | + |
| 146 | + public void atualizarTableView() { |
| 147 | + |
| 148 | + List<Agendamento> novosAgendamentos = agendamentoRepository.findStatusFinalizado(); |
| 149 | + agendamentosList.setAll(novosAgendamentos); // Apenas atualiza os dados existentes |
| 150 | + |
| 151 | + numberOfResults.setText(agendamentosList.size() + " registro(s) retornado(s)"); |
| 152 | + atualizarPaginacao(); |
| 153 | + } |
| 154 | + |
| 155 | + private void configurarColunas() { |
| 156 | + tipoColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getServico().getNomeServico())); |
| 157 | + dataColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getDataHoraFormatada())); |
| 158 | + petColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getAnimal().getNome())); |
| 159 | + responsavelColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getResponsavelAtendimento())); |
| 160 | + donoColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getCliente().getNome())); |
| 161 | + } |
| 162 | + |
| 163 | + private void configurarBuscaAgendamentos() { |
| 164 | + filterField.textProperty().addListener((observable, oldValue, newValue) -> { |
| 165 | + filteredData.setPredicate(agendamento -> { |
| 166 | + if (newValue == null || newValue.isEmpty()) { |
| 167 | + return true; |
| 168 | + } |
| 169 | + |
| 170 | + String lowerCaseFilter = newValue.toLowerCase(); |
| 171 | + |
| 172 | + boolean matchesCliente = agendamento.getCliente().getNome().toLowerCase().contains(lowerCaseFilter); |
| 173 | + boolean matchesAnimal = agendamento.getAnimal().getNome().toLowerCase().contains(lowerCaseFilter); |
| 174 | + boolean matchesResponsavel = agendamento.getResponsavelAtendimento() != null && |
| 175 | + agendamento.getResponsavelAtendimento().toLowerCase().contains(lowerCaseFilter); |
| 176 | + boolean matchesServico = agendamento.getServico().getNomeServico().toLowerCase().contains(lowerCaseFilter); |
| 177 | + |
| 178 | + return matchesCliente || matchesAnimal || matchesResponsavel || matchesServico; |
| 179 | + }); |
| 180 | + |
| 181 | + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); |
| 182 | + atualizarPaginacao(); |
| 183 | + }); |
| 184 | + } |
| 185 | + |
| 186 | + private void atualizarPaginacao() { |
| 187 | + int totalPages = (int) Math.ceil((double) filteredData.size() / itemsPerPage.get()); |
| 188 | + paginator.setPageCount(Math.max(totalPages, 1)); |
| 189 | + |
| 190 | + paginator.setPageFactory(pageIndex -> { |
| 191 | + atualizarPagina(pageIndex); |
| 192 | + return new VBox(); // Retorna um nó vazio |
| 193 | + }); |
| 194 | + |
| 195 | + atualizarPagina(0); |
| 196 | + } |
| 197 | + |
| 198 | + private void atualizarPagina(int pageIndex) { |
| 199 | + int fromIndex = pageIndex * itemsPerPage.get(); |
| 200 | + int toIndex = Math.min(fromIndex + itemsPerPage.get(), filteredData.size()); |
| 201 | + |
| 202 | + if (fromIndex <= toIndex) { |
| 203 | + tableView.setItems(FXCollections.observableArrayList(sortedData.subList(fromIndex, toIndex))); |
| 204 | + } |
| 205 | + } |
| 206 | + |
| 207 | + @FXML |
| 208 | + private void aplicarFiltroData() { |
| 209 | + LocalDate dataInicial = dataInicialPicker.getValue(); |
| 210 | + LocalDate dataFinal = dataFinalPicker.getValue(); |
| 211 | + |
| 212 | + if (dataInicial != null && dataFinal != null) { |
| 213 | + filteredData.setPredicate(agendamento -> { |
| 214 | + LocalDate dataAgendamento = agendamento.getDataAgendamento(); |
| 215 | + return !dataAgendamento.isBefore(dataInicial) && !dataAgendamento.isAfter(dataFinal); |
| 216 | + }); |
| 217 | + } else { |
| 218 | + resetarFiltros(); |
| 219 | + } |
| 220 | + |
| 221 | + atualizarResultados(); |
| 222 | + } |
| 223 | + |
| 224 | + @FXML |
| 225 | + private void limparFiltroData() { |
| 226 | + // Limpa os campos de data |
| 227 | + dataInicialPicker.setValue(null); |
| 228 | + dataFinalPicker.setValue(null); |
| 229 | + |
| 230 | + // Reseta os filtros e atualiza a tabela |
| 231 | + resetarFiltros(); |
| 232 | + atualizarResultados(); |
| 233 | + } |
| 234 | + |
| 235 | + // Método auxiliar para resetar os filtros |
| 236 | + private void resetarFiltros() { |
| 237 | + filteredData.setPredicate(agendamento -> true); |
| 238 | + } |
| 239 | + |
| 240 | + // Método auxiliar para atualizar contagem e paginação |
| 241 | + private void atualizarResultados() { |
| 242 | + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); |
| 243 | + atualizarPaginacao(); |
| 244 | + } |
| 245 | +} |
0 commit comments