Skip to content

Instantly share code, notes, and snippets.

@julioaze
Created July 6, 2020 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save julioaze/74319e0265a038ab5ca357625307b778 to your computer and use it in GitHub Desktop.
Save julioaze/74319e0265a038ab5ca357625307b778 to your computer and use it in GitHub Desktop.
import React, {
useEffect,
useState,
useRef,
useMemo,
useCallback,
} from 'react';
import { PropTypes } from 'prop-types';
import { useWindowSize } from '@react-hook/window-size';
import { toast } from 'react-toastify';
import base64 from 'base-64';
import {
FaDollarSign,
FaSave,
FaBroom,
FaTimes,
FaPlus,
FaMinus,
} from 'react-icons/fa';
import { format, parseISO } from 'date-fns';
import { Scope } from '@unform/core';
import * as Yup from 'yup';
import { confirmAlert } from 'react-confirm-alert';
import api from '~/services/api';
import history from '~/services/history';
import { useAuth } from '~/hooks/auth';
import {
FormContainer,
FormLoading,
Select,
Input,
InputMask,
} from '~/components/Form';
import {
Container,
Header,
Controls,
Content,
Company,
Periods,
FormScroll,
} from './styles';
const { v4: uuid } = require('uuid');
const Form = ({ match }) => {
const { id } = match.params;
const { company, companyUsers } = useAuth();
const formRef = useRef(null);
const [, height] = useWindowSize();
const [loading, setLoading] = useState(false);
const [clients, setClients] = useState([]);
const [periods, setPeriods] = useState([]);
const [selectedClient, setSelectedClient] = useState({
value: '',
label: 'Selecione uma empresa',
});
const [selectedContractType, setSelectedContractType] = useState({
value: '',
label: 'Selecione um tipo',
});
const [checkStatus, setCheckStatus] = useState(true);
useEffect(() => {
async function loadProfile() {
if (id) {
try {
setLoading(true);
const response = await api.get(
`/register/tributaryProfile/${company.id}/${base64.decode(id)}`
);
const { data } = response;
formRef.current.setFieldValue('client', {
value: data.client.id,
label: data.client.name,
});
formRef.current.setFieldValue('contract_type', {
value: data.contract_type,
label: data.contract_type_label,
});
const formattedPeriods = data.periods.map(period => ({
...period,
start_period:
period.start_period &&
format(parseISO(period.start_period), 'dd/MM/yyyy'),
end_period:
period.end_period &&
format(parseISO(period.end_period), 'dd/MM/yyyy'),
}));
setPeriods(formattedPeriods);
setCheckStatus(data.status);
} catch (err) {
toast.error(err.response.data.error, {
position: toast.POSITION.BOTTOM_RIGHT,
});
} finally {
setLoading(false);
}
}
const response = await api.get(`/register/client/${company.id}`);
const clientsOptions = response.data.docs.map(client => ({
value: client.id,
label: client.name,
}));
setClients(clientsOptions);
}
loadProfile();
}, [company.id, id]);
const handleSubmit = useCallback(
async data => {
try {
const schema = Yup.object().shape({
client: Yup.string().required('A empresa é obrigatória'),
contract_type: Yup.string().required(
'O tipo de contrato é obrigatório'
),
periods: Yup.array()
.min(1)
.of(
Yup.object().shape({
taxation_type: Yup.number()
.required('A tributação é obrigatória')
.nullable(),
start_period: Yup.string().required(
'A data de início é obrigatória'
),
end_period: Yup.string().required(
'A data de término é obrigatória'
),
labour: Yup.number(),
tax: Yup.number(),
accounting: Yup.number(),
})
)
.required(),
});
await schema.validate(data, {
abortEarly: false,
});
const tributaryProfileData = {
client_id: data.client,
contract_type: data.contract_type,
status: checkStatus,
};
if (id) {
const decodedId = base64.decode(id);
const periodsData = data.periods.map(period => {
const startPeriodTimes = period.start_period.split('/');
const endPeriodTimes = period.end_period.split('/');
return {
id: Number(period.id),
tributary_id: Number(decodedId),
taxation_type: period.taxation_type,
start_period: new Date(
startPeriodTimes[2],
startPeriodTimes[1] - 1,
startPeriodTimes[0]
),
end_period: new Date(
endPeriodTimes[2],
endPeriodTimes[1] - 1,
endPeriodTimes[0]
),
responsible_labour: period.labour,
responsible_tax: period.tax,
responsible_accounting: period.accounting,
};
});
await Promise.all([
api.put(
`register/tributaryProfile/${base64.decode(id)}`,
tributaryProfileData
),
api.post('register/tributaryProfilePeriods', periodsData),
]);
} else {
tributaryProfileData.company_id = company.id;
const response = await api.post(
'register/tributaryProfile',
tributaryProfileData
);
const { id: tributary_id } = response.data;
const periodsData = data.periods.map(period => {
const startPeriodTimes = period.start_period.split('/');
const endPeriodTimes = period.end_period.split('/');
return {
id: Number(period.id),
tributary_id,
taxation_type: period.taxation_type,
start_period: new Date(
startPeriodTimes[2],
startPeriodTimes[1] - 1,
startPeriodTimes[0]
),
end_period: new Date(
endPeriodTimes[2],
endPeriodTimes[1] - 1,
endPeriodTimes[0]
),
responsible_labour: period.labour,
responsible_tax: period.tax,
responsible_accounting: period.accounting,
};
});
await api.post('register/tributaryProfilePeriods', periodsData);
}
formRef.current.setErrors({});
toast.success('Compromisso salvo com sucesso', {
position: toast.POSITION.BOTTOM_RIGHT,
});
history.push('/tributaryProfile');
} catch (err) {
if (err instanceof Yup.ValidationError) {
const errorMessages = {};
err.inner.forEach(error => {
errorMessages[error.path] = error.message;
});
formRef.current.setErrors(errorMessages);
} else {
toast.error('Falha ao salvar compromisso', {
position: toast.POSITION.BOTTOM_RIGHT,
});
}
}
},
[id, checkStatus, company.id]
);
const contractTypeOptions = useMemo(() => {
return [
{ value: 1, label: 'Terceirizado' },
{ value: 2, label: 'Próprio' },
];
}, []);
const taxationTypeOptions = useMemo(() => {
return [
{ value: 1, label: 'MEI' },
{ value: 2, label: 'Simples Nacional' },
{ value: 3, label: 'Lucro Presumido' },
{ value: 4, label: 'Lucro Real' },
{ value: 5, label: 'Empregador Doméstico' },
{ value: 6, label: 'Empregador PF' },
];
}, []);
const usersOptions = useMemo(() => {
return companyUsers.map(userItem => {
return { value: userItem.id, label: userItem.short_name };
});
}, [companyUsers]);
const resetForm = useCallback(() => {
formRef.current.reset();
setSelectedClient({
value: '',
label: 'Selecione uma empresa',
});
setSelectedContractType({
value: '',
label: 'Selecione um tipo',
});
}, [formRef]);
const confirmResetForm = useCallback(() => {
confirmAlert({
title: 'Alerta',
message: 'Tem certeza? Todos os dados digitados serão perdidos',
closeOnEscape: false,
closeOnClickOutside: false,
buttons: [
{
label: 'Sim',
onClick: () => resetForm(),
},
{
label: 'Não',
},
],
});
}, [resetForm]);
const handleClose = useCallback(() => {
confirmAlert({
title: 'Alerta',
message: 'Tem certeza? Todos os dados não salvos serão perdidos',
closeOnEscape: false,
closeOnClickOutside: false,
buttons: [
{
label: 'Sair',
onClick: () => history.push('/tributaryProfile'),
},
{
label: 'Não',
},
],
});
}, []);
const handleAddNewPeriod = useCallback(() => {
const blankPeriod = {
id: uuid(),
taxation_type: null,
start_period: '',
end_period: '',
labour: '',
tax: '',
accounting: '',
};
setPeriods(oldPeriods => [...oldPeriods, { ...blankPeriod }]);
}, []);
const handleRemovePeriod = useCallback(periodId => {
setPeriods(oldPeriods => oldPeriods.filter(item => item.id !== periodId));
}, []);
return (
<Container>
<Header>
<div>
<FaDollarSign size={20} color="#44546a" />
<h1>Perfil Tributário</h1>
</div>
</Header>
<Controls>
<button type="submit" form="form">
<FaSave size={15} color="#44546a" />
<span>Salvar</span>
</button>
<button type="button" onClick={confirmResetForm}>
<FaBroom size={15} color="#44546a" />
<span>Limpar</span>
</button>
<button type="button" onClick={handleClose}>
<FaTimes size={15} color="#44546a" />
<span>Fechar</span>
</button>
</Controls>
{loading && <FormLoading className="loading" />}
<Content>
<FormContainer
id="form"
ref={formRef}
loading={loading}
onSubmit={handleSubmit}
>
<FormScroll height={height}>
<Company>
<h4>EMPRESA</h4>
<section>
<Select
name="client"
className="client"
label="Empresa"
placeholder="Selecione uma empresa"
options={clients}
defaultValue={selectedClient}
/>
<Select
name="contract_type"
className="contract_type"
label="Tipo de Contrato"
placeholder="Selecione um tipo"
options={contractTypeOptions}
defaultValue={selectedContractType}
/>
<Input
type="checkbox"
name="status"
className="status"
label="Ativo"
defaultChecked={checkStatus}
checkbox
onChange={e => setCheckStatus(e.target.checked)}
/>
</section>
</Company>
<Periods>
<header>
<h4>PERÍODOS</h4>
<button type="button" onClick={handleAddNewPeriod}>
<FaPlus size={10} />
</button>
</header>
{periods &&
periods.length > 0 &&
periods.map((period, index) => (
<Scope path={`periods[${index}]`} key={String(period.id)}>
<section>
<button
type="button"
onClick={() => handleRemovePeriod(period.id)}
>
<FaMinus size={10} />
</button>
<Input
name="id"
value={period.id}
type="text"
className="hide"
readOnly
/>
<Select
name="taxation_type"
className="taxation_type"
label="Tributação"
placeholder="Selecione um tipo"
options={taxationTypeOptions}
defaultValue={{
value: period.taxation_type,
label: period.taxation_type_label,
}}
/>
<InputMask
name="start_period"
className="start_period"
label="Início"
mask="99/99/9999"
defaultValue={period.start_period}
/>
<InputMask
name="end_period"
className="end_period"
label="Fim"
mask="99/99/9999"
defaultValue={period.end_period}
/>
</section>
<section className="responsibles">
<Select
name="labour"
className="labour"
label="Responsável Trabalhista"
placeholder="Selecione um tipo"
options={usersOptions}
defaultValue={{
value: period.labour?.id,
label: period.labour?.short_name,
}}
/>
<Select
name="tax"
className="tax"
label="Responsável Tributário"
placeholder="Selecione um tipo"
options={usersOptions}
defaultValue={{
value: period.tax?.id,
label: period.tax?.short_name,
}}
/>
<Select
name="accounting"
className="accounting"
label="Responsável Contábil"
placeholder="Selecione um tipo"
options={usersOptions}
defaultValue={{
value: period.accounting?.id,
label: period.accounting?.short_name,
}}
/>
</section>
</Scope>
))}
</Periods>
</FormScroll>
</FormContainer>
</Content>
</Container>
);
};
export default Form;
Form.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
id: PropTypes.node,
}).isRequired,
}).isRequired,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment