Criação de Categorias no CrazyStack Next.js
Tags: braziliandevs, react, nextjs, javascript
Published: 2023-06-28 14:11:22

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](https://www.udemy.com/course/nextjs-avancado-atomic-design/?referralCode=931E9B885B9F032D1CB1)

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:
```tsx
import { CategoryProps } from "entidades/category";
import { useCreateCategory } from "./createCategory.hook";
import { BoxCreateItem, FormControl, Checkbox, GridForm } from "shared/ui";
```
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`.

2. Componente `CreateCategoryForm`:
```tsx
export const CreateCategoryForm = () => {
  const { formState, register, handleSubmit, handleCreateCategory, active, setActive } =
    useCreateCategory();
  return (
    // Conteúdo do formulário
  );
};
```
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.

3. Componente `BoxCreateItem`:
```tsx
<BoxCreateItem
  onSubmit={handleSubmit(handleCreateCategory)}
  title={"Createar categoria"}
  isLoadingSaveButton={formState.isSubmitting}
  cancelRoute={"/categorys/1"}
>
  // Conteúdo interno do BoxCreateItem
</BoxCreateItem>
```
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.

4. Componente `GridForm`:
```tsx
<GridForm>
  // Conteúdo interno do GridForm
</GridForm>
```
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.

5. Componente `FormControl`:
```tsx
<FormControl
  label="Nome da categoria"
  error={formState.errors.name}
  {...register("name")}
/>
```
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.

6. Componente `Checkbox`:
```tsx
<Checkbox
  label="Ativo"
  colorScheme="green"
  isChecked={active}
  onChange={(e) => {
    e.preventDefault();
    setActive(e.target.checked);
  }}
/>
```
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)
```tsx
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>
  );
};
```
Veja o código a seguir:
## CreateCategory Lib (código final)
```tsx
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 };
};
```
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:
```tsx
import { SubmitHandler, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
```
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`.

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

export type SubmitCreateCategoryHandler = SubmitHandler<CreateCategoryFormData>;
```
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.

3. Esquema de validação:
```tsx
export const createCategoryFormSchema = yup.object().shape({
  name: yup.string().required("Nome é obrigatório"),
});
```
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".

4. Hook personalizado `useCreateCategoryLib`:
```tsx
export const useCreateCategoryLib = () => {
  const formProps = useForm<CreateCategoryFormData>({
    resolver: yupResolver(createCategoryFormSchema),
    defaultValues: {
      name: "",
    },
  });
  return { ...formProps };
};
```
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:
```tsx
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 };
};
```
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:
```tsx
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";
```
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.

2. Hook personalizado `useCreateCategory`:
```tsx
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 };
};
```
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:
```tsx
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>
    </>
  );
};
```
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:
```tsx
import { Box, Head } from "shared/ui";
import { CategoryProps } from "entidades/category";
import { CreateCategoryForm } from "features/category/create";
```
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.

2. Componente `CategoryCreatePage`:
```tsx
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>
    </>
  );
};
```
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!