Kaballas's picture
initialize project structure with essential configurations and components
56b6519
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;