Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
128ab2c
Remove outdated modules and challenges from the repository, including…
DIEGOHORVATTI Jul 15, 2026
7af6fb5
chore: set up bun workspaces monorepo
DIEGOHORVATTI Jul 16, 2026
4436be8
chore(contracts): add package scaffolding
DIEGOHORVATTI Jul 16, 2026
b2575ed
feat(contracts): add machine and sensor zod schemas
DIEGOHORVATTI Jul 16, 2026
9f8a391
feat(contracts): add machine routes
DIEGOHORVATTI Jul 16, 2026
dff0096
feat(contracts): add measurement series zod schemas
DIEGOHORVATTI Jul 16, 2026
9cabfa2
feat(contracts): add measurement routes
DIEGOHORVATTI Jul 16, 2026
fdc9064
feat(contracts): aggregate contracts as single source of truth
DIEGOHORVATTI Jul 16, 2026
6bdfea4
chore(backend): add package scaffolding
DIEGOHORVATTI Jul 16, 2026
1a2ae51
feat(backend): add pure series domain
DIEGOHORVATTI Jul 16, 2026
836c088
test(backend): cover series domain rules
DIEGOHORVATTI Jul 16, 2026
d8707c3
feat(backend): declare repository contracts
DIEGOHORVATTI Jul 16, 2026
f224bc2
feat(backend): add machines mock
DIEGOHORVATTI Jul 16, 2026
5ef7e52
feat(backend): implement repositories over json mocks
DIEGOHORVATTI Jul 16, 2026
054fabd
feat(backend): add machine use cases
DIEGOHORVATTI Jul 16, 2026
d571037
feat(backend): add get machine measurements use case
DIEGOHORVATTI Jul 16, 2026
6a6df5d
test(backend): cover measurements use case
DIEGOHORVATTI Jul 16, 2026
816b1d5
feat(backend): wire dependencies in container
DIEGOHORVATTI Jul 16, 2026
a593801
feat(backend): implement orpc contract
DIEGOHORVATTI Jul 16, 2026
a77790f
feat(backend): add machine route handlers
DIEGOHORVATTI Jul 16, 2026
e2884cc
feat(backend): add measurement route handler
DIEGOHORVATTI Jul 16, 2026
cbb5542
feat(backend): compose router
DIEGOHORVATTI Jul 16, 2026
462c83e
feat(backend): serve api with cors and openapi docs
DIEGOHORVATTI Jul 16, 2026
55d03b1
chore(frontend): add vite scaffolding
DIEGOHORVATTI Jul 16, 2026
7eaf5d3
feat(frontend): port minimals theme to vite
DIEGOHORVATTI Jul 16, 2026
4bfaa74
feat(frontend): port minimals components in use
DIEGOHORVATTI Jul 16, 2026
7df6851
feat(frontend): add global styles and runtime config
DIEGOHORVATTI Jul 16, 2026
10d3de9
feat(frontend): add typed orpc client
DIEGOHORVATTI Jul 16, 2026
39d9d16
test(frontend): cover orpc error messages
DIEGOHORVATTI Jul 16, 2026
5ee45cb
feat(frontend): add shared request status type
DIEGOHORVATTI Jul 16, 2026
21d77dc
feat(frontend): add machines slice
DIEGOHORVATTI Jul 16, 2026
3985daf
feat(frontend): add period to time range mapping
DIEGOHORVATTI Jul 16, 2026
3ea436d
feat(frontend): add measurements slice
DIEGOHORVATTI Jul 16, 2026
869c945
feat(frontend): add selectors grouping series by metric
DIEGOHORVATTI Jul 16, 2026
a0adf2e
feat(frontend): add machines saga
DIEGOHORVATTI Jul 16, 2026
ce59297
feat(frontend): add measurements saga
DIEGOHORVATTI Jul 16, 2026
a8d92ac
feat(frontend): compose root reducer and saga
DIEGOHORVATTI Jul 16, 2026
8a808ea
feat(frontend): add store factory and typed hooks
DIEGOHORVATTI Jul 16, 2026
f747e83
test(frontend): add fixtures and setup
DIEGOHORVATTI Jul 16, 2026
2ca5246
test(frontend): cover slices
DIEGOHORVATTI Jul 16, 2026
2e9bcef
test(frontend): cover period mapping and selectors
DIEGOHORVATTI Jul 16, 2026
1178527
feat(frontend): add highcharts options builder
DIEGOHORVATTI Jul 16, 2026
6bc5527
test(frontend): cover chart options
DIEGOHORVATTI Jul 16, 2026
c97bce5
feat(frontend): sync crosshair and tooltip across charts
DIEGOHORVATTI Jul 16, 2026
b0c6a3b
feat(frontend): add series stats row
DIEGOHORVATTI Jul 16, 2026
dfa4ca1
feat(frontend): add metric chart
DIEGOHORVATTI Jul 16, 2026
10bb65c
feat(frontend): add machine header
DIEGOHORVATTI Jul 16, 2026
3716e34
feat(frontend): add machine and period toolbar
DIEGOHORVATTI Jul 16, 2026
3f3d4b0
feat(frontend): add /data view
DIEGOHORVATTI Jul 16, 2026
4b8a403
feat(frontend): keep machine and period filters in the url
DIEGOHORVATTI Jul 16, 2026
7ed060e
test(frontend): cover /data view and url filters
DIEGOHORVATTI Jul 16, 2026
8459861
feat(frontend): add router and app entrypoint
DIEGOHORVATTI Jul 16, 2026
b947ca4
chore: add bun lockfile
DIEGOHORVATTI Jul 16, 2026
113a0f0
docs: document setup, architecture and trade-offs
DIEGOHORVATTI Jul 16, 2026
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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules/
dist/
.env
.env.local
*.log
coverage/
.vite/
cypress/videos/
cypress/screenshots/
storybook-static/
85 changes: 53 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,76 @@
# Dynamox Developer Challenges
> 📋 **Solução deste desafio: [SOLUTION.md](./SOLUTION.md)** — como executar, arquitetura e decisões.
>
> TL;DR: `bun install && bun run dev` → http://localhost:3000/data

## About Dynamox
---

[Dynamox](https://dynamox.net/) is a high-tech firm specializing in vibration analysis and industrial asset condition monitoring. Our expert team develops comprehensive hardware and software solutions, encompassing firmware, mobile applications (Android and iOS), and full-stack cloud native applications.
# Desafio de Desenvolvedor Front-end da Dynamox

With our proficiency in signal processing for vibration and acoustics, we deliver advanced and precise monitoring systems. We are committed to optimizing operational efficiency and facilitating proactive maintenance through our innovative technology and integrated solutions.
A equipe de desenvolvimento front-end da Dynamox apresenta o seguinte desafio:

## Positions
**Utilizando React e TypeScript, desenvolva uma aplicação de dashboard robusta e intuitiva que permita analisar os dados coletados por nossos sensores.**

We are looking for developers who are passionate about learning, growing, and contributing to our team. You will play a key role in our development efforts, working on a variety of projects and collaborating with different teams to build and improve our solutions.
---

We value flexibility and collaboration, hence we provide opportunities for you to lend your skills to other teams when required. Join us on this exciting journey as we revolutionize our digital platforms. Currently we are particularly interested in individuals who can identify with one of the following role descriptions:
Lembre-se de que o desafio visa reproduzir um ambiente onde você possa demonstrar suas habilidades.

### Junior Software Developer
Para orientar seu processo de desenvolvimento, forneceremos alguns requisitos. Não é obrigatório atender a todos os requisitos para enviar sua implementação. Quanto mais requisitos você implementar, mais elementos teremos para avaliar suas habilidades e conhecimentos.

With limited experience, assists in coding, testing, and stabilizing systems under supervision. Communicates with immediate team members and solves straightforward problems with guidance. Should display a willingness to learn and grow professionally. This is an individual contributor role.
Use seu bom senso para priorizar tarefas de acordo com o tempo disponível. Sinta-se à vontade para fazer quaisquer suposições que considere necessárias para concluir a tarefa.

### Mid-level Software Developer
## Requisitos Funcionais e Histórias de Usuário

With a certain level of proven experience, contributes to software development, solves moderate problems, and starts handling ambiguous situations with minimal guidance. Communicates with the broader team and engages in code reviews and documentation. This role also includes supporting junior engineers and commitment to continuous learning. This is an individual contributor role.
Na indústria de manutenção, a análise de vibração desempenha um papel fundamental: ela utiliza grandezas físicas, como aceleração e velocidade, para identificar indícios que ajudem a prever a ocorrência de falhas ou degradações em máquinas.

### Senior-level Software Developer
A página que exibe essas informações no DynaPredict, nossa plataforma de monitoramento de condições de ativos, foi projetada de forma muito semelhante a [este modelo no Figma](https://www.figma.com/file/QxUZkTUIzQA7cvyiMvVyxK/Front-end---Teste?type=design&node-id=1001%3A3&mode=design&t=JLnbGmQJcSlnYYE2-1).

With vast experience, enhances software development, leading complex system development and ambiguous situation handling. Tackles intricate problems and mentors junior and mid-level engineers. Champions coding standards, project strategy, and technology adoption. Communicates across teams, influencing technical and non-technical stakeholders. This individual contributor role blends technical expertise with leadership, focusing on innovation, mentorship, and strategic contributions to the development process.
Utilize o Figma como referência para construir a interface e desenvolver as seguintes histórias de usuário:

## Challenges Full-Stack
1 - Histórias de Usuário

- [ ] [01 - Dynamox Full-Stack Node.js React Developer Challenge](./full-stack-challenge.md)
- [ ] [02 - Dynamox Full-Stack C# React Developer Challenge](./full-stack-csharp-react-challenge.md)

## Challenges Front-End
1. [ ] Como usuário, quero acessar a rota `/data` da minha aplicação e visualizar uma tela contendo um cabeçalho com informações sobre a máquina e alguns gráficos de série temporal.

- [ ] [01 - Dynamox Front-end React Developer Challenge Marketing Teams](./front-end-challenge-v1.md)
- [ ] [02 - Dynamox Front-end React Developer Challenge Product Teams](./front-end-challenge-v2.md)
1. [ ] Como usuário, quero visualizar 3 gráficos de série temporal para as seguintes métricas: aceleração, velocidade e temperatura. Cada série temporal deve apresentar um eixo horizontal para o tempo e um eixo vertical para a magnitude da métrica.

## Challenges DevOps
1. [ ] Como usuário, quero que os dados que alimentarão esses gráficos sejam buscados sempre que eu acessar a rota `/data`. Utilize os dados disponíveis em [Responses](./response-challenge-v2.json) como _mock_ e use um pacote como o [json-server](https://www.npmjs.com/package/json-server) para criar uma API REST.

- [ ] [01 - Dynamox DevOps Developer Challenge Foundation Teams](./dev-sec-fin-ops-challenge-v1/README.md)
1. [ ] Como usuário, quero passar o mouse sobre um ponto de um gráfico e ver uma linha de referência vertical (_crosshair_) marcando os _timestamps_ correspondentes em todos os gráficos de séries temporais, acompanhada de um _tooltip_ descrevendo aquele ponto. [Confira](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/synchronized-charts) um exemplo dessa funcionalidade.

## Challenges Mobile
2 - Requisitos técnicos

- [ ] [01 - Dynamox Kotlin Multiplatform Developer Challenge](./kotlin-multiplatform-challenge.md)
- [ ] [02 - Dynamox Android Developer Challenge](./android-challenge.md)
- [ ] [03 - Dynamox iOS Developer Challenge](./ios-challenge.md)
1. [ ] Utilize TypeScript.
1. [ ] Utilize React.
1. [ ] Utilize Redux para gerenciar estados globais. [Confira](https://redux-toolkit.js.org/introduction/getting-started) como começar.
1. [ ] Utilize Redux Saga para lidar com efeitos colaterais assíncronos. [Confira](https://redux-saga.js.org/docs/introduction/GettingStarted) como começar.
1. [ ] Utilize Vite para o _build_ da aplicação. [Confira](https://vitejs.dev/guide/) como começar.
1. [ ] Utilize Material UI 5 para estilizar a aplicação. [Confira](https://mui.com/material-ui/getting-started/) como começar.
1. [ ] Utilize Highcharts, Plotly, D3 ou qualquer biblioteca similar para exibir os gráficos. [Confira](https://www.highcharts.com/docs/index) como começar.
1. [ ] Garanta a lógica de negócio e o comportamento corretos por meio de testes unitários automatizados.

## Challenge Back-End
- [ ] [01 - Dynamox Back-End Time Series ](./back-end-challenge-v1.md)
Incentivamos o uso das ferramentas da nossa _stack_ de _frontend_ para que o desafio se assemelhe às nossas tarefas do dia a dia.

## Challenge QA
- [ ] [01- Dynamox QA Challenge](./qa-challenge.md)
3 - Bônus

</br>
1. [ ] Utilize Storybook para documentação. [Confira](https://storybook.js.org/docs) como começar.
1. [ ] Adicione testes E2E com Cypress. [Confira](https://learn.cypress.io/) como começar.
1. [ ] Faça o _deploy_ da sua aplicação em um provedor de nuvem e forneça o link da aplicação em execução. ## Critérios de Avaliação

**Good luck! We look forward to reviewing your submission.** 🚀
Cada um dos itens acima será avaliado como "Não Implementado", "Implementado com Problemas", "Implementado" ou "Implementado com Excelência". Para avaliar diferentes perfis e experiências, esperamos que candidatos a níveis mais seniores demonstrem uma compreensão mais profunda dos requisitos e implementem uma quantidade maior deles dentro do mesmo prazo.

De modo geral, avaliaremos os seguintes pontos:

1. [ ] Qualquer pessoa deve conseguir seguir as instruções e executar a aplicação.
1. [ ] O código do front-end está integrado com sucesso a uma API REST simulada (fake API).
1. [ ] As histórias foram implementadas de acordo com os requisitos funcionais.
1. [ ] Habilidades de resolução de problemas e capacidade de lidar com ambiguidades.
1. [ ] Qualidade, legibilidade e manutenibilidade do código.
1. [ ] O código está bem organizado e documentado.
1. [ ] O layout da aplicação é responsivo.

## Pronto para começar os desafios?

- Faça um _fork_ deste repositório para a sua própria conta no GitHub.
- Crie uma nova _branch_ usando seu primeiro e último nome. Por exemplo: `caroline-oliveira`.
- Após concluir o desafio, crie um _pull request_ para este repositório (https://github.com/dynamox-s-a/js-ts-full-stack-test) apontando para a _branch_ `main`.
- Receberemos uma notificação sobre o seu _pull request_, analisaremos sua solução e entraremos em contato com você.
<br>
144 changes: 144 additions & 0 deletions SOLUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Solução — Desafio Front-end Dynamox

Dashboard de análise de vibração: rota `/data` com cabeçalho da máquina e três séries temporais
(aceleração, velocidade e temperatura) com crosshair e tooltip sincronizados entre os gráficos.

## Como executar

Pré-requisito: [Bun](https://bun.sh) 1.3+ (`curl -fsSL https://bun.sh/install | bash`).

```bash
bun install
bun run dev
```

Isso sobe os dois apps em paralelo:

| Serviço | URL |
| -------- | ------------------------------- |
| Frontend | http://localhost:3000/data |
| API | http://localhost:3333/api |
| Docs API | http://localhost:3333/api/docs |

Para rodar separadamente: `bun run dev:backend` e `bun run dev:frontend`.

Verificação:

```bash
bun run test # 59 testes (12 backend + 47 frontend)
bun run typecheck
bun run build
```

Variáveis de ambiente são opcionais — os padrões já apontam para o ambiente local.
Veja `apps/*/.env.example`.

## Estrutura

```
├── packages/
│ └── contracts/ # fonte da verdade: rotas oRPC + schemas Zod
└── apps/
├── backend/ # implementa o contrato, lê os mocks JSON
└── frontend/ # consome o contrato (Vite + React + Redux Saga)
```

O pacote `contracts` fica **fora de `apps/`** de propósito: ele não é uma aplicação, é o
acoplamento entre as duas. O backend implementa esse contrato e o frontend o consome, então
tipos e validação fluem dos dois lados sem geração de código e sem duplicar tipo nenhum.
Mudar um schema quebra o build de quem estiver desalinhado — que é exatamente o objetivo.

### Backend

Clean Architecture com injeção de dependência por _factory function_:

```
presentation → application → domain ← infrastructure
```

`domain/` e `application/` são puros: não importam `@orpc/*` nem conhecem JSON, HTTP ou o
formato do mock. oRPC vive só em `presentation/`, e a leitura dos arquivos só em
`infrastructure/`. É por isso que os casos de uso são testáveis mockando apenas o repositório.

Trocar os mocks por um banco real significa trocar a implementação do repositório e a linha
correspondente no `container.ts` — nenhuma regra de negócio muda.

### Frontend

- **Estado global (Redux Toolkit):** máquina selecionada, período e séries carregadas.
- **Efeitos assíncronos (Redux Saga):** `watchFetchMachines` e `watchFetchMeasurements`, ambos
com `takeLatest` — trocar de máquina rápido cancela as buscas anteriores, então uma resposta
atrasada nunca sobrescreve a seleção atual.
- **Transporte:** todo o acesso à API passa por `lib/orpc-client.ts`. Nenhum componente conhece
URL ou header.
- **Filtros na URL:** máquina e período vivem em `?machine=MCH-003&period=7d`, então a tela é
compartilhável e sobrevive ao refresh.
- **UI:** tema e componentes do [Minimals](https://minimals.cc) v7.5.0 portados para Vite
(o kit original é Next.js).

## Decisões e trade-offs

**Um único fetch por acesso à rota.** A rota `/data` busca as 7 séries do sensor de uma vez, em
vez de uma requisição por gráfico. São 181 pontos por série — o custo de trazer tudo é menor que
o de três _round-trips_, e garante que os três gráficos exibam exatamente a mesma janela de
tempo (requisito do crosshair sincronizado).

**Margem fixa nos gráficos.** Todos os gráficos usam `marginLeft` fixo. Não é estética: a
sincronização localiza o ponto de cada gráfico pela coordenada X em pixels. Deixando o
Highcharts dimensionar cada margem pelo tamanho do próprio rótulo do eixo Y (`0.1` contra `15`),
as áreas de plot ficam deslocadas entre si e o mesmo X aponta para instantes diferentes — o
crosshair mente. Isso apareceu em teste manual: os gráficos reportavam 02:03 e 07:03 para o
mesmo cursor.

**Período ancorado na última leitura.** Os dados do mock terminam em dezembro de 2023. Ancorar
"últimos 7 dias" em `Date.now()` devolveria uma tela vazia, então a âncora é o `lastReadingAt`
da máquina.

**Filtros hidratados da URL uma única vez.** A sincronização é assimétrica de propósito: a URL
alimenta a store no mount, e só a partir daí a store passa a escrever na URL (`replace`, para
não empilhar histórico a cada clique). Sincronizar nos dois sentidos por efeito parece simétrico,
mas o efeito de escrita roda no mesmo commit com o estado ainda antigo e apaga os filtros do
link antes de eles serem aplicados — foi exatamente o que aconteceu na primeira tentativa.

**Estatísticas calculadas sobre a janela filtrada.** O "máximo do período" acompanha o recorte
escolhido pelo usuário, não a série inteira — é o número que ele espera ver ao filtrar.

**Três máquinas, um sensor.** O mock fornecido cobre um único sensor. As três máquinas existem
para exercitar a listagem e a troca de seleção, mas todas leem a mesma série; o ponto de
particionar por máquina está isolado no repositório.

## Requisitos

| Requisito | Situação |
| -------------------------------- | ----------------------------------------------------- |
| Rota `/data` com cabeçalho | ✅ |
| 3 gráficos de série temporal | ✅ aceleração (3 eixos), velocidade (3 eixos), temperatura |
| Busca a cada acesso à rota | ✅ |
| Crosshair + tooltip sincronizados| ✅ |
| TypeScript | ✅ modo estrito, sem `any` |
| React | ✅ 19 |
| Redux | ✅ Redux Toolkit |
| Redux Saga | ✅ |
| Vite | ✅ 7 |
| Material UI | ✅ v7 — ver observação abaixo |
| Biblioteca de gráficos | ✅ Highcharts 12 |
| Testes unitários | ✅ 59 testes |
| Layout responsivo | ✅ |
| API REST simulada | ⚠️ oRPC sobre os mocks, no lugar do `json-server` |
| Storybook / Cypress / deploy | ❌ bônus não implementados |

**Sobre o `json-server`.** O enunciado sugere `json-server` para servir
`response-challenge-v2.json`. A API aqui é um servidor oRPC próprio que lê o mesmo arquivo
(`apps/backend/src/mock/accelerationRms.json`) — nenhum banco de dados externo. A troca entrega
o que o `json-server` não daria: contrato tipado compartilhado com o frontend, validação Zod nas
duas pontas, filtros de verdade (`from`/`to`/`metrics`) e documentação OpenAPI automática. As
rotas continuam REST e podem ser chamadas com `curl`:

```bash
curl 'localhost:3333/api/machines'
curl 'localhost:3333/api/machines/MCH-001/measurements?metrics[]=temperature'
```

**Sobre o Material UI 5.** O enunciado pede MUI 5; a solução usa MUI 7, versão exigida pelo tema
do Minimals v7.5.0 (que depende da API de CSS variables e `colorSchemes`, ausente na v5).
Fixar a v5 significaria abrir mão do kit ou reescrever o tema.
Loading