Giter Club home page Giter Club logo

cars-list's Introduction

Projeto de listagem de carros.

Deploy vercel: Cars list

Para rodar o projeto

Após clonar esse repositório:

npm install
# or
yarn

Para rodar o projeto:

npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev

Abra http://localhost:3000 no seu navegador para ver o resultado

Sobre o projeto

Optei por consumir o json no formato proposto https://wswork.com.br/cars_by_brand.json, criando uma cópia local como base, pois assim evitava pegar o json quebrado no formato da resposta.

Optei por usar os cookies para persitir os dados após adicionar algum carro a listagem, também mantendo o formato dos dados.

Tecnologias:

-Next.js como framework.

-Zod e react-hook-form com typescript para validar os dados para cadastro de um novo carro. Pela facilidade em validar os dados e a forma como trabalham juntos

-Zustand para gerenciamento de estados globais e juntamente com a lib nookies para persistir os mesmos. Pela simplicidade de implementar o zustand e para usar a maioria dos dados de forma global.

-Tailwind para estilização.

Componente List - documentação

Como usar o componente de listagem:

-interface que é usado na listagem de carros:

interface Cars= {
  id: number,
  timestamp_cadastro: number,
  modelo_id: number,
  ano: number,
  combustivel: string,
  num_portas: number,
  cor: string,
  nome_modelo: string,
  valor: number,
  brand: number,
}[]

-Estrutura

/src
    /components
      /list
      ComplementsCar.tsx
      InfosCar.tsx
      ListCars.tsc
        /aside
          Aside.tsx
          Filters.tsx
  • Você pode pode copiar toda a pasta list dentro de src/components

    -E importar o componente ListCars na página que deseja exibi-lá

    Exemplo:

    import { ListCars } from "@/components/list/ListCars";
    //O componente pega a lista de carros diretamente do state global de carros
    export default async function Home() {
      return (
        <>
          <main className="p-6 flex gap-2 max-w-4xl mx-auto">
            <ListCars />
          </main>
        </>
      );
    }
  • Acessando a pasta components/list teremos a seguinte estrutura

//ListCars.tsx
export function ListCars() {
  //list sendo um state para atualizar a lista que será renderizada
  const [list, setList] = useState([] as Cars[]);
  //cars é um state global gerenciado pelo zustand, persiste o array de carros
  const { cars } = carListStore();
  //filteresCars é um state que gerencia se a lista está filtrada por marca
  const { filteredCars, setFilter } = filterCarsStore();
  //brands é um state de marcas existentes, e também inclui novas marcas caso ainda não existam
  const { brands } = brandListStore();
  //começa com o filtro de marcas vazio, cada vez que se volta a rota home, onde ela está sendo renderizada
  useEffect(() => {
    setFilter([]);
  }, []);
  //observa alterações de filtro, e caso aconteça, joga o filtro para a lista a ser renderizada
  useEffect(() => {
    if (filteredCars.length > 0) {
      setList(filteredCars);
    } else {
      setList(cars);
    }
  }, [filteredCars]);
  //espera que a lista esteja pronta para ser renderizada, caso contrário exibe o <Loading/>
  return list.length > 0 ? (
    <>
      /*importa e usa o componente <Aside /> responsável pela exibição do filtro
      de marcas*/
      <Aside />
      <ul className="w-11/12 max-w-3xl m-auto flex flex-wrap justify-center gap-3 items-start mb-20">
        <h2 className="w-full text-center text-gray-900 font-bold">
          Mostrando:{" "}
          {filteredCars.length == 0 ? (
            <span>TODOS</span>
          ) : (
            <span>{brands[list[0].brand - 1]}</span>
          )}
        </h2>
        /*faz um map na lista, trazendo o componente <InfosCar /> passando o carro
        como prop*/
        {list.map((car: Cars) => (
          <InfosCar key={car.id} car={car} />
        ))}
      </ul>
    </>
  ) : (
    <Loading />
  );
}
//Aside.tsx
//responsável por montar o componente de listagem de marcas em tela
//importa e usa o componente <Filters/>
export function Aside() {
  return (
    <aside className="bg-gray-400 fixed bottom-0 left-[5%] w-11/12 mx-auto flex  h-20 max-h-24 pb-4 sm:w-max sm:left-0  sm:h-max sm:max-h-none  sm:sticky sm:top-5 sm:mt-8">
      <div className="bg-blue-500 w-full rounded-xl px-4 py-2 max-h-16 overflow-auto sm:gap-2 sm:w-max sm:flex sm:flex-col sm:max-h-none">
        <h3 className="text-center text-slate-200 font-bold">
          Filtro pro marca
        </h3>
        <Filters />
      </div>
    </aside>
  );
}
//Filters.tsx
const { brands } = brandListStore();
//brandList é um state que atualiza as marcas disponíveis para listagem e filtragem
const [brandList, setBrandList] = useState([] as string[]);
//faz uso dos states globais
const { cars } = carListStore();
const { setFilter } = filterCarsStore();
//getByBrand pega a marca clicada e seta o filtro de carros, para definir qual a marca de carros que deve ser exibida
function getByBrand(brand: string) {
  if (brand == "todos") {
    setFilter([]);
  } else {
    const findIndexBrand = brandList.findIndex(
      (item) => item.toUpperCase() == brand.toUpperCase()
    );

    const filtered = cars.filter((car) => car.brand == findIndexBrand + 1);

    setFilter(filtered);
  }
  scroll({ top: 0 });
}
//começa setando as marcas com aquelas que estão sendo gerenciadas pelo state global
useEffect(() => {
  setBrandList(brands);
}, []);
//retorna um botão para cada marca, passando a função getByBrand com seu nome como parâmetro
return (
  <div className="flex gap-2 items-center justify-start overflow-auto sm:flex-col sm:max-h-none">
    <button
      onClick={() => getByBrand("todos")}
      className="focus:bg-blue-700 focus:text-gray-300 hover:bg-blue-800 hover:text-gray-300 shadow-md bg-blue-400 px-1 rounded-md"
    >
      TODOS
    </button>
    {brandList &&
      brandList.map((brand) => (
        <button
          onClick={() => getByBrand(brand)}
          key={brand}
          className="focus:bg-blue-700 focus:text-gray-300 hover:bg-blue-800 hover:text-gray-300 shadow-md bg-blue-400 px-1 rounded-md"
        >
          {brand}
        </button>
      ))}
  </div>
);
//InfosCar.tsx
//recebe o carro como prop
//traz as informações principais de cada carro na lista
export function InfosCar({ car }: InfosCarProps) {
  //faz uso da lista de marcas do store
  const { brands } = brandListStore();
  //pega a lista de marcas e atribui um nome de acordo com a key brand do carro
  const brandName = brands[car.brand - 1];
  return (
    <>
      <li className="p-4 flex w-full flex-wrap items-center bg-gray-800 shadow-md gap-2 rounded-lg hover:bg-black hover:shadow-2xl justify-evenly transition-all duration-500 ease-in-out border-gray-50 border max-w-[300px]">
        <h3 className="font-bold w-full text-[12px] text-blue-200 bg-gray-100 px-4 rounded-md shadow-md">
          {car.nome_modelo.toUpperCase()}
        </h3>
        <div className="w-[48%] flex flex-col items-start gap-2 ">
          <p className="text-indigo-500">{brandName}</p>
          <p className="text-gray-400">{car.ano}</p>
          <p className="text-gray-400">
            R$ {car.valor.toFixed(2).replaceAll(".", ",")}
          </p>
        </div>
        /** importa o componente <ComplementsCar /> passando o carro como prop.
        * */
        <ComplementsCar car={car} />
      </li>
    </>
  );
}
//ComplementsCar.tsx
//exibe as informações complementares de cada carro
export function ComplementsCar({ car }: ComplementsCarProps) {
  return (
    <div className="flex min-w-[48%] text-[12px] flex-col items-end gap-2 justify-start w-max">
      <span className="bg-blue-400 rounded-md p-1 text-gray-200">
        {car.combustivel}
      </span>
      <span className="bg-blue-50 rounded-md p-1 text-gray-100 w-max">
        {car.num_portas} portas
      </span>
      <span className="bg-gray-500 rounded-md p-1 uppercase text-gray-100">
        {car.cor}
      </span>
    </div>
  );
}

cars-list's People

Contributors

andersonkeller avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.