Skip to content

Commit

Permalink
feat(company): Integrate Company Management Controller and Thymeleaf UI
Browse files Browse the repository at this point in the history
- Added `CompanyController` for handling company-related requests, including listing, adding, and deleting companies.
- Implemented `CompanyService` with pagination support for companies and added new methods to handle CRUD operations.
- Updated `CompanyDTO` and `Company` models to ensure consistency, including renaming `phone` to `phoneNumber`.
- Added new Thymeleaf templates for managing companies: `company.html`, `modal-register.html`, and `modal-delete.html`.
- Created JavaScript utilities in `form-utils.js` to support form submission and deletion actions, shared across company and employee management.
- Updated security configuration to restrict company-related API access to users with the "ADMIN" role.
- Renamed CSS file from `employee-styles.css` to `tables-styles.css` to generalize table styling across entities.
- Refactored JavaScript in `employee-script.js` to improve code reusability and maintainability.

Closes #5
  • Loading branch information
gabrizord authored Sep 1, 2024
1 parent 009d9ac commit c07a880
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(HttpMethod.GET, "/login").permitAll()
.requestMatchers(HttpMethod.GET, "/error").permitAll()
.requestMatchers("/api/employee/**").hasAuthority("ADMIN")
.requestMatchers("/api/company/**").hasAuthority("ADMIN")
.anyRequest().authenticated())
.formLogin(form -> form.loginPage("/login")
.failureUrl("/login?error=true")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package br.com.gabrizord.gzgestao.controller;

import br.com.gabrizord.gzgestao.model.Company;
import br.com.gabrizord.gzgestao.service.CompanyService;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class CompanyController {

private final CompanyService companyService;

public CompanyController(CompanyService companyService) {
this.companyService = companyService;
}

@GetMapping("/companies")
public String viewCompanies(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "12") int size,
@RequestParam(defaultValue = "id") String sortField,
@RequestParam(defaultValue = "asc") String sortDirection,
Model model) {

Page<Company> companyPage = companyService.getPaginatedCompanies(page, size, sortField, sortDirection);
model.addAttribute("companyPage", companyPage);

model.addAttribute("currentPage", page);
model.addAttribute("totalPages", companyPage.getTotalPages());
model.addAttribute("totalItems", companyPage.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDirection", sortDirection);
model.addAttribute("reverseSortDirection", sortDirection.equals("asc") ? "desc" : "asc");
return "company/company";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public Company convertToEntity() {
company.setPostalCode(postalCode);
company.setStateRegistrationIndicator(stateRegistrationIndicator);
company.setMunicipalityCode(municipalityCode);
company.setPhone(phone);
company.setPhoneNumber(phone);
company.setEmail(email);
return company;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/br/com/gabrizord/gzgestao/model/Company.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class Company implements Serializable {
private String municipalityCode; // Código do Município (IBGE)

@Column(length = 11)
private String phone;
private String phoneNumber;

@Column(length = 100)
private String email;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import br.com.gabrizord.gzgestao.model.Company;
import br.com.gabrizord.gzgestao.repository.CompanyRepository;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
Expand Down Expand Up @@ -41,4 +45,10 @@ public void deleteCompanyById(Long id) {
Company company = getCompanyById(id);
companyRepository.delete(company);
}

public Page<Company> getPaginatedCompanies(int page, int size, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() : Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(page, size, sort);
return companyRepository.findAll(pageable);
}
}
15 changes: 15 additions & 0 deletions src/main/resources/static/protected/js/company-script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
setupFormSubmission(
'#registerCompanyForm',
'/api/company',
'Empresa cadastrada com sucesso!',
'Ocorreu um erro ao cadastrar a empresa.',
'registerCompanyModal'
);

setupDeleteAction(
'.btn-danger[title="Excluir"]',
'deleteCompanyModal',
'/api/company/{id}',
'Empresa excluída com sucesso!',
'Ocorreu um erro ao excluir a empresa.'
);
137 changes: 28 additions & 109 deletions src/main/resources/static/protected/js/employee-script.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,28 @@
/**
* This script handles the registration and deletion of employees via AJAX requests.
* It also formats the phone number input.
*/

$(document).ready(function() {
/**
* Fetch the CSRF token and header from the meta tags.
*/
const token = $('meta[name="_csrf"]').attr('content');
const header = $('meta[name="_csrf_header"]').attr('content');

/**
* Handle the form submission for registering a new employee.
*/
$('#registerEmployeeForm').on('submit', function(event) {
event.preventDefault();
const formData = {};
$(this).serializeArray().forEach(function(item) {
formData[item.name] = item.value;
});

/**
* Send the form data to the API endpoint for registration.
*/
$.ajax({
url: '/api/employee',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success: function() {
alert('Colaborador cadastrado com sucesso!');
let myModal = bootstrap.Modal.getInstance(document.getElementById('registerEmployeeModal'));
myModal.hide();
location.reload();
},
error: function(xhr) {
console.error('Erro:', xhr.responseText);
alert('Ocorreu um erro ao cadastrar o colaborador.');
}
});
});

/**
* Store the ID of the employee to be deleted.
*/
let employeeIdToDelete = null;

/**
* Handle the click event for deleting an employee.
*/
$('.btn-danger[title="Excluir"]').on('click', function() {
const row = $(this).closest('tr');
employeeIdToDelete = row.find('td:first-child').text();
const employeeName = row.find('td:nth-child(2)').text();

$('#employeeNameToDelete').text(employeeName);

/**
* Show the delete confirmation modal.
*/
const deleteModal = new bootstrap.Modal(document.getElementById('deleteEmployeeModal'));
deleteModal.show();
});

/**
* Handle the click event for confirming the deletion of an employee.
*/
$('#confirmDeleteButton').on('click', function() {
if (employeeIdToDelete) {
/**
* Send a DELETE request to the API endpoint for deleting the employee.
*/
$.ajax({
url: `/api/employee/${employeeIdToDelete}`,
type: 'DELETE',
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success: function() {
alert('Colaborador excluído com sucesso!');
location.reload();
},
error: function(xhr) {
console.error('Erro:', xhr.responseText);
alert('Ocorreu um erro ao excluir o colaborador.');
}
});
}
});

/**
* Format the phone number input.
*/
document.getElementById('formattedPhone').addEventListener('input', function (e) {
let input = e.target.value;
let rawValue = input.replace(/\D/g, '');

input = input.replace(/\D/g, '');
input = input.replace(/^(\d{2})(\d)/g, '($1) $2');
input = input.replace(/(\d{5})(\d)/, '$1-$2');

e.target.value = input;
document.getElementById('employeePhone').value = rawValue;
});
});
setupFormSubmission(
'#registerEmployeeForm',
'/api/employee',
'Colaborador cadastrado com sucesso!',
'Ocorreu um erro ao cadastrar o colaborador.',
'registerEmployeeModal'
);

setupDeleteAction(
'.btn-danger[title="Excluir"]',
'deleteEmployeeModal',
'/api/employee/{id}',
'Colaborador excluído com sucesso!',
'Ocorreu um erro ao excluir o colaborador.'
);

// Adicionalmente, o código de formatação de telefone específico para employees
document.getElementById('formattedPhone').addEventListener('input', function (e) {
let input = e.target.value;
let rawValue = input.replace(/\D/g, '');

input = input.replace(/\D/g, '');
input = input.replace(/^(\d{2})(\d)/g, '($1) $2');
input = input.replace(/(\d{5})(\d)/, '$1-$2');

e.target.value = input;
document.getElementById('employeePhone').value = rawValue;
});
73 changes: 73 additions & 0 deletions src/main/resources/static/protected/js/form-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
function setupFormSubmission(formSelector, apiUrl, successMessage, errorMessage, modalId) {
$(document).ready(function() {
const token = $('meta[name="_csrf"]').attr('content');
const header = $('meta[name="_csrf_header"]').attr('content');

$(formSelector).on('submit', function(event) {
event.preventDefault();
const formData = {};
$(this).serializeArray().forEach(function(item) {
formData[item.name] = item.value;
});

$.ajax({
url: apiUrl,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(formData),
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success: function() {
alert(successMessage);
let myModal = bootstrap.Modal.getInstance(document.getElementById(modalId));
myModal.hide();
location.reload();
},
error: function(xhr) {
console.error('Erro:', xhr.responseText);
alert(errorMessage);
}
});
});
});
}

function setupDeleteAction(buttonSelector, modalId, apiUrlTemplate, successMessage, errorMessage) {
$(document).ready(function() {
const token = $('meta[name="_csrf"]').attr('content');
const header = $('meta[name="_csrf_header"]').attr('content');
let itemIdToDelete = null;

$(buttonSelector).on('click', function() {
const row = $(this).closest('tr');
itemIdToDelete = row.find('td:first-child').text();
const itemName = row.find('td:nth-child(2)').text();

$(`#${modalId}NameToDelete`).text(itemName);

const deleteModal = new bootstrap.Modal(document.getElementById(modalId));
deleteModal.show();
});

$(`#confirmDeleteButton`).on('click', function() {
if (itemIdToDelete) {
$.ajax({
url: apiUrlTemplate.replace('{id}', itemIdToDelete),
type: 'DELETE',
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success: function() {
alert(successMessage);
location.reload();
},
error: function(xhr) {
console.error('Erro:', xhr.responseText);
alert(errorMessage);
}
});
}
});
});
}
Loading

0 comments on commit c07a880

Please sign in to comment.