Consultas: pesquisar, filtrar e ordenar

Com muitos clientes, encontrar um deles é difícil. Hoje crias uma barra de pesquisa, um filtro por estado e a ordenação por nome: as mesmas operações que uma base de dados faz com SQL.

🎯 No fim desta aula vais conseguir

🧰 Antes de começar

  1. Abre a pasta meu-crm no VS Code e o index.html com o Live Server.
  2. Confirma que os botões ✏️ e 🗑️ da aula 9 funcionam.
  3. Para testar bem as consultas precisas de pelo menos 6 clientes, com estados diferentes e dois com o mesmo apelido. Se não tiveres, cria estes (só precisas de nome e estado): Ana Silva (Lead), Carlos Silva (Cliente), Bruno Matos (Inativo), Rita Sousa (Cliente), Zé Pinto (Lead), Marta Costa (Cliente).

😕 Faltaste à aula 9 ou o teu projeto não funciona? Descarrega o ponto de controlo da aula 9, descompacta-o e trabalha a partir dele.

Parte A — A barra de pesquisa (≈ 10 min)

1 A barra de pesquisa e os filtros

Por cima da tabela vamos pôr três controlos: uma caixa de pesquisa (type="search"), uma lista para escolher o estado e outra para escolher a ordem.

Repara nos «avisos» que cada um dá ao JavaScript:

Nos dois casos, a tabela volta a ser desenhada.

✅ Deves ver: a barra com a caixa «🔎 Pesquisar por nome...» e as duas listas, por cima da tabela. Se escreveres, ainda não acontece nada: falta ensinar o JavaScript a fazer a consulta.

Parte B — A consulta: filtrar e ordenar (≈ 35 min)

2 A função que faz a consulta

Uma consulta é uma pergunta que fazes aos dados, por exemplo: «Quais são os Leads, por ordem alfabética?». Responder tem dois passos, que já conheces do Excel:

  1. Filtrar — ficar só com os registos que interessam (como o Filtro automático do Excel). Em SQL chama-se WHERE.
  2. Ordenar — pôr os registos pela ordem pedida (como Ordenar de A a Z). Em SQL chama-se ORDER BY.

A função clientesVisiveis() não mexe na lista clientes: cria uma lista nova, resultado, com «fotocópias» só dos clientes que passam. Lê com atenção:

🤔 Prevê: se escreveres só a letra a na pesquisa, que clientes vão aparecer? Só os que começam por A?

3 A tabela passa a mostrar só os «visíveis»

Falta uma mudança pequena, mas importante: a função desenharTabela() desenhava todos os clientes. Agora vai desenhar só os que a consulta devolve.

⚠️ Atenção: aqui só substituis uma linha. Dentro da função desenharTabela(), apaga só a linha for (const cliente of clientes) { e cola a nova no lugar dela. Não apagues a linha seguinte (html += linhaCliente(cliente);) nem a } que fecha o ciclo.

O ciclo deve ficar assim:

4 Testar

  1. Grava tudo e volta ao CRM.
  2. Escreve silva na pesquisa, letra a letra. Depois apaga e escreve SILVA.
  3. Apaga a pesquisa e escolhe o estado Lead. Depois Todos os estados.
  4. Muda a ordem para Nome (Z → A).
  5. Combina: pesquisa a + estado Cliente + ordem Z → A.

✅ Deves ver: a tabela muda enquanto escreves; «silva» e «SILVA» mostram os mesmos clientes; o filtro mostra só os clientes daquele estado; a ordem inverte. Ao apagar a pesquisa e escolher «Todos os estados», voltam todos.

😕 Não funciona?
  • Abre a Consola (F12). Cannot read properties of null (reading 'value') → falta a barra do passo 1, ou um id está diferente (têm de ser pesquisa, filtro-estado e ordenacao).
  • clientesVisiveis is not defined → falta o passo 2 (ou não gravaste o app.js).
  • Unexpected token '}' ou Unexpected end of input → no passo 3 apagaste ou duplicaste uma chaveta. Compara o teu ciclo com o exemplo do passo 3.
  • Escreves e nada muda, sem erros → falta o oninput="desenharTabela()" na caixa, ou o passo 3 não foi feito (a tabela ainda desenha clientes).
  • A tabela fica sempre vazia → confirma que a primeira opção do estado tem value="".

Parte C — A ponte para o SQL (≈ 5 min)

5 A mesma consulta, em SQL

As empresas guardam os dados em bases de dados e fazem as consultas com uma linguagem chamada SQL. A consulta «os Leads, por ordem alfabética do nome» escreve-se assim:

Compara com o teu JavaScript:

No teu JavaScriptEm SQL
for (const cliente of clientes)SELECT * FROM clientes
cliente.estado === estadoWHERE estado = 'Lead'
cliente.nome.toLowerCase().includes(texto)WHERE nome LIKE '%silva%'
sort((a, b) => a.nome.localeCompare(b.nome))ORDER BY nome
sort((a, b) => b.nome.localeCompare(a.nome))ORDER BY nome DESC

Na próxima aula vais escrever SQL a sério, numa base de dados dentro do browser.

🔎 Investiga

  1. Pesquisa SILVA em maiúsculas. Porque é que encontra «Ana Silva», escrito com minúsculas? Que duas partes do código tornam isto possível?
  2. Combina a pesquisa com o filtro de estado. Qual das duas condições tem de ser verdadeira para um cliente aparecer: uma delas ou as duas? Que operador decide isso?
  3. Na função clientesVisiveis, apaga estado === '' || e grava. O que acontece quando escolhes «Todos os estados»? Porquê? (Volta a pôr como estava!)

✍️ Exercícios (fazem parte do teu projeto)

Estes exercícios não têm o código pronto: usa o que aprendeste hoje.

E1 · Ordenar por data

Acrescenta à lista de ordenação duas opções: «Mais recentes primeiro» e «Mais antigos primeiro», que ordenam os clientes pela data de criação (criadoEm).

💡 Dica: cada <option> precisa de um value diferente (por exemplo recentes e antigos). Em clientesVisiveis, acrescenta mais dois casos como os do nome, mas com criadoEm. Como a data é um texto AAAA-MM-DD, o localeCompare também serve! Para os mais recentes primeiro, quem vem primeiro: a ou b?

💡 Para testar: os clientes criados hoje têm todos a mesma data. Muda a data de alguns na Consola, por exemplo: clientes[0].criadoEm = '2025-12-25'; guardar('clientes', clientes); desenharTabela(); (repete com clientes[1] e outra data).

E2 · Contagem por estado

Por baixo da barra de pesquisa, mostra uma linha como «Leads: 3 | Clientes: 5 | Inativos: 1», com os números certos de todos os clientes. Tem de se atualizar sempre que crias, editas ou apagas um cliente.

💡 Dica: cria um <p id="contagem-estados"></p>. Numa função nova, começa com três contadores a zero (let leads = 0;…), percorre a lista clientes com um ciclo e, com if / else if, soma 1 ao contador certo (leads++). No fim, escreve o texto com textContent. Onde deves chamar essa função para que se atualize sempre? (Lembra-te do E1 da aula 7.)

⭐ Desafio · Pesquisar também na empresa e no email

Faz com que a pesquisa encontre clientes também pelo nome da empresa e pelo email (por exemplo, escrever padaria encontra a cliente da «Padaria Silva»).

💡 Dica: é a linha const nomePassa = … que decide se a pesquisa passa. Junta mais condições com || («ou»), usando o mesmo .toLowerCase().includes(texto).

📦 O que mostrar ao professor

⏱️ Se sobrar tempo

🤖 Pedir ajuda à IA (opcional)

Se a ordenação com sort te parece magia, pede a uma IA gratuita que te explique como funciona, sem te dar código para o projeto:

📖 Palavras novas

Consulta
Pergunta feita aos dados, que devolve só os registos pedidos, pela ordem pedida.
Filtrar
Ficar só com os registos que cumprem uma condição (em SQL, WHERE).
Ordenar
Pôr os registos por uma ordem (em SQL, ORDER BY).
toLowerCase()
Devolve o texto todo em minúsculas.
includes(texto)
Diz se um texto contém outro (true ou false).
sort e localeCompare
Ordenam uma lista; o localeCompare compara textos respeitando os acentos.
oninput / onchange
Avisos que correm código quando se escreve numa caixa / quando se escolhe outra opção.