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
117 changes: 113 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ O arquivo `src/main/resources/application.properties` já está versionado. Atua

```properties
spring.datasource.url=jdbc:postgresql://localhost:5433/projeto_vendas
spring.datasource.username=postgres
spring.datasource.password=12345
spring.datasource.username=nome do seu banco aqui
spring.datasource.password=sua senha do banco aqui
spring.datasource.driver-class-name=org.postgresql.Driver

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect


server.port=8081
```

> **Observação:** ajuste porta, usuário e senha para combinar com a sua instalação. Os endpoints usam Bean Validation (`spring-boot-starter-validation`); payloads inválidos retornam `400 Bad Request` com mensagens indicando cada campo.
Expand All @@ -59,7 +62,9 @@ Ou, na IDE, execute a classe `SistemaVendasApiApplication`.

## 5. Endpoints

Base URL padrão: `http://localhost:8080`
Base URL padrão: `http://localhost:8081`

> **Nota:** A aplicação está configurada para rodar na porta 8081. A interface web está disponível em `http://localhost:8081`.

### Clientes

Expand Down Expand Up @@ -161,7 +166,111 @@ mvn test

---

## 9. Próximos passos sugeridos
## 9. Interface Web

A aplicação possui uma interface web moderna e responsiva desenvolvida com Bootstrap 5 e Thymeleaf, proporcionando uma experiência de usuário intuitiva para gerenciar clientes, produtos e pedidos.

### 🏠 Página Inicial

A página inicial apresenta um dashboard com acesso rápido às principais funcionalidades do sistema:

<img width="1338" height="632" alt="image" src="https://github.com/user-attachments/assets/5cd743e7-8d02-4a28-9a60-c0b7038ad37f" />


**Características:**
- Design moderno com gradiente roxo e animação de partículas no fundo
- Cards interativos para navegação rápida
- Layout responsivo que se adapta a diferentes tamanhos de tela
- Header centralizado com ícone e descrição do sistema

### 👥 Gerenciamento de Clientes

Interface completa para cadastro e gerenciamento de clientes:

<img width="1319" height="631" alt="image" src="https://github.com/user-attachments/assets/9191a168-eed1-440d-b55b-f510674c4149" />



**Funcionalidades:**
- Formulário de cadastro com validação em tempo real
- Lista de clientes em formato de tabela
- Botões de edição e exclusão para cada cliente
- Layout em duas colunas (formulário e lista)

**Exemplo de uso:**
1. Preencha o formulário com nome, e-mail e telefone
2. Clique em "Cadastrar Cliente"
3. O cliente aparece automaticamente na lista
4. Use os botões "Editar" ou "Excluir" para gerenciar

### 📦 Gerenciamento de Produtos

Controle completo do catálogo de produtos e estoque:

<img width="1288" height="639" alt="image" src="https://github.com/user-attachments/assets/decd88f4-fc96-4716-859a-cdfcc3404d06" />




**Funcionalidades:**
- Cadastro de produtos com nome, descrição, preço e quantidade em estoque
- Lista completa de produtos cadastrados
- Edição e exclusão de produtos
- Validação de preços e estoque

**Exemplo de uso:**
1. Preencha os dados do produto (nome, descrição, preço, estoque)
2. Clique em "Cadastrar Produto"
3. O produto é adicionado ao catálogo
4. Gerencie produtos existentes através dos botões de ação

### 🛍️ Gerenciamento de Pedidos

Criação e acompanhamento de pedidos de venda:

<img width="1292" height="620" alt="image" src="https://github.com/user-attachments/assets/8049b87c-9f42-4ff8-ad36-28166e384129" />


**Funcionalidades:**
- Seleção de cliente para o pedido
- Adição de múltiplos itens ao pedido
- Seleção de produto e quantidade para cada item
- Lista de todos os pedidos criados
- Validação automática de estoque

**Exemplo de uso:**
1. Selecione um cliente no dropdown
2. Escolha um produto e informe a quantidade
3. Clique em "+ Adicionar Item" para adicionar mais produtos
4. Clique em "Criar Pedido" para finalizar
5. O sistema valida o estoque automaticamente

### 🎨 Design e Experiência do Usuário

**Características visuais:**
- **Cores:** Gradiente roxo moderno (#667eea a #764ba2)
- **Animações:** Partículas flutuantes sutis no fundo
- **Tipografia:** Fonte Segoe UI para melhor legibilidade
- **Cards:** Efeitos de hover e sombras suaves
- **Responsividade:** Layout adaptável para mobile, tablet e desktop

**Componentes:**
- Botões com gradiente e efeitos de hover
- Formulários com validação visual
- Tabelas responsivas com scroll horizontal em telas pequenas
- Footer com informações da equipe e links para GitHub

### 📱 Responsividade

A aplicação é totalmente responsiva, adaptando-se perfeitamente a diferentes dispositivos:

- **Desktop:** Layout em duas colunas para formulários e listas
- **Tablet:** Layout adaptado mantendo usabilidade
- **Mobile:** Layout em coluna única com elementos empilhados

---

## 10. Próximos passos sugeridos

- Adicionar paginação e filtros nas listagens.
- Criar testes de integração cobrindo fluxos de pedidos.
Expand Down
3 changes: 3 additions & 0 deletions docs/screenshots/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Esta pasta contém as screenshots da aplicação
# Adicione as imagens conforme descrito no README.md desta pasta

27 changes: 27 additions & 0 deletions docs/screenshots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Screenshots da Aplicação

Esta pasta contém as capturas de tela da interface web do Sistema de Gerenciamento de Vendas.

## Imagens necessárias

Para completar a documentação, adicione as seguintes screenshots:

1. **home.png** - Página inicial com os cards de navegação
2. **clientes.png** - Página de gerenciamento de clientes (formulário e lista)
3. **produtos.png** - Página de gerenciamento de produtos (formulário e lista)
4. **pedidos.png** - Página de gerenciamento de pedidos (formulário e lista)

## Como capturar as screenshots

1. Execute a aplicação: `mvn spring-boot:run`
2. Acesse `http://localhost:8081` no navegador
3. Navegue pelas páginas e capture as telas
4. Salve as imagens nesta pasta com os nomes indicados acima

## Recomendações

- Use formato PNG para melhor qualidade
- Capture em resolução de pelo menos 1920x1080
- Certifique-se de que os dados de exemplo estejam visíveis nas capturas
- Considere capturar versões desktop e mobile se necessário

13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>com.example</groupId>
<artifactId>sistema-vendas-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>1.0.0-SNAPSHOT</version>
<name>sistema-vendas-api</name>
<description>Demo project for Spring Boot</description>
<url/>
Expand All @@ -30,6 +30,12 @@
<java.version>17</java.version>
</properties>
<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
Expand All @@ -44,6 +50,11 @@
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.sistema_vendas_api.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig {

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(@NonNull CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("http://localhost:*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@


import com.example.sistema_vendas_api.model.Cliente;
import com.example.sistema_vendas_api.repository.ClienteRepository;
import com.example.sistema_vendas_api.service.ClienteService;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.validation.Valid;
import jakarta.validation.ConstraintViolationException;
Expand All @@ -25,48 +26,51 @@
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/clientes")
@RequestMapping("/api/clientes")
public class ClienteController {
@Autowired
private ClienteRepository clienteRepository;
private ClienteService clienteService;
// listar
@GetMapping
public List<Cliente> findAll(){
return clienteRepository.findAll();
return clienteService.listarClientes();
}
// cadastro
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Cliente> save(@Valid @RequestBody Cliente cliente){
Cliente salvo = clienteRepository.save(cliente);
Cliente salvo = clienteService.salvarCliente(cliente);
return ResponseEntity.status(HttpStatus.CREATED).body(salvo);
}
// busca por id
@GetMapping("/{id}")
public ResponseEntity<Cliente> findById(@PathVariable Integer id){
return clienteRepository.findById(id)
return clienteService.buscarPorId(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
// atualizar por id
@PutMapping("/{id}")
public ResponseEntity<Cliente> atualizar(@PathVariable Integer id, @Valid @RequestBody Cliente clienteAtualizado) {
return clienteRepository.findById(id)
.map(clienteExistente -> {
clienteExistente.setNome(clienteAtualizado.getNome());
clienteExistente.setEmail(clienteAtualizado.getEmail());
clienteExistente.setTelefone(clienteAtualizado.getTelefone());

Cliente atualizado = clienteRepository.save(clienteExistente);
return ResponseEntity.ok(atualizado);
})
.orElseGet(() -> ResponseEntity.notFound().build());
Cliente atualizado = clienteService.atualizarCliente(id, clienteAtualizado);
return ResponseEntity.ok(atualizado);
}
// deletar
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Integer id){
clienteRepository.deleteById(id);
public ResponseEntity<?> delete(@PathVariable Integer id){
try {
clienteService.deletarCliente(id);
return ResponseEntity.noContent().build();
} catch (EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", e.getMessage()));
} catch (IllegalStateException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", e.getMessage()));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", "Erro ao excluir cliente: " + e.getMessage()));
}
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
Expand All @@ -88,4 +92,16 @@ public Map<String, String> handleConstraintViolation(ConstraintViolationExceptio
violation -> violation.getMessage(),
(msg1, msg2) -> msg1));
}

@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(EntityNotFoundException.class)
public Map<String, String> handleEntityNotFound(EntityNotFoundException ex) {
return Map.of("message", ex.getMessage());
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalStateException.class)
public Map<String, String> handleIllegalState(IllegalStateException ex) {
return Map.of("message", ex.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@
import com.example.sistema_vendas_api.dto.PedidoDTO;
import com.example.sistema_vendas_api.model.Pedido;
import com.example.sistema_vendas_api.service.PedidoService;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/pedidos")
@RequestMapping("/api/pedidos")
public class PedidoController {
@Autowired
private PedidoService pedidoService;
Expand All @@ -34,4 +38,18 @@ public ResponseEntity<List<Pedido>> listarPedidos() {
List<Pedido> pedidos = pedidoService.listarPedidos();
return ResponseEntity.ok(pedidos);
}

@DeleteMapping("/{id}")
public ResponseEntity<?> delete(@PathVariable Integer id) {
try {
pedidoService.deletarPedido(id);
return ResponseEntity.noContent().build();
} catch (EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(Map.of("message", e.getMessage()));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", "Erro ao excluir pedido: " + e.getMessage()));
}
}
}
Loading