Skip to content

tarcisioandrade/librasoft-client

Repository files navigation

LibraSoft é um sistema bibliotecário que proporciona aos usuários acesso online ao acervo de livros, bem como a realização de solicitações de empréstimo e o gerenciamento através de um dashboard disponivel para usuários administradores.

Vercel Deploy Stars

Note

Este sistema funciona como o cliente/frontend que interage com a API REST desenvolvida em .NET Core 8. Para obter informações mais detalhadas, consulte o repositório da API.

Tecnologias Utilizadas

  • TypeScript: Linguagem de programação fortemente tipada que se baseia no JavaScript.
  • Next.js (App Router): Utilizado para gerenciamento de rotas com a nova abordagem de roteamento do Next.js.
  • Tailwind CSS: Framework CSS utilitário para um design rápido e responsivo.
  • Shadcn/UI: Biblioteca de componentes acessíveis e personalizados para melhorar a interface do usuário.
  • Zod: Biblioteca para validação de esquemas e tipos TypeScript de forma simples e eficiente.
  • React Hook Form: Gerenciamento de formulários no React com foco em desempenho e facilidade de uso.
  • Better Fetch: Fetch wrapper com compatibilidade ao cacheamento do next e schemas do zod.
  • Vitest: Framework de testes rápido e moderno.

Abordagem

Rotas Privadas

A verificação do acesso a rotas por usuários não autenticados foi implementada por meio de um middleware. Nesse processo, verificamos se existe uma sessão armazenada nos cookies que é salva com a diretiva httpOnly e se o token de acesso ainda é válido. Essa abordagem garante a segurança e a restrição adequada de acesso às rotas privadas da aplicação.

Cache

Para reduzir requisições desnecessárias e evitar sobrecarga no servidor, algumas rotas foram configuradas para aproveitar o recurso de cache oferecido pelo Next.js. Com o objetivo de facilitar futuras alterações, as chaves usadas para identificar as tags das requisições em cache foram centralizadas na classe CacheKeys.

export class CacheKeys {
  static Review = {
    Get: "get-review",
    GetAll: "get-all-review",
  };
  static Bag = {
    GetAll: "get-all-bag",
  };
  static Book = {
    GetAll: "get-all-book",
  };
  static User = { Get: "get-user" };
  static Rent = { GetAll: "get-all-rent" };
  static Punishment = { GetAll: "get-all-punishment" };
}

Serviços & Requisições

A aplicação possui requisições de diversos tipos, user, books, author, etc. Todas elas estão no diretório src/services separadas por entidade. Todas as requisições são feitas utilizando a biblioteca better fetch que possui compatibilidade com o fetch do next e schemas do zod.

Gerenciamento de Erros

Para o gerenciamento de erros, adotei o padrão Result, implementado em utils/result.ts. Essa abordagem permite uma manipulação mais segura e expressiva de possíveis falhas, tornando o código mais robusto e fácil de entender. Ao encapsular o resultado de uma operação e um possível erro em uma única estrutura de dados.

async ChangePassword(input: ChangePasswordInput) {
    const { error } = await fetchWithCredentials(this.endpoint + "/change-password", {
      method: "PATCH",
      body: input,
    });
    if (error) {
      return Err({ message: error.errors });
    }
    return Ok({ message: "Sua senha foi atualizada com sucesso!" });
  }

Formulários & Ações no Servidor

Os formulários são gerenciados por meio da biblioteca React Hook Form e validados utilizando o Zod antes de serem submetidos a uma server action. Todas as actions estão agrupadas no diretório src/actions. Uma vez submetidos, os dados são enviados para uma requisição, que, em caso de sucesso, pode resultar na invalidação do cache ou no redirecionamento do usuário, conforme necessário.

Perfomance

Memorização

Com o intuito de aprimorar a performance da aplicação, foram adotadas estratégias de memorização para evitar execuções desnecessárias a cada rerender.

const categoriesOptions: Option[] = useMemo(
    () =>
      categories.map((categ) => ({
        label: categ.title,
        value: categ.title,
      })),
    [],
  );

Essa abordagem é comumente utilizada na aplicação, conforme demonstrado no exemplo acima, que emprega a função map para gerar um array com uma estrutura específica. Envolvelo com o useMemo garante que esse cálculo seja realizado apenas uma vez, melhorando assim a eficiência do processo.

Importações Dinâmicas

Para melhorar o desempenho do carregamento inicial da aplicação, foi usado importações dinâmicas que divide ó código em partes menores que podem ser carregadas conforme necessário, reduzindo a quantidade de código que precisa ser analisada e executada quando um usuário visita a página pela primeira vez.

const FormRentDynamic = dynamic(() => import("./components/form-rent").then((mod) => ({ default: mod.default })));

export const ...

<FormRentDynamic book={book.data} />

Testes

Para garantir a funcionalidade do código e facilitar a escrita de testes, implementei o padrão MVVM (Model-View-ViewModel) em diversos componentes. Essa abordagem permitiu que eu mockasse dados e métodos, possibilitando testar todos os casos de uso com facilidade.

Estrutura de Arquivos


  • Model: Lógica de negócios do componente, onde são definidas as regras e comportamentos.
  • View: Interface do usuário (JSX), onde são utilizadas as propriedades e métodos do Model.
  • Types: Definições de tipos e interfaces para serviços e outras tipagens utilizadas no componente.
  • Service: Serviço responsável por realizar operações externas, geralmente utilizando Server Actions.
  • Test: Testes unitários e integração do componente.
  • Index.tsx: Componente que instancia o serviço e o model, injetando-os na view.

Créditos

Design "Inspirado" 😅

Releases

No releases published

Packages

No packages published

Languages