Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import br.edu.utfpr.servicebook.model.entity.City;
import br.edu.utfpr.servicebook.model.entity.Expertise;
import br.edu.utfpr.servicebook.model.entity.Individual;
import br.edu.utfpr.servicebook.model.entity.JobImages;
import br.edu.utfpr.servicebook.model.entity.JobRequest;
import br.edu.utfpr.servicebook.model.mapper.ExpertiseMapper;
import br.edu.utfpr.servicebook.model.mapper.IndividualMapper;
Expand All @@ -14,6 +15,7 @@
import br.edu.utfpr.servicebook.service.ExpertiseService;
import br.edu.utfpr.servicebook.service.IndividualService;
import br.edu.utfpr.servicebook.service.JobRequestService;
import br.edu.utfpr.servicebook.service.JobImagesService;
import br.edu.utfpr.servicebook.util.DateUtil;
import br.edu.utfpr.servicebook.util.WizardSessionUtil;
import com.cloudinary.Cloudinary;
Expand All @@ -37,6 +39,7 @@
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
Expand All @@ -51,6 +54,9 @@ public class JobRequestController {
@Autowired
private JobRequestService jobRequestService;

@Autowired
private JobImagesService jobImagesService;

@Autowired
private IndividualService individualService;

Expand Down Expand Up @@ -237,28 +243,55 @@ public String saveFormDescription(HttpSession httpSession, @Validated(JobRequest
}
@PostMapping("/passo-5")
public String saveFormImagePath(HttpSession httpSession, RedirectAttributes redirectAttributes, JobRequestDTO dto, Model model) throws IOException {
try {
if (dto.images.get(0).getOriginalFilename().equals("")) {
redirectAttributes.addFlashAttribute("errors", "Nenhuma imagem enviada.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Quando é erro, faça um encaminhamento.
  • Assim, o formulário continua preenchido e mesmo que ele dê F5, cairá no mesmo erro.
  • Só quando o POST é sucesso é que se faz redirecionamento.

return "redirect:/requisicoes?passo=5";
}

JobRequestDTO sessionDTO = wizardSessionUtil.getWizardState(httpSession, JobRequestDTO.class, WizardSessionUtil.KEY_WIZARD_JOB_REQUEST);

//persiste na sessão
JobRequestDTO sessionDTO = wizardSessionUtil.getWizardState(httpSession, JobRequestDTO.class, WizardSessionUtil.KEY_WIZARD_JOB_REQUEST);
sessionDTO.setImageFile(dto.getImageFile());
List<String> successImages = new ArrayList<>();
List<String> invalidImages = new ArrayList<>();

System.out.println(sessionDTO.getExpertiseId());
for (MultipartFile image : dto.getImages()) {
if (isValidateImage(image)) {
File jobImage = Files.createTempFile("temp", image.getOriginalFilename()).toFile();
image.transferTo(jobImage);
Map data = cloudinary.uploader().upload(jobImage, ObjectUtils.asMap("folder", "jobs"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Só para contextualização. Não tem problema de salvar no Cloudinary mesmo que o usuário desista do cadastro. Mesmo se o usuário desistir do cadastro, haverá outra história que removerá imagens antigas do Cloudinary. A API permite buscar as fotos por data.


if(isValidateImage(dto.getImageFile())){
File jobImage = Files.createTempFile("temp", dto.getImageFile().getOriginalFilename()).toFile();
dto.getImageFile().transferTo(jobImage);
Map data = cloudinary.uploader().upload(jobImage, ObjectUtils.asMap("folder", "jobs"));
successImages.add((String) data.get("url"));
} else {
invalidImages.add("Não foi possível anexar o arquivo " + image.getOriginalFilename() + ".");
}
}

if (!successImages.isEmpty())
sessionDTO.setImagesSession(successImages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Como este POST está recebendo apenas uma ou mais imagem enviadas pelo botão adicionar, então este vetor successImage só vai ter estas imagens.
  • Assim, se no POST anterior, a sessão já tinha uma imagem armazenada, a linha a seguir está substituindo o vetor anterior pelo atual, ou seja, com as imagens do POST corrente.
  • Por isso, o JSP está apresentado sempre as imagens enviadas no Adicionar corrente.
  • Lembre, o usuário pode usar esta página escolhendo uma imagem por vez, também. Para cada imagem, ele clica em adicionar. Então, estas imagens precisam ser acumuladas na sessão e apresentadas como thumbnails.


sessionDTO.setImageSession((String)data.get("url"));
log.debug("Passo 5 {}", sessionDTO);
if (!invalidImages.isEmpty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Como boa prática, trate os erros no início do método.
  • Geralmente, não usamos else, apenas if
  • Então, se houver um erro, isto é verificado no if e se entrar nele, faz o encaminhamento para a página que vai mostrar o erro.
  • Isso evita o uso do else, deixa o código mais difícil de ler.
  • Com os tratamentos de erro no início com o uso do encaminhamento, todo o código que estiver abaixo destes ifs de validação, poderão executar com segurança, ou seja, os dados já foram validados.

redirectAttributes.addFlashAttribute("errors", invalidImages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Inclua o vetor de imagens que já estão na sessão também no escopo de flash. Assim, o código fica mais legível e o JSP vai apresentar os thumbnails de acordo com a lista de URLs que estão neste atributo no escopo de flash.
  • Lembre que o escopo de flash guarda temporariamente na sessão, usado em redirecionamentos, quando uma nova requisição é criada no redirect.


return "redirect:/requisicoes/passo=6";
} else {
return "redirect:/requisicoes/passo=6";
return "redirect:/requisicoes?passo=5";
}
catch (Exception e){
redirectAttributes.addFlashAttribute("errors", "Um erro inesperado aconteceu. Tente novamente!");
return "redirect:/requisicoes?passo=5";
}
}
@DeleteMapping("/session/image/{url}")
public String deleteImageSession(@PathVariable String url, HttpSession httpSession, RedirectAttributes redirectAttributes, JobRequestDTO dto, Model model) throws IOException {

JobRequestDTO sessionDTO = wizardSessionUtil.getWizardState(httpSession, JobRequestDTO.class, WizardSessionUtil.KEY_WIZARD_JOB_REQUEST);

for (String path : sessionDTO.getImagesSession()) {
if(path.contains(url)) {
int index = sessionDTO.getImagesSession().indexOf(path);
sessionDTO.getImagesSession().remove(index);
return "Imagem apagada.";
}
}
return "Não foi possível apagar imagem.";

}
@GetMapping("passo=6")
Expand Down Expand Up @@ -296,13 +329,17 @@ public String saveFormVerification(HttpSession httpSession, JobRequestDTO dto, R

sessionDTO.setClientConfirmation(true);
sessionDTO.setDateCreated(DateUtil.getToday());
sessionDTO.setStatus("Requerido");
sessionDTO.setStatus("AVAILABLE");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Usar o enum AVAILABLE
  • public class JobRequest {
    /**
    • AVAILABLE: disponível para candidaturas e permanece neste estado também durante o recebimento de candidaturas
    • BUDGET: passa para este estado quando alcançado o total de candidaturas esperado ou quando o cliente encerra o recebimento de candidaturas
    • TO_DO: o profissional foi escolhido para fazer o serviço e o serviço ainda não foi realizado
    • CLOSED: o serviço foi realizado
      */
      public enum Status {
      AVAILABLE, BUDGET, TO_DO, CLOSED
      };

log.debug("Passo 7 {}", sessionDTO);
JobRequest jobRequest = jobRequestMapper.toEntity(sessionDTO);
jobRequest.setIndividual(client);
jobRequest.setExpertise(exp);
//jobRequest.setImage(sessionDTO.getImageSession());
jobRequestService.save(jobRequest);
for (String path : sessionDTO.getImagesSession()) {
JobImages jobImage = new JobImages(jobRequest, path);
jobImagesService.save(jobImage);
}
redirectAttributes.addFlashAttribute("msg", "Requisição confirmada!");
status.setComplete();
return "redirect:/requisicoes?passo=8";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javax.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.List;

@Data
@NoArgsConstructor
Expand Down Expand Up @@ -36,8 +37,8 @@ public class JobRequestDTO implements Serializable {
@NotBlank(message = "A Descrição não pode ser vazia.", groups = RequestDescriptionGroupValidation.class)
private String description;

private MultipartFile imageFile;
private String imageSession;
public List<MultipartFile> images;
private List<String> imagesSession;

// @NotBlank(message = "O campo CEP é de preenchimento obrigatório", groups = RequestClientInfoGroupValidation.class)
// @Pattern(regexp="\\d{5}-?\\d{3}",message="Por favor, preencha um CEP válido", groups = RequestClientInfoGroupValidation.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class JobImages {

@ManyToOne
@JoinColumn(name = "job_request_id")
@NonNull
private JobRequest jobRequest;

@NonNull
Expand Down
27 changes: 22 additions & 5 deletions src/main/webapp/WEB-INF/view/client/job-request/wizard-step-05.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<div class="container">
<c:if test="${not empty errors}">
<div class="card-panel red">
<c:forEach var="e" items="${errors}">
<span class="white-text">${e.getDefaultMessage()}</span><br>
<c:forEach var="error" items="${errors}">
<span class="white-text">${error}</span><br>
</c:forEach>
</div>
</c:if>
Expand All @@ -26,22 +26,37 @@
<div class="col s12 m6 offset-m3 l4 offset-l4 ">
<div class="file-field input-field">
<div class="btn">
<span>Choose File</span>
<input type="file" value="${dto.imageFile}" name="imageFile" accept=".jpg, .jpeg, .png">
<span>Escolha as Imagens</span>
<input type="file" name="images" multiple="multiple" accept=".jpg, .jpeg, .png">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" placeholder="image.jpg" type="text">
</div>
</div>
</div>
<div class="col s12 m6 offset-m3 l4 offset-l4 ">
<div class="spacing-buttons">
<button class="waves-effect waves-light btn btn-validate" >Adicionar</button>
</div>
</div>
<div class="gallery" id="galley">
<c:if test="${not empty dto.getImagesSession()}" >
<c:forEach items="${dto.getImagesSession()}" var="image">
<div class="box-image">
<img src="${image}">
<a class="waves-effect waves-light" onclick="deleteImage('${image}')"><i class="material-icons">delete_forever</i></a>
</div>
</c:forEach>
</c:if>
</div>
<div class="col s6 m6 spacing-buttons">
<div class="center">
<a href="requisicoes?passo=4" class="waves-effect waves-light btn btn-gray" href="#!">Voltar</a>
</div>
</div>
<div class="col s6 m6 spacing-buttons">
<div class="center">
<button class="waves-effect waves-light btn" >Próximo</button>
<a href="requisicoes/passo=6" class="waves-effect waves-light btn" href="#!">Próximo</a>
</div>
</div>
</form>
Expand All @@ -54,3 +69,5 @@

</jsp:body>
</t:client>

<script src="assets/resources/scripts/job-image.js"></script>
14 changes: 14 additions & 0 deletions src/main/webapp/assets/resources/scripts/job-image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function deleteImage(url){
let path = url.split('/');
let filename = path[path.length - 1];

$.ajax({
type: 'DELETE',
url: 'requisicoes/session/image/' + filename,
complete: function() {
location. reload();
}
});
}


31 changes: 31 additions & 0 deletions src/main/webapp/assets/resources/styles/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ blockquote{
background-color: #3f51b5;
}

.btn-validate{
color: #fff;
background-color: #28a745;
}

.btn-validate:hover{
background-color: #28a745 !important;
}

.btn-gray{
color: #1c2231;
background-color: #cccccc;
Expand Down Expand Up @@ -635,6 +644,28 @@ textarea {
margin-top: 14px;
}

.gallery {
margin: -3px;
width: 100%;
overflow: hidden;
}

.box-image{
width: 100px;
height: 140px;
display: inline-block;
}

.box-image img {
width: 100px;
height: 100px;
object-fit: contain;
}

.box-image a {
color: #f44336;
}

@media only screen and (max-width : 992px) {
.parallax-container .section {
position: absolute;
Expand Down