Kaballas's picture
initialize project structure with essential configurations and components
56b6519
import dayjs, { Dayjs } from 'dayjs';
import { t } from 'i18next';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { toast } from 'sonner';
import DayPicker from '../../../../components/button/DayPicker';
import MultiSelectDropdown from '../../../../components/dropdown/MultiSelectDropdown';
import SelectDropdown from '../../../../components/dropdown/SelectDropdown';
import SimpleInput from '../../../../components/input/SimpleInput';
import TextArea from '../../../../components/text/TextArea';
import TopMenu from '../../../../components/topmenu/Topmenu';
import type {
AuditById,
Client,
Company,
Language,
Template,
UpdateAudit,
User as Collaborator,
} from '../../../../services/audits';
import {
getAuditById,
getClients,
getCollaborators,
getCompanies,
getLanguages,
getTemplates,
updateAudit,
} from '../../../../services/audits';
import { GeneralDivWrapper } from './GeneralDivWrapper';
type ListItem = {
id: number;
value: string;
label?: string;
};
export const General = () => {
const { auditId } = useParams();
const [dataAudit, setDataAudit] = useState<AuditById>();
const [nameAudit, setNameAudit] = useState<string>('');
const [auditType, setAuditType] = useState<string>('');
const [languageOptions, setLanguageOptions] = useState<ListItem[]>([]);
const [currentLanguage, setCurrentLanguage] = useState<ListItem | null>(null);
const [loadingLanguages, setLoadingLanguages] = useState<boolean>(true);
const [templates, setTemplates] = useState<Template[]>([]);
const [templateOptions, setTemplateOptions] = useState<ListItem[]>([]);
const [currentTemplate, setCurrentTemplate] = useState<ListItem | null>(null);
const [loadingTemplates, setLoadingTemplates] = useState<boolean>(true);
const [companies, setCompanies] = useState<Company[]>([]);
const [companyOptions, setCompanyOptions] = useState<ListItem[]>([]);
const [currentCompany, setCurrentCompany] = useState<ListItem | null>(null);
const [loadingCompanies, setLoadingCompanies] = useState<boolean>(true);
const [clients, setClients] = useState<Client[]>([]);
const [clientOptions, setClientOptions] = useState<ListItem[]>([]);
const [currentClient, setCurrentClient] = useState<ListItem | null>(null);
const [loadingClients, setLoadingClients] = useState<boolean>(true);
const [collaborators, setCollaborators] = useState<Collaborator[]>([]);
const [collaboratorOptions, setCollaboratorOptions] = useState<ListItem[]>(
[],
);
const [currentCollaborators, setCurrentCollaborators] = useState<ListItem[]>(
[],
);
const [loadingCollaborators, setLoadingCollaborators] =
useState<boolean>(true);
const [startDate, setStartDate] = useState<Dayjs | null>(null);
const [endDate, setEndDate] = useState<Dayjs | null>(null);
const [reportingDate, setReportingDate] = useState<Dayjs | null>(null);
const [scope, setScope] = useState<string[]>([]);
const changeScope = (value: string) => {
setScope(value.split('\n'));
};
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
const fetchData = async () => {
try {
const dataLanguages = await getLanguages();
const languageNames = dataLanguages.datas.map(
(item: Language, index: number): ListItem => ({
id: index,
value: item.locale,
label: item.language,
}),
);
setLanguageOptions(languageNames);
setLoadingLanguages(false);
const dataTemplates = await getTemplates();
setTemplates(dataTemplates.datas);
const templateNames = dataTemplates.datas.map(
(item: Template, index: number): ListItem => ({
id: index,
value: item.name,
label: item.name,
}),
);
setTemplateOptions(templateNames);
setLoadingTemplates(false);
const dataCompanies = await getCompanies();
setCompanies(dataCompanies.datas);
const companyNames = dataCompanies.datas.map(
(item: Company, index: number): ListItem => ({
id: index,
value: item._id,
label: item.name,
}),
);
setCompanyOptions(companyNames.length > 0 ? companyNames : []);
setLoadingCompanies(false);
const dataClients = await getClients();
setClients(dataClients.datas);
const clientNames = dataClients.datas.map(
(item: Client, index: number): ListItem => ({
id: index,
value: item._id,
label: item.email,
}),
);
setClientOptions(clientNames.length > 0 ? clientNames : []);
setLoadingClients(false);
const dataCollaborators = await getCollaborators();
setCollaborators(dataCollaborators.datas);
const collaboratorNames = dataCollaborators.datas.map(
(item: Collaborator, index: number): ListItem => ({
id: index,
value: `${item._id}`,
label: `${item.firstname} ${item.lastname}`,
}),
);
setCollaboratorOptions(collaboratorNames);
setLoadingCollaborators(false);
setLoadingCollaborators(false);
setLoading(false);
} catch (err) {
setLoading(false);
}
};
fetchData().catch(console.error);
}, []);
useEffect(() => {
if (currentCompany) {
setClientOptions(
clients
.filter(client => client.company.name === currentCompany.label)
.map(
(client: Client, index: number): ListItem => ({
id: index,
value: client._id,
label: client.email,
}),
),
);
} else {
getClients()
.then(res => {
setClients(res.datas);
})
.catch(console.error);
}
// Se deshabilita porque no se debe volver a ejecutar cuando se actualizan los clientes, a fin de no crear un loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentCompany]);
useEffect(() => {
if (currentClient && !currentCompany) {
setCurrentCompany(
companyOptions.find(
company =>
company.label ===
clients.filter(client => client._id === currentClient.value)[0]
.company.name,
) ?? null,
);
}
}, [clients, companyOptions, currentClient, currentCompany]);
useEffect(() => {
const fetchAuditData = async () => {
if (!auditId) {
return;
}
try {
const dataAudit = await getAuditById(auditId);
setDataAudit(dataAudit.datas);
setNameAudit(dataAudit.datas.name);
setAuditType(dataAudit.datas.auditType);
const selectedLanguage =
languageOptions.find(
item => item.value === dataAudit.datas.language,
) ?? null;
setCurrentLanguage(selectedLanguage);
if (!('template' in dataAudit.datas)) {
setCurrentTemplate(null);
} else {
const selectedTemplate =
templateOptions.find(
item => item.value === dataAudit.datas.template.name,
) ?? null;
setCurrentTemplate(selectedTemplate ?? null);
}
const selectedCompany = companyOptions.find(
item => item.value === dataAudit.datas.company?._id,
);
setCurrentCompany(selectedCompany ?? null);
const selectedClient = clientOptions.find(
item => item.value === dataAudit.datas.client?._id,
);
setCurrentClient(selectedClient ?? null);
const selectedCollaborators = dataAudit.datas.collaborators.map(
(item: Collaborator, index: number): ListItem => ({
id: index,
value: item._id,
label: `${item.firstname} ${item.lastname}`,
}),
);
setCurrentCollaborators(selectedCollaborators);
setStartDate(dayjs(dataAudit.datas.date_start));
setEndDate(dayjs(dataAudit.datas.date_end));
setReportingDate(dayjs(dataAudit.datas.date));
setScope(dataAudit.datas.scope.map(item => item.name));
} catch (error) {
console.error('Error fetching audit data:', error);
}
};
if (
auditId &&
languageOptions.length > 0 &&
templateOptions.length > 0 &&
collaboratorOptions.length > 0
) {
fetchAuditData().catch(console.error);
}
// Se deshabilita porque no se debe volver a ejecutar cuando se actualizan las opciones, debido a filtrado por compañía de cliente
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [auditId, collaboratorOptions]);
const getClientData = () => {
if (!currentClient) {
return undefined;
}
const clientData = clients.find(
client => client._id === currentClient.value,
);
return clientData
? {
_id: clientData._id,
company: {
_id: currentCompany?.value ?? '',
name: clientData.company.name,
},
email: clientData.email,
firstname: clientData.firstname,
lastname: clientData.lastname,
}
: {
_id: '',
company: { _id: '', name: '' },
email: '',
firstname: '',
lastname: '',
};
};
const getCollaboratorsData = () => {
return collaborators
.filter(collaborator =>
currentCollaborators.some(item => item.value === collaborator._id),
)
.map(collaborator => ({
_id: collaborator._id,
firstname: collaborator.firstname,
lastname: collaborator.lastname,
username: collaborator.username,
}));
};
const getCompanyData = () => {
const companyData = companies.find(
company => company._id === currentCompany?.value,
);
return companyData
? {
__v: 0,
_id: companyData._id,
createdAt: dataAudit?.company?.createdAt ?? '',
name: companyData.name,
shortName: companyData.shortName,
updatedAt: dataAudit?.company?.updatedAt ?? '',
}
: {
__v: 0,
_id: '',
name: '',
shortName: '',
createdAt: '',
updatedAt: '',
};
};
const handleSaveButton = async () => {
if (!auditId) {
console.error('No audit ID available');
return;
}
const updatedAudit: UpdateAudit = {
_id: auditId,
auditType,
name: nameAudit,
language: currentLanguage?.value ?? '',
client: getClientData(),
collaborators: getCollaboratorsData(),
company: getCompanyData(),
creator: {
_id: dataAudit?.creator._id ?? '',
firstname: dataAudit?.creator.firstname ?? '',
lastname: dataAudit?.creator.lastname ?? '',
username: dataAudit?.creator.username ?? '',
},
customFields: [],
date: reportingDate ? reportingDate.toISOString() : '',
date_end: endDate ? endDate.toISOString() : '',
date_start: startDate ? startDate.toISOString() : '',
reviewers: [],
scope,
template:
templates.find(template => template.name === currentTemplate?.value)
?._id ?? '',
};
try {
await updateAudit(auditId, updatedAudit);
toast.success(t('msg.auditUpdatedOk'));
} catch (error) {
toast.error(t('err.failedUpdateAudit'));
}
};
return (
<GeneralDivWrapper>
{loading ? (
<div className="flex justify-center items-center h-screen">
<div className="loader" />
<svg
className="animate-spin -ml-1 mr-3 h-10 w-10 text-white"
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
fill="currentColor"
/>
</svg>
</div>
) : (
<>
<TopMenu
auditName={nameAudit}
auditType={auditType}
onSave={handleSaveButton}
/>
<div className="flex flex-col">
<SimpleInput
id="name"
label={t('name')}
name="name"
onChange={setNameAudit}
placeholder={t('search')}
type="text"
value={nameAudit}
/>
</div>
<div className="flex space-x-6">
<div className="w-1/2">
{!loadingLanguages ? (
<SelectDropdown
items={languageOptions}
onChange={setCurrentLanguage}
selected={currentLanguage}
title={t('language')}
/>
) : null}
</div>
<div className="w-1/2">
{!loadingTemplates ? (
<SelectDropdown
items={templateOptions}
onChange={setCurrentTemplate}
selected={currentTemplate}
title={t('template')}
/>
) : null}
</div>
</div>
<div className="flex space-x-6">
<div className="w-1/2">
{!loadingCompanies ? (
<SelectDropdown
items={companyOptions}
onChange={setCurrentCompany}
selected={currentCompany}
title={t('company')}
/>
) : null}
</div>
<div className="w-1/2">
{!loadingClients ? (
<SelectDropdown
items={clientOptions}
onChange={setCurrentClient}
selected={currentClient}
title={t('client')}
/>
) : null}
</div>
</div>
<div className="flex flex-col">
{!loadingCollaborators ? (
<MultiSelectDropdown
items={collaboratorOptions}
onChange={setCurrentCollaborators}
selected={currentCollaborators}
title={t('collaborators')}
/>
) : null}
</div>
<div className="flex space-x-6">
<div className="w-1/3">
<DayPicker
label={t('startDate')}
onChange={setStartDate}
selectedDay={startDate}
/>
</div>
<div className="w-1/3">
<DayPicker
label={t('endDate')}
onChange={setEndDate}
selectedDay={endDate}
/>
</div>
<div className="w-1/3">
<DayPicker
label={t('reportingDate')}
onChange={setReportingDate}
selectedDay={reportingDate}
/>
</div>
</div>
<div className="flex flex-col">
<TextArea
id="references"
label={t('auditScope')}
name="references"
onChange={changeScope}
placeholder=""
rows={4}
value={scope.join('\n')}
/>
</div>
</>
)}
</GeneralDivWrapper>
);
};