Criação de Categorias no CrazyStack Next.js

Dev Doido - Jun 28 '23 - - Dev Community

Nesta aula, você aprenderá como criar um formulário interativo para a criação de categorias usando React. O código fornecido ilustra a implementação de um formulário de criação de categorias, onde os usuários podem inserir o nome da categoria e selecionar se ela está ativa ou não.

Durante a aula, você aprenderá sobre os seguintes tópicos:

  1. Configuração do ambiente de desenvolvimento React.
  2. Criação de componentes reutilizáveis, como BoxCreateItem, FormControl e Checkbox.
  3. Utilização de hooks personalizados, como useCreateCategory, para gerenciar o estado do formulário e suas interações.
  4. Validação de dados do formulário utilizando formState.errors.
  5. Manipulação de eventos e atualização de estado utilizando register e setActive.
  6. Envio do formulário e tratamento de ações utilizando handleSubmit e handleCreateCategory.
  7. Navegação entre rotas utilizando cancelRoute.

Ao final da aula, você terá adquirido conhecimentos práticos sobre como criar formulários interativos no React, além de ter uma compreensão mais sólida sobre o desenvolvimento de componentes reutilizáveis e o uso de hooks personalizados.

O vídeo dessa aula está publicada no bootcamp CrazyStack, se você ainda não garantiu sua vaga clique aqui

O código apresentado refere-se à implementação de um formulário de criação de categoria usando React. Vamos analisar detalhadamente cada parte do código:

  1. Importações de dependências:
import { CategoryProps } from "entidades/category";
import { useCreateCategory } from "./createCategory.hook";
import { BoxCreateItem, FormControl, Checkbox, GridForm } from "shared/ui";
Enter fullscreen mode Exit fullscreen mode

Nesta seção, são importadas as dependências necessárias para a implementação do formulário. Isso inclui a definição de CategoryProps, um hook personalizado useCreateCategory para gerenciar o estado do formulário, além de componentes reutilizáveis como BoxCreateItem, FormControl, Checkbox e GridForm do pacote shared/ui.

  1. Componente CreateCategoryForm:
export const CreateCategoryForm = () => {
  const { formState, register, handleSubmit, handleCreateCategory, active, setActive } =
    useCreateCategory();
  return (
    // Conteúdo do formulário
  );
};
Enter fullscreen mode Exit fullscreen mode

Aqui, temos a definição do componente CreateCategoryForm, que é uma função de componente sem parâmetros. No interior da função, é feita a desestruturação dos valores retornados pelo useCreateCategory hook. Esses valores incluem formState para gerenciar o estado do formulário, register para registrar campos do formulário, handleSubmit para lidar com o envio do formulário, handleCreateCategory para executar a ação de criação da categoria, active para controlar o estado do checkbox "Ativo" e setActive para atualizar o estado do checkbox.

  1. Componente BoxCreateItem:
<BoxCreateItem
  onSubmit={handleSubmit(handleCreateCategory)}
  title={"Createar categoria"}
  isLoadingSaveButton={formState.isSubmitting}
  cancelRoute={"/categorys/1"}
>
  // Conteúdo interno do BoxCreateItem
</BoxCreateItem>
Enter fullscreen mode Exit fullscreen mode

Neste trecho, o componente BoxCreateItem é renderizado. Ele envolve todo o conteúdo do formulário e possui propriedades que definem o comportamento e a aparência do componente. As propriedades incluem onSubmit, que define a função a ser chamada ao enviar o formulário, title, que define o título exibido no cabeçalho do componente, isLoadingSaveButton, que indica se o botão de salvar está em estado de carregamento, e cancelRoute, que define a rota para onde o usuário será redirecionado ao cancelar o formulário.

  1. Componente GridForm:
<GridForm>
  // Conteúdo interno do GridForm
</GridForm>
Enter fullscreen mode Exit fullscreen mode

Dentro do componente BoxCreateItem, temos o componente GridForm, que é usado para organizar os campos do formulário em uma grade. Ele envolve os campos do formulário e fornece uma estrutura visual agradável.

  1. Componente FormControl:
<FormControl
  label="Nome da categoria"
  error={formState.errors.name}
  {...register("name")}
/>
Enter fullscreen mode Exit fullscreen mode

Aqui, temos o componente FormControl, que representa um campo de formulário. Ele recebe propriedades como label, que define o rótulo exibido ao lado do campo, error, que exibe mensagens de erro caso ocorram validações inválidas, e {...register("name")}, que registra o campo "name" no formulário para ser controlado pelo useCreateCategory hook.

  1. Componente Checkbox:
<Checkbox
  label="Ativo"
  colorScheme="green"
  isChecked={active}
  onChange={(e) => {
    e.preventDefault();
    setActive(e.target.checked);
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Por fim, temos o componente Checkbox, que representa uma caixa de seleção. Ele recebe propriedades como label, que define o texto exibido ao lado da caixa de seleção, colorScheme, que define o esquema de cores do componente, isChecked, que indica se a caixa de seleção está marcada ou não, e onChange, que define a função a ser chamada quando o estado do checkbox é alterado.

Esse código apresenta um exemplo prático de como criar um formulário de criação de categoria interativo utilizando React e componentes reutilizáveis. Com ele, é possível coletar informações do usuário, validar os campos e executar a ação de criação da categoria.

CreateCategoryForm (código final)

import { CategoryProps } from "entidades/category";
import { useCreateCategory } from "./createCategory.hook";
import { BoxCreateItem, FormControl, Checkbox, GridForm } from "shared/ui";

export const CreateCategoryForm = () => {
  const { formState, register, handleSubmit, handleCreateCategory, active, setActive } =
    useCreateCategory();
  return (
    <BoxCreateItem
      onSubmit={handleSubmit(handleCreateCategory)}
      title={"Createar categoria"}
      isLoadingSaveButton={formState.isSubmitting}
      cancelRoute={"/categorys/1"}
    >
      <GridForm>
        <FormControl
          label="Nome da categoria"
          error={formState.errors.name}
          {...register("name")}
        />
        <Checkbox
          label="Ativo"
          colorScheme="green"
          isChecked={active}
          onChange={(e) => {
            e.preventDefault();
            setActive(e.target.checked);
          }}
        />
      </GridForm>
    </BoxCreateItem>
  );
};
Enter fullscreen mode Exit fullscreen mode

Veja o código a seguir:

CreateCategory Lib (código final)

import { SubmitHandler, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
export type CreateCategoryFormData = {
  name: string;
  active?: boolean;
};

export type SubmitCreateCategoryHandler = SubmitHandler<CreateCategoryFormData>;

export const createCategoryFormSchema = yup.object().shape({
  name: yup.string().required("Nome é obrigatório"),
});

export const useCreateCategoryLib = () => {
  const formProps = useForm<CreateCategoryFormData>({
    resolver: yupResolver(createCategoryFormSchema),
    defaultValues: {
      name: "",
    },
  });
  return { ...formProps };
};
Enter fullscreen mode Exit fullscreen mode

O código apresentado refere-se a uma biblioteca (CreateCategory Lib) que contém funções e esquemas de validação para o formulário de criação de categoria. Vamos analisar cada parte do código em detalhes:

  1. Importações de dependências:
import { SubmitHandler, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
Enter fullscreen mode Exit fullscreen mode

Nesta seção, são importadas as dependências necessárias para o funcionamento da biblioteca. Isso inclui o useForm e SubmitHandler do pacote react-hook-form para gerenciar o estado e lidar com o envio do formulário, o yup para definir os esquemas de validação e o yupResolver para resolver os esquemas de validação no useForm.

  1. Tipos e interfaces:
export type CreateCategoryFormData = {
  name: string;
  active?: boolean;
};

export type SubmitCreateCategoryHandler = SubmitHandler<CreateCategoryFormData>;
Enter fullscreen mode Exit fullscreen mode

Nesta seção, são definidos os tipos utilizados pela biblioteca. CreateCategoryFormData representa os dados do formulário de criação de categoria, contendo um campo name do tipo string e um campo opcional active do tipo boolean. SubmitCreateCategoryHandler é um tipo que representa a função de manipulação do envio do formulário.

  1. Esquema de validação:
export const createCategoryFormSchema = yup.object().shape({
  name: yup.string().required("Nome é obrigatório"),
});
Enter fullscreen mode Exit fullscreen mode

Aqui, é definido o esquema de validação utilizando o yup. O esquema createCategoryFormSchema é um objeto que especifica as regras de validação para o campo name. Neste caso, é definido que o campo é uma string e é obrigatório, caso contrário, será exibida a mensagem de erro "Nome é obrigatório".

  1. Hook personalizado useCreateCategoryLib:
export const useCreateCategoryLib = () => {
  const formProps = useForm<CreateCategoryFormData>({
    resolver: yupResolver(createCategoryFormSchema),
    defaultValues: {
      name: "",
    },
  });
  return { ...formProps };
};
Enter fullscreen mode Exit fullscreen mode

Aqui, é definido o hook personalizado useCreateCategoryLib. Esse hook retorna um objeto contendo as propriedades do formulário e as funções relacionadas. Dentro do hook, é utilizado o useForm para inicializar o estado do formulário. São passados dois argumentos para o useForm: o resolver que utiliza o yupResolver para resolver o esquema de validação definido anteriormente e o defaultValues que define os valores iniciais do formulário, no caso, apenas o campo name vazio.

Ao chamar o useCreateCategoryLib em um componente, você terá acesso a todas as propriedades e funções do formulário, como o estado do formulário (formState), o registro de campos (register), a função de envio do formulário (handleSubmit) e outras propriedades relacionadas ao estado do formulário.

Essa biblioteca facilita a criação e validação do formulário de criação de categoria, fornecendo uma estrutura sólida e consistente para a manipulação dos dados e validação dos campos.

CreateCategory Hook

Veja o código da hook:

import { useUi } from "shared/libs";
import {
  CreateCategoryFormData,
  SubmitCreateCategoryHandler,
  useCreateCategoryLib,
} from "./createCategory.lib";
import { useRouter } from "next/router";
import { api } from "shared/api";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
export const useCreateCategory = () => {
  const { showModal } = useUi();
  const router = useRouter();
  const [active, setActive] = useState(false);
  const createCategory = useMutation(async (category: CreateCategoryFormData) => {
    try {
      const { data } = await api.post("/category/add", {
        ...category,
      });
      if (!data) {
        showModal({
          content: "Ocorreu um erro inesperado no servidor, tente novamente mais tarde",
          title: "Erro no servidor",
          type: "error",
        });
        return;
      }
      showModal({
        content:
          "Categoria criada com sucesso, você será redirecionado para a lista de categorias",
        title: "Sucesso",
        type: "success",
      });
      router.push("/categorys/1");
      return data;
    } catch (error) {
      showModal({
        content: "Ocorreu um erro inesperado no servidor, tente novamente mais tarde",
        title: "Erro no servidor",
        type: "error",
      });
    }
  }, {});
  const { register, handleSubmit, formState } = useCreateCategoryLib();
  const handleCreateCategory: SubmitCreateCategoryHandler = async (
    values: CreateCategoryFormData
  ) => {
    await createCategory.mutateAsync({ ...values, active });
  };
  return { formState, register, handleSubmit, handleCreateCategory, active, setActive };
};
Enter fullscreen mode Exit fullscreen mode

O código apresentado é um hook personalizado chamado useCreateCategory, que contém a lógica e funcionalidade para criar uma nova categoria. Vamos analisar cada parte do código em detalhes:

  1. Importações de dependências:
import { useUi } from "shared/libs";
import {
  CreateCategoryFormData,
  SubmitCreateCategoryHandler,
  useCreateCategoryLib,
} from "./createCategory.lib";
import { useRouter } from "next/router";
import { api } from "shared/api";
import { useMutation } from "@tanstack/react-query";
import { useState } from "react";
Enter fullscreen mode Exit fullscreen mode

Nesta seção, são importadas as dependências necessárias para o funcionamento do hook. Isso inclui useUi para exibir modais, CreateCategoryFormData e SubmitCreateCategoryHandler do arquivo createCategory.lib para lidar com os tipos e manipulação do formulário, useRouter para obter o objeto de roteamento do Next.js, api para fazer chamadas à API e useMutation do pacote react-query para gerenciar a mutação dos dados.

  1. Hook personalizado useCreateCategory:
export const useCreateCategory = () => {
  const { showModal } = useUi();
  const router = useRouter();
  const [active, setActive] = useState(false);
  const createCategory = useMutation(async (category: CreateCategoryFormData) => {
    // Lógica de criação de categoria
  }, {});
  const { register, handleSubmit, formState } = useCreateCategoryLib();
  const handleCreateCategory: SubmitCreateCategoryHandler = async (
    values: CreateCategoryFormData
  ) => {
    // Função de manipulação do envio do formulário
  };
  return { formState, register, handleSubmit, handleCreateCategory, active, setActive };
};
Enter fullscreen mode Exit fullscreen mode

Aqui, é definido o hook personalizado useCreateCategory. Esse hook retorna um objeto contendo as propriedades e funções relacionadas à criação de categoria. Vamos analisar cada parte:

  • const { showModal } = useUi();: Aqui, é desestruturado o showModal do useUi, que é um hook utilizado para exibir modais na interface.

  • const router = useRouter();: Aqui, é utilizado o hook useRouter do Next.js para obter o objeto de roteamento, que permite redirecionar o usuário após a criação da categoria.

  • const [active, setActive] = useState(false);: Aqui, é utilizado o hook useState para gerenciar o estado do campo active, que representa se a categoria está ativa ou não. O valor inicial é definido como false.

  • const createCategory = useMutation(async (category: CreateCategoryFormData) => { ... }, {});: Aqui, é utilizado o useMutation do pacote react-query para criar uma mutação assíncrona. A função de mutação é uma função assíncrona que recebe os dados da categoria como argumento e realiza a lógica de criação da categoria. É feita uma chamada à API utilizando api.post e, em seguida, é exibido um modal de sucesso ou erro dependendo do resultado da chamada. Por fim, a função router.push é utilizada para redirecionar o usuário para a lista de categorias. O objeto vazio {} passado como segundo argumento é opcional e permite a configuração de op

ções adicionais para a mutação.

  • const { register, handleSubmit, formState } = useCreateCategoryLib();: Aqui, é utilizado o hook useCreateCategoryLib importado anteriormente para obter as propriedades e funções relacionadas ao formulário de criação de categoria.

  • const handleCreateCategory: SubmitCreateCategoryHandler = async (values: CreateCategoryFormData) => { ... };: Aqui, é definida a função handleCreateCategory que será chamada quando o formulário for enviado. Essa função recebe os valores do formulário como argumento e chama a função de mutação createCategory.mutateAsync para criar a categoria com os valores fornecidos e o estado active.

  • return { formState, register, handleSubmit, handleCreateCategory, active, setActive };: Por fim, são retornadas as propriedades e funções necessárias para o componente que utilizará o hook useCreateCategory.

Esse hook personalizado encapsula a lógica de criação de categoria, facilitando o uso e a reutilização em diferentes componentes, fornecendo as funções e estados necessários para manipular o formulário de criação de categoria, exibir modais e redirecionar o usuário após a criação da categoria.

CategoryCreatePage

Por fim temos o código da página que chama esse trambolho todo:

import { Box, Head } from "shared/ui";
import { CategoryProps } from "entidades/category";
import { CreateCategoryForm } from "features/category/create";

export const CategoryCreatePage = () => {
  return (
    <>
      <Head
        title={"Belezix Admin | Categorias"}
        description="Página de criação de categorias do painel de Admin Belezix"
      />
      <Box flex="1" borderRadius={8} bg="purple.800" p="8">
        <CreateCategoryForm />
      </Box>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

O código apresentado define um componente chamado CategoryCreatePage que representa a página de criação de categorias. Vamos analisar cada parte do código em detalhes:

  1. Importações de dependências:
import { Box, Head } from "shared/ui";
import { CategoryProps } from "entidades/category";
import { CreateCategoryForm } from "features/category/create";
Enter fullscreen mode Exit fullscreen mode

Nesta seção, são importadas as dependências necessárias para o funcionamento da página. Isso inclui componentes como Box e Head do pacote "shared/ui" para estruturar a página e definir o título e descrição, além de CategoryProps do arquivo "entidades/category" para lidar com os tipos relacionados a categoria, e CreateCategoryForm do arquivo "features/category/create" que é o formulário de criação de categoria.

  1. Componente CategoryCreatePage:
export const CategoryCreatePage = () => {
  return (
    <>
      <Head
        title={"Belezix Admin | Categorias"}
        description="Página de criação de categorias do painel de Admin Belezix"
      />
      <Box flex="1" borderRadius={8} bg="purple.800" p="8">
        <CreateCategoryForm />
      </Box>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Aqui, é definido o componente CategoryCreatePage que representa a página de criação de categorias. Esse componente renderiza o conteúdo principal da página, incluindo o cabeçalho e o formulário de criação de categoria.

  • <Head ... />: O componente Head é utilizado para definir as meta tags do documento HTML, como o título e a descrição da página. Neste caso, o título é definido como "Belezix Admin | Categorias" e a descrição é definida como "Página de criação de categorias do painel de Admin Belezix".

  • <Box ... />: O componente Box é utilizado para criar uma caixa retangular que envolve o conteúdo da página. Ele define algumas propriedades como flex, borderRadius, bg e p para estilizar a caixa. No exemplo, a caixa possui uma flexibilidade de 1, um raio de borda de 8 pixels, uma cor de fundo roxa escura (purple.800) e um espaçamento interno de 8 pixels.

  • <CreateCategoryForm />: O componente CreateCategoryForm é importado e renderizado dentro da caixa. Ele representa o formulário de criação de categoria, permitindo que o usuário insira os dados necessários para criar uma nova categoria.

Em resumo, o componente CategoryCreatePage representa a página de criação de categorias. Ele inclui um cabeçalho com título e descrição, e renderiza o formulário de criação de categoria dentro de uma caixa estilizada. Isso fornece uma estrutura básica para a página e possibilita a criação de novas categorias no sistema.

Resumindo

Nesta aula, exploramos o desenvolvimento de uma funcionalidade de gerenciamento de categorias em um sistema de administração. Ao longo da aula, abordamos diferentes aspectos do desenvolvimento, desde a criação dos componentes e formulários até a integração com APIs e tratamento de erros.

Começamos entendendo a estrutura básica da funcionalidade de gerenciamento de categorias, identificando as principais entidades envolvidas, como CategoryProps. Em seguida, discutimos a criação do formulário de edição de categorias, detalhando os componentes EditCategoryForm, EditCategoryLib e EditCategoryHook. Esses componentes foram responsáveis por gerenciar o estado do formulário, a validação dos dados e a comunicação com a API para atualização da categoria.

Além disso, também exploramos o desenvolvimento do formulário de criação de categorias, utilizando os componentes CreateCategoryForm, CreateCategoryLib e CreateCategoryHook. Esses componentes seguiram uma estrutura semelhante ao formulário de edição, porém com a funcionalidade de criação de novas categorias.

Para garantir uma experiência amigável ao usuário, abordamos o tratamento de erros durante as requisições à API, exibindo mensagens de erro adequadas em caso de falhas. Utilizamos o componente showModal do pacote "shared/libs" para exibir essas mensagens.

Além disso, também discutimos o roteamento entre as páginas, utilizando o useRouter do pacote "next/router" para redirecionar o usuário para a lista de categorias após a criação ou atualização de uma categoria.

Durante a aula, aprendemos sobre a utilização de bibliotecas populares como react-hook-form e yup para facilitar o gerenciamento de formulários e a validação dos dados. Também exploramos a biblioteca react-query para facilitar a comunicação com a API e gerenciar o estado dos dados.

Ao final da aula, tivemos a oportunidade de criar uma página de criação de categorias, utilizando os componentes desenvolvidos anteriormente e integrando-os em uma página completa.

Através dessa aula, expandimos nossos conhecimentos sobre o desenvolvimento de funcionalidades complexas em aplicações React. Aprendemos sobre a estruturação de componentes, gerenciamento de estado, integração com APIs e tratamento de erros. Essas habilidades serão valiosas para o desenvolvimento de futuros projetos.

Parabéns pelo seu progresso até aqui! Agora você está pronto para aplicar esses conhecimentos em seus próprios projetos e continuar aprimorando suas habilidades como desenvolvedor React.

Desafio do Doido

Desafio Prático: Implementar a funcionalidade de exclusão de categorias

Agora que você já está familiarizado com a funcionalidade de criação e edição de categorias, vamos propor um desafio para você aprimorar ainda mais o código existente.

Neste desafio, você deverá implementar a funcionalidade de exclusão de categorias. Para isso, siga as etapas abaixo:

  1. Crie um novo componente chamado DeleteCategoryButton dentro da pasta features/category/delete. Esse componente será responsável por exibir um botão de exclusão de categoria.

  2. No componente DeleteCategoryButton, adicione uma prop chamada categoryId para receber o ID da categoria que será excluída.

  3. No componente DeleteCategoryButton, utilize o componente AlertDialog do pacote shared/ui para exibir um diálogo de confirmação antes de excluir a categoria. O diálogo deve exibir uma mensagem de confirmação, como "Tem certeza que deseja excluir esta categoria?". O usuário terá a opção de confirmar ou cancelar a exclusão.

  4. No componente DeleteCategoryButton, utilize o useMutation do pacote @tanstack/react-query para realizar a exclusão da categoria. Ao confirmar a exclusão, faça uma requisição DELETE para a API, passando o ID da categoria a ser excluída.

  5. Trate os possíveis cenários de sucesso e erro durante a exclusão da categoria. Utilize o showModal do pacote shared/libs para exibir mensagens de sucesso ou erro ao usuário.

  6. Atualize o componente EditCategoryForm para incluir o componente DeleteCategoryButton logo abaixo do formulário de edição. Passe o categoryId da categoria atual como prop para o componente DeleteCategoryButton.

  7. Na página CategoryEditPage, importe o componente DeleteCategoryButton e passe o categoryId para o componente.

  8. Na página CategoryCreatePage, adicione um botão de cancelar que redirecione o usuário de volta para a lista de categorias. Utilize o useRouter do pacote next/router para realizar o redirecionamento.

Após concluir o desafio, você terá implementado a funcionalidade de exclusão de categorias, permitindo que o usuário delete uma categoria existente. Isso proporcionará uma experiência mais completa e robusta para o gerenciamento de categorias em seu sistema de administração.

Lembre-se de testar cuidadosamente sua implementação e garantir que todas as funcionalidades estejam funcionando corretamente. Boa sorte e divirta-se codificando!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .