Spaces:
Runtime error
Runtime error
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> | |
); | |
}; | |