diff --git a/README.md b/README.md index e0ea843..2864584 100644 --- a/README.md +++ b/README.md @@ -1 +1,55 @@ -# MundoAnimal +# 🐾 Mundo Animal - Sistema de Gestão para Petshop + +Mundo Animal é um sistema desktop desenvolvido em **JavaFX** com **PostgreSQL** e **Hibernate (JPA)**, projetado para a gestão eficiente de um petshop. O sistema permite o gerenciamento de clientes, animais, agendamentos, serviços e secretários. + +## 🚀 Tecnologias Utilizadas +- **Java 23** +- **JavaFX** (para interface gráfica) +- **PostgreSQL** (banco de dados relacional) +- **Hibernate/JPA** (persistência de dados) +- **Maven** (gerenciador de dependências) +- **BCrypt** (para criptografar senhas) + +## 📌Funcionalidades Principais +- **Autenticação:** Login seguro para administradores e secretários. +- **Gerenciamento de Agendamentos:** Exibição dinâmica dos próximos atendimentos na tela inicial. +- **Cadastro de Clientes e Animais:** Relacionamento entre clientes e seus respectivos pets. +- **Controle de Serviços:** Registros de atendimento e relatório de operações. +- **Interface intuitiva:** Com animações suaves e pop-ups de confirmação. + +## 🛠️ Instalação e Execução +### Pré-requisitos +- Java 21+ instalado +- PostgreSQL instalado +- Maven instalado + +## 📂 Estrutura do Projeto +``` +MundoAnimal/ +│── src/ +│ ├── main/ +│ │ ├── java/com/carvalhotechsolutions/mundoanimal/ +│ │ │ ├── controllers/ +│ │ │ ├── models/ +│ │ │ ├── repository/ +│ │ │ ├── services/ +│ │ ├── resources/ +│ │ │ ├── fxml/ +│ │ │ ├── images/ +│ │ │ ├── styles/ +│── pom.xml +│── README.md +``` + +## 🤝 Contribuição +Se deseja contribuir, siga os passos: +1. Fork este repositório +2. Crie uma branch (`git checkout -b feature/nova-funcionalidade`) +3. Commit suas alterações (`git commit -m 'Adiciona nova funcionalidade'`) +4. Envie um push para a branch (`git push origin feature/nova-funcionalidade`) +5. Abra um Pull Request + +## 📜 Licença +Este projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes. + +--- diff --git a/pom.xml b/pom.xml index 5a30722..62a4c67 100644 --- a/pom.xml +++ b/pom.xml @@ -93,10 +93,46 @@ fontawesomefx 8.2 + + + io.github.typhon0 + AnimateFX + 1.3.0 + + + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + + + org.mockito + mockito-core + 5.5.0 + test + + + + + com.h2database + h2 + 2.3.232 + test + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + org.apache.maven.plugins maven-jar-plugin diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/Main.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/Main.java index 00aedf6..1513553 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/Main.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/Main.java @@ -1,48 +1,29 @@ package com.carvalhotechsolutions.mundoanimal; +import animatefx.animation.FadeIn; +import com.carvalhotechsolutions.mundoanimal.database.DatabaseChecker; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManager; import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.Alert; import javafx.stage.Stage; -import java.io.IOException; - public class Main extends Application { @Override - public void start(Stage stage) throws IOException { - - boolean isConnected = DatabaseChecker.testConnectionAndInitializeAdmin(); - if (!isConnected) { - Alert alert = new Alert(Alert.AlertType.ERROR); - alert.setTitle("Erro de Conexão"); - alert.setHeaderText("Não foi possível conectar ao banco de dados."); - alert.setContentText("Verifique as configurações do banco e tente novamente."); - alert.showAndWait(); - Platform.exit(); // Encerra a aplicação - return; - } - - // Carrega o arquivo FXML da interface principal - FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/autenticacao/login.fxml")); - Parent root = fxmlLoader.load(); - - // Define a cena com o layout carregado - Scene scene = new Scene(root); - // Aplica o CSS à cena - String css = this.getClass().getResource("/css/style.css").toExternalForm(); - scene.getStylesheets().add(css); - - // Configura o palco principal - stage.setTitle("Login"); - stage.setScene(scene); - stage.setFullScreen(true); - stage.setFullScreenExitHint(""); - stage.setResizable(false); - // Exibe a interface + public void start(Stage stage) { + + // Verifica conexão com o banco de dados + DatabaseChecker.testConnectionAndInitializeAdmin(); + // Inicializa o SceneManager + ScreenManager sceneManager = new ScreenManager(stage); + // Inicializa o SceneManagerHolder + ScreenManagerHolder.initialize(sceneManager); + // Seleciona a primeira tela da aplicação + sceneManager.switchTo(ScreenEnum.LOGIN); + // Exibe a tela stage.show(); + // Aplicando FadeIn (AnimateFX) + new FadeIn(sceneManager.getScreen(ScreenEnum.LOGIN)).play(); } public static void main(String[] args) { diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/LoginController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/LoginController.java similarity index 66% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/LoginController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/LoginController.java index db50883..41e5c45 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/LoginController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/LoginController.java @@ -1,18 +1,17 @@ -package com.carvalhotechsolutions.mundoanimal.controllers.login; +package com.carvalhotechsolutions.mundoanimal.controllers.autenticacao; -import com.carvalhotechsolutions.mundoanimal.JPAutil; +import animatefx.animation.FadeIn; +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; import com.carvalhotechsolutions.mundoanimal.model.Usuario; -import com.carvalhotechsolutions.mundoanimal.security.PasswordUtils; -import com.carvalhotechsolutions.mundoanimal.utils.NavigationManager; +import com.carvalhotechsolutions.mundoanimal.utils.PasswordManager; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; import com.carvalhotechsolutions.mundoanimal.utils.SessionManager; import jakarta.persistence.EntityManager; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.PasswordField; -import javafx.scene.control.TextField; - -import java.io.IOException; +import javafx.scene.Node; +import javafx.scene.control.*; public class LoginController { @FXML @@ -57,14 +56,23 @@ private void handleLogin(ActionEvent event) { } // Verifica a senha - if (!PasswordUtils.checkPassword(password, usuario.getSenha())) { + if (!PasswordManager.checkPassword(password, usuario.getSenha())) { showAlert("Erro", "Senha incorreta."); return; } - // Login bem-sucedido - Direciona para o menu principal + // Login bem-sucedido SessionManager.setCurrentUser(usuario); - NavigationManager.switchScene(event, "/fxml/gerenciamento/menu.fxml", "Pet Shop Mundo Animal"); + +// PopupManager.showLoginSuccessPopup(ScreenManagerHolder.getInstance().getStage()); + + // Atualiza a interface do menu através do ScreenManagerHolder + ScreenManagerHolder.getInstance().getMenuController().updateUserInterface(usuario); + + // Chamando tela de menu e aplicando FadeIn + Node menuScreen = ScreenManagerHolder.getInstance().getScreen(ScreenEnum.MENU); + new FadeIn(menuScreen).play(); + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.MENU); } catch (Exception e) { showAlert("Erro", "Ocorreu um erro ao verificar as credenciais: " + e.getMessage()); @@ -72,10 +80,9 @@ private void handleLogin(ActionEvent event) { } } - // Método temporário, apenas para testar a troca de telas, não há lógica alguma aplicada @FXML - private void handleForgotPassword(ActionEvent event) throws IOException { - NavigationManager.switchScene(event, "/fxml/autenticacao/recuperar-senha.fxml", "Recuperar senha"); + private void handleForgotPassword() { + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.RECUPERAR_SENHA); } private void showAlert(String title, String message) { diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RecuperarSenhaController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RecuperarSenhaController.java new file mode 100644 index 0000000..eb7decb --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RecuperarSenhaController.java @@ -0,0 +1,110 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.autenticacao; + +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import com.carvalhotechsolutions.mundoanimal.model.Administrador; +import com.carvalhotechsolutions.mundoanimal.model.Secretario; +import com.carvalhotechsolutions.mundoanimal.model.Usuario; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; +import com.carvalhotechsolutions.mundoanimal.utils.SessionManager; +import jakarta.persistence.EntityManager; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.TextField; + +import java.io.IOException; + +public class RecuperarSenhaController { + + @FXML + private TextField recovery_cpf_field; + + @FXML + private TextField recovery_username_field; + + // Método temporário, apenas para testar a troca de telas, não há lógica alguma aplicada + @FXML + private void backToLogin() throws IOException { + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.LOGIN); + } + + @FXML + private void handleResetBtn() throws IOException { + + String cpf = recovery_cpf_field.getText(); + String username = recovery_username_field.getText(); + + if (cpf.isEmpty() || username.isEmpty()) { + showAlert("Erro.", "Por favor preencha todos os campos."); + return; + } + + try (EntityManager em = JPAutil.getEntityManager()) { + Usuario usuario = null; + + // Busca o CPF do administrador padrão + Administrador adminPadrao = em.createQuery( + "SELECT a FROM Administrador a WHERE a.nomeUsuario = 'admin'", Administrador.class) + .getSingleResult(); + + if (adminPadrao == null) { + showAlert("Erro", "Administrador padrão não encontrado no sistema."); + return; + } + + // Busca o usuário pelo nome de usuário + Administrador admin = em.createQuery( + "SELECT a FROM Administrador a WHERE a.nomeUsuario = :username", Administrador.class) + .setParameter("username", username) + .getResultStream() + .findFirst() + .orElse(null); + + if (admin != null) { + // Se for administrador, valida o CPF com o próprio admin padrão + if (!cpf.equals(admin.getCpf())) { + showAlert("Erro", "CPF inválido para o administrador."); + return; + } + usuario = admin; + } else { + // Se não for admin, busca em Secretário + Secretario secretario = em.createQuery( + "SELECT s FROM Secretario s WHERE s.nomeUsuario = :username", Secretario.class) + .setParameter("username", username) + .getResultStream() + .findFirst() + .orElse(null); + + if (secretario == null) { + showAlert("Erro", "Usuário não encontrado."); + return; + } + + // Valida o CPF do secretário com o CPF do administrador padrão + if (!cpf.equals(adminPadrao.getCpf())) { + showAlert("Erro", "CPF inválido para recuperação de senha."); + return; + } + usuario = secretario; + } + + // Se passou todas as validações + SessionManager.setCurrentUser(usuario); + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.REDEFINIR_SENHA); + + } catch (Exception e) { + showAlert("Erro", "Erro ao buscar usuário: " + e.getMessage()); + e.printStackTrace(); + } + + } + + private void showAlert(String title, String message) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RedefinirSenhaController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RedefinirSenhaController.java new file mode 100644 index 0000000..b044259 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/autenticacao/RedefinirSenhaController.java @@ -0,0 +1,84 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.autenticacao; + +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import com.carvalhotechsolutions.mundoanimal.model.Usuario; +import com.carvalhotechsolutions.mundoanimal.utils.PasswordManager; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; +import com.carvalhotechsolutions.mundoanimal.utils.SessionManager; +import jakarta.persistence.EntityManager; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.PasswordField; + + +public class RedefinirSenhaController { + + @FXML + private PasswordField reset_confirm_password; + + @FXML + private PasswordField reset_new_password; + + @FXML + private void handleResetPassword() { + String newPassword = reset_new_password.getText(); + String confirmPassword = reset_confirm_password.getText(); + + if (newPassword.isEmpty() || confirmPassword.isEmpty()) { + showAlert("Erro", "Por favor, preencha todos os campos."); + return; + } + + if (!newPassword.equals(confirmPassword)) { + showAlert("Erro", "As senhas não coincidem. Tente novamente."); + return; + } + + if (newPassword.length() < 6) { + showAlert("Erro", "A senha deve ter no mínimo 6 caracteres."); + return; + } + + try (EntityManager em = JPAutil.getEntityManager()) { + em.getTransaction().begin(); + + // Obtém o usuário da sessão + Usuario usuario = SessionManager.getCurrentUser(); + + if (usuario == null) { + showAlert("Erro", "Nenhum usuário autenticado. Tente novamente."); + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.LOGIN); + return; + } + + // Atualiza a senha do usuário + usuario.setSenha(PasswordManager.hashPassword(newPassword)); // Hash da senha + em.merge(usuario); // Atualiza no banco + em.getTransaction().commit(); + + showAlert("Sucesso", "Senha redefinida com sucesso!"); + SessionManager.clearSession(); + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.LOGIN); + + } catch (Exception e) { + showAlert("Erro", "Erro ao redefinir a senha: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void showAlert(String title, String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + + @FXML + private void backToRecovery() { + SessionManager.clearSession(); // Se ele desistir de redefinir a senha, deverá ser limpa a sessão + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.RECUPERAR_SENHA); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AgendamentoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AgendamentoController.java new file mode 100644 index 0000000..42b4bac --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AgendamentoController.java @@ -0,0 +1,329 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; + +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalConfirmarRemocaoController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarAgendamentoController; +import com.carvalhotechsolutions.mundoanimal.model.Agendamento; +import com.carvalhotechsolutions.mundoanimal.repositories.AgendamentoRepository; +import com.carvalhotechsolutions.mundoanimal.utils.FeedbackManager; +import javafx.beans.binding.DoubleBinding; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.HBox; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ResourceBundle; + +public class AgendamentoController implements Initializable { + @FXML + private HBox feedbackContainer; + + @FXML + private TableView tableView; + + @FXML + private TableColumn servicoColumn; + + @FXML + private TableColumn horarioColumn; + + @FXML + private TableColumn petColumn; + + @FXML + private TableColumn clienteColumn; + + @FXML + private TableColumn acaoColumn; + + @FXML + private Label numberOfResults; + + @FXML + private TextField filterField; + + private FilteredList filteredData; + + private AgendamentoRepository agendamentoRepository = new AgendamentoRepository(); + + private ObservableList agendamentosList = FXCollections.observableArrayList(); + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + + // Define a largura fixa da coluna de ação + acaoColumn.setPrefWidth(354); + acaoColumn.setMinWidth(354); + acaoColumn.setMaxWidth(354); + + // Faz um bind da largura disponível (largura total da tabela menos a largura fixa da coluna de ação) + DoubleBinding larguraDisponivel = tableView.widthProperty().subtract(354); + + // Configura as outras colunas para se redimensionarem proporcionalmente + servicoColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.22)); // 22% do espaço restante + horarioColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.34)); // 34% do espaço restante + petColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.22)); // 22% do espaço restante + clienteColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.22)); // 22% do espaço restante + + servicoColumn.setCellValueFactory(new PropertyValueFactory<>("servico")); + horarioColumn.setCellValueFactory(new PropertyValueFactory<>("dataHoraFormatada")); + petColumn.setCellValueFactory(new PropertyValueFactory<>("animal")); + clienteColumn.setCellValueFactory(new PropertyValueFactory<>("cliente")); + + configurarColunaAcao(); + atualizarTableView(); + configurarBuscaAgendamentos(); + } + + private void configurarColunaAcao() { + acaoColumn.setCellFactory(param -> new TableCell<>() { + private final Button editarButton = new Button("Editar"); + private final Button cancelarButton = new Button("Cancelar"); + private final Button finalizarButton = new Button("Finalizar"); + + private final HBox container = new HBox(editarButton, cancelarButton, finalizarButton); + + { + // Estilizando os botões + editarButton.setStyle( + "-fx-background-color: #686AFF; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + cancelarButton.setStyle( + "-fx-background-color: #FF6F6F; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + finalizarButton.setStyle( + "-fx-background-color: #F2F5FA; -fx-font-size: 18px; -fx-text-fill: black; -fx-font-weight: 400; -fx-cursor: hand; -fx-min-width: 90px; -fx-border-color: #CCCCCC; -fx-border-radius: 2px;"); + + container.setSpacing(16); + container.setPadding(new Insets(0, 16, 0, 0)); + container.setAlignment(Pos.CENTER); + + // Configurar evento para deletar + cancelarButton.setOnAction(event -> { + Agendamento agendamento = getTableView().getItems().get(getIndex()); + abrirModalCancelar(agendamento.getId()); + }); + + // Configurar evento para editar + editarButton.setOnAction(event -> { + Agendamento agendamento = getTableView().getItems().get(getIndex()); + abrirModalEditar(agendamento.getId()); + }); + + // Configurar evento para finalizar + finalizarButton.setOnAction(event -> { + Agendamento agendamento = getTableView().getItems().get(getIndex()); + abrirModalFinalizar(agendamento.getId()); + }); + } + + @Override + protected void updateItem(Void item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + } else { + setGraphic(container); + } + } + }); + } + + private void abrirModalEditar(Long agendamentoId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarAgendamento.fxml")); + Parent modalContent = loader.load(); + + // Obter o controlador do modal + ModalCriarAgendamentoController modalController = loader.getController(); + modalController.setAgendamentoController(this); // Passa referência do controlador principal + + // Buscar o serviço pelo ID + Agendamento agendamento = agendamentoRepository.findById(agendamentoId); + + // Configurar o modal para edição + modalController.configurarParaEdicao(agendamento); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Editar Agendamento"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + atualizarTableView(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void abrirModalCancelar(Long agendamentoId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalConfirmarRemocao.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalConfirmarRemocaoController modalController = loader.getController(); + modalController.setRegisterId(agendamentoId); + modalController.configurarParaCancelamento(); + modalController.setConfirmCallback(() -> { + agendamentoRepository.deleteById(agendamentoId); + atualizarTableView(); // Atualizar tabela após exclusão + handleSuccessfulOperation("Agendamento cancelado com sucesso!"); + }); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Confirmar Cancelamento"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void abrirModalFinalizar(Long agendamentoId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarAgendamento.fxml")); + Parent modalContent = loader.load(); + + // Obter o controlador do modal + ModalCriarAgendamentoController modalController = loader.getController(); + modalController.setAgendamentoController(this); // Passa referência do controlador principal + + // Buscar o serviço pelo ID + Agendamento agendamento = agendamentoRepository.findById(agendamentoId); + + // Configurar o modal para edição + modalController.configurarParaFinalizar(agendamento); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Editar Serviço"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + atualizarTableView(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void abrirModalCadastrarAgendamento() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarAgendamento.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalCriarAgendamentoController modalController = loader.getController(); + modalController.setAgendamentoController(this); // Passa referência do controlador principal + + modalController.configurarParaCadastro(); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Cadastrar Agendamento"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Erro ao abrir o modal: " + e.getMessage()); + } + } + + public void atualizarTableView() { + agendamentosList.setAll(agendamentoRepository.findAll()); + numberOfResults.setText(agendamentosList.size() + " registro(s) retornado(s)"); + } + + public void configurarBuscaAgendamentos() { + filteredData = new FilteredList<>(agendamentosList, p -> true); + filterField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(agendamento -> { + if (newValue == null || newValue.isEmpty()) { + return true; + } + String lowerCaseFilter = newValue.toLowerCase(); + + boolean matchesCliente = agendamento.getCliente().getNome().toLowerCase().contains(lowerCaseFilter); + boolean matchesAnimal = agendamento.getAnimal().getNome().toLowerCase().contains(lowerCaseFilter); + boolean matchesServico = agendamento.getServico().getNomeServico().toLowerCase().contains(lowerCaseFilter); + boolean matchesData = agendamento.getDataHoraFormatada().toLowerCase().contains(lowerCaseFilter); + boolean matchesStatus = agendamento.getStatus().toString().toLowerCase().contains(lowerCaseFilter); + boolean matchesResponsavel = agendamento.getResponsavelAtendimento() != null && + agendamento.getResponsavelAtendimento().toLowerCase().contains(lowerCaseFilter); + + return matchesCliente || matchesAnimal || matchesServico || + matchesData || matchesStatus || matchesResponsavel; + }); +// SortedList sortedData = new SortedList<>(filteredData); +// sortedData.comparatorProperty().bind(tableView.comparatorProperty()); +// +// tableView.setItems(sortedData); + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); + }); + tableView.setItems(filteredData); + } + + public void handleSuccessfulOperation(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.SUCCESS + ); + } + + public void handleError(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.ERROR + ); + } + + public void salvarAgendamento(Agendamento agendamento) { + boolean horarioDisponivel = verificarDisponibilidadeHorario( + agendamento.getDataAgendamento(), + agendamento.getHorarioAgendamento() + ); + + if (!horarioDisponivel && agendamento.getId() == null) { + throw new RuntimeException("Horário já está ocupado"); + } + + agendamentoRepository.save(agendamento); + } + + private boolean verificarDisponibilidadeHorario(LocalDate data, LocalTime horario) { + // Lógica para verificar se já existe agendamento no mesmo horário + return agendamentoRepository.verificarDisponibilidadeHorario(data, horario); + } + + public void finalizarAgendamento(Long id) { + agendamentoRepository.deleteById(id); + handleSuccessfulOperation("Agendamento finalizado com sucesso!"); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AnimalController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AnimalController.java new file mode 100644 index 0000000..bbcd504 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/AnimalController.java @@ -0,0 +1,293 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; + +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalConfirmarRemocaoController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarClienteController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarPetController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalDetalhesPetController; +import com.carvalhotechsolutions.mundoanimal.enums.EspecieAnimal; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import com.carvalhotechsolutions.mundoanimal.model.Animal; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import com.carvalhotechsolutions.mundoanimal.repositories.AnimalRepository; +import com.carvalhotechsolutions.mundoanimal.repositories.ClienteRepository; +import com.carvalhotechsolutions.mundoanimal.utils.FeedbackManager; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; +import javafx.beans.binding.DoubleBinding; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.HBox; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +public class AnimalController implements Initializable { + @FXML + private HBox feedbackContainer; + + @FXML + private TableView tableView; + + @FXML + private TableColumn nomeColumn; + + @FXML + private TableColumn especieColumn; + + @FXML + private TableColumn acaoColumn; + + @FXML + private Label numberOfResults; + + @FXML + private TextField filterField; + + private FilteredList filteredData; + + private Cliente cliente; // Dono dos pets que serão exibidos na tabela + + private ObservableList petsList = FXCollections.observableArrayList(); + + private AnimalRepository animalRepository = new AnimalRepository(); + + private ClienteRepository clienteRepository = new ClienteRepository(); + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + + // Define a largura fixa da coluna de ação + acaoColumn.setPrefWidth(354); + acaoColumn.setMinWidth(354); + acaoColumn.setMaxWidth(354); + + // Faz um bind da largura disponível (largura total da tabela menos a largura fixa da coluna de ação) + DoubleBinding larguraDisponivel = tableView.widthProperty().subtract(354); + + // Configura as outras colunas para se redimensionarem proporcionalmente + nomeColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.50)); // 50% do espaço restante + especieColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.50)); // 50% do espaço restante + + // Configurar colunas + nomeColumn.setCellValueFactory(new PropertyValueFactory<>("nome")); + especieColumn.setCellValueFactory(new PropertyValueFactory<>("especie")); + + // Configurar botões da coluna de ações + configurarColunaAcao(); + } + + private void configurarColunaAcao() { + acaoColumn.setCellFactory(param -> new TableCell<>() { + private final Button editarButton = new Button("Editar"); + private final Button deletarButton = new Button("Deletar"); + private final Button detalhesButton = new Button("Detalhes"); + + private final HBox container = new HBox(editarButton, deletarButton, detalhesButton); + + { + // Estilizando os botões + editarButton.setStyle( + "-fx-background-color: #686AFF; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + deletarButton.setStyle( + "-fx-background-color: #FF6F6F; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + detalhesButton.setStyle( + "-fx-background-color: #F2F5FA; -fx-font-size: 18px; -fx-text-fill: black; -fx-font-weight: 400; -fx-cursor: hand; -fx-min-width: 90px; -fx-border-color: #CCCCCC; -fx-border-radius: 2px;"); + + container.setSpacing(16); + container.setPadding(new Insets(0, 16, 0, 0)); + container.setAlignment(Pos.CENTER); + + // Configurar evento para deletar + deletarButton.setOnAction(event -> { + Animal animal = getTableView().getItems().get(getIndex()); + abrirModalExcluir(animal.getId()); + }); + + // Configurar evento para editar + editarButton.setOnAction(event -> { + Animal animal = getTableView().getItems().get(getIndex()); + abrirModalEditar(animal.getId()); + }); + + detalhesButton.setOnAction(event -> { + Animal animal = getTableView().getItems().get(getIndex()); + abrirModalDetalhes(animal.getId()); + }); + } + + @Override + protected void updateItem(Void item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + } else { + setGraphic(container); + } + } + }); + } + + private void abrirModalDetalhes(Long animalId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalDetalhesPet.fxml")); + Parent modalContent = loader.load(); + + // Obter o controlador do modal + ModalDetalhesPetController modalController = loader.getController(); + + // Buscar o serviço pelo ID + Animal animal = animalRepository.findById(animalId); + + // Configurar o modal para edição + modalController.configurarParaExibicao(animal); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Detalhes do Pet"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void abrirModalEditar(Long animalId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarPet.fxml")); + Parent modalContent = loader.load(); + + // Obter o controlador do modal + ModalCriarPetController modalController = loader.getController(); + modalController.setAnimalController(this); + + // Buscar o serviço pelo ID + Animal animal = animalRepository.findById(animalId); + + // Configurar o modal para edição + modalController.configurarParaEdicao(animal); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Editar Pet"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + // Recarrega o cliente do banco de dados para ter a lista atualizada + cliente = clienteRepository.findById(cliente.getId()); + + // Atualiza a página de clientes + ScreenManagerHolder.getInstance().getClienteController().atualizarTableView(); + atualizarTableView(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void abrirModalExcluir(Long animalId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalConfirmarRemocao.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalConfirmarRemocaoController modalController = loader.getController(); + modalController.setRegisterId(animalId); + modalController.setConfirmCallback(() -> { + animalRepository.deleteById(animalId); + + // Recarrega o cliente do banco de dados para ter a lista atualizada + cliente = clienteRepository.findById(cliente.getId()); + + // Atualiza a página de clientes + ScreenManagerHolder.getInstance().getClienteController().atualizarTableView(); + atualizarTableView(); + handleSuccessfulOperation("Pet removido com sucesso!"); + }); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Confirmar Exclusão"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void configurarBuscaPets() { + filteredData = new FilteredList<>(petsList, p -> true); + filterField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(animal -> { + if (newValue == null || newValue.isEmpty()) { + return true; + } + String lowerCaseFilter = newValue.toLowerCase(); + boolean matchesNome = animal.getNome().toLowerCase().contains(lowerCaseFilter); + boolean matchesEspecie = animal.getEspecie().toString().toLowerCase().contains(lowerCaseFilter); + + return matchesNome || matchesEspecie; + }); + + SortedList sortedData = new SortedList<>(filteredData); + sortedData.comparatorProperty().bind(tableView.comparatorProperty()); + + tableView.setItems(sortedData); + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); + }); + } + + public void voltarParaPaginaClientes() { + filterField.clear(); + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.CLIENTES); + } + + public void setCliente(Cliente cliente) { + this.cliente = cliente; + atualizarTableView(); + configurarBuscaPets(); + } + + public void atualizarTableView() { + petsList.setAll(cliente.getPets()); + tableView.setItems(petsList); + numberOfResults.setText(petsList.size() + " registro(s) retornado(s)"); + } + + public void handleSuccessfulOperation(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.SUCCESS + ); + } + + public void handleError(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.ERROR + ); + } +} + diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ClienteController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ClienteController.java new file mode 100644 index 0000000..72675bf --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ClienteController.java @@ -0,0 +1,325 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; + +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalConfirmarRemocaoController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarClienteController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarPetController; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import com.carvalhotechsolutions.mundoanimal.repositories.ClienteRepository; +import com.carvalhotechsolutions.mundoanimal.utils.FeedbackManager; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; +import jakarta.persistence.RollbackException; +import javafx.beans.binding.DoubleBinding; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.HBox; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; +import java.sql.SQLException; +import java.util.ResourceBundle; + +public class ClienteController implements Initializable { + @FXML + private HBox feedbackContainer; + + @FXML + private TableView tableView; + + @FXML + private TableColumn nomeColumn; + + @FXML + private TableColumn telefoneColumn; + + @FXML + private TableColumn petsColumn; + + @FXML + private TableColumn acaoColumn; + + @FXML + private Label numberOfResults; + + @FXML + private TextField filterField; + + private ClienteRepository clienteRepository = new ClienteRepository(); + + private ObservableList clientesList = FXCollections.observableArrayList(); + + private FilteredList filteredData; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + + // Define a largura fixa da coluna de ação + acaoColumn.setPrefWidth(462); + acaoColumn.setMinWidth(462); + acaoColumn.setMaxWidth(462); + + // Faz um bind da largura disponível (largura total da tabela menos a largura fixa da coluna de ação) + DoubleBinding larguraDisponivel = tableView.widthProperty().subtract(462); + + // Configura as outras colunas para se redimensionarem proporcionalmente + nomeColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.33)); // 33% do espaço restante + telefoneColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.33)); // 33% do espaço restante + petsColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.34)); // 34% do espaço restante + + nomeColumn.setCellValueFactory(new PropertyValueFactory<>("nome")); + telefoneColumn.setCellValueFactory(new PropertyValueFactory<>("telefone")); + petsColumn.setCellValueFactory(cellData -> + new SimpleStringProperty(cellData.getValue().getPetsFormatados()) + ); + + configurarColunaAcao(); + atualizarTableView(); + configurarBuscaClientes(); // apos atualizarTableView() + } + + private void configurarColunaAcao() { + acaoColumn.setCellFactory(param -> new TableCell<>() { + private final Button editarButton = new Button("Editar"); + private final Button deletarButton = new Button("Deletar"); + private final Button novoPetButton = new Button("Novo pet"); + private final Button verPetsButton = new Button("Ver pets"); + + private final HBox container = new HBox(editarButton, deletarButton, novoPetButton, verPetsButton); + + { + // Estilizando os botões + editarButton.setStyle( + "-fx-background-color: #686AFF; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + deletarButton.setStyle( + "-fx-background-color: #FF6F6F; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + novoPetButton.setStyle( + "-fx-background-color: #2cc428; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + verPetsButton.setStyle( + "-fx-background-color: #F2F5FA; -fx-font-size: 18px; -fx-text-fill: black; -fx-font-weight: 400; -fx-cursor: hand; -fx-min-width: 90px; -fx-border-color: #CCCCCC; -fx-border-radius: 2px;"); + + container.setSpacing(16); + container.setPadding(new Insets(0, 16, 0, 0)); + container.setAlignment(Pos.CENTER); + + // Configurar evento para deletar + deletarButton.setOnAction(event -> { + Cliente cliente = getTableView().getItems().get(getIndex()); + abrirModalExcluir(cliente.getId()); + }); + + // Configurar evento para editar + editarButton.setOnAction(event -> { + Cliente cliente = getTableView().getItems().get(getIndex()); + abrirModalEditar(cliente.getId()); + }); + + novoPetButton.setOnAction(event -> { + Cliente cliente = getTableView().getItems().get(getIndex()); + abrirModalCadastrarPet(cliente.getId()); + }); + + verPetsButton.setOnAction(event -> { + Cliente cliente = getTableView().getItems().get(getIndex()); + abrirPaginaVerPets(cliente); + }); + } + + @Override + protected void updateItem(Void item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + } else { + setGraphic(container); + } + } + }); + } + + @FXML + public void abrirModalCadastrarCliente() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarCliente.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalCriarClienteController modalController = loader.getController(); + modalController.setClienteController(this); // Passa referência do controlador principal + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Cadastrar Cliente"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Erro ao abrir o modal: " + e.getMessage()); + } + } + + private void abrirModalEditar(Long clienteId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarCliente.fxml")); + Parent modalContent = loader.load(); + + // Obter o controlador do modal + ModalCriarClienteController modalController = loader.getController(); + modalController.setClienteController(this); // Passa referência do controlador principal + + // Buscar o serviço pelo ID + Cliente cliente = clienteRepository.findById(clienteId); + + // Configurar o modal para edição + modalController.configurarParaEdicao(cliente); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Editar Serviço"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + atualizarTableView(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void abrirModalExcluir(Long clienteId) { + if (clienteRepository.clientePossuiAgendamentos(clienteId)) { + handleError("Cliente possui agendamento(s) pendente(s)"); + return; + } + + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalConfirmarRemocao.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalConfirmarRemocaoController modalController = loader.getController(); + modalController.setRegisterId(clienteId); + modalController.setConfirmCallback(() -> { + clienteRepository.deleteById(clienteId); + atualizarTableView(); // Atualizar tabela após exclusão + handleSuccessfulOperation("Cliente removido com sucesso!"); + }); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Confirmar Exclusão"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void abrirModalCadastrarPet(Long clienteId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarPet.fxml")); + Parent modalContent = loader.load(); + + // Configurar o controlador do modal + ModalCriarPetController modalController = loader.getController(); + modalController.setClienteController(this); // Passa referência do controlador principal + + // Configurar campos do cliente no modal + modalController.configurarParaCadastro(clienteId); + + // Configurar o Stage do modal + Stage modalStage = new Stage(); + modalStage.initModality(Modality.APPLICATION_MODAL); + modalStage.setTitle("Cadastrar Pet"); + modalStage.setScene(new Scene(modalContent)); + modalStage.setResizable(false); + modalStage.showAndWait(); + + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Erro ao abrir o modal: " + e.getMessage()); + } + } + + private void abrirPaginaVerPets(Cliente cliente) { + if (cliente.getPets().isEmpty()) { + handleError("Este cliente não possui pets cadastrados!"); + return; + } + + // Configurar o controlador do modal + AnimalController animalController = ScreenManagerHolder.getInstance().getAnimalController(); + animalController.setCliente(cliente); + animalController.atualizarTableView(); + + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.PETS); + } + + public void atualizarTableView() { + clientesList.setAll(clienteRepository.findAll()); + numberOfResults.setText(clientesList.size() + " registro(s) retornado(s)"); + } + + private void configurarBuscaClientes() { + filteredData = new FilteredList<>(clientesList, p -> true); + filterField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(cliente -> { + if (newValue == null || newValue.isEmpty()) { + return true; + } + String lowerCaseFilter = newValue.toLowerCase(); + boolean matchesCliente = cliente.getNome().toLowerCase().contains(lowerCaseFilter) || + cliente.getTelefone().toLowerCase().contains(lowerCaseFilter); + boolean matchesPet = cliente.getPets().stream() + .anyMatch(pet -> pet.getNome().toLowerCase().contains(lowerCaseFilter)); + return matchesCliente || matchesPet; + }); + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); + }); + tableView.setItems(filteredData); + } + + public void handleSuccessfulOperation(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.SUCCESS + ); + } + + public void handleError(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.ERROR + ); + } + + private void mostrarAlerta(String titulo, String mensagem, Alert.AlertType tipo) { + Alert alerta = new Alert(tipo); + alerta.setTitle(titulo); + alerta.setHeaderText(null); + alerta.setContentText(mensagem); + alerta.showAndWait(); + } +} \ No newline at end of file diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/MenuController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/MenuController.java similarity index 54% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/MenuController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/MenuController.java index cc1f26f..562f3b1 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/MenuController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/MenuController.java @@ -1,14 +1,15 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; +import animatefx.animation.FadeIn; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; import com.carvalhotechsolutions.mundoanimal.model.Usuario; -import com.carvalhotechsolutions.mundoanimal.model.enums.TipoUsuario; +import com.carvalhotechsolutions.mundoanimal.enums.TipoUsuario; +import com.carvalhotechsolutions.mundoanimal.utils.ScreenManagerHolder; import com.carvalhotechsolutions.mundoanimal.utils.SessionManager; +import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; @@ -16,17 +17,11 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - -import java.io.IOException; import java.net.URL; import java.util.Optional; import java.util.ResourceBundle; public class MenuController implements Initializable { - @FXML - private StackPane contentArea; - @FXML private Button inicio_btn; @@ -61,37 +56,46 @@ public class MenuController implements Initializable { @Override public void initialize(URL url, ResourceBundle resourceBundle) { - Usuario usuarioLogado = SessionManager.getCurrentUser(); - if (usuarioLogado.getTipoUsuario() == TipoUsuario.SECRETARIO) { - servicos_btn.setVisible(false); - secretarios_btn.setVisible(false); - userTypeLabel.setText("Secretário"); - } - userNameLabel.setText(usuarioLogado.getNomeUsuario()); // Set up button actions - servicos_btn.setOnAction(event -> loadPage("servicos.fxml")); - secretarios_btn.setOnAction(event -> loadPage("secretarios.fxml")); sair_btn.setOnAction(event -> logout()); + // Configure ações para cada botão - configureButton(inicio_btn, "inicio.fxml"); - configureButton(clientes_btn, "clientes.fxml"); - configureButton(agendamentos_btn, "agendamentos.fxml"); - configureButton(historico_btn, "historico.fxml"); - configureButton(relatorio_btn, "relatorio.fxml"); - configureButton(secretarios_btn, "secretarios.fxml"); - configureButton(servicos_btn, "servicos.fxml"); +// configureButton(inicio_btn, "inicio.fxml"); +// configureButton(historico_btn, "historico.fxml"); +// configureButton(relatorio_btn, "relatorio.fxml"); + configureButton(agendamentos_btn, ScreenEnum.AGENDAMENTOS); + configureButton(secretarios_btn, ScreenEnum.SECRETARIOS); + configureButton(servicos_btn, ScreenEnum.SERVICOS); + configureButton(clientes_btn, ScreenEnum.CLIENTES); + } + + public void updateUserInterface(Usuario usuario) { + Platform.runLater(() -> { + userNameLabel.setText(usuario.getNomeUsuario()); + userTypeLabel.setText(usuario.getTipoUsuario() == TipoUsuario.SECRETARIO ? "Secretário(a)" : "Administrador(a)"); + + boolean isSecretario = usuario.getTipoUsuario() == TipoUsuario.SECRETARIO; + servicos_btn.setVisible(!isSecretario); + secretarios_btn.setVisible(!isSecretario); + }); } - private void configureButton(Button button, String fxmlFile) { + private void configureButton(Button button, ScreenEnum screen) { button.setOnAction(event -> { // Gerenciar o botão ativo setActiveButton(button); // Carregar a página no contentArea - loadPage(fxmlFile); + Node animatedScreen = ScreenManagerHolder.getInstance().getScreen(screen); + new FadeIn(animatedScreen).play(); + ScreenManagerHolder.getInstance().switchTo(screen); }); } + public Button getActiveButton() { + return activeButton; + } + private void setActiveButton(Button button) { // Remover a classe 'active' do botão anteriormente ativo, se houver if (activeButton != null) { @@ -105,17 +109,6 @@ private void setActiveButton(Button button) { activeButton = button; } - private void loadPage(String fxmlFile) { - try { - // Carregar a nova página dentro do contentArea - Node page = FXMLLoader.load(getClass().getResource("/fxml/gerenciamento/" + fxmlFile)); - contentArea.getChildren().clear(); - contentArea.getChildren().add(page); - } catch (IOException e) { - e.printStackTrace(); - } - } - private void logout() { // Cria um alerta de confirmação Alert alert = new Alert(Alert.AlertType.CONFIRMATION); @@ -135,17 +128,13 @@ private void logout() { // Verifica a escolha if (result.isPresent() && result.get() == ButtonType.OK) { - try { - Stage stage = (Stage) contentArea.getScene().getWindow(); // Obtém o estágio atual a partir do contentArea - Parent loginPage = FXMLLoader.load(getClass().getResource("/fxml/autenticacao/login.fxml")); // Carrega a página de login - Scene newScene = new Scene(loginPage); // Cria uma nova cena com a página de login carregada - stage.setScene(newScene); // Define a nova cena no estágio - stage.show(); // Exibe o estágio atualizado - - SessionManager.setCurrentUser(null); // Limpa o usuário logado - } catch (IOException e) { - e.printStackTrace(); + if (activeButton != null) { + activeButton.getStyleClass().remove("active"); } + ScreenManagerHolder.getInstance().switchTo(ScreenEnum.LOGIN); + SessionManager.setCurrentUser(null); // Limpa o usuário logado + Node loginScreen = ScreenManagerHolder.getInstance().getScreen(ScreenEnum.LOGIN); + new FadeIn(loginScreen).play(); } else { // Usuário cancelou a ação alert.close(); diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/SecretarioController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/SecretarioController.java similarity index 59% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/SecretarioController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/SecretarioController.java index 74d46f9..2987089 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/SecretarioController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/SecretarioController.java @@ -1,11 +1,15 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalConfirmarRemocaoController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarSecretarioController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalEditarSecretarioController; import com.carvalhotechsolutions.mundoanimal.model.Secretario; -import com.carvalhotechsolutions.mundoanimal.model.Servico; import com.carvalhotechsolutions.mundoanimal.repositories.SecretarioRepository; -import com.carvalhotechsolutions.mundoanimal.repositories.ServicoRepository; +import com.carvalhotechsolutions.mundoanimal.utils.FeedbackManager; +import javafx.beans.binding.DoubleBinding; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; @@ -13,10 +17,7 @@ import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; +import javafx.scene.control.*; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.HBox; import javafx.stage.Modality; @@ -28,7 +29,13 @@ public class SecretarioController implements Initializable { @FXML - private TableView tableViewSecretarios; + private HBox feedbackContainer; + + @FXML + private Label numberOfResults; + + @FXML + private TableView tableView; @FXML private TableColumn nomeColumn; // Nome de Usuario @@ -39,24 +46,43 @@ public class SecretarioController implements Initializable { @FXML private TableColumn acaoColumn; // Ação + @FXML + private TextField filterField; + private SecretarioRepository secretarioRepository = new SecretarioRepository(); - private ObservableList secretariosList; + private ObservableList secretariosList = FXCollections.observableArrayList(); + + private FilteredList filteredData; @Override public void initialize(URL url, ResourceBundle resourceBundle) { + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + + // Define a largura fixa da coluna de ação + acaoColumn.setPrefWidth(246); + acaoColumn.setMinWidth(246); + acaoColumn.setMaxWidth(246); + + // Faz um bind da largura disponível (largura total da tabela menos a largura fixa da coluna de ação) + DoubleBinding larguraDisponivel = tableView.widthProperty().subtract(246); + + // Configura as outras colunas para se redimensionarem proporcionalmente + nomeColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.50)); // 50% do espaço restante + phoneColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.50)); // 50% do espaço restante + nomeColumn.setCellValueFactory(new PropertyValueFactory<>("nomeUsuario")); phoneColumn.setCellValueFactory(new PropertyValueFactory<>("telefone")); configurarColunaAcao(); - atualizarTableView(); + configurarBuscaSecretarios(); } public void atualizarTableView() { - secretariosList = FXCollections.observableArrayList(secretarioRepository.findAll()); - tableViewSecretarios.setItems(secretariosList); + secretariosList.setAll(secretarioRepository.findAll()); + numberOfResults.setText(secretariosList.size() + " registro(s) retornado(s)"); } private void configurarColunaAcao() { @@ -67,10 +93,13 @@ private void configurarColunaAcao() { { // Estilize os botões - editarButton.setStyle("-fx-background-color: #2E86C1; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand;"); - deletarButton.setStyle("-fx-background-color: #C0392B; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand;"); - container.setSpacing(18); - container.setPadding(new Insets(10, 24, 10, 24)); + editarButton.setStyle( + "-fx-background-color: #686AFF; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + deletarButton.setStyle( + "-fx-background-color: #FF6F6F; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + + container.setSpacing(16); + container.setPadding(new Insets(0, 16, 0, 0)); container.setAlignment(Pos.CENTER); // Configurar evento para deletar @@ -101,7 +130,7 @@ protected void updateItem(Void item, boolean empty) { @FXML public void abrirModalCadastrarSecretario() { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-novo-secretario.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarSecretario.fxml")); Parent modalContent = loader.load(); // Configurar o controlador do modal @@ -124,11 +153,12 @@ public void abrirModalCadastrarSecretario() { private void abrirModalEditar(Long secretarioId) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-editar-secretario.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalEditarSecretario.fxml")); Parent modalContent = loader.load(); // Obter o controlador do modal ModalEditarSecretarioController modalController = loader.getController(); + modalController.setSecretarioController(this); // Buscar o serviço pelo ID Secretario secretario = secretarioRepository.findById(secretarioId); @@ -153,15 +183,16 @@ private void abrirModalEditar(Long secretarioId) { private void abrirModalExcluir(Long secretarioId) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-confirmar-remocao.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalConfirmarRemocao.fxml")); Parent modalContent = loader.load(); // Configurar o controlador do modal ModalConfirmarRemocaoController modalController = loader.getController(); - modalController.setServicoId(secretarioId); + modalController.setRegisterId(secretarioId); modalController.setConfirmCallback(() -> { secretarioRepository.deleteById(secretarioId); atualizarTableView(); // Atualizar tabela após exclusão + handleSuccessfulOperation("Secretário(a) removido(a) com sucesso!"); }); // Configurar o Stage do modal @@ -177,4 +208,36 @@ private void abrirModalExcluir(Long secretarioId) { } } + private void configurarBuscaSecretarios() { + filteredData = new FilteredList<>(secretariosList, p -> true); + filterField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(secretario -> { + if (newValue == null || newValue.isEmpty()) { + return true; + } + String lowerCaseFilter = newValue.toLowerCase(); + return secretario.getNomeUsuario().toLowerCase().contains(lowerCaseFilter) + || secretario.getTelefone().toLowerCase().contains(lowerCaseFilter); + }); + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); + }); + tableView.setItems(filteredData); + } + + public void handleSuccessfulOperation(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.SUCCESS + ); + } + + public void handleError(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.ERROR + ); + } + } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ServicoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ServicoController.java similarity index 60% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ServicoController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ServicoController.java index de04c48..58a4540 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ServicoController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/ServicoController.java @@ -1,9 +1,14 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalConfirmarRemocaoController; +import com.carvalhotechsolutions.mundoanimal.controllers.modals.ModalCriarServicoController; import com.carvalhotechsolutions.mundoanimal.model.Servico; import com.carvalhotechsolutions.mundoanimal.repositories.ServicoRepository; +import com.carvalhotechsolutions.mundoanimal.utils.FeedbackManager; +import javafx.beans.binding.DoubleBinding; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; @@ -11,10 +16,7 @@ import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; +import javafx.scene.control.*; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.HBox; import javafx.stage.Modality; @@ -26,6 +28,12 @@ import java.util.ResourceBundle; public class ServicoController implements Initializable { + @FXML + private HBox feedbackContainer; + + @FXML + private Label numberOfResults; + @FXML private TableView tableView; @@ -41,12 +49,32 @@ public class ServicoController implements Initializable { @FXML private TableColumn acaoColumn; + @FXML + private TextField filterField; + private ServicoRepository servicoRepository = new ServicoRepository(); private ObservableList servicosList = FXCollections.observableArrayList(); + private FilteredList filteredData; + @Override public void initialize(URL url, ResourceBundle resourceBundle) { + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + + // Define a largura fixa da coluna de ação + acaoColumn.setPrefWidth(246); + acaoColumn.setMinWidth(246); + acaoColumn.setMaxWidth(246); + + // Faz um bind da largura disponível (largura total da tabela menos a largura fixa da coluna de ação) + DoubleBinding larguraDisponivel = tableView.widthProperty().subtract(246); + + // Configura as outras colunas para se redimensionarem proporcionalmente + nomeColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.25)); // 25% do espaço restante + descricaoColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.60)); // 60% do espaço restante + valorColumn.prefWidthProperty().bind(larguraDisponivel.multiply(0.15)); // 15% do espaço restante + nomeColumn.setCellValueFactory(new PropertyValueFactory<>("nomeServico")); descricaoColumn.setCellValueFactory(new PropertyValueFactory<>("descricao")); valorColumn.setCellValueFactory(new PropertyValueFactory<>("valorServico")); @@ -54,6 +82,7 @@ public void initialize(URL url, ResourceBundle resourceBundle) { configurarColunaValor(); configurarColunaAcao(); atualizarTableView(); + configurarBuscaServicos(); } private void configurarColunaAcao() { @@ -64,10 +93,13 @@ private void configurarColunaAcao() { { // Estilize os botões - editarButton.setStyle("-fx-background-color: #2E86C1; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand;"); - deletarButton.setStyle("-fx-background-color: #C0392B; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand;"); - container.setSpacing(18); - container.setPadding(new Insets(10, 24, 10, 24)); + editarButton.setStyle( + "-fx-background-color: #686AFF; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + deletarButton.setStyle( + "-fx-background-color: #FF6F6F; -fx-font-size: 18px; -fx-text-fill: white; -fx-font-weight: 800; -fx-cursor: hand; -fx-min-width: 90px;"); + + container.setSpacing(16); + container.setPadding(new Insets(0, 16, 0, 0)); container.setAlignment(Pos.CENTER); // Configurar evento para deletar @@ -115,7 +147,7 @@ protected void updateItem(BigDecimal item, boolean empty) { @FXML public void abrirModalCadastrarServico() { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-novo-servico.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarServico.fxml")); Parent modalContent = loader.load(); // Configurar o controlador do modal @@ -138,11 +170,12 @@ public void abrirModalCadastrarServico() { private void abrirModalEditar(Long servicoId) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-novo-servico.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalCriarServico.fxml")); Parent modalContent = loader.load(); // Obter o controlador do modal ModalCriarServicoController modalController = loader.getController(); + modalController.setServicoController(this); // Buscar o serviço pelo ID Servico servico = servicoRepository.findById(servicoId); @@ -167,15 +200,16 @@ private void abrirModalEditar(Long servicoId) { private void abrirModalExcluir(Long servicoId) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modal-confirmar-remocao.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/modals/modalConfirmarRemocao.fxml")); Parent modalContent = loader.load(); // Configurar o controlador do modal ModalConfirmarRemocaoController modalController = loader.getController(); - modalController.setServicoId(servicoId); + modalController.setRegisterId(servicoId); modalController.setConfirmCallback(() -> { servicoRepository.deleteById(servicoId); atualizarTableView(); // Atualizar tabela após exclusão + handleSuccessfulOperation("Serviço removido com sucesso!"); }); // Configurar o Stage do modal @@ -192,9 +226,46 @@ private void abrirModalExcluir(Long servicoId) { } public void atualizarTableView() { - servicosList = FXCollections.observableArrayList(servicoRepository.findAll()); - tableView.setItems(servicosList); + servicosList.setAll(servicoRepository.findAll()); + numberOfResults.setText(servicosList.size() + " registro(s) retornado(s)"); } + private void configurarBuscaServicos() { + filteredData = new FilteredList<>(servicosList, p -> true); + filterField.textProperty().addListener((observable, oldValue, newValue) -> { + filteredData.setPredicate(servico -> { + if (newValue == null || newValue.isEmpty()) { + return true; // Se o campo de busca estiver vazio, mostra todos os serviços + } + String lowerCaseFilter = newValue.toLowerCase(); + + // Verificando se o nome do serviço, descrição ou preço contém o termo de busca + boolean matchesNome = servico.getNomeServico().toLowerCase().contains(lowerCaseFilter); + boolean matchesDescricao = servico.getDescricao() != null && servico.getDescricao().toLowerCase().contains(lowerCaseFilter); + boolean matchesPreco = servico.getValorServico().toString().contains(newValue); // Comparando com o preço + + // Retorna true se qualquer um dos campos for um match + return matchesNome || matchesDescricao || matchesPreco; + }); + numberOfResults.setText(filteredData.size() + " registro(s) retornado(s)"); + }); + tableView.setItems(filteredData); + } + + public void handleSuccessfulOperation(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.SUCCESS + ); + } + + public void handleError(String message) { + FeedbackManager.showFeedback( + feedbackContainer, + message, + FeedbackManager.FeedbackType.ERROR + ); + } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RecuperarSenhaController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RecuperarSenhaController.java deleted file mode 100644 index 53bd2c5..0000000 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RecuperarSenhaController.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.carvalhotechsolutions.mundoanimal.controllers.login; - -import com.carvalhotechsolutions.mundoanimal.utils.NavigationManager; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; - -import java.io.IOException; - -public class RecuperarSenhaController { - // Método temporário, apenas para testar a troca de telas, não há lógica alguma aplicada - @FXML - private void backToLogin(ActionEvent event) throws IOException { - NavigationManager.switchScene(event, "/fxml/autenticacao/login.fxml", "Login"); - } - - // Método temporário, apenas para testar a troca de telas, não há lógica alguma aplicada - @FXML - private void handleResetBtn(ActionEvent event) throws IOException { - NavigationManager.switchScene(event, "/fxml/autenticacao/redefinir-senha.fxml", "Redefinir senha"); - } -} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RedefinirSenhaController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RedefinirSenhaController.java deleted file mode 100644 index 5882cd7..0000000 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/login/RedefinirSenhaController.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.carvalhotechsolutions.mundoanimal.controllers.login; - -import com.carvalhotechsolutions.mundoanimal.utils.NavigationManager; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; - -import java.io.IOException; - -public class RedefinirSenhaController { - // Método temporário, apenas para testar a troca de telas, não há lógica alguma aplicada - @FXML - private void backToRecovery(ActionEvent event) throws IOException { - NavigationManager.switchScene(event, "/fxml/autenticacao/recuperar-senha.fxml", "Recuperar senha"); - } -} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalConfirmarRemocaoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalConfirmarRemocaoController.java similarity index 60% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalConfirmarRemocaoController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalConfirmarRemocaoController.java index 1d57660..f366294 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalConfirmarRemocaoController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalConfirmarRemocaoController.java @@ -1,23 +1,30 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.modals; import javafx.fxml.FXML; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.stage.Stage; public class ModalConfirmarRemocaoController { + @FXML + private Label modalMessage; + + @FXML + private Label modalTitle; + @FXML private Button cancelarButton; @FXML private Button deletarButton; - private Long servicoId; // Armazena o ID do serviço a ser excluído + private Long registerId; // Armazena o ID do registro a ser excluído private Runnable confirmCallback; // Função para executar após confirmação // Define o ID do serviço - public void setServicoId(Long servicoId) { - this.servicoId = servicoId; + public void setRegisterId(Long registerIdId) { + this.registerId = registerId; } // Define a ação a ser executada após confirmação @@ -42,4 +49,11 @@ private void fecharModal() { Stage stage = (Stage) cancelarButton.getScene().getWindow(); stage.close(); } + + public void configurarParaCancelamento() { + modalTitle.setText("Confirmar Cancelamento"); + modalMessage.setText("Cancelar este agendamento permanentemente?"); + deletarButton.setText("Sim"); + cancelarButton.setText("Não"); + } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarAgendamentoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarAgendamentoController.java new file mode 100644 index 0000000..6b1c50a --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarAgendamentoController.java @@ -0,0 +1,342 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.modals; + +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.AgendamentoController; +import com.carvalhotechsolutions.mundoanimal.model.Agendamento; +import com.carvalhotechsolutions.mundoanimal.model.Animal; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import com.carvalhotechsolutions.mundoanimal.model.Servico; +import com.carvalhotechsolutions.mundoanimal.repositories.AgendamentoRepository; +import com.carvalhotechsolutions.mundoanimal.repositories.AnimalRepository; +import com.carvalhotechsolutions.mundoanimal.repositories.ClienteRepository; +import com.carvalhotechsolutions.mundoanimal.repositories.ServicoRepository; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; +import javafx.util.StringConverter; + +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.ResourceBundle; + +public class ModalCriarAgendamentoController implements Initializable { + @FXML + private TextField agendamento_id_field; + + @FXML + private TextField finish; + + @FXML + private DatePicker create_agendamento_date_field; + + @FXML + private ComboBox create_agendamento_time_field; + + @FXML + private ComboBox create_agendamento_servico_field; + + @FXML + private ComboBox create_agendamento_client_field; + + @FXML + private ComboBox create_agendamento_pet_field; + + @FXML + private TextField create_agendamento_responsavel_field; + + @FXML + private ComboBox create_agendamento_depTime_field; + + @FXML Button actionButton; + + @FXML + private HBox endServiceContainer; + + @FXML + private HBox lastRegisterContainer; + + private AgendamentoController agendamentoController; + + private ServicoRepository servicoRepository = new ServicoRepository(); + + private ClienteRepository clienteRepository = new ClienteRepository(); + + private AgendamentoRepository agendamentoRepository = new AgendamentoRepository(); + + private Agendamento agendamentoAtual; // em caso de ser edição ou finalizacao + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + configurarCampoData(); + configurarHorariosDisponiveis(); + carregarServicos(); + carregarClientes(); + gerarHorarios(); + } + + @FXML + public void cadastrarAgendamento() { + // Validações + if (!validarCampos()) { + return; + } + + if (agendamentoController == null) { + System.out.println("ERRO: agendamentoController é nulo!"); // Log para debug + return; + } + + if(finish.getText() != null && !finish.getText().isEmpty()) { + agendamentoController.finalizarAgendamento(agendamentoAtual.getId()); + fecharModal(); + return; + } + + try { + Agendamento agendamento; + boolean isEdicao = agendamento_id_field.getText() != null && !agendamento_id_field.getText().isEmpty(); + + if(isEdicao) { + Long id = Long.parseLong(agendamento_id_field.getText()); + agendamento = agendamentoRepository.findById(id); + System.out.println("Editando cliente com ID: " + id); + } else { + agendamento = new Agendamento(); + System.out.println("Criando novo cliente"); + } + + // Criar novo agendamento + agendamento.setDataAgendamento(create_agendamento_date_field.getValue()); + agendamento.setHorarioAgendamento( + LocalTime.parse(create_agendamento_time_field.getValue(), + DateTimeFormatter.ofPattern("HH:mm")) + ); + agendamento.setServico(create_agendamento_servico_field.getValue()); + agendamento.setCliente(create_agendamento_client_field.getValue()); + agendamento.setAnimal(create_agendamento_pet_field.getValue()); + + agendamentoController.salvarAgendamento(agendamento); + agendamentoController.atualizarTableView(); + + String mensagem = isEdicao ? + "Agendamento atualizado com sucesso!" : + "Agendamento cadastrado com sucesso!"; + + System.out.println("Exibindo mensagem: " + mensagem); // Log para debug + agendamentoController.handleSuccessfulOperation(mensagem); + + fecharModal(); + } catch (Exception e) { + e.printStackTrace(); + agendamentoController.handleError("Ocorreu um erro inesperado!"); + } + } + + private void configurarHorariosDisponiveis() { + // Adicionar listener para atualizar horários disponíveis quando a data for selecionada + create_agendamento_date_field.valueProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + atualizarHorariosDisponiveis(newValue); + } + }); + } + + private void atualizarHorariosDisponiveis(LocalDate data) { + // Limpar horários anteriores + create_agendamento_time_field.getItems().clear(); + + // Buscar agendamentos para a data selecionada + List agendamentosNaData = agendamentoRepository.buscarAgendamentosPorData(data); + + // Gerar lista de horários + List horariosDisponiveis = gerarHorariosDisponiveis(data, agendamentosNaData); + + create_agendamento_time_field.getItems().addAll(horariosDisponiveis); + } + + private void gerarHorarios() { + List horarios = new ArrayList<>(); + LocalTime inicio = LocalTime.of(6, 0); + LocalTime fim = LocalTime.of(20, 0); + + while (inicio.isBefore(fim.plusMinutes(1))) { + horarios.add(inicio.format(DateTimeFormatter.ofPattern("HH:mm"))); + inicio = inicio.plusMinutes(15); + } + + create_agendamento_depTime_field.getItems().addAll(horarios); + } + + private List gerarHorariosDisponiveis(LocalDate data, List agendamentosExistentes) { + List horariosDisponiveis = new ArrayList<>(); + LocalTime inicio = LocalTime.of(6, 0); + LocalTime fim = LocalTime.of(20, 0); + + while (inicio.isBefore(fim.plusMinutes(1))) { + String horarioStr = inicio.format(DateTimeFormatter.ofPattern("HH:mm")); + + // Verificar se o horário já está ocupado + LocalTime finalInicio = inicio; + boolean horarioOcupado = agendamentosExistentes.stream() + .anyMatch(a -> a.getHorarioAgendamento().equals(finalInicio)); + + if (!horarioOcupado) { + horariosDisponiveis.add(horarioStr); + } + + inicio = inicio.plusMinutes(15); + } + + return horariosDisponiveis; + } + + private void configurarCampoData() { + // Definir formato de data brasileiro + create_agendamento_date_field.setConverter(new StringConverter() { + final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + + @Override + public String toString(LocalDate date) { + return date != null ? dateFormatter.format(date) : ""; + } + + @Override + public LocalDate fromString(String string) { + return string != null && !string.isEmpty() ? + LocalDate.parse(string, dateFormatter) : null; + } + }); + + // Impedir seleção de datas anteriores + create_agendamento_date_field.setDayCellFactory(picker -> new DateCell() { + @Override + public void updateItem(LocalDate date, boolean empty) { + super.updateItem(date, empty); + setDisable(empty || date.isBefore(LocalDate.now())); + } + }); + } + + private void carregarServicos() { + List servicos = servicoRepository.findAll(); + create_agendamento_servico_field.getItems().addAll(servicos); + } + + private void carregarClientes() { + List clientes = clienteRepository.findAll(); + create_agendamento_client_field.getItems().addAll(clientes); + + // Adicionar listener para filtrar pets quando cliente for selecionado + create_agendamento_client_field.setOnAction(event -> { + Cliente clienteSelecionado = create_agendamento_client_field.getValue(); + if (clienteSelecionado != null) { + carregarPetsPorCliente(clienteSelecionado); + } + }); + } + + private void carregarPetsPorCliente(Cliente cliente) { + // Limpar pets anteriores + create_agendamento_pet_field.getItems().clear(); + + // Carregar apenas pets do cliente selecionado + create_agendamento_pet_field.getItems().addAll(cliente.getPets()); + } + + private void fecharModal() { + // Obter a janela atual e fechá-la + Stage stage = (Stage) create_agendamento_date_field.getScene().getWindow(); + stage.close(); + } + + private boolean validarCampos() { + if (create_agendamento_date_field.getValue() == null) { + mostrarErro("Selecione uma data"); + return false; + } + if (create_agendamento_time_field.getValue() == null) { + mostrarErro("Selecione um horário"); + return false; + } + if (create_agendamento_servico_field.getValue() == null) { + mostrarErro("Selecione um serviço"); + return false; + } + if (create_agendamento_client_field.getValue() == null) { + mostrarErro("Selecione um cliente"); + return false; + } + if (create_agendamento_pet_field.getValue() == null) { + mostrarErro("Selecione um pet"); + return false; + } + return true; + } + + private void mostrarErro(String mensagem) { + // Implementar exibição de erro (Alert, por exemplo) + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("Erro de Validação"); + alert.setHeaderText(null); + alert.setContentText(mensagem); + alert.showAndWait(); + } + + public void configurarParaCadastro() { + endServiceContainer.setVisible(false); + endServiceContainer.setManaged(false); + lastRegisterContainer.setStyle("-fx-border-color: transparent transparent #cccccc transparent;"); + lastRegisterContainer.setPadding(new Insets(0, 30, 20, 30)); + actionButton.setText("Cadastrar"); + } + + public void setAgendamentoController(AgendamentoController agendamentoController) { + this.agendamentoController = agendamentoController; + } + + public void configurarParaEdicao(Agendamento agendamento) { + this.agendamentoAtual = agendamento; + agendamento_id_field.setText(agendamento.getId().toString()); // Preencher o campo de ID invisível + + configurarParaCadastro(); + actionButton.setText("Atualizar"); + + // populando campos do formulario com dados atuais do agendamento + create_agendamento_date_field.setValue(agendamento.getDataAgendamento()); + create_agendamento_time_field.setValue( + agendamento.getHorarioAgendamento().format(DateTimeFormatter.ofPattern("HH:mm")) + ); + create_agendamento_servico_field.setValue(agendamento.getServico()); + create_agendamento_client_field.setValue(agendamento.getCliente()); + create_agendamento_pet_field.setValue(agendamento.getAnimal()); + } + + public void configurarParaFinalizar(Agendamento agendamento) { + this.agendamentoAtual = agendamento; + finish.setText("true"); + actionButton.setText("Finalizar"); + + create_agendamento_date_field.setValue(agendamento.getDataAgendamento()); + create_agendamento_date_field.setDisable(true); + + create_agendamento_time_field.setValue( + agendamento.getHorarioAgendamento().format(DateTimeFormatter.ofPattern("HH:mm")) + ); + create_agendamento_time_field.setDisable(true); + + create_agendamento_servico_field.setValue(agendamento.getServico()); + create_agendamento_servico_field.setDisable(true); + + create_agendamento_client_field.setValue(agendamento.getCliente()); + create_agendamento_client_field.setDisable(true); + + create_agendamento_pet_field.setValue(agendamento.getAnimal()); + create_agendamento_pet_field.setDisable(true); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarClienteController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarClienteController.java new file mode 100644 index 0000000..fdae565 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarClienteController.java @@ -0,0 +1,163 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.modals; + +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.ClienteController; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import com.carvalhotechsolutions.mundoanimal.repositories.ClienteRepository; +import com.carvalhotechsolutions.mundoanimal.utils.MaskedTextField; +import com.carvalhotechsolutions.mundoanimal.utils.TextFormatterManager; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.ResourceBundle; + +public class ModalCriarClienteController { + @FXML + private Label titleLabel; + + @FXML + private Button actionButton; + + @FXML + private TextField client_id_field; + + @FXML + private TextField create_client_name_field; + + @FXML + private MaskedTextField create_client_phone_field; + + private final ClienteRepository clienteRepository = new ClienteRepository(); + + // Referência para o controlador principal + private ClienteController clienteController; + + private Cliente clienteAtual; // Armazena o cliente a ser editado (se edição) + + + public void setClienteController(ClienteController clienteController) { + this.clienteController = clienteController; + } + + @FXML + public void cadastrarCliente() { + String nome = create_client_name_field.getText(); + String telefone = create_client_phone_field.getText(); + + if (!validarInputs(nome, telefone)) { + return; + } + + try { + Cliente cliente; + boolean isEdicao = client_id_field.getText() != null && !client_id_field.getText().isEmpty(); + + if (isEdicao) { + Long id = Long.parseLong(client_id_field.getText()); + cliente = clienteRepository.findById(id); + System.out.println("Editando cliente com ID: " + id); // Log para debug + } else { + cliente = new Cliente(); + System.out.println("Criando novo cliente"); // Log para debug + } + + cliente.setNome(nome); + cliente.setTelefone(telefone); + clienteRepository.save(cliente); + + if (clienteController == null) { + System.out.println("ERRO: clienteController é nulo!"); // Log para debug + return; + } + + // Atualizar a TableView e mostrar feedback + clienteController.atualizarTableView(); + + String mensagem = isEdicao ? + "Cliente atualizado com sucesso!" : + "Cliente cadastrado com sucesso!"; + + System.out.println("Exibindo mensagem: " + mensagem); // Log para debug + clienteController.handleSuccessfulOperation(mensagem); + + fecharModal(); + } catch (Exception e) { + System.out.println("Erro ao salvar cliente: " + e.getMessage()); // Log para debug + if (clienteController != null) { + clienteController.handleError("Erro ao " + + (client_id_field.getText().isEmpty() ? "cadastrar" : "atualizar") + + " cliente!"); + } + e.printStackTrace(); + } + } + + // Configurar o modal para edição + public void configurarParaEdicao(Cliente cliente) { + this.clienteAtual = cliente; + + // Atualizar campos + client_id_field.setText(cliente.getId().toString()); // Preencher o campo de ID invisível + create_client_name_field.setText(cliente.getNome()); + create_client_phone_field.setText(cliente.getTelefone()); + + // Alterar título e botão + titleLabel.setText("Editar Cliente"); + actionButton.setText("Salvar"); + } + + private boolean validarInputs(String nome, String telefone) { + + nome = nome.trim(); + telefone = telefone.trim(); // trim() usado para remover espaços desnecessários + + if (nome.isEmpty() || telefone.isEmpty()) { + mostrarAlerta("Erro", "Campo(s) obrigatório(s) vazio(s)!", Alert.AlertType.ERROR); + return false; + } + String finalTelefone = telefone; + boolean telefoneJaCadastrado = clienteRepository.findAll().stream() + .anyMatch(cliente -> cliente.getTelefone().equals(finalTelefone) && + (clienteAtual == null || !cliente.getId().equals(clienteAtual.getId()))); + if (telefoneJaCadastrado){ + mostrarAlerta("Erro", "O telefone informado já está cadastrado no sistema.", Alert.AlertType.ERROR); + return false; + } + String finalNome = nome; + boolean nomeJaCadastrado = clienteRepository.findAll().stream() + .anyMatch(cliente -> cliente.getNome().equalsIgnoreCase(finalNome) && + (clienteAtual == null || !cliente.getId().equals(clienteAtual.getId()))); + + if (nomeJaCadastrado) { + mostrarAlerta("Erro", "Já existe um cliente cadastrado com esse nome.", Alert.AlertType.ERROR); + return false; + } + + return true; + } + + @FXML + private void phoneKeyReleased(){ + TextFormatterManager tfm = new TextFormatterManager(); + tfm.setMask("(##)#####-####"); + tfm.setCaracteresValidos("0123456789"); + tfm.setTf(create_client_phone_field); + tfm.formatter(); + } + + @FXML + public void fecharModal() { + Stage stage = (Stage) create_client_name_field.getScene().getWindow(); + stage.close(); + } + + private void mostrarAlerta(String titulo, String mensagem, Alert.AlertType tipo) { + Alert alerta = new Alert(tipo); + alerta.setTitle(titulo); + alerta.setHeaderText(null); + alerta.setContentText(mensagem); + alerta.showAndWait(); + } +} \ No newline at end of file diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarPetController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarPetController.java new file mode 100644 index 0000000..09fb859 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarPetController.java @@ -0,0 +1,187 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.modals; + +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.AnimalController; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.ClienteController; +import com.carvalhotechsolutions.mundoanimal.enums.EspecieAnimal; +import com.carvalhotechsolutions.mundoanimal.model.Animal; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import com.carvalhotechsolutions.mundoanimal.repositories.AnimalRepository; +import com.carvalhotechsolutions.mundoanimal.repositories.ClienteRepository; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.ResourceBundle; + +public class ModalCriarPetController implements Initializable { + @FXML + private Label titleLabel; + + @FXML + private Button actionButton; + + @FXML + private TextField pet_id_field; + + @FXML + private TextField create_pet_clientName_field; + + @FXML + private TextField create_pet_clientPhone_field; + + @FXML + private TextField create_pet_name_field; + + @FXML + private ComboBox create_pet_specie_field; + + @FXML + private TextField create_pet_race_field; + + @FXML + private TextField create_pet_age_field; + + @FXML + private TextArea create_pet_notes_field; + + private AnimalRepository animalRepository = new AnimalRepository(); + + private ClienteRepository clienteRepository = new ClienteRepository(); + + private ClienteController clienteController; + + private AnimalController animalController; + + private Cliente cliente; // Armazena o dono do pet que está sendo cadastrado + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + create_pet_specie_field.getItems().addAll(EspecieAnimal.values()); + Platform.runLater(() -> create_pet_name_field.requestFocus()); + } + + @FXML + public void cadastrarPet() { + String nome = create_pet_name_field.getText(); + EspecieAnimal especie = create_pet_specie_field.getValue(); + String raca = create_pet_race_field.getText(); + String idade = create_pet_age_field.getText(); + String observacoes = create_pet_notes_field.getText(); + + if (!validarInputs(nome, especie)) { + return; + } + + try { + Animal animal; + boolean isEdicao = pet_id_field.getText() != null && !pet_id_field.getText().isEmpty(); + + if (isEdicao) { + Long id = Long.parseLong(pet_id_field.getText()); + animal = animalRepository.findById(id); // Recupera o animal existente + System.out.println("Editando animal com ID: " + id); // Log para debug + } else { + animal = new Animal(); // Novo animal + System.out.println("Criando novo animal"); // Log para debug + } + + animal.setNome(nome); + animal.setEspecie(especie); + animal.setRaca(raca.isEmpty() ? null : raca); + animal.setIdade(idade.isEmpty() ? null : Integer.parseInt(idade)); + animal.setObservacoes(observacoes.isEmpty() ? null : observacoes); + animal.setDono(this.cliente); + animalRepository.save(animal); + + if (clienteController != null) { + clienteController.atualizarTableView(); + } + + String mensagem = isEdicao ? + "Pet atualizado com sucesso!" : + "Pet cadastrado com sucesso!"; + + + System.out.println("Exibindo mensagem: " + mensagem); // Log para debug + + if(isEdicao) { + animalController.handleSuccessfulOperation(mensagem); + } + else { + clienteController.handleSuccessfulOperation(mensagem); + } + + fecharModal(); + + } catch (NumberFormatException e) { + mostrarAlerta("Erro", "Idade deve ser um número válido!", Alert.AlertType.ERROR); + } catch (Exception e) { + mostrarAlerta("Erro", "Erro ao cadastrar pet: " + e.getMessage(), Alert.AlertType.ERROR); + e.printStackTrace(); + } + } + + private boolean validarInputs(String nome, EspecieAnimal especie) { + if (nome.isEmpty() || especie == null) { + mostrarAlerta("Erro", "Nome do pet e espécie são obrigatórios!", Alert.AlertType.ERROR); + return false; + } + return true; + } + + public void configurarParaCadastro(Long clientId) { + this.cliente = clienteRepository.findById(clientId); + + create_pet_clientName_field.setText(cliente.getNome()); + create_pet_clientPhone_field.setText(cliente.getTelefone()); + create_pet_clientName_field.setEditable(false); + create_pet_clientPhone_field.setEditable(false); + } + + private void mostrarAlerta(String titulo, String mensagem, Alert.AlertType tipo) { + Alert alerta = new Alert(tipo); + alerta.setTitle(titulo); + alerta.setHeaderText(null); + alerta.setContentText(mensagem); + alerta.showAndWait(); + } + + @FXML + public void fecharModal() { + Stage stage = (Stage) create_pet_name_field.getScene().getWindow(); + stage.close(); + } + + public void configurarParaEdicao(Animal animal) { + this.cliente = animal.getDono(); + + // Atualizar campos + pet_id_field.setText(animal.getId().toString()); // Preencher o campo de ID invisível + create_pet_clientName_field.setText(cliente.getNome()); + create_pet_clientPhone_field.setText(cliente.getTelefone()); + create_pet_clientName_field.setEditable(false); + create_pet_clientPhone_field.setEditable(false); + + // Preenchendo campos referentes ao animal + create_pet_name_field.setText(animal.getNome()); + create_pet_specie_field.setValue(animal.getEspecie()); + create_pet_race_field.setText(animal.getRaca() == null ? "" : animal.getRaca()); + create_pet_age_field.setText(animal.getIdade() == null ? "" : animal.getIdade().toString()); + create_pet_notes_field.setText(animal.getObservacoes() == null ? "" : animal.getObservacoes()); + + // Alterar título e botão + titleLabel.setText("Editar Pet"); + actionButton.setText("Salvar"); + } + + public void setClienteController(ClienteController clienteController) { + this.clienteController = clienteController; + } + + public void setAnimalController(AnimalController animalController) { + this.animalController = animalController; + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarSecretarioController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarSecretarioController.java similarity index 60% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarSecretarioController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarSecretarioController.java index 83937fb..c291e8c 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarSecretarioController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarSecretarioController.java @@ -1,15 +1,14 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.modals; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.SecretarioController; import com.carvalhotechsolutions.mundoanimal.model.Secretario; -import com.carvalhotechsolutions.mundoanimal.model.Servico; -import com.carvalhotechsolutions.mundoanimal.model.enums.TipoUsuario; +import com.carvalhotechsolutions.mundoanimal.enums.TipoUsuario; import com.carvalhotechsolutions.mundoanimal.repositories.SecretarioRepository; -import com.carvalhotechsolutions.mundoanimal.security.PasswordUtils; +import com.carvalhotechsolutions.mundoanimal.utils.MaskedTextField; +import com.carvalhotechsolutions.mundoanimal.utils.PasswordManager; +import com.carvalhotechsolutions.mundoanimal.utils.TextFormatterManager; import javafx.fxml.FXML; -import javafx.geometry.Insets; import javafx.scene.control.*; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import javafx.stage.Stage; public class ModalCriarSecretarioController { @@ -17,7 +16,7 @@ public class ModalCriarSecretarioController { private TextField create_secretary_name_field; @FXML - private TextField create_secretary_phone_field; + private MaskedTextField create_secretary_phone_field; @FXML private PasswordField create_secretary_password_field; @@ -38,7 +37,6 @@ public void setSecretarioController(SecretarioController secretarioController) { @FXML public void cadastrarSecretario() { - String nome = create_secretary_name_field.getText(); String telefone = create_secretary_phone_field.getText(); String password = create_secretary_password_field.getText(); @@ -54,13 +52,14 @@ public void cadastrarSecretario() { secretario.setNomeUsuario(nome); secretario.setTelefone(telefone); - secretario.setSenha(PasswordUtils.hashPassword(create_secretary_password_field.getText())); + secretario.setSenha(PasswordManager.hashPassword(create_secretary_password_field.getText())); secretario.setTipoUsuario(TipoUsuario.SECRETARIO); secretarioRepository.save(secretario); if (secretarioController != null) { secretarioController.atualizarTableView(); + secretarioController.handleSuccessfulOperation("Secretário(a) cadastrado(a) com sucesso!"); } fecharModal(); @@ -78,14 +77,42 @@ public void fecharModal() { } private boolean validarInputs(String nome, String telefone, String password, String passwordConfirmation) { + nome = nome.trim(); + telefone = telefone.trim(); + if (nome.isEmpty() || telefone.isEmpty() || password.isEmpty() || passwordConfirmation.isEmpty()) { mostrarAlerta("Erro", "Campo(s) obrigatório(s) vazio(s)!", Alert.AlertType.ERROR); return false; } + if (!password.equals(passwordConfirmation)) { mostrarAlerta("Erro", "Senhas não estão iguais.", Alert.AlertType.ERROR); return false; } + + if (password.length() < 6) { + mostrarAlerta("Erro", "A senha deve ter no mínimo 6 caracteres.", Alert.AlertType.ERROR); + return false; + } + + String finalTelefone = telefone; + + boolean telefoneJaCadastrado = secretarioRepository.findAll().stream() + .anyMatch(secretario -> secretario.getTelefone().equals(finalTelefone) && + (secretarioAtual == null || !secretario.getId().equals(secretarioAtual.getId()))); + if (telefoneJaCadastrado) { + mostrarAlerta("Erro", "O telefone informado já está cadastrado no sistema.", Alert.AlertType.ERROR); + return false; + } + + String finalNome = nome; + boolean nomeJaCadastrado = secretarioRepository.findAll().stream() + .anyMatch(secretario -> secretario.getNomeUsuario().equalsIgnoreCase(finalNome) && + (secretarioAtual == null || !secretario.getId().equals(secretarioAtual.getId()))); + if (nomeJaCadastrado) { + mostrarAlerta("Erro", "Já existe um secretário cadastrado com esse nome.", Alert.AlertType.ERROR); + return false; + } return true; } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarServicoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarServicoController.java similarity index 68% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarServicoController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarServicoController.java index bd27605..1e576f2 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalCriarServicoController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalCriarServicoController.java @@ -1,8 +1,10 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.modals; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.ServicoController; import com.carvalhotechsolutions.mundoanimal.model.Servico; import com.carvalhotechsolutions.mundoanimal.repositories.ServicoRepository; +import com.carvalhotechsolutions.mundoanimal.utils.TextFormatterManager; import javafx.fxml.FXML; import javafx.scene.control.*; import javafx.stage.Stage; @@ -28,7 +30,7 @@ public class ModalCriarServicoController { @FXML private TextArea create_service_description_field; - private ServicoRepository servicoRepository = new ServicoRepository(); + private final ServicoRepository servicoRepository = new ServicoRepository(); // Referência para o controlador principal private ServicoController servicoController; @@ -53,15 +55,16 @@ public void cadastrarServico() { try { BigDecimal valor = new BigDecimal(valorStr.replace(",", ".")); - Servico servico; + boolean isEdicao = service_id_field.getText() != null && !service_id_field.getText().isEmpty(); - // Verificar se o serviço já existe (edição) - if (service_id_field.getText() != null && !service_id_field.getText().isEmpty()) { + if (isEdicao) { Long id = Long.parseLong(service_id_field.getText()); servico = servicoRepository.findById(id); // Recupera o serviço existente + System.out.println("Editando serviço com ID: " + id); // Log para debug } else { servico = new Servico(); // Novo serviço + System.out.println("Criando novo serviço"); // Log para debug } servico.setNomeServico(nome); @@ -71,12 +74,20 @@ public void cadastrarServico() { // Persistir no banco de dados servicoRepository.save(servico); - // Atualizar a TableView no controlador principal - if (servicoController != null) { - servicoController.atualizarTableView(); + if (servicoController == null) { + System.out.println("ERRO: servicoController é nulo!"); // Log para debug + return; } - // Fechar modal + servicoController.atualizarTableView(); + + String mensagem = isEdicao ? + "Serviço atualizado com sucesso!" : + "Serviço cadastrado com sucesso!"; + + System.out.println("Exibindo mensagem: " + mensagem); // Log para debug + servicoController.handleSuccessfulOperation(mensagem); + fecharModal(); } catch (NumberFormatException e) { @@ -100,11 +111,12 @@ public void configurarParaEdicao(Servico servico) { } private boolean validarInputs(String nome, String valor) { + nome = nome.trim(); + valor = valor.trim(); if (nome.isEmpty() || valor.isEmpty()) { mostrarAlerta("Erro", "Campo(s) obrigatório(s) vazio(s)!", Alert.AlertType.ERROR); return false; } - // Verificar se o valor é numérico try { new BigDecimal(valor.replace(",", ".")); @@ -112,6 +124,14 @@ private boolean validarInputs(String nome, String valor) { mostrarAlerta("Erro", "O campo 'Valor' deve ser numérico!", Alert.AlertType.ERROR); return false; } + String nomeServico = nome; + boolean servicoJaCadastrado = servicoRepository.findAll().stream().anyMatch(s -> + nomeServico.equalsIgnoreCase(s.getNomeServico()) && (servicoAtual == null || + !servicoAtual.getId().equals(s.getId()))); + if (servicoJaCadastrado) { + mostrarAlerta("Erro", "Já existe um serviço cadastrado com esse nome.", Alert.AlertType.ERROR); + return false; + } return true; } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalDetalhesPetController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalDetalhesPetController.java new file mode 100644 index 0000000..87e5f18 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalDetalhesPetController.java @@ -0,0 +1,42 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.modals; + +import com.carvalhotechsolutions.mundoanimal.model.Animal; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.text.Text; +import javafx.stage.Stage; + +public class ModalDetalhesPetController { + @FXML + private Text pet_name_label; + + @FXML + private Text pet_specie_label; + + @FXML + private Text pet_race_label; + + @FXML + private Text pet_age_label; + + @FXML + private Text pet_notes_label; + + private Animal animal; + + public void configurarParaExibicao(Animal animal) { + this.animal = animal; + + pet_name_label.setText(animal.getNome()); + pet_specie_label.setText(animal.getEspecie().toString()); + pet_race_label.setText(animal.getRaca() == null ? "Sem informações" : animal.getRaca()); + pet_age_label.setText(animal.getIdade() == null ? "Sem informações" : animal.getIdade().toString() + " anos"); + pet_notes_label.setText(animal.getObservacoes() == null ? "Sem informações" : animal.getObservacoes()); + } + + @FXML + public void fecharModal() { + Stage stage = (Stage) pet_name_label.getScene().getWindow(); + stage.close(); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalEditarSecretarioController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalEditarSecretarioController.java similarity index 83% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalEditarSecretarioController.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalEditarSecretarioController.java index 98ecf65..f95cb60 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/ModalEditarSecretarioController.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalEditarSecretarioController.java @@ -1,5 +1,6 @@ -package com.carvalhotechsolutions.mundoanimal.controllers; +package com.carvalhotechsolutions.mundoanimal.controllers.modals; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.SecretarioController; import com.carvalhotechsolutions.mundoanimal.model.Secretario; import com.carvalhotechsolutions.mundoanimal.repositories.SecretarioRepository; import javafx.fxml.FXML; @@ -43,10 +44,14 @@ public void editarSecretario() { secretarioRepository.save(secretario); - if (secretarioController != null) { - secretarioController.atualizarTableView(); + if (secretarioController == null) { + System.out.println("ERRO: secretarioController é nulo!"); // Log para debug + return; } + secretarioController.atualizarTableView(); + secretarioController.handleSuccessfulOperation("Secretário(a) atualizado(a) com sucesso!"); + fecharModal(); } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalLoginSucessoController.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalLoginSucessoController.java new file mode 100644 index 0000000..c672674 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/modals/ModalLoginSucessoController.java @@ -0,0 +1,4 @@ +package com.carvalhotechsolutions.mundoanimal.controllers.modals; + +public class ModalLoginSucessoController { +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/DatabaseChecker.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/database/DatabaseChecker.java similarity index 73% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/DatabaseChecker.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/database/DatabaseChecker.java index 79e9152..21d5b9e 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/DatabaseChecker.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/database/DatabaseChecker.java @@ -1,9 +1,11 @@ -package com.carvalhotechsolutions.mundoanimal; +package com.carvalhotechsolutions.mundoanimal.database; import com.carvalhotechsolutions.mundoanimal.model.Administrador; -import com.carvalhotechsolutions.mundoanimal.model.enums.TipoUsuario; -import com.carvalhotechsolutions.mundoanimal.security.PasswordUtils; +import com.carvalhotechsolutions.mundoanimal.enums.TipoUsuario; +import com.carvalhotechsolutions.mundoanimal.utils.PasswordManager; import jakarta.persistence.EntityManager; +import javafx.application.Platform; +import javafx.scene.control.Alert; public class DatabaseChecker { @@ -12,8 +14,7 @@ private DatabaseChecker() { throw new UnsupportedOperationException("Esta classe não pode ser instanciada"); } - public static boolean testConnectionAndInitializeAdmin() { - + public static void testConnectionAndInitializeAdmin() { try (EntityManager em = JPAutil.getEntityManager()) { em.getTransaction().begin(); @@ -24,11 +25,15 @@ public static boolean testConnectionAndInitializeAdmin() { // Após verificar a conexão, inicializa o administrador padrão initializeAdmin(em); - return true; } catch (Exception e) { System.err.println("Erro ao conectar ao banco de dados: " + e.getMessage()); - return false; + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("Erro de Conexão"); + alert.setHeaderText("Não foi possível conectar ao banco de dados."); + alert.setContentText("Verifique as configurações do banco e tente novamente."); + alert.showAndWait(); + Platform.exit(); // Encerra a aplicação } } @@ -45,7 +50,7 @@ private static void initializeAdmin(EntityManager em) { Administrador admin = new Administrador(); admin.setCpf("123.456.789-10"); admin.setNomeUsuario("admin"); - admin.setSenha(PasswordUtils.hashPassword("admin")); + admin.setSenha(PasswordManager.hashPassword("admin")); admin.setTipoUsuario(TipoUsuario.ADMINISTRADOR); // Persiste o administrador no banco diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/JPAutil.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/database/JPAutil.java similarity index 86% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/JPAutil.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/database/JPAutil.java index 9f96da7..55daddc 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/JPAutil.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/database/JPAutil.java @@ -1,4 +1,4 @@ -package com.carvalhotechsolutions.mundoanimal; +package com.carvalhotechsolutions.mundoanimal.database; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/EspecieAnimal.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/EspecieAnimal.java new file mode 100644 index 0000000..e02d0de --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/EspecieAnimal.java @@ -0,0 +1,26 @@ +package com.carvalhotechsolutions.mundoanimal.enums; + +public enum EspecieAnimal { + CACHORRO("Cachorro"), + GATO("Gato"), + PEIXE("Peixe"), + PASSARO("Pássaro"), + COELHO("Coelho"), + HAMSTER("Hamster"), + CAVALO("Cavalo"), + REPTIL("Réptil"), + ANFIBIO("Anfíbio"), + OUTRO("Outro"); + + private final String displayName; + + EspecieAnimal(String displayName) { + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } +} + diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/ScreenEnum.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/ScreenEnum.java new file mode 100644 index 0000000..81741d1 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/ScreenEnum.java @@ -0,0 +1,42 @@ +package com.carvalhotechsolutions.mundoanimal.enums; + +public enum ScreenEnum { + LOGIN("/fxml/autenticacao/login.fxml", "Login", ScreenType.FULL), + RECUPERAR_SENHA("/fxml/autenticacao/recuperarSenha.fxml", "Recuperar senha", ScreenType.FULL), + REDEFINIR_SENHA("/fxml/autenticacao/redefinirSenha.fxml", "Redefinir Senha", ScreenType.FULL), + MENU("/fxml/gerenciamento/menu.fxml", "Menu Principal", ScreenType.TEMPLATE), + SECRETARIOS("/fxml/gerenciamento/secretarios.fxml", "Secretários", ScreenType.CONTENT), + SERVICOS("/fxml/gerenciamento/servicos.fxml", "Serviços", ScreenType.CONTENT), + CLIENTES("/fxml/gerenciamento/clientes.fxml", "Clientes", ScreenType.CONTENT), + PETS("/fxml/gerenciamento/pets.fxml", "Pets", ScreenType.CONTENT), + AGENDAMENTOS("/fxml/gerenciamento/agendamentos.fxml", "Agendamentos", ScreenType.CONTENT); + + private final String fxmlPath; + private final String title; + private final ScreenType type; + + ScreenEnum(String fxmlPath, String title, ScreenType type) { + this.fxmlPath = fxmlPath; + this.title = title; + this.type = type; + } + + public String getFxmlPath() { + return fxmlPath; + } + + public String getTitle() { + return title; + } + + public ScreenType getType() { + return type; + } + + // Enum para definir o tipo de tela + public enum ScreenType { + FULL, // Tela completa (como login) + TEMPLATE, // Template com menu (como menu.fxml) + CONTENT // Conteúdo para ser carregado no contentArea + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/StatusAgendamento.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/StatusAgendamento.java similarity index 75% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/StatusAgendamento.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/enums/StatusAgendamento.java index 40858eb..db6c94a 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/StatusAgendamento.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/StatusAgendamento.java @@ -1,4 +1,4 @@ -package com.carvalhotechsolutions.mundoanimal.model.enums; +package com.carvalhotechsolutions.mundoanimal.enums; public enum StatusAgendamento { PENDENTE, // Agendamento criado e aguardando realização diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/TipoUsuario.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/TipoUsuario.java similarity index 52% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/TipoUsuario.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/enums/TipoUsuario.java index d474d4f..63fbd82 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/TipoUsuario.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/enums/TipoUsuario.java @@ -1,4 +1,4 @@ -package com.carvalhotechsolutions.mundoanimal.model.enums; +package com.carvalhotechsolutions.mundoanimal.enums; public enum TipoUsuario { ADMINISTRADOR, diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Administrador.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Administrador.java index ca18cda..de9ee0d 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Administrador.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Administrador.java @@ -7,7 +7,6 @@ @Entity @Table(name = "tb_admin") public class Administrador extends Usuario { - @Column(nullable = false, unique = true) private String cpf; diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Agendamento.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Agendamento.java index d716c74..b833bd0 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Agendamento.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Agendamento.java @@ -1,11 +1,11 @@ package com.carvalhotechsolutions.mundoanimal.model; -import com.carvalhotechsolutions.mundoanimal.model.enums.StatusAgendamento; +import com.carvalhotechsolutions.mundoanimal.enums.StatusAgendamento; import jakarta.persistence.*; import java.time.LocalDate; import java.time.LocalTime; -import java.util.UUID; +import java.time.format.DateTimeFormatter; @Entity @Table(name = "tb_agendamentos") @@ -47,6 +47,14 @@ public Agendamento() { this.status = StatusAgendamento.PENDENTE; } + public String getDataHoraFormatada() { + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm"); + + return dataAgendamento.format(dateFormatter) + " - " + + horarioAgendamento.format(timeFormatter); + } + public Long getId() { return id; } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Animal.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Animal.java index 6160581..dca3d02 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Animal.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Animal.java @@ -1,10 +1,8 @@ package com.carvalhotechsolutions.mundoanimal.model; -import com.carvalhotechsolutions.mundoanimal.model.enums.EspecieAnimal; +import com.carvalhotechsolutions.mundoanimal.enums.EspecieAnimal; import jakarta.persistence.*; -import java.util.UUID; - @Entity @Table(name = "tb_animais") public class Animal { @@ -20,13 +18,13 @@ public class Animal { @Column(nullable = false) private EspecieAnimal especie; - @Column(nullable = false) + @Column() private String raca; - @Column(nullable = false) + @Column() private Integer idade; - @Column(nullable = false) + @Column() private String observacoes; @ManyToOne @@ -88,4 +86,9 @@ public Cliente getDono() { public void setDono(Cliente dono) { this.dono = dono; } + + @Override + public String toString() { + return this.nome; + } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Cliente.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Cliente.java index 10d62b4..ee46438 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Cliente.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Cliente.java @@ -2,7 +2,9 @@ import jakarta.persistence.*; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Entity @Table(name = "tb_cliente") @@ -18,8 +20,8 @@ public class Cliente { @Column(unique = true, nullable = false) private String telefone; - @OneToMany(mappedBy = "dono", cascade = CascadeType.ALL, orphanRemoval = true) - private List pets; + @OneToMany(mappedBy = "dono", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + private List pets = new ArrayList<>(); public Long getId() { return id; @@ -52,4 +54,18 @@ public List getPets() { public void setPets(List pets) { this.pets = pets; } + + public String getPetsFormatados() { + if (pets.isEmpty()) { + return ""; + } + return pets.stream() + .map(Animal::getNome) + .collect(Collectors.joining(", ")); + } + + @Override + public String toString() { + return getNome(); + } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Servico.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Servico.java index 7f894f3..d12a209 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Servico.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Servico.java @@ -52,4 +52,10 @@ public String getDescricao() { public void setDescricao(String descricao) { this.descricao = descricao; } + + @Override + public String toString() { + return getNomeServico(); + } } + diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Usuario.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Usuario.java index 5e7177d..06ea560 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Usuario.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/Usuario.java @@ -1,10 +1,8 @@ package com.carvalhotechsolutions.mundoanimal.model; -import com.carvalhotechsolutions.mundoanimal.model.enums.TipoUsuario; +import com.carvalhotechsolutions.mundoanimal.enums.TipoUsuario; import jakarta.persistence.*; -import java.util.UUID; - @MappedSuperclass public abstract class Usuario { diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/EspecieAnimal.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/EspecieAnimal.java deleted file mode 100644 index 38b8772..0000000 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/model/enums/EspecieAnimal.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.carvalhotechsolutions.mundoanimal.model.enums; - -public enum EspecieAnimal { - CACHORRO, - GATO, - PEIXE, - PASSARO, - COELHO, - HAMSTER, - CAVALO, - REPTIL, - ANFIBIO, - OUTRO -} - diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AgendamentoRepository.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AgendamentoRepository.java new file mode 100644 index 0000000..24102f4 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AgendamentoRepository.java @@ -0,0 +1,79 @@ +package com.carvalhotechsolutions.mundoanimal.repositories; + +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.model.Agendamento; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +public class AgendamentoRepository { + public Agendamento save(Agendamento agendamento) { + try(EntityManager em = JPAutil.getEntityManager()) { + em.getTransaction().begin(); + if (agendamento.getId() == null) { + em.persist(agendamento); + } else { + em.merge(agendamento); + } + em.getTransaction().commit(); + return agendamento; + } + } + + public Agendamento findById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + TypedQuery query = em.createQuery("SELECT a FROM Agendamento a WHERE a.id = :id", Agendamento.class); + query.setParameter("id", id); + return query.getSingleResult(); + } + } + + public List findAll() { + try(EntityManager em = JPAutil.getEntityManager()) { + String jpql = "SELECT a FROM Agendamento a " + + "ORDER BY a.dataAgendamento ASC, a.horarioAgendamento ASC"; + + return em.createQuery(jpql, Agendamento.class) + .getResultList(); + } + } + + public void deleteById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + Agendamento agendamento = em.find(Agendamento.class, id); + if(agendamento != null) { + em.getTransaction().begin(); + em.remove(agendamento); + em.getTransaction().commit(); + } + else { + throw new IllegalArgumentException("Agendamento not found!"); + } + } + } + + public boolean verificarDisponibilidadeHorario(LocalDate data, LocalTime horario) { + String jpql = "SELECT COUNT(a) FROM Agendamento a " + + "WHERE a.dataAgendamento = :data " + + "AND a.horarioAgendamento = :horario"; + + EntityManager em = JPAutil.getEntityManager(); + Long count = em.createQuery(jpql, Long.class) + .setParameter("data", data) + .setParameter("horario", horario) + .getSingleResult(); + + return count == 0; + } + + public List buscarAgendamentosPorData(LocalDate data) { + String jpql = "SELECT a FROM Agendamento a WHERE a.dataAgendamento = :data"; + EntityManager em = JPAutil.getEntityManager(); + return em.createQuery(jpql, Agendamento.class) + .setParameter("data", data) + .getResultList(); + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AnimalRepository.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AnimalRepository.java new file mode 100644 index 0000000..9abc83a --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AnimalRepository.java @@ -0,0 +1,60 @@ +package com.carvalhotechsolutions.mundoanimal.repositories; + +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.model.Animal; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; + +import java.util.List; + +public class AnimalRepository { + public Animal save(Animal animal) { + try(EntityManager em = JPAutil.getEntityManager()) { + em.getTransaction().begin(); + if (animal.getId() == null) { + em.persist(animal); + } else { + em.merge(animal); + } + em.getTransaction().commit(); + return animal; + } + } + + public Animal findById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + TypedQuery query = em.createQuery("SELECT a FROM Animal a WHERE a.id = :id", Animal.class); + query.setParameter("id", id); + return query.getSingleResult(); + } + } + + public List findAll() { + try(EntityManager em = JPAutil.getEntityManager()) { + TypedQuery query = em.createQuery("SELECT a FROM Animal a ORDER BY a.nome", Animal.class); + return query.getResultList(); + } + } + + public void deleteById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + Animal animal = em.find(Animal.class, id); + if(animal != null) { + em.getTransaction().begin(); + + // Remover o animal da lista de pets do cliente + Cliente dono = animal.getDono(); + dono.getPets().remove(animal); + + em.remove(animal); + + em.remove(animal); + em.getTransaction().commit(); + } + else { + throw new IllegalArgumentException("Animal not found!"); + } + } + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ClienteRepository.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ClienteRepository.java new file mode 100644 index 0000000..39e8d13 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ClienteRepository.java @@ -0,0 +1,62 @@ +package com.carvalhotechsolutions.mundoanimal.repositories; + +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; +import com.carvalhotechsolutions.mundoanimal.model.Cliente; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; + +import java.util.List; + +public class ClienteRepository { + public Cliente save(Cliente cliente) { + try(EntityManager em = JPAutil.getEntityManager()) { + em.getTransaction().begin(); + if (cliente.getId() == null) { + em.persist(cliente); + } else { + em.merge(cliente); + } + em.getTransaction().commit(); + return cliente; + } + } + + public Cliente findById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + TypedQuery query = em.createQuery("SELECT c FROM Cliente c WHERE c.id = :id", Cliente.class); + query.setParameter("id", id); + return query.getSingleResult(); + } + } + + public List findAll() { + try(EntityManager em = JPAutil.getEntityManager()) { + TypedQuery query = em.createQuery("SELECT c FROM Cliente c ORDER BY c.nome", Cliente.class); + return query.getResultList(); + } + } + + public void deleteById(Long id) { + try(EntityManager em = JPAutil.getEntityManager()) { + Cliente cliente = em.find(Cliente.class, id); + if(cliente != null) { + em.getTransaction().begin(); + em.remove(cliente); + em.getTransaction().commit(); + } + else { + throw new IllegalArgumentException("Cliente not found!"); + } + } + } + + public boolean clientePossuiAgendamentos(Long clienteId) { + try (EntityManager em = JPAutil.getEntityManager()) { + String jpql = "SELECT COUNT(a) FROM Agendamento a WHERE a.cliente.id = :clienteId"; + Long count = em.createQuery(jpql, Long.class) + .setParameter("clienteId", clienteId) + .getSingleResult(); + return count > 0; + } + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/SecretarioRepository.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/SecretarioRepository.java index 9922e66..bd4e251 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/SecretarioRepository.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/SecretarioRepository.java @@ -1,6 +1,6 @@ package com.carvalhotechsolutions.mundoanimal.repositories; -import com.carvalhotechsolutions.mundoanimal.JPAutil; +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; import com.carvalhotechsolutions.mundoanimal.model.Secretario; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; @@ -32,7 +32,7 @@ public Secretario findById(Long id) { public List findAll() { try(EntityManager em = JPAutil.getEntityManager()) { - TypedQuery query = em.createQuery("SELECT s FROM Secretario s", Secretario.class); + TypedQuery query = em.createQuery("SELECT s FROM Secretario s ORDER BY s.nomeUsuario", Secretario.class); return query.getResultList(); } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ServicoRepository.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ServicoRepository.java index 191a2ba..0646c71 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ServicoRepository.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/ServicoRepository.java @@ -1,6 +1,6 @@ package com.carvalhotechsolutions.mundoanimal.repositories; -import com.carvalhotechsolutions.mundoanimal.JPAutil; +import com.carvalhotechsolutions.mundoanimal.database.JPAutil; import com.carvalhotechsolutions.mundoanimal.model.Servico; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; @@ -32,7 +32,7 @@ public Servico findById(Long id) { public List findAll() { try(EntityManager em = JPAutil.getEntityManager()) { - TypedQuery query = em.createQuery("SELECT s FROM Servico s", Servico.class); + TypedQuery query = em.createQuery("SELECT s FROM Servico s ORDER BY s.nomeServico", Servico.class); return query.getResultList(); } } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/FeedbackManager.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/FeedbackManager.java new file mode 100644 index 0000000..9a498c5 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/FeedbackManager.java @@ -0,0 +1,81 @@ +package com.carvalhotechsolutions.mundoanimal.utils; + +import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon; +import javafx.animation.FadeTransition; +import javafx.animation.PauseTransition; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.util.Duration; + +import java.io.IOException; + +public class FeedbackManager { + private static final String POPUP_FXML = "/fxml/popup/feedback.fxml"; + + public enum FeedbackType { + SUCCESS("CHECK_CIRCLE", "#FFFFFF", "#43b554"), + ERROR("TIMES_CIRCLE", "#FFFFFF" ,"#FF6F6F"), + WARNING("EXCLAMATION_CIRCLE", "#FFFFFF" ,"#FFA500"), + INFO("INFO_CIRCLE", "#FFFFFF" ,"#686AFF"); + + private final String icon; + private final String color; + private final String backgroundColor; + + FeedbackType(String icon, String color, String backgroundColor) { + this.icon = icon; + this.color = color; + this.backgroundColor = backgroundColor; + } + } + + public static void showFeedback(HBox container, String message, FeedbackType type) { + try { + FXMLLoader loader = new FXMLLoader(FeedbackManager.class.getResource(POPUP_FXML)); + HBox popupContent = loader.load(); + + // Configurar o estilo do popup + popupContent.getStyleClass().add("popup"); + popupContent.setStyle(String.format("-fx-background-color: %s;", type.backgroundColor)); + + // Encontrar e configurar os componentes + FontAwesomeIcon icon = (FontAwesomeIcon) popupContent.lookup("#icon"); + Label messageLabel = (Label) popupContent.lookup("#message"); + + // Configurar o conteúdo + icon.setGlyphName(type.icon); + icon.setFill(Color.web(type.color)); + messageLabel.setText(message); + + // Limpar popups anteriores + container.getChildren().removeIf(node -> node.getStyleClass().contains("popup")); + + // Adicionar o novo popup + container.getChildren().add(popupContent); + + // Animação de entrada + FadeTransition fadeIn = new FadeTransition(Duration.seconds(0.5), popupContent); + fadeIn.setFromValue(0); + fadeIn.setToValue(1); + fadeIn.play(); + + // Timer para remoção + PauseTransition delay = new PauseTransition(Duration.seconds(3)); + delay.setOnFinished(event -> { + FadeTransition fadeOut = new FadeTransition(Duration.seconds(0.5), popupContent); + fadeOut.setFromValue(1); + fadeOut.setToValue(0); + fadeOut.setOnFinished(e -> { + container.getChildren().remove(popupContent); + }); + fadeOut.play(); + }); + delay.play(); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/MaskedTextField.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/MaskedTextField.java new file mode 100644 index 0000000..06374de --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/MaskedTextField.java @@ -0,0 +1,502 @@ +package com.carvalhotechsolutions.mundoanimal.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.control.IndexRange; +import javafx.scene.control.TextField; + +/** + * This component receives a mask that dictate the valid input for this field. + * @author gbfragoso + * @version 2.1 + */ +public class MaskedTextField extends TextField { + + private static final char MASK_ESCAPE = '\''; + private static final char MASK_NUMBER = '#'; + private static final char MASK_CHARACTER = '?'; + private static final char MASK_HEXADECIMAL = 'H'; + private static final char MASK_UPPER_CHARACTER = 'U'; + private static final char MASK_LOWER_CHARACTER = 'L'; + private static final char MASK_CHAR_OR_NUM = 'A'; + private static final char MASK_ANYTHING = '*'; + + private int maskLength; + private char placeholder; + private StringProperty mask; + private StringProperty plainText; + private StringBuilder plainTextBuilder; + + private List semanticMask; + + public MaskedTextField() { + this("", '_'); + } + + public MaskedTextField(String mask) { + this(mask, '_'); + } + + public MaskedTextField(String mask, char placeholder) { + this.mask = new SimpleStringProperty(this, "mask", mask); + this.placeholder = placeholder; + this.plainText = new SimpleStringProperty(this, "plaintext", ""); + this.plainTextBuilder = new StringBuilder(); + this.semanticMask = new ArrayList<>(); + + init(); + } + + private void init() { + buildSemanticMask(); + updateSemanticMask(""); + + // When MaskedTextField gains focus caret goes to first placeholder position + focusedProperty().addListener((observable, oldValue, newValue) -> { + if(newValue) { + Platform.runLater(() -> { + int pos = firstPlaceholderPosition(); + selectRange(pos, pos); + positionCaret(pos); + }); + } + }); + + // Add a listener to the plain text property so that binding will properly update the formatting as well + this.plainTextProperty().addListener((observable, oldValue, newValue) -> + { + this.updateSemanticMask(newValue); + }); + } + + // ******************************************************* + // Properties + // ******************************************************* + public String getPlainText() { + return plainText.get(); + } + + public void setPlainText(String text) { + setPlainTextWithUpdate(text); + } + + public StringProperty plainTextProperty() { + return this.plainText; + } + + public String getMask() { + return mask.get(); + } + + /** + * Set input mask, rebuild internal mask and update view. + * @param mask Mask dictating legal character values. + */ + public void setMask(String mask) { + this.mask.set(mask); + buildSemanticMask(); + updateSemanticMask(""); + } + + public StringProperty maskProperty() { + return this.mask; + } + + // ******************************************************* + // Getters and Setters + // ******************************************************* + + /** + * Returns the character to use in place of characters that are not present in the value, ie the user must fill them in. + */ + public char getPlaceholder() { + return this.placeholder; + } + + /** + * Set placeholder character. + */ + public void setPlaceholder(char placeholder) { + this.placeholder = placeholder; + } + + // ******************************************************* + // Semantic Mask Methods + // ******************************************************* + + /** + * Build internal mask from input mask using AbstractFactory to add the right MaskCharacter. + */ + private void buildSemanticMask() { + char[] newMask = getMask().toCharArray(); + int i = 0; + int length = newMask.length; + + semanticMask.clear(); + + MaskFactory factory = new MaskFactory(); + while(i < length) { + char maskValue = newMask[i]; + + // If the actual char is MASK_ESCAPE look the next char as literal + if (maskValue == MASK_ESCAPE) { + semanticMask.add(factory.createMask(maskValue, newMask[i + 1])); + i++; + } else { + char value = isLiteral(maskValue) ? maskValue : placeholder; + semanticMask.add(factory.createMask(maskValue, value)); + } + + i++; + } + + maskLength = semanticMask.size(); + } + + private void resetSemanticMask() { + semanticMask.stream().forEach(maskCharacter-> maskCharacter.setValue(placeholder)); + } + + private void updateSemanticMask(String newText) { + resetSemanticMask(); + stringToValue(newText); + setText(valuesToString()); + } + + // ******************************************************* + // Private Methods + // ******************************************************* + + /** + * Given a position in mask convert it into plainText position + */ + private int convertToPlainTextPosition(int pos) { + int count = 0; + + for (int i = 0; i < maskLength && i < pos; i++) { + MaskCharacter m = semanticMask.get(i); + if (!(m.getValue() == placeholder || m.isLiteral())) { + count++; + } + } + + return count; + } + + /** + * Given a position in plain text convert it into mask position + */ + private int convertToMaskPosition(int pos) { + int countLiterals = 0; + int countNonLiterals = 0; + + for (int i = 0; i < maskLength && countNonLiterals < pos; i++) { + if (semanticMask.get(i).isLiteral()) { + countLiterals++; + } else { + countNonLiterals++; + } + } + + return countLiterals + countNonLiterals; + } + + /** + * Return true if a given char isn't a mask. + */ + private boolean isLiteral(char c){ + return (c != MASK_ANYTHING && + c != MASK_CHARACTER && + c != MASK_ESCAPE && + c != MASK_NUMBER && + c != MASK_CHAR_OR_NUM && + c != MASK_HEXADECIMAL && + c != MASK_LOWER_CHARACTER && + c != MASK_UPPER_CHARACTER); + } + + /** + * Return the position of first mask with placeholder on value. + */ + private int firstPlaceholderPosition() { + for (int i = 0; i < maskLength; i++) { + if (semanticMask.get(i).getValue() == placeholder) { + return i; + } + } + return -1; + } + + private void setPlainTextWithUpdate(String text) { + String newText = (text != null) ? text : ""; + setPlainTextWithoutUpdate(newText); + updateSemanticMask(newText); + } + + private void setPlainTextWithoutUpdate(String text) { + plainTextBuilder = new StringBuilder(text); + plainText.set(text); + } + + /** + * Insert values on semantic mask. + * @param text Plain text to be inserted + */ + private void stringToValue(String text) { + StringBuilder inputText = new StringBuilder(text); + StringBuilder validText = new StringBuilder(); + + int maskPosition = 0; + int textLength = text.length(); + int textPosition = 0; + + while (textPosition < textLength) { + if (maskPosition < maskLength) { + MaskCharacter maskCharacter = semanticMask.get(maskPosition); + + if (!maskCharacter.isLiteral()) { + char ch = inputText.charAt(textPosition); + + if(maskCharacter.accept(ch)) { + maskCharacter.setValue(ch); + validText.append(maskCharacter.getValue()); + maskPosition++; + } + + textPosition++; + } else { + maskPosition++; + } + } else { + break; + } + } + + setPlainTextWithoutUpdate(validText.toString()); + } + + /** + * Get all the semanticMask's values and convert it into an string. + */ + private String valuesToString() { + StringBuilder value = new StringBuilder(); + semanticMask.stream().forEach(character -> value.append(character.getValue())); + return value.toString(); + } + + // ******************************************************* + // Overrides + // ******************************************************* + + @Override + public void replaceText(int start, int end, String newText) { + int position = convertToPlainTextPosition(start); + int endPosition = convertToPlainTextPosition(end); + + String newString = null; + if (start != end) { + newString = plainTextBuilder.replace(position, endPosition, newText).toString(); + } else { + newString = plainTextBuilder.insert(position, newText).toString(); + } + updateSemanticMask(newString); + + int newCaretPosition = convertToMaskPosition(position + newText.length()); + selectRange(newCaretPosition, newCaretPosition); + } + + @Override + public void replaceSelection(String string) { + IndexRange range = getSelection(); + if (string.equals("")) { + deleteText(range.getStart(), range.getEnd()); + } else { + replaceText(range.getStart(), range.getEnd(), string); + } + } + + @Override + public void deleteText(int start, int end) { + + int plainStart = convertToPlainTextPosition(start); + int plainEnd = convertToPlainTextPosition(end); + + plainTextBuilder.delete(plainStart, plainEnd); + updateSemanticMask(plainTextBuilder.toString()); + + selectRange(start, start); + } + + @Override + public void clear() { + setPlainText(""); + } + + // ******************************************************* + // Private Classes + // ******************************************************* + private abstract class MaskCharacter { + + private char value; + + public MaskCharacter(char value) { + this.value = value; + } + + public char getValue() { + return this.value; + } + + public void setValue(char value) { + this.value = value; + } + + public boolean isLiteral() { + return false; + } + + abstract boolean accept(char value); + } + + private class MaskFactory { + + public MaskCharacter createMask(char mask, char value) { + switch (mask) { + case MASK_ANYTHING: + return new AnythingCharacter(value); + case MASK_CHARACTER: + return new LetterCharacter(value); + case MASK_NUMBER: + return new NumericCharacter(value); + case MASK_CHAR_OR_NUM: + return new AlphaNumericCharacter(value); + case MASK_HEXADECIMAL: + return new HexCharacter(value); + case MASK_LOWER_CHARACTER: + return new LowerCaseCharacter(value); + case MASK_UPPER_CHARACTER: + return new UpperCaseCharacter(value); + default: + return new LiteralCharacter(value); + } + + } + } + + private class AnythingCharacter extends MaskCharacter { + + public AnythingCharacter(char value) { + super(value); + } + + public boolean accept(char value) { + return true; + } + } + + private class AlphaNumericCharacter extends MaskCharacter { + + public AlphaNumericCharacter(char value) { + super(value); + } + + public boolean accept(char value) { + return Character.isLetterOrDigit(value); + } + } + + private class LiteralCharacter extends MaskCharacter { + + public LiteralCharacter(char value) { + super(value); + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public void setValue(char value) { + // Literal can't be changed + } + + public boolean accept(char value) { + return false; + } + } + + private class LetterCharacter extends MaskCharacter { + + public LetterCharacter(char value) { + super(value); + } + + public boolean accept(char value) { + return Character.isLetter(value); + } + + } + + private class LowerCaseCharacter extends MaskCharacter { + + public LowerCaseCharacter(char value) { + super(value); + } + + @Override + public char getValue() { + return Character.toLowerCase(super.getValue()); + } + + public boolean accept(char value) { + return Character.isLetter(value); + } + } + + private class UpperCaseCharacter extends MaskCharacter { + + public UpperCaseCharacter(char value) { + super(value); + } + + @Override + public char getValue() { + return Character.toUpperCase(super.getValue()); + } + + public boolean accept(char value) { + return Character.isLetter(value); + } + } + + private class NumericCharacter extends MaskCharacter { + + public NumericCharacter(char value) { + super(value); + } + + @Override + public boolean accept(char value) { + return Character.isDigit(value); + } + + } + + private class HexCharacter extends MaskCharacter { + + public HexCharacter(char value) { + super(value); + } + + @Override + public boolean accept(char value) { + return Pattern.matches("[0-9a-fA-F]", String.valueOf(value)); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/NavigationManager.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/NavigationManager.java deleted file mode 100644 index 4cfba8c..0000000 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/NavigationManager.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.carvalhotechsolutions.mundoanimal.utils; - -import javafx.event.ActionEvent; -import javafx.fxml.FXMLLoader; -import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; - -import java.io.IOException; - -public class NavigationManager { - public static void switchScene(ActionEvent event, String fxmlPath, String title) throws IOException { - FXMLLoader loader = new FXMLLoader(NavigationManager.class.getResource(fxmlPath)); - Parent root = loader.load(); - - Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); - stage.setScene(new Scene(root)); - stage.setTitle(title); - } -} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/security/PasswordUtils.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/PasswordManager.java similarity index 78% rename from src/main/java/com/carvalhotechsolutions/mundoanimal/security/PasswordUtils.java rename to src/main/java/com/carvalhotechsolutions/mundoanimal/utils/PasswordManager.java index f74d575..dbdeb6f 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/security/PasswordUtils.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/PasswordManager.java @@ -1,10 +1,10 @@ -package com.carvalhotechsolutions.mundoanimal.security; +package com.carvalhotechsolutions.mundoanimal.utils; import org.mindrot.jbcrypt.BCrypt; -public class PasswordUtils { +public class PasswordManager { - private PasswordUtils() { + private PasswordManager() { throw new UnsupportedOperationException("Esta classe não pode ser instanciada"); } diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManager.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManager.java new file mode 100644 index 0000000..dda0649 --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManager.java @@ -0,0 +1,156 @@ +package com.carvalhotechsolutions.mundoanimal.utils; + +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.AnimalController; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.ClienteController; +import com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento.MenuController; +import com.carvalhotechsolutions.mundoanimal.enums.ScreenEnum; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +public class ScreenManager { + private final Stage stage; + private final BorderPane masterLayout; + private final Map screens; + private Map controllers = new HashMap<>(); + private ScreenEnum currentScreen; + private BorderPane menuTemplate; + private StackPane contentArea; + + public ScreenManager(Stage stage) { + this.stage = stage; + this.masterLayout = new BorderPane(); + this.screens = new EnumMap<>(ScreenEnum.class); + + // Configuração inicial do stage + this.stage.setTitle("Sistema PetShop Mundo Animal"); + this.stage.setMinWidth(1400); + this.stage.setMinHeight(820); + this.stage.setWidth(1400); + this.stage.setHeight(820); + + // Aplicar o CSS global ao masterLayout + masterLayout.getStylesheets().add(getClass().getResource("/css/style.css").toExternalForm()); + + // Criar a cena principal com o layout mestre + Scene masterScene = new Scene(masterLayout); + this.stage.setScene(masterScene); + + // Carregar todas as telas + loadScreens(); + } + + private void loadScreens() { + for (ScreenEnum screen : ScreenEnum.values()) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource(screen.getFxmlPath())); + Node loadedNode = loader.load(); + + if (screen.getType() == ScreenEnum.ScreenType.TEMPLATE) { + // Se for o template do menu, guarda referência especial + if (loadedNode instanceof BorderPane) { + menuTemplate = (BorderPane) loadedNode; + // Guarda referência da área de conteúdo + contentArea = (StackPane) menuTemplate.getCenter(); + } + } + + // Configurar visibilidade inicial + loadedNode.setVisible(false); + loadedNode.setManaged(false); + + // Adicionar aos mapas de telas e controladores + screens.put(screen, loadedNode); + controllers.put(screen, loader.getController()); + + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Erro ao carregar a tela: " + screen.getFxmlPath()); + } + } + } + + public void switchTo(ScreenEnum screen) { + switch (screen.getType()) { + case FULL: + switchToFullScreen(screen); + break; + case TEMPLATE: + switchToTemplate(screen); + break; + case CONTENT: + switchToContent(screen); + break; + } + currentScreen = screen; + } + + private void switchToFullScreen(ScreenEnum screen) { + Node newScreen = screens.get(screen); + if (newScreen != null) { + contentArea.getChildren().clear(); + masterLayout.getChildren().clear(); + masterLayout.setCenter(newScreen); + newScreen.setVisible(true); + newScreen.setManaged(true); + } + } + + private void switchToTemplate(ScreenEnum screen) { + Node template = screens.get(screen); + if (template != null) { + masterLayout.getChildren().clear(); + masterLayout.setCenter(template); + template.setVisible(true); + template.setManaged(true); + } + } + + private void switchToContent(ScreenEnum screen) { + // Garante que o template do menu está visível + if (!menuTemplate.isVisible()) { + masterLayout.getChildren().clear(); + masterLayout.setCenter(menuTemplate); + menuTemplate.setVisible(true); + menuTemplate.setManaged(true); + } + + // Carrega o novo conteúdo na área de conteúdo + Node content = screens.get(screen); + if (content != null && contentArea != null) { + contentArea.getChildren().clear(); + contentArea.getChildren().add(content); + content.setVisible(true); + content.setManaged(true); + } + } + + public Stage getStage() { + return this.stage; + } + + public Node getScreen(ScreenEnum screen) { + return screens.get(screen); + } + + public MenuController getMenuController() { + return (MenuController) controllers.get(ScreenEnum.MENU); + } + + public AnimalController getAnimalController() { + return (AnimalController) controllers.get(ScreenEnum.PETS); + } + + public ClienteController getClienteController() { + return (ClienteController) controllers.get(ScreenEnum.CLIENTES); + } +} \ No newline at end of file diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManagerHolder.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManagerHolder.java new file mode 100644 index 0000000..0d93cff --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/ScreenManagerHolder.java @@ -0,0 +1,16 @@ +package com.carvalhotechsolutions.mundoanimal.utils; + +public class ScreenManagerHolder { + private static ScreenManager instance; + + public static void initialize(ScreenManager manager) { + instance = manager; + } + + public static ScreenManager getInstance() { + if (instance == null) { + throw new IllegalStateException("ScreenManager ainda não foi inicializado."); + } + return instance; + } +} diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/SessionManager.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/SessionManager.java index ec671d2..c91a1e2 100644 --- a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/SessionManager.java +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/SessionManager.java @@ -3,7 +3,6 @@ import com.carvalhotechsolutions.mundoanimal.model.Usuario; public class SessionManager { - private static Usuario currentUser; public static Usuario getCurrentUser() { @@ -13,5 +12,9 @@ public static Usuario getCurrentUser() { public static void setCurrentUser(Usuario user) { currentUser = user; } + + public static void clearSession() { + currentUser = null; + } -} +} \ No newline at end of file diff --git a/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/TextFormatterManager.java b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/TextFormatterManager.java new file mode 100644 index 0000000..bb09e0b --- /dev/null +++ b/src/main/java/com/carvalhotechsolutions/mundoanimal/utils/TextFormatterManager.java @@ -0,0 +1,105 @@ +package com.carvalhotechsolutions.mundoanimal.utils; + +import java.text.ParseException; +import javafx.scene.control.TextField; +import javax.swing.text.MaskFormatter; + +/** + * + * @author Marcos Ricardo Rodrigues + */ +public class TextFormatterManager { + + private final MaskFormatter mf; + private TextField tf; + private String CaracteresValidos; + private String mask; + + public TextFormatterManager() { + mf = new MaskFormatter(); + } + + public void formatter(TextField tf, String CaracteresValidos, String mask) { + try { + mf.setMask(mask); + } catch (ParseException ex) { + System.out.println(ex.getMessage()); + } + + mf.setValidCharacters(CaracteresValidos); + mf.setValueContainsLiteralCharacters(false); + String text = tf.getText().replaceAll("[\\W]", ""); + + boolean repetir = true; + while (repetir) { + + char ultimoCaractere; + + if (text.equals("")) { + break; + } else { + ultimoCaractere = text.charAt(text.length() - 1); + } + + try { + text = mf.valueToString(text); + repetir = false; + } catch (ParseException ex) { + text = text.replace(ultimoCaractere + "", ""); + repetir = true; + } + + } + + tf.setText(text); + + if (!text.equals("")) { + for (int i = 0; tf.getText().charAt(i) != ' ' && i < tf.getLength() - 1; i++) { + tf.forward(); + } + } + } + + public void formatter(){ + formatter(this.tf, this.CaracteresValidos, this.mask); + } + + /** + * @return the tf + */ + public TextField getTf() { + return tf; + } + + /** + * @param tf the tf to set + */ + public void setTf(TextField tf) { + this.tf = tf; + } + + /** + * @return the CaracteresValidos + */ + public String getCaracteresValidos() { + return CaracteresValidos; + } + + /** + * @param CaracteresValidos the CaracteresValidos to set + */ + public void setCaracteresValidos(String CaracteresValidos) { + this.CaracteresValidos = CaracteresValidos; + } + + public String getMask() { + return mask; + } + + /** + * @param mask the mask to set + */ + public void setMask(String mask) { + this.mask = mask; + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 587a443..7c5b241 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -7,10 +7,11 @@ requires jakarta.persistence; requires org.hibernate.orm.core; requires jbcrypt; + requires animatefx; + requires fontawesomefx; + requires java.desktop; // Export and open the controllers package - opens com.carvalhotechsolutions.mundoanimal.controllers to javafx.fxml; - exports com.carvalhotechsolutions.mundoanimal.controllers; opens com.carvalhotechsolutions.mundoanimal to javafx.fxml; exports com.carvalhotechsolutions.mundoanimal; @@ -19,6 +20,14 @@ org.hibernate.orm.core, jakarta.persistence; exports com.carvalhotechsolutions.mundoanimal.model; - exports com.carvalhotechsolutions.mundoanimal.controllers.login; - opens com.carvalhotechsolutions.mundoanimal.controllers.login to javafx.fxml; + exports com.carvalhotechsolutions.mundoanimal.controllers.autenticacao; + opens com.carvalhotechsolutions.mundoanimal.controllers.autenticacao to javafx.fxml; + exports com.carvalhotechsolutions.mundoanimal.controllers.modals; + opens com.carvalhotechsolutions.mundoanimal.controllers.modals to javafx.fxml; + exports com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento; + opens com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento to javafx.fxml; + exports com.carvalhotechsolutions.mundoanimal.database; + opens com.carvalhotechsolutions.mundoanimal.database to javafx.fxml; + exports com.carvalhotechsolutions.mundoanimal.utils; + opens com.carvalhotechsolutions.mundoanimal.utils to javafx.fxml; } \ No newline at end of file diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index fd56067..460cbbb 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -19,10 +19,32 @@ - + + + + + com.carvalhotechsolutions.mundoanimal.model.Usuario + com.carvalhotechsolutions.mundoanimal.model.Administrador + com.carvalhotechsolutions.mundoanimal.model.Agendamento + com.carvalhotechsolutions.mundoanimal.model.Animal + com.carvalhotechsolutions.mundoanimal.model.Cliente + com.carvalhotechsolutions.mundoanimal.model.Secretario + com.carvalhotechsolutions.mundoanimal.model.Servico + + + + + + + + + + + + diff --git a/src/main/resources/assets/images/confirmacao-login.png b/src/main/resources/assets/images/confirmacao-login.png new file mode 100644 index 0000000..1eb009e Binary files /dev/null and b/src/main/resources/assets/images/confirmacao-login.png differ diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css index d18dfbf..65ded6c 100644 --- a/src/main/resources/css/style.css +++ b/src/main/resources/css/style.css @@ -19,6 +19,16 @@ root { -fx-font-family: "Roboto", serif; } +.search_input { + -fx-font-family: "Roboto", serif; + -fx-font-weight: 400; + -fx-font-style: normal; + -fx-font-size: 18px; + -fx-background-color: white; + -fx-border-color: #CCCCCC; + -fx-border-radius: 5px; +} + .default_text_input { -fx-font-family: "Roboto", serif; -fx-font-weight: 400; @@ -27,6 +37,12 @@ root { -fx-background-color: transparent; -fx-border-color: #CCCCCC; -fx-border-radius: 5px; + -fx-background-insets: 0; +} + +.text-area .content { + -fx-background-insets: 0; + -fx-background-color:white; } .default_label { @@ -36,11 +52,18 @@ root { -fx-font-size: 18px; } +.default_text { + -fx-font-family: "Roboto", serif; + -fx-font-style: normal; + -fx-font-weight: 200; + -fx-font-size: 18px; +} + .white_bold_text { -fx-font-family: "Roboto", serif; - -fx-font-weight: 800; + -fx-font-weight: 700; -fx-text-fill: white; - -fx-font-size: 24px; + -fx-font-size: 22px; } .white_icon { @@ -128,14 +151,14 @@ root { -fx-font-family: "Roboto", serif; -fx-font-style: normal; -fx-font-weight: 400; - -fx-font-size: 26px; + -fx-font-size: 20px; } #top_pane_user_name { -fx-font-family: "Roboto", serif; -fx-font-weight: 900; -fx-font-style: normal; - -fx-font-size: 24px; + -fx-font-size: 20px; } #top_pane_user_role { @@ -143,7 +166,7 @@ root { -fx-font-family: "Roboto", serif; -fx-font-weight: 300; -fx-font-style: normal; - -fx-font-size: 18px; + -fx-font-size: 14px; } .menu_btn, .menu_btn_red { @@ -190,24 +213,37 @@ root { /* CSS TABELAS - INÍCIO */ +.table-view .scroll-bar * { + -fx-min-width: 0; + -fx-pref-width: 0; + -fx-max-width: 0; + + -fx-min-height: 0; + -fx-pref-height: 0; + -fx-max-height: 0; +} + .table-column { -fx-font-family: "Roboto", serif; -fx-font-size: 18px; -fx-alignment: CENTER_LEFT; -fx-padding: 0 0 0 16px; -fx-pref-height: 79px; + -fx-border-color: transparent #e2e2e2 #e2e2e2 transparent; } .column-header { -fx-background-color: #F2F5FA; - -fx-font-weight: 800; -fx-alignment: CENTER_LEFT; - -fx-text-fill: #828282; - -fx-border-color: transparent #cccccc #cccccc transparent; + -fx-border-color: transparent #e2e2e2 #e2e2e2 transparent; +} + +#acaoColumn.table-column { + -fx-border-color: transparent transparent #e2e2e2 transparent; } #acaoColumn.column-header { - -fx-border-color: transparent transparent #cccccc transparent; + -fx-border-color: transparent transparent #e2e2e2 transparent; } /* CSS TABELAS - FIM */ @@ -238,7 +274,7 @@ root { } .delete_btn { - -fx-background-color: #FF0000; + -fx-background-color: #FF6F6F; } .cancel_btn { @@ -246,3 +282,13 @@ root { } /* CSS MODALS - FIM */ + +/* CSS POPUP - INÍCIO */ + +.popup { + -fx-background-radius: 5px; + -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 10, 0, 0, 0); + -fx-font-weight: 800; +} + +/* CSS POPUP - FIM */ \ No newline at end of file diff --git a/src/main/resources/fxml/autenticacao/login.fxml b/src/main/resources/fxml/autenticacao/login.fxml index f70fb4e..556108c 100644 --- a/src/main/resources/fxml/autenticacao/login.fxml +++ b/src/main/resources/fxml/autenticacao/login.fxml @@ -3,47 +3,41 @@ + - + - - - - + +
+ - + - + - - + - + - - + -
+
diff --git a/src/main/resources/fxml/autenticacao/recuperar-senha.fxml b/src/main/resources/fxml/autenticacao/recuperar-senha.fxml deleted file mode 100644 index ba51fad..0000000 --- a/src/main/resources/fxml/autenticacao/recuperar-senha.fxml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + diff --git a/src/main/resources/fxml/gerenciamento/clientes.fxml b/src/main/resources/fxml/gerenciamento/clientes.fxml new file mode 100644 index 0000000..a6c03df --- /dev/null +++ b/src/main/resources/fxml/gerenciamento/clientes.fxml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/gerenciamento/menu.fxml b/src/main/resources/fxml/gerenciamento/menu.fxml index 37f06a8..2ed121d 100644 --- a/src/main/resources/fxml/gerenciamento/menu.fxml +++ b/src/main/resources/fxml/gerenciamento/menu.fxml @@ -6,69 +6,188 @@ - - - + - + - - - - - - + - + - + - + + + + + - - + + + + + + + + + + + - + - + - - - - + - + - + + + + + + + + - - - - - - - - - - - - - + + + + + + +
- - - - - +
diff --git a/src/main/resources/fxml/gerenciamento/pets.fxml b/src/main/resources/fxml/gerenciamento/pets.fxml new file mode 100644 index 0000000..0ab5c0c --- /dev/null +++ b/src/main/resources/fxml/gerenciamento/pets.fxml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/gerenciamento/secretarios.fxml b/src/main/resources/fxml/gerenciamento/secretarios.fxml index b1a451a..04266ee 100644 --- a/src/main/resources/fxml/gerenciamento/secretarios.fxml +++ b/src/main/resources/fxml/gerenciamento/secretarios.fxml @@ -6,40 +6,81 @@ - + - + - + - + - - + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/src/main/resources/fxml/gerenciamento/servicos.fxml b/src/main/resources/fxml/gerenciamento/servicos.fxml index f49db84..9a43358 100644 --- a/src/main/resources/fxml/gerenciamento/servicos.fxml +++ b/src/main/resources/fxml/gerenciamento/servicos.fxml @@ -6,41 +6,87 @@ - + - - + + - + - + - - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + diff --git a/src/main/resources/fxml/modals/modal-confirmar-remocao.fxml b/src/main/resources/fxml/modals/modalConfirmarRemocao.fxml similarity index 83% rename from src/main/resources/fxml/modals/modal-confirmar-remocao.fxml rename to src/main/resources/fxml/modals/modalConfirmarRemocao.fxml index 2a4a922..0013706 100644 --- a/src/main/resources/fxml/modals/modal-confirmar-remocao.fxml +++ b/src/main/resources/fxml/modals/modalConfirmarRemocao.fxml @@ -6,14 +6,14 @@ - + - @@ -21,7 +21,7 @@ - diff --git a/src/main/resources/fxml/modals/modalCriarAgendamento.fxml b/src/main/resources/fxml/modals/modalCriarAgendamento.fxml new file mode 100644 index 0000000..03759ef --- /dev/null +++ b/src/main/resources/fxml/modals/modalCriarAgendamento.fxml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +