Spaces:
Runtime error
Runtime error
import { XMarkIcon } from '@heroicons/react/24/outline'; | |
import { t } from 'i18next'; | |
import { useCallback, useEffect, useState } from 'react'; | |
import { toast, Toaster } from 'sonner'; | |
import DefaultRadioGroup from '../../../components/button/DefaultRadioGroup'; | |
import PrimaryButton from '../../../components/button/PrimaryButton'; | |
import SelectDropdown from '../../../components/dropdown/SelectDropdown'; | |
import { mergeVulnerability } from '../../../services/vulnerabilities'; | |
type Details = { | |
locale: string; | |
title?: string; | |
vulnType?: string; | |
description?: string; | |
observation?: string; | |
remediation?: string; | |
cwes: string[]; | |
references: string[]; | |
customFields: string[]; | |
}; | |
type VulnerabilityData = { | |
_id: string; | |
details: Details[]; | |
}; | |
type ListItem = { | |
id: number; | |
value: string; | |
label?: string; | |
locale?: string; | |
}; | |
type RadioOption = { | |
id: string; | |
label: string; | |
value: string; | |
disabled?: boolean; | |
}; | |
type MergeVulnerability = { | |
idIzq: string; | |
rightSide: { | |
vulnId: string; | |
locale: string; | |
}; | |
}; | |
type MergeVulnProps = { | |
isOpen: boolean; | |
handlerIsOpen: React.Dispatch<React.SetStateAction<boolean>>; | |
vulnerabilities: VulnerabilityData[]; | |
languages: ListItem[]; | |
refreshVulns: () => void; | |
handleOnSuccess: (message: string) => void; | |
}; | |
const MergeVulnerabilities: React.FC<MergeVulnProps> = ({ | |
isOpen, | |
handlerIsOpen, | |
vulnerabilities, | |
languages, | |
refreshVulns, | |
handleOnSuccess, | |
}) => { | |
const [selectedLanguageLeft, setSelectedLanguageLeft] = | |
useState<ListItem | null>(null); | |
const [currentRadioOptionsLeft, setCurrentRadioOptionsLeft] = useState< | |
RadioOption[] | |
>([]); | |
const [selectedRadioLeft, setSelectedRadioLeft] = useState(''); | |
const [selectedLanguageRight, setSelectedLanguageRight] = | |
useState<ListItem | null>(null); | |
const [currentRadioOptionsRight, setCurrentRadioOptionsRight] = useState< | |
RadioOption[] | |
>([]); | |
const [selectedRadioRight, setSelectedRadioRight] = useState(''); | |
const [allDetails] = useState( | |
vulnerabilities.flatMap(vuln => | |
vuln.details.map(detail => ({ | |
id: vuln._id, | |
locale: detail.locale, | |
title: detail.title, | |
})), | |
), | |
); | |
const checkLeft = useCallback(() => { | |
const leftFiltered = allDetails.filter( | |
detailIter => detailIter.locale === selectedLanguageLeft?.value, | |
); | |
const rightFilteredIds = new Set( | |
allDetails | |
.filter( | |
detailIter => detailIter.locale === selectedLanguageRight?.value, | |
) | |
.map(item => item.id), | |
); | |
const leftOptions = leftFiltered | |
.filter(item => !rightFilteredIds.has(item.id)) | |
.map(item => ({ | |
id: item.id, | |
value: [item.id, item.locale].join('.'), | |
label: item.title ?? '', | |
})); | |
setCurrentRadioOptionsLeft(leftOptions); | |
}, [allDetails, selectedLanguageLeft?.value, selectedLanguageRight?.value]); | |
const checkRight = useCallback(() => { | |
const rightFiltered = allDetails.filter( | |
detailIter => detailIter.locale === selectedLanguageRight?.value, | |
); | |
const leftFilteredIds = new Set( | |
allDetails | |
.filter(detailIter => detailIter.locale === selectedLanguageLeft?.value) | |
.map(item => item.id), | |
); | |
const rightOptions = rightFiltered | |
.filter(item => !leftFilteredIds.has(item.id)) | |
.map(item => ({ | |
id: item.id, | |
value: [item.id, item.locale].join('.'), | |
label: item.title ?? '', | |
})); | |
setCurrentRadioOptionsRight(rightOptions); | |
}, [allDetails, selectedLanguageLeft?.value, selectedLanguageRight?.value]); | |
useEffect(() => { | |
checkLeft(); | |
checkRight(); | |
}, [checkLeft, checkRight, selectedLanguageLeft, selectedLanguageRight]); | |
const handlerLeftChange = (item: ListItem) => { | |
setSelectedLanguageLeft(item); | |
}; | |
const handlerRightChange = (item: ListItem) => { | |
setSelectedLanguageRight(item); | |
}; | |
const onSubmitMerge = async () => { | |
if (selectedRadioLeft !== '' && selectedRadioRight !== '') { | |
try { | |
const [leftId] = selectedRadioLeft.split('.'); | |
const [rightId, rightLocale] = selectedRadioRight.split('.'); | |
const mergeObject: MergeVulnerability = { | |
idIzq: leftId, | |
rightSide: { | |
vulnId: rightId, | |
locale: rightLocale, | |
}, | |
}; | |
const response = await mergeVulnerability(mergeObject); | |
if (response.status === 'success') { | |
handleOnSuccess(t('msg.vulnerabilityMergeOk')); | |
} | |
} catch (error) { | |
console.error('Error:', error); | |
toast.error(t('err.failedMergeVulnerabilities')); | |
return; | |
} | |
} else { | |
toast.error(t('err.mergeNoVulnerabilitiesSelected')); | |
return; | |
} | |
refreshVulns(); | |
handlerIsOpen(false); | |
}; | |
return ( | |
<div | |
className={`fixed inset-0 flex justify-center items-center transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`} | |
> | |
<div | |
aria-hidden="true" | |
className="fixed inset-0 bg-black bg-opacity-50" | |
onClick={() => handlerIsOpen(false)} | |
/> | |
<Toaster /> | |
<div className="relative bg-gray-700 p-2 rounded-lg shadow-lg w-3/5 h-1/2 flex flex-col"> | |
<div className="mx-3 mt-2 flex items-center justify-between"> | |
<span className="text-xl text-white font-bold p-1"> | |
{t('mergeVulnerabilities')} | |
</span> | |
<button | |
className="bg-transparent text-white rounded" | |
onClick={() => handlerIsOpen(false)} | |
type="button" | |
> | |
<XMarkIcon className="h-6 w-6" /> | |
</button> | |
</div> | |
<hr className="h-1 mx-3 mb-1 bg-gray-600 border-0 rounded" /> | |
<div className="flex justify-between space-x-4 mx-3 flex-1 overflow-hidden"> | |
<div className="w-1/2 mt-2"> | |
<SelectDropdown | |
items={languages} | |
onChange={value => handlerLeftChange(value)} | |
placeholder={t('languageAddFromRight')} | |
selected={selectedLanguageLeft} | |
title="" | |
/> | |
<div className="pl-1 mt-3 overflow-auto h-full pb-14"> | |
<DefaultRadioGroup | |
name="left" | |
onChange={setSelectedRadioLeft} | |
options={currentRadioOptionsLeft} | |
value={selectedRadioLeft} | |
/> | |
</div> | |
</div> | |
<div className="w-0.5 h-full bg-gray-600" /> | |
<div className="w-1/2 mt-2"> | |
<SelectDropdown | |
items={languages} | |
onChange={value => handlerRightChange(value)} | |
placeholder={t('languageMoveToLeft')} | |
selected={selectedLanguageRight} | |
title="" | |
/> | |
<div className="pl-1 mt-3 overflow-auto h-full pb-14"> | |
<DefaultRadioGroup | |
name="right" | |
onChange={setSelectedRadioRight} | |
options={currentRadioOptionsRight} | |
value={selectedRadioRight} | |
/> | |
</div> | |
</div> | |
</div> | |
<div className="flex justify-center pt-2 border-t-gray-600 border-t-2"> | |
<PrimaryButton onClick={onSubmitMerge}> | |
<span>{t('merge')}</span> | |
</PrimaryButton> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default MergeVulnerabilities; | |