Untitled

mail@pastecode.io avatar
unknown
javascript
2 years ago
7.4 kB
2
Indexable
Never
/* eslint-disable no-nested-ternary */
import { Link } from 'react-router-dom';
import { useState, useEffect, useMemo, useCallback } from 'react';

import { Loader } from '../../components/Loader';
import { Error } from '../../components/Error';
import { Modal } from '../../components/Modal';

import ContactsService from '../../services/ContactsService';

import formatPhone from '../../utils/formatPhone';
import toast from '../../utils/toast';

import APIError from '../../errors/APIError';

import arrowIcon from '../../assets/icons/arrow.svg';
import editIcon from '../../assets/icons/edit.svg';
import deleteIcon from '../../assets/icons/delete.svg';
import sadIcon from '../../assets/icons/sad.svg';
import emptyBoxIcon from '../../assets/icons/empty-box.svg';
import magnifierQuestionIcon from '../../assets/icons/magnifier-question.svg';

import {
  Container,
  InputSearchBarContainer,
  ListHeader,
  Divider,
  EmptyContainer,
  SearchEmptyContainer,
  ListContainer,
  Card
} from './styles';

export function Home() {
  const [contacts, setContacts] = useState([]);
  const [orderByName, setOrderByName] = useState('ASC');
  const [searchTerm, setSearchTerm] = useState('');
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);
  const [isVisibleModalDeleteContact, setIsVisibleModalDeleteContact] =
    useState(false);
  const [contactBeingDelete, setContactBeingDelete] = useState(null);
  const [isLoadingDeleteContact, setIsLoadingDeleteContact] = useState(false);

  const filteredContacts = useMemo(
    () =>
      contacts.filter((contact) =>
        contact.name.toLowerCase().includes(searchTerm.toLowerCase())
      ),
    [contacts, searchTerm]
  );

  const loadContacts = useCallback(async () => {
    try {
      setIsLoading(true);

      const result = await ContactsService.listContacts(orderByName);

      const formattedContacts = result.map((contact) => ({
        ...contact,
        phoneFormatted: contact.telephone && formatPhone(contact.telephone)
      }));

      setHasError(false);
      setContacts(formattedContacts);
    } catch (error) {
      if (error instanceof APIError) {
        // MOSTRAR ALGO PARA USUÁRIO RELACIONADO ALGUM ERRO DA API
        console.log(error);
      } else {
        // MOSTRAR ALGO PARA USUÁRIO RELACIONADO ALGUM ERRO NO CÓDIGO/JAVASCRIPT NO FRONT-END
        console.log(error);
      }

      setHasError(error.name);
    } finally {
      setIsLoading(false);
    }
  }, [orderByName]);

  useEffect(() => {
    loadContacts();
  }, [loadContacts]);

  function handleChangeSearchTerm(e) {
    setSearchTerm(e.target.value);
  }

  function handleToggleOrderByName() {
    setOrderByName((state) => (state === 'ASC' ? 'DESC' : 'ASC'));
  }

  function handleLoadContacts() {
    loadContacts();
  }

  function handleDeleteContact(contact) {
    setIsVisibleModalDeleteContact(true);
    setContactBeingDelete(contact);
  }

  function handleCloseDeleteModal() {
    setIsVisibleModalDeleteContact(false);
    setContactBeingDelete(null);
  }

  async function handleConfirmDeleteContact() {
    try {
      setIsLoadingDeleteContact(true);
      await ContactsService.deleteContactById(contactBeingDelete.id);

      setContacts((state) =>
        state.filter((contact) => contact.id !== contactBeingDelete.id)
      );

      setIsLoadingDeleteContact(false);

      handleCloseDeleteModal();
      toast({ type: 'success', text: 'Contato deletado com sucesso!' });
    } catch (error) {
      toast({ type: 'danger', text: 'Ocorreu um erro ao deletar o contato' });
    }
  }

  return (
    <Container>
      <Loader isLoading={isLoading} />

      <Modal
        isLoading={isLoadingDeleteContact}
        isVisible={isVisibleModalDeleteContact}
        title={`Tem certeza que deseja remover o contato ”${contactBeingDelete?.name}”?`}
        subtitle="Esta ação não poderá ser desfeita!"
        confirmLabel="Deletar"
        danger
        onCancel={handleCloseDeleteModal}
        onConfirm={handleConfirmDeleteContact}
      />

      {!hasError && contacts.length > 0 && (
        <InputSearchBarContainer>
          <input
            value={searchTerm}
            type="text"
            placeholder="Pesquisar contato..."
            onChange={(e) => handleChangeSearchTerm(e)}
          />
        </InputSearchBarContainer>
      )}

      <ListHeader
        hasError={hasError}
        justifyContent={
          hasError
            ? 'flex-end'
            : contacts.length > 0
            ? 'space-between'
            : 'center'
        }
      >
        {!hasError && contacts.length > 0 && (
          <strong>
            {filteredContacts.length}{' '}
            {filteredContacts.length === 1 ? 'contato' : 'contatos'}
          </strong>
        )}
        <Link to="/new">Novo Contato</Link>
      </ListHeader>

      <Divider />

      {hasError && (
        <Error
          icon={{ src: sadIcon, alt: 'sad' }}
          message="Ocorreu um erro ao obter os seus contatos!"
          onCLick={() => handleLoadContacts}
        />
      )}

      {contacts.length === 0 && !isLoading && !hasError && (
        <EmptyContainer>
          <img src={emptyBoxIcon} alt="Empty box" />
          <span>
            Você ainda não tem nenhum contato cadastrado! <br />
            Clique no botão<strong> ”Novo contato” </strong>à cima para
            cadastrar o seu primeiro!
          </span>
        </EmptyContainer>
      )}

      {!hasError && contacts.length > 0 && filteredContacts.length < 1 && (
        <SearchEmptyContainer>
          <img src={magnifierQuestionIcon} alt="Magnifier question" />
          <span>
            Nenhum resultado foi encontrado para
            <b> ”{searchTerm}”</b>.
          </span>
        </SearchEmptyContainer>
      )}

      {!hasError && (
        <ListContainer orderByName={orderByName}>
          {filteredContacts.length > 0 && (
            <header>
              <button type="button" onClick={handleToggleOrderByName}>
                <span>Nome</span>
                <img src={arrowIcon} alt="Arrow" />
              </button>
            </header>
          )}

          {filteredContacts.map((contact) => (
            <Card key={contact.id}>
              <div className="info">
                <div className="info-header">
                  <strong>{contact.name}</strong>
                  {contact.category.name && (
                    <small>{contact.category.name}</small>
                  )}
                </div>
                <span>{contact.email}</span>
                <span>{contact.phoneFormatted}</span>
              </div>
              <div className="actions">
                <Link to={`/edit/${contact.id}`}>
                  <img src={editIcon} alt="Edit" />
                </Link>
                <button
                  type="button"
                  onClick={() => handleDeleteContact(contact)}
                >
                  <img src={deleteIcon} alt="Delete" />
                </button>
              </div>
            </Card>
          ))}
        </ListContainer>
      )}
    </Container>
  );
}