O Instituto Senai de Inovação está em busca de desenvolvedores com perfil versátil, capazes de atuar tanto no backend quanto no frontend, para resolver desafios reais e relevantes.
Este desafio faz parte do nosso processo seletivo para a vaga de Desenvolvedor Fullstack Júnior.
O objetivo é avaliar sua habilidade técnica, clareza de raciocínio, organização de código e capacidade de entregar uma aplicação funcional com base em requisitos enxutos, algo que acontece com frequência em projetos de inovação.
Mais do que domínio técnico, valorizamos a capacidade de pensar de forma estruturada, escrever código limpo, seguir boas práticas e tomar decisões conscientes durante o desenvolvimento.
- Organização do projeto com estrutura clara e lógica
- Capacidade de construir uma aplicação funcional, mesmo com requisitos limitados
- Uso consistente de boas práticas no frontend e backend
- Uso de Git com histórico coerente de commits
- Código limpo e legível, seguindo os princípios do Clean Code (nomes significativos, pequenas funções, baixo acoplamento, alta coesão)
- Arquitetura consistente, com separação clara entre camadas da aplicação (ex: MVC, DDD)
- Tratamento de erros e respostas HTTP padronizadas (mensagens claras e status codes corretos)
- Testes automatizados (unitários ou de integração ou E2E) com cobertura mínima dos fluxos principais – não são obrigatórios, mas contam como diferencial
- Documentação técnica clara no
README.mdexplicando o setup, execução e decisões técnicas - Interface com responsividade básica, priorizando usabilidade
- Uso de Docker (
Dockerfilee/oudocker-compose) para facilitar o deploy e garantir portabilidade do ambiente, não é obrigatório, mas será considerado um diferencial - Uso consciente de bibliotecas externas com explicações, quando necessário, das escolhas de dependência
| Critério | Peso |
|---|---|
| Estrutura e organização do projeto | ★★★★★ |
| Aplicação correta das regras de negócio | ★★★★☆ |
| Clareza e limpeza do código | ★★★★☆ |
| Tratamento de erros | ★★★☆☆ |
| README técnico completo | ★★★★☆ |
| Git com granularidade e histórico limpo | ★★★☆☆ |
| Aderência a padrões REST e boas práticas HTTP | ★★★☆☆ |
| Experiência do Usuário (Frontend) | ★★★★☆ |
Este desafio técnico tem como propósito simular um fluxo real de vendas dentro de um ambiente corporativo, focando no cadastro, listagem, edição e gerenciamento de produtos, bem como na aplicação de descontos com regras de negócio rigorosas e realistas. A ideia é proporcionar uma experiência próxima da realidade de sistemas de e-commerce ou ERPs modernos, exigindo atenção aos detalhes, domínio de boas práticas de desenvolvimento e compreensão sólida de integrações e processos de validação.
Você será responsável por desenvolver uma aplicação web fullstack, contemplando as seguintes funcionalidades principais:
- Cadastro de produtos com controle de preço, estoque e unicidade de nome
- Listagem de produtos com filtros avançados e paginação
- Aplicação e remoção de descontos percentuais ou cupons promocionais, seguindo regras específicas de negócio
A proposta visa não apenas testar a sua capacidade técnica e organização de projeto, mas também avaliar sua sensibilidade às regras de negócio, tratamento de exceções e comportamento frente a requisitos ambíguos ou propensos a falhas. Essa simulação aproxima-se dos desafios enfrentados por times de desenvolvimento que lidam com fluxos reais de negócio em empresas que prezam pela confiabilidade e manutenção da lógica da aplicação ao longo do tempo.
- NestJS: framework moderno e escalável que utiliza TypeScript por padrão e favorece uma arquitetura modular.
- Express.js: framework web minimalista e flexível, bastante difundido para criação de APIs REST com Node.js.
Observação: Também é permitida a utilização de outras linguagens, como Java com Spring Boot, desde que a arquitetura web REST seja mantida e os requisitos sejam atendidos.
-
Biblioteca Base: ReactJS
Preferencialmente, utilize ReactJS, com ou sem frameworks adicionais. Esperamos a construção de componentes reutilizáveis, aplicação de um controle de estado simples comuseState,useEffect, e, se necessário, Context API para compartilhamento de estado global.A integração com a API backend deve ser clara, funcional e bem organizada (ex.: fetch/axios com tratamento de loading e erro).
-
Liberdade de Framework:
Você tem liberdade para utilizar frameworks baseados em React, como:- Next.js: oferece roteamento baseado em arquivos, SSR, SSG e ótimo suporte a SEO.
- Remix: traz uma abordagem centrada em rotas, com carregamento de dados no servidor, ideal para experiências rápidas e declarativas.
Ambos são bem-vindos e aceitos neste desafio, desde que o projeto mantenha clareza arquitetural, organização e cobertura dos requisitos funcionais.
-
Liberdade de Biblioteca de Estado:
Também é permitido utilizar a biblioteca de gerenciamento de estado de sua preferência, como:O mais importante é que a implementação seja bem estruturada, compreensível e adequada à complexidade da aplicação.
-
Outras Bibliotecas/Frameworks:
Frameworks modernos como Vue.js ou Angular também são aceitos, desde que respeitem a arquitetura proposta e cumpram todos os requisitos funcionais com clareza.
- Tipo: Relacional em memória ou Relacional
- Exemplo: SQLite utilizando
':memory:'como caminho para rodar totalmente em memória.
Alternativas Aceitas:
- H2 ou HSQLDB (para implementações em Java)
better-sqlite3no Node.js (com suporte a':memory:')- Qualquer outro banco relacional leve, desde que mantenha a persistência temporária ou fixa (Postgres, MySQL) e seja fácil de configurar para fins de teste e demonstração
Você deverá criar:
-
Backend com API RESTful para produtos e cupons, incluindo:
- Endpoints para criação, edição, inativação (soft delete) e listagem paginada de produtos
- Endpoints para aplicar e remover descontos percentuais e cupons promocionais
- Validações de unicidade, integridade referencial e regras específicas de negócio
-
Frontend funcional, contendo:
- Listagem de produtos com paginação, busca e filtros (nome, faixa de preço, com ou sem desconto)
- Formulário de cadastro e edição de produtos (com suporte a atualização via patch)
- Interface para aplicar e remover cupons/descontos, com feedback visual claro
- Destaques visuais para produtos com estoque zerado ou com desconto ativo
-
Validações robustas, cobrindo:
- Formatos de preço, normalização de nome e restrições de valor
- Prevenção à aplicação múltipla de descontos no mesmo produto
- Rejeição de cupons inválidos, expirados ou que resultem em valor final abaixo de R$ 0,01
- Retorno de códigos HTTP apropriados para erros esperados (
422,409,412, entre outros)
Deve ser possível:
- Visualizar uma tabela com as colunas:
- Nome
- Descrição
- Estoque atual
- Preço original
- Preço com desconto (se aplicável)
- Exibir badges ou indicadores visuais para:
- Produtos com desconto ativo
- Produtos com estoque zerado
- Filtrar produtos por:
- Nome (busca textual)
- Faixa de preço (mínimo e máximo)
- Com ou sem desconto ativo (booleano)
- Paginar os resultados com paginação padrão de 10 por página
Os seguintes parâmetros podem ser utilizados para refinar a listagem de produtos:
page(integer ≥ 1): número da página atual.limit(integer, entre 1 e 50): quantidade de itens por página.search(string): busca textual no nome ou descrição do produto.minPrice/maxPrice(decimal): filtro por faixa de preço mínima e máxima.hasDiscount(boolean):trueoufalsepara filtrar produtos com ou sem desconto.sortBy(string): campo para ordenação. Valores possíveis:name,price,created_at,stock.sortOrder(string): direção da ordenação. Pode serascoudesc.includeDeleted(boolean): setrue, inclui produtos inativos (soft deleted). Recomendado apenas para administradores.onlyOutOfStock(boolean): setrue, retorna apenas produtos com estoque igual a zero.withCouponApplied(boolean): setrue, retorna apenas produtos que possuem cupom promocional aplicado.
- Garantir que a ordenação por nome, preço e estoque seja suportada em ambas as direções (
ascedesc). - Retornar resposta
200 OKcom uma lista vazia caso nenhuma correspondência seja encontrada. - Adotar comportamento consistente de paginação, mesmo quando não houver resultados.
Exemplo: sepage=10não possui itens, ainda assim deve retornar um array vazio com os metadados corretos (page,limit,totalItems,totalPages).
O cadastro de produtos deve ser feito com especial atenção às validações de regras de negócio, estrutura dos campos e consistência dos dados. A aplicação deverá ser robusta o suficiente para impedir erros simples e respeitar todas as regras de domínio.
-
name(string, obrigatório)- Deve ser único após normalização (remover espaços duplicados)
- Mínimo: 3 caracteres
- Máximo: 100 caracteres
- Regex recomendado:
^[a-zA-Z0-9\s\-_,.]+$
-
description(string, opcional)- Máximo: 300 caracteres
-
stock(integer, obrigatório)- Valor mínimo: 0
- Valor máximo: 999999
-
price(decimal, obrigatório)- Mínimo: 0.01
- Máximo: 1.000.000,00
- Deve aceitar formatos brasileiros (ex: "1.234,56") e internacionais (ex: 1234.56), mas ser persistido corretamente como decimal
-
created_at(ISODate, obrigatório)- Deve ser preenchido com a data/hora atual no momento do cadastro
- Deve ser imutável após criação
- Deve estar no passado ou presente (nunca no futuro)
- O campo
namedeve ser único no banco mesmo após normalização. - Produtos com
stock == 0devem ser destacados na listagem com um badge visual "Esgotado". - O valor original nunca deve ser alterado por descontos. Os descontos devem ser armazenados separadamente.
- O valor com desconto (se houver) deve ser sempre recalculado dinamicamente e não salvo de forma persistente no banco.
- É proibido criar produtos com o preço com desconto menor que R$ 0,01.
- O campo
created_atdeve refletir corretamente a data/hora da criação e não pode ser editado posteriormente. - Adicionar o campo
updated_atpara rastrear modificações (opcional, mas recomendado).
-
Desconto percentual direto:
O produto pode receber um desconto percentual, aplicado diretamente sobre o valor original.- O valor do desconto deve estar entre 1% e 80%.
- O cálculo do valor final deve ser feito de forma dinâmica no momento da exibição da tabela.
- Este tipo de desconto não requer código promocional e é aplicado automaticamente conforme regras do sistema (ex: campanhas sazonais, queima de estoque etc.).
-
Cupom promocional:
O sistema deve permitir a aplicação de cupons promocionais, que podem ser de dois tipos:- fixed (ex: R$ 50 de desconto)
- percent (ex: 10%, 25% etc.)
Os cupons devem ser previamente cadastrados no sistema e conter:
- Um código único identificador, como
PROMO10,QUERO25, etc. - Uma regra de validade (data de expiração ou quantidade de usos).
- Um tipo (fixed ou percent) e valor correspondente.
- Aplicação dinâmica no checkout, sem persistência do valor com desconto.
⚠️ É responsabilidade do sistema validar se o cupom é válido, aplicável ao produto, e se respeita os limites mínimos de valor final (por exemplo, não permitir que o produto fique abaixo de R$ 0,01).
-
Apenas um único desconto pode estar ativo por produto.
Caso já exista um desconto aplicado (seja percentual ou via cupom), a tentativa de aplicar outro deve resultar em erro409 Conflict. -
Validações para cupons promocionais
Todo cupom promocional deve conter os seguintes campos e validações obrigatórias:-
code(string, obrigatória e única)- Deve ter entre 4 e 20 caracteres
- Apenas caracteres alfanuméricos (sem símbolos especiais)
- Sem espaços
- Deve ser normalizado: remover acentos, converter para lowercase
- Códigos reservados como
admin,auth,null,undefineddevem ser rejeitados - Deve ser único no banco, considerando a versão normalizada do código
-
type(fixedoupercent)- Campo obrigatório
- Somente os valores
fixedoupercentsão aceitos - Qualquer outro valor deve gerar erro
400 Bad Request
-
value(número, obrigatório)- Se
type == percent, o valor deve estar entre 1 e 80 - Se
type == fixed, o valor monetário deve ser positivo (ex: R$ 10.00)
- Se
-
oneShot(booleano, obrigatório)- Se
true, o cupom pode ser usado apenas uma vez por produto - Aplicações repetidas do mesmo cupom no mesmo produto devem retornar
409 Conflict
- Se
-
valid_from/valid_until(datas ISO, obrigatórias)valid_fromdefine a data/hora em que o cupom se torna válidovalid_untildefine a data limite de validade do cupom- A data atual deve estar entre
valid_fromevalid_untilpara que o cupom seja aceito - O valor de
valid_untildeve ser maior quevalid_from - A validade máxima permitida para cupons é de 5 anos a partir de
valid_from
-
-
Cupons fora da janela de validade (ou seja, com a data atual anterior a
valid_fromou posterior avalid_until) devem ser rejeitados com erro400 Bad Request. -
Cupons que resultem em um preço final inferior a R$ 0,01 devem ser rejeitados com erro
422 Unprocessable Entity.
Essa validação deve ocorrer antes da persistência e impedir a conclusão da operação. -
A aplicação de descontos (seja percentual ou por cupom) deve ocorrer de forma transacional.
Ou seja, o cupom, o vínculo ao produto e o cálculo do valor com desconto devem ser tratados como uma única operação atômica evitando estados inconsistentes no banco de dados. -
A remoção de um desconto (cancelamento ou expiração) deve restaurar automaticamente o valor original do produto na exibição.
Importante: o campopricedeve sempre manter o valor original do produto, o valor com desconto nunca deve ser salvo diretamente nesse campo.
⚠️ Esta documentação apresenta uma sugestão de estrutura para os endpoints da API.
Ela serve como base para implementação e validação dos requisitos funcionais descritos no desafio.
Adaptações podem ser feitas conforme a arquitetura adotada, desde que respeitem os princípios RESTful e garantam clareza e consistência no uso da API.
Base URL: /api/v1
Todos os exemplos usam JSON. Datas em ISO-8601 (UTC).
| Método & Rota | Descrição | Status codes principais |
|---|---|---|
GET /products |
Lista paginada com filtros avançados. | 200 |
GET /products/{id} |
Detalhes de um produto. | 200, 404 |
POST /products |
Cria produto. | 201, 400, 409 |
PATCH /products/{id} |
Atualização parcial via JSON Patch; | 200, 400, 404, 412 |
DELETE /products/{id} |
Inativa (soft-delete) o produto. | 204, 404 |
POST /products/{id}/restore |
Restaura produto inativo. | 200, 404 |
POST /products/{id}/discount/percent |
Aplica desconto percentual. | 200, 400, 409, 422 |
POST /products/{id}/discount/coupon |
Aplica cupom promocional. | 200, 400, 404, 409, 422 |
DELETE /products/{id}/discount |
Remove desconto ativo. | 204, 404 |
GET /api/v1/products?page=1&limit=10&search=cafe&minPrice=100&hasDiscount=true
Accept: application/json
Query params: page, limit, search, minPrice, maxPrice, hasDiscount, sortBy, sortOrder, includeDeleted, onlyOutOfStock, withCouponApplied
{
"page": 1,
"limit": 10,
"search": "cafe",
"minPrice": 100,
"maxPrice": 1000,
"hasDiscount": true,
"sortBy": "name",
"sortOrder": "asc",
"includeDeleted": false,
"onlyOutOfStock": false,
"withCouponApplied": false
}Exemplo de Resposta 200
{
"data": [
{
"id": 42,
"name": "Café Premium",
"description": "100% arábica",
"stock": 3,
"is_out_of_stock": false,
"price": 2590,
"finalPrice": 2201,
"discount": {
"type": "percent",
"value": 15,
"applied_at": "2025-06-04T18:15:21Z"
},
"hasCouponApplied": true,
"created_at": "2025-05-30T12:00:00Z",
"updated_at": "2025-06-01T09:20:05Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"totalItems": 1,
"totalPages": 1
}
}discountdeve sernullquando não houver desconto ativo ou se o desconto estiver expirado.- Produtos inativos só devem aparecer na listagem se o parâmetro
includeDeleted=truefor utilizado.
POST /api/v1/products
Content-Type: application/json
{
"name": "Café Premium",
"description": "100% arábica",
"stock": 250,
"price": 2590
}Cria produto e devolve cabeçalho Location: /products/{id}.
Erros
400 Bad Request– Corpo da requisição inválido, falha na regex, preço negativo, campocreated_atcom data futura, entre outros erros de validação.409 Conflict– Nome duplicado no banco de dados após normalização (remoção de acentos, espaços extras, conversão para lowercase).
Cabeçalho obrigatório:
Content-Type: application/json-patch+json
Exemplo de patch:
[
{ "op": "replace", "path": "/stock", "value": 300 },
{ "op": "replace", "path": "/price", "value": 2790 }
]| Método & Rota | Descrição |
|---|---|
GET /coupons |
Lista / busca códigos. |
POST /coupons |
Cria cupom. |
GET /coupons/{code} |
Detalhes de cupom. |
PATCH /coupons/{code} |
Edita (exceto code). |
DELETE /coupons/{code} |
Inativa cupom. |
{
"code": "promo10",
"type": "percent",
"value": 10,
"oneShot": false,
"valid_from": "2025-06-01T00:00:00Z",
"valid_until": "2027-06-01T00:00:00Z"
}- O campo
codedeve ser único após normalização (remoção de acentos, conversão para lowercase e remoção de espaços). - O campo
typedeve estar contido no conjunto{ "fixed", "percent" }. - O campo
valuedeve respeitar:- Para
type = "percent": valor entre 1 e 80. - Para
type = "fixed": valor monetário positivo (maior que 0).
- Para
- O campo
valid_untildeve ser uma data posterior avalid_frome com no máximo 5 anos de diferença entre elas.
| Código | Quando ocorre |
|---|---|
| 400 Bad Request | Violação de validação; campo imutável; cupom expirado ou ainda não válido. |
| 409 Conflict | Tentar cadastrar nome normalizado já existente; aplicar segundo desconto; cupom oneShot reutilizado. |
| 422 Unprocessable Entity | Desconto faz preço cair < R$ 0,01. |
| 404 Not Found | Recurso inexistente ou inativo sem includeDeleted=true. |
| 500 Internal Server Error | Falha inesperada (transação revertida). |
A modelagem relacional proposta inclui três tabelas principais:
| Campo | Tipo | Descrição |
|---|---|---|
id |
INTEGER | Identificador único do produto (PK). |
name |
STRING | Nome do produto. |
description |
STRING | Descrição do produto (opcional, até 300 caracteres). |
price |
DECIMAL | Preço original (mínimo 0,01; formato decimal). |
stock |
INTEGER | Quantidade disponível em estoque (0 a 999 999). |
created_at |
DATETIME | Timestamp de criação (não editável). |
updated_at |
DATETIME | Timestamp da última atualização (opcional). |
deleted_at |
DATETIME | Timestamp de inativação soft-delete (opcional). |
| Campo | Tipo | Descrição |
|---|---|---|
id |
INTEGER | Identificador único do cupom (PK). |
code |
STRING | Código único do cupom (ex: "SAVE10"). Deve ser normalizado (lowercase, sem acentos ou espaços). |
type |
ENUM | Tipo de desconto: "percent" ou "fixed". |
value |
DECIMAL | Valor do desconto: - Percentual: 1–80 (para 1% a 80%)- Fixo: valor monetário (> 0). |
one_shot |
BOOLEAN | Se true, só pode ser usado uma única vez por produto. |
max_uses |
INTEGER | Número máximo de usos permitidos (quando one_shot = false). |
uses_count |
INTEGER | Contador de quantas vezes o cupom já foi aplicado. |
valid_from |
DATETIME | Data/hora em que o cupom se torna válido. |
valid_until |
DATETIME | Data/hora de expiração do cupom (máx. 5 anos após valid_from). |
created_at |
DATETIME | Timestamp de criação (não editável). |
updated_at |
DATETIME | Timestamp da última atualização (opcional). |
deleted_at |
DATETIME | Timestamp de inativação soft-delete (opcional). |
Tabela responsável por registrar a aplicação de um cupom promocional a um produto.
Cada produto pode ter no máximo um cupom aplicado por vez.
Essa estrutura garante o controle transacional da aplicação e remoção de descontos, respeitando a regra de exclusividade.
| Campo | Tipo | Descrição |
|---|---|---|
id |
INTEGER | Identificador único da aplicação (PK). |
product_id |
INTEGER | Chave estrangeira para products.id. |
coupon_id |
INTEGER | Chave estrangeira para coupons.id. |
applied_at |
TIMESTAMP | Data/hora da aplicação do cupom (ISO 8601 UTC). |
removed_at |
TIMESTAMP | Data/hora da remoção do cupom (nulo enquanto ativo). |
Regras adicionais:
- Um produto pode ter apenas um registro ativo por vez (
removed_at IS NULL). - A tentativa de aplicar um novo cupom enquanto outro estiver ativo deve resultar em erro
409 Conflict. - A aplicação e remoção devem ocorrer de forma transacional, garantindo consistência.
- Após remoção, o sistema deve exibir o valor original (
price) do produto, sem alterar permanentemente seu valor base.
Este fluxo representa o caminho ideal de uso da aplicação, cobrindo o cadastro de produto, criação e aplicação de cupom de desconto, consulta do produto com desconto aplicado e remoção do desconto.
-
Criar um produto
POST /products- Exemplo: cria um produto chamado "Café", que será retornado com
id = 42.
-
Cadastrar um cupom de desconto
POST /coupons- Exemplo: cria um cupom com o código
"promo15".
-
Aplicar o cupom ao produto
-
POST /products/42/discount/coupon -
Corpo da requisição (JSON):
{ "code": "promo15" } -
Resultado: o produto passa a exibir o preço com o desconto aplicado, conforme as regras definidas pelo cupom.
-
-
Consultar o produto com o desconto aplicado -
GET /products/42- A resposta inclui uma seçãodiscount, com detalhes do desconto atual. -
Remover o desconto aplicado -
DELETE /products/42/discount- O preço do produto volta ao valor original, sem desconto.
sequenceDiagram
participant Usuário
participant Frontend
participant API
participant Banco
Usuário->>Frontend: Digita código do cupom
Frontend->>API: POST /products/42/discount/coupon\n{ "code": "promo15" }
API->>Banco: Valida existência do produto
Banco-->>API: Produto encontrado
API->>Banco: Verifica cupom por código
Banco-->>API: Cupom válido retornado
API->>Banco: Verifica se já existe desconto ativo
Banco-->>API: Nenhum desconto aplicado
API->>Banco: Valida datas (valid_from/valid_until)
Banco-->>API: Cupom está dentro do período válido
API->>Banco: Aplica cupom e registra applied_at
Banco-->>API: Atualização realizada com sucesso
API-->>Frontend: 200 OK\nCupom aplicado com sucesso
Frontend-->>Usuário: Feedback visual de sucesso
💡 Os passos de 2 a 5 podem ser encapsulados em uma mesma transação caso seja necessário garantir consistência transacional.
A interface do sistema deve ser desenvolvida com base no mockup de referência apresentado abaixo.
Embora não seja necessário seguir o design com precisão pixel a pixel, espera-se que o fluxo de interação, a disposição dos elementos e a estrutura geral da tela (ex: tabela, filtros, botões) sejam respeitados e reflitam a proposta visual fornecida.
Você tem total liberdade para escolher a biblioteca ou framework de frontend que preferir.
Será dada preferência para implementações em ReactJS, mas qualquer solução moderna que atenda aos requisitos funcionais será aceita.
- TailwindCSS
- Shadcn/UI
- Material UI
- Chakra UI
- Bootstrap
- CSS tradicional, SCSS, ou soluções CSS-in-JS como styled-components e Emotion
Você pode utilizar qualquer biblioteca para gerenciar o estado da aplicação, conforme sua familiaridade ou necessidade do projeto. Exemplos:
É permitido o uso de bibliotecas que otimizem a manipulação de formulários, chamadas assíncronas e validações, desde que seu uso seja justificado tecnicamente no projeto. Exemplos:
- React Hook Form — gerenciamento eficiente de formulários com integração a validações
- React Query — para controle de estados de requisição, cache e sincronização com servidor
- Axios ou
fetchpara chamadas HTTP - Formik — alternativa para controle de formulários
- Yup ou Zod — para validação de schemas de dados
💡 Ao Utilize essas bibliotecas, informe a necessidade ou motivo por exemplo no README, para que possamos entender a necessidade do uso dessa ou de qualquer outra lib.
O objetivo principal da interface é entregar uma experiência de uso fluida, clara e funcional.
Espera-se uma aplicação com alta usabilidade, acessibilidade básica e foco na qualidade do código considerando organização, padronização, legibilidade e boas práticas de desenvolvimento frontend.
Além da fidelidade ao fluxo proposto no mockup, a implementação deve demonstrar domínio técnico na construção de componentes reutilizáveis, tratamento adequado de estados e formulários, e feedback visual eficaz ao usuário.
A aplicação deve conter, no mínimo, as seguintes funcionalidades implementadas:
-
Tabela paginada de produtos, com:
- Filtros por nome, faixa de preço e status de desconto
- Colunas: nome, descrição, estoque, preço original e preço com desconto
- Destaques visuais para:
- Produtos esgotados (badge ou rótulo com "Esgotado")
- Produtos com desconto ativo
-
Formulário de cadastro e edição de produto
- Com validações de campos e mensagens de erro claras e acessíveis
- Estrutura consistente para inserção e atualização dos dados
-
Modal para aplicação de cupom promocional
- Permite informar o código do cupom e aplicá-lo ao produto selecionado
- Exibe feedback imediato em caso de sucesso ou erro
- Ex: cupom inválido, expirado, ou do tipo
oneShotjá utilizado anteriormente
- Ex: cupom inválido, expirado, ou do tipo
-
Botão para remoção de desconto
- Disponível na interface do produto
- Deve refletir a alteração imediatamente na listagem e/ou na visualização de detalhes
Para acessar o mockup de interface do usuário, utilize o link abaixo.
🔗 Clique aqui para abrir o mockup no Figma
O README.md deve conter os seguintes tópicos:
-
Como rodar o projeto
- Instruções para executar o projeto localmente (por exemplo:
npm install && npm run dev) - Incluir um arquivo
.env.examplecom as variáveis de ambiente necessárias para configuração
- Instruções para executar o projeto localmente (por exemplo:
-
Estrutura do projeto
- Listar as principais pastas do projeto
- Descrever brevemente a função de cada uma (ex:
/components,/pages,/services)
-
Tecnologias utilizadas
- Informar as bibliotecas e frameworks principais utilizados
- Exemplos esperados:
ReactJS,TailwindCSS,Axios,NestJS,Prisma
Sabemos que o tempo e a complexidade podem variar para cada participante.
Por isso, mesmo que nem todas as funcionalidades tenham sido implementadas, você pode encaminhar o desafio mesmo incompleto.
O objetivo é também avaliar sua organização de código, clareza técnica, arquitetura, e boas práticas aplicadas, independentemente do nível de conclusão funcional.
✅ Ao enviar o que já foi feito, nossa equipe técnica irá revisar o código com atenção e fornecer feedback construtivo, destacando os pontos positivos, boas decisões e oportunidades de melhoria.
💡 Dica: Caso envie parcialmente, aproveite o
README.mdpara explicar suas escolhas, dificuldades encontradas e o que pretendia implementar a seguir.
Para padronizarmos o recebimento e garantir que nada se perca no processo, siga as instruções abaixo:
-
Compactação do Projeto:
- Compacte seu projeto em um arquivo
.zipcontendo todo o código-fonte, incluindo oREADME.md.
- Compacte seu projeto em um arquivo
-
Nomeação do Arquivo:
- Nomeie o arquivo com seu nome completo (exemplo:
igor_conde_desafio_fullstack.zip).
- Nomeie o arquivo com seu nome completo (exemplo:
-
Envio por E-mail:
-
Assunto: Use o seguinte título padrão:
[Entrega Desafio Técnico] Nome Completo – Desenvolvedor(a) Fullstack
Exemplo:[Entrega Desafio Técnico] Igor Conde – Desenvolvedor Fullstack -
Corpo do E-mail:
- Escreva seu nome completo novamente.
- Adicione, se desejar, um breve comentário sobre o envio.
-
Anexo: Anexe o arquivo
.zipao e-mail.
-
-
Destinatário:
- Envie para:
igor.conde@sistemafiepe.org.br
- Envie para:
Boa sorte! 🚀
