@chakra-ui/react#FormHelperText JavaScript Examples
The following examples show how to use
@chakra-ui/react#FormHelperText.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: components.js From idena-web with MIT License | 5 votes |
export function OracleFormHelperText(props) {
return <FormHelperText color="muted" fontSize="md" {...props} />
}
Example #2
Source File: components.js From idena-web with MIT License | 5 votes |
export function OracleFormHelperValue(props) {
return <FormHelperText color="gray.500" fontSize="md" {...props} />
}
Example #3
Source File: Send.js From web-client with Apache License 2.0 | 5 votes |
SendReport = () => {
const navigate = useNavigate();
const { projectId } = useParams();
const [project] = useFetch(`/projects/${projectId}`)
const [revisions] = useFetch(`/reports?projectId=${projectId}`)
const [deliverySettings, setDeliverySettings] = useState({
report_id: null,
recipients: null,
subject: "[CONFIDENTIAL] Security report attached",
body: "Please review attachment containing a security report."
})
const handleSend = async (ev) => {
ev.preventDefault();
secureApiFetch(`/reports/${deliverySettings.report_id}/send`, { method: 'POST', body: JSON.stringify(deliverySettings) })
.then(() => {
navigate(`/projects/${project.id}/report`);
})
.catch(err => {
console.error(err);
})
}
const handleFormChange = ev => {
const target = ev.target;
const name = target.name;
const value = target.value;
setDeliverySettings({ ...deliverySettings, [name]: value });
};
useEffect(() => {
if (revisions && deliverySettings.report_id === null)
setDeliverySettings({ ...deliverySettings, report_id: revisions[0].id })
}, [revisions, deliverySettings]);
if (!project) return <Loading />
return <div>
<PageTitle value="Send report" />
<div className='heading'>
<Breadcrumb>
<Link to="/projects">Projects</Link>
{project && <Link to={`/projects/${project.id}`}>{project.name}</Link>}
{project && <Link to={`/projects/${project.id}/report`}>Report</Link>}
</Breadcrumb>
</div>
<form onSubmit={handleSend}>
<Title title='Send report' />
<FormControl isRequired>
<FormLabel for="reportId">Revision</FormLabel>
<Select id="reportId" name="report_id" onChange={handleFormChange}>
{revisions && revisions.map(revision => <option value={revision.id}>{revision.version_name}</option>)}
</Select>
</FormControl>
<FormControl isRequired>
<FormLabel>Recipients</FormLabel>
<Input type="text" name="recipients" onChange={handleFormChange} autoFocus
placeholder="[email protected]" />
<FormHelperText>Comma separated list of email addresses.</FormHelperText>
</FormControl>
<FormControl isRequired>
<FormLabel>Subject</FormLabel>
<Input type="text" name="subject" onChange={handleFormChange}
value={deliverySettings.subject} />
</FormControl>
<FormControl isRequired>
<FormLabel>Body</FormLabel>
<Textarea name="body" onChange={handleFormChange} value={deliverySettings.body} />
</FormControl>
<PrimaryButton type="submit">Send</PrimaryButton>
</form>
</div>
}
Example #4
Source File: MakeClaim.js From DAOInsure with MIT License | 4 votes |
function MakeClaim() {
const [currentImage, setCurrentImage] = useState(undefined);
const [images, setImages] = useState([]);
const [isPageLoading, setIsPageLoading] = useState(false);
const { textileClient } = useContext(AppContext);
const [claimTitle, setClaimTitle] = useState();
const [claimSummary, setClaimSummary] = useState();
const [dateOfIncident, setDateOfIncident] = useState();
const [startTime, setStartTime] = useState();
const web3Context = useContext(Web3Context);
const {
createProposal,
signerAddress,
claimableAmount,
getClaimableAmount,
} = web3Context;
useEffect(() => {
getClaimableAmount();
}, []);
const handleImage = async (e) => {
console.log("uploading");
let file = e.target.files[0];
try {
let result = await uploadToSlate(file);
setImages([
...images,
{
isUploading: false,
url: `https://slate.textile.io/ipfs/${result.data.cid}`,
},
]);
setCurrentImage(`https://slate.textile.io/ipfs/${result.data.cid}`);
console.log("uploaded");
} catch (e) {
console.log(e);
}
};
const handleCurrentImage = (e) => {
e.preventDefault();
setCurrentImage(e.target.src);
};
const handleInputChange = (e, setter) => {
e.preventDefault();
setter(e.target.value);
};
const handleClaimSubmit = async (e) => {
e.preventDefault();
let imageUrls = images.map((image) => {
return image.url;
});
let claimObj = {
images: imageUrls,
claimTitle,
claimSummary,
dateOfIncident,
claimAmount: claimableAmount,
author: signerAddress,
};
console.log(claimObj);
// tried using fleek instead of threadDB.
// let response = await fleekStorage.upload({
// apiKey: "3aFyv9UlnpyVvuhdoy+WMA==",
// apiSecret: "vUREhYRSH5DP8WehKP+N8jTLoOJUBw+RA9TPLUKneK8=",
// key: uuidv4(),
// data: JSON.stringify(claimObj),
// });
// adding claim data to threadDB.
let response = await addToThread(
textileClient,
"bafkyspsyykcninhqn4ht6d6jeqmzq4cepy344akmkhjk75dmw36wq4q",
"claimsData",
claimObj
);
// create proposal on contract basically store the hash.
createProposal(
claimTitle,
(new Date(dateOfIncident).getTime() / 1000).toString(),
response.hash
);
};
return (
<Grid
paddingBottom='20px'
pt='20px'
height='100%'
px='250px'
width='100%'
templateColumns='2fr 1fr'
gridGap={5}
alignItems='flex-start'>
<VStack width='100%' alignItems='flex-start'>
<Skeleton isLoaded={!isPageLoading}>
<Heading as='h4' fontSize='28px'>
Make a Claim
</Heading>
</Skeleton>
<form style={{ width: "100%" }}>
<VStack width='100%' spacing={5} alignItems='flex-start'>
<input
multiple
onChange={(e) => handleImage(e)}
type='file'
style={{ display: "none" }}
id='image-input'
accept='image/*'
/>
{images.length == 0 ? (
isPageLoading ? (
<Spinner
colorScheme='whatsapp'
color='whatsapp.500'
alignSelf='center'
/>
) : (
<Box
cursor='pointer'
as='label'
htmlFor='image-input'
px='35px'
width='100%'
borderRadius='10px'
height='70px'
borderWidth='1px'
borderStyle='solid'
borderColor='whatsapp.500'>
<VStack
height='100%'
width='100%'
justifyContent='center'>
<TiPlus style={{ fill: "#22C35E" }} />
<Text fontWeight='600' color='#22C35E'>
Image
</Text>
</VStack>
</Box>
)
) : (
<>
<Box
mt='10px !important'
boxShadow='lg'
borderRadius='10px'>
<Image
borderRadius='10px'
src={currentImage}
/>
</Box>
<HStack width='100%' overflowX='scroll'>
{images.map((image, index) => {
return image.isUploading ? (
<Spinner key={index} />
) : (
<Image
key={image.url}
onClick={(e) => {
handleCurrentImage(e);
}}
borderRadius='10px'
height='70px'
src={image.url}
/>
);
})}
<Box
cursor='pointer'
as='label'
htmlFor='image-input'
px='35px'
borderRadius='10px'
height='70px'
borderWidth='1px'
borderStyle='solid'
borderColor='whatsapp.500'>
<VStack
height='100%'
width='100%'
justifyContent='center'>
<TiPlus
style={{ fill: "#22C35E" }}
/>
<Text
fontWeight='600'
color='#22C35E'>
Image
</Text>
</VStack>
</Box>
</HStack>
</>
)}
<VStack spacing={5} width='100%'>
<FormControl isRequired>
<Skeleton isLoaded={!isPageLoading}>
<FormLabel>Claim Title</FormLabel>
</Skeleton>
<Skeleton isLoaded={!isPageLoading}>
<Input
onChange={(e) =>
handleInputChange(e, setClaimTitle)
}
/>
</Skeleton>
</FormControl>
<FormControl isRequired>
<Skeleton isLoaded={!isPageLoading}>
<FormLabel>Summary of Incident</FormLabel>
</Skeleton>
<Skeleton isLoaded={!isPageLoading}>
<Textarea
onChange={(e) =>
handleInputChange(
e,
setClaimSummary
)
}
row='10'
/>
</Skeleton>
<Skeleton isLoaded={!isPageLoading}>
<FormHelperText>
Try to precise
</FormHelperText>
</Skeleton>
</FormControl>
<VStack width='100%'>
<HStack width='100%'>
<FormControl isRequired>
<Skeleton isLoaded={!isPageLoading}>
<FormLabel>
Date of Incident
</FormLabel>
</Skeleton>
<Skeleton isLoaded={!isPageLoading}>
<Input
onChange={(e) =>
handleInputChange(
e,
setDateOfIncident
)
}
type='date'
/>
</Skeleton>
</FormControl>
</HStack>
</VStack>
<Button
onClick={(e) => handleClaimSubmit(e)}
isLoading={isPageLoading}
mt='30px !important'
width='100%'
textTransform='uppercase'
type='submit'
colorScheme='whatsapp'>
Submit Claim
</Button>
</VStack>
</VStack>
</form>
</VStack>
<VStack width='100%'>
<Card isLoading={isPageLoading} cardTitle='Claimable Amount'>
<Skeleton isLoaded={!isPageLoading}>
<Heading
textColor='whatsapp.500'
fontSize='24px'
as='h3'>
{parseFloat(claimableAmount).toFixed(6)} DAI
</Heading>
</Skeleton>
</Card>
</VStack>
</Grid>
);
}
Example #5
Source File: containers.js From idena-web with MIT License | 4 votes |
AdForm = React.forwardRef(function AdForm(
{ad, onSubmit, ...props},
ref
) {
const {t} = useTranslation()
const [thumb, setThumb] = React.useState(ad?.thumb)
const [media, setMedia] = React.useState(ad?.media)
const [titleCharacterCount, setTitleCharacterCount] = React.useState(40)
const [descCharacterCount, setDescCharacterCount] = React.useState(70)
const [fieldErrors, setFieldErrors] = React.useState({})
return (
<form
ref={ref}
onChange={e => {
const {name, value} = e.target
if (name === 'title') {
setTitleCharacterCount(40 - value.length)
}
if (name === 'desc') {
setDescCharacterCount(70 - value.length)
}
}}
onSubmit={async e => {
e.preventDefault()
const formAd = Object.fromEntries(new FormData(e.target).entries())
const maybePersistedAd = ad ? await db.table('ads').get(ad.id) : null
const nextAd = {
...formAd,
thumb: isValidImage(formAd.thumb)
? formAd.thumb
: maybePersistedAd?.thumb,
media: isValidImage(formAd.media)
? formAd.media
: maybePersistedAd?.media,
}
const errors = validateAd(nextAd)
if (Object.values(errors).some(Boolean)) {
setFieldErrors(errors)
} else {
onSubmit(nextAd)
}
}}
{...props}
>
<Stack spacing={6} w="mdx">
<FormSection>
<FormSectionTitle>{t('Content')}</FormSectionTitle>
<Stack spacing={3}>
<AdFormField label={t('Title')} maybeError={fieldErrors.title}>
<InputGroup>
<Input name="title" defaultValue={ad?.title} maxLength={40} />
<InputCharacterCount>{titleCharacterCount}</InputCharacterCount>
</InputGroup>
</AdFormField>
<AdFormField label={t('Description')} maybeError={fieldErrors.desc}>
<InputGroup>
<Textarea
name="desc"
defaultValue={ad?.desc}
maxLength={70}
resize="none"
/>
<InputCharacterCount>{descCharacterCount}</InputCharacterCount>
</InputGroup>
</AdFormField>
<AdFormField label="Link" maybeError={fieldErrors.url}>
<Input type="url" name="url" defaultValue={ad?.url} />
</AdFormField>
</Stack>
</FormSection>
<FormSection>
<FormSectionTitle>{t('Media')}</FormSectionTitle>
<HStack spacing="10">
<Box flex={1}>
<AdMediaInput
name="media"
value={media}
label={t('Upload media')}
description={t('640x640px, no more than 1 Mb')}
fallbackSrc="/static/upload-cover-icn.svg"
maybeError={fieldErrors.media}
onChange={setMedia}
/>
</Box>
<Box flex={1}>
<AdMediaInput
name="thumb"
value={thumb}
label={t('Upload thumbnail')}
description={t('80x80px, no more than 1 Mb')}
fallbackSrc="/static/upload-thumbnail-icn.svg"
maybeError={fieldErrors.thumb}
onChange={setThumb}
/>
</Box>
</HStack>
</FormSection>
<FormSection>
<FormSectionTitle>{t('Target audience')}</FormSectionTitle>
<Stack spacing={3} shouldWrapChildren>
<AdFormField label={t('Language')}>
<Select
name="language"
defaultValue={ad?.language}
_hover={{
borderColor: 'gray.100',
}}
>
<option></option>
{AVAILABLE_LANGS.map(lang => (
<option key={lang}>{lang}</option>
))}
</Select>
</AdFormField>
<AdFormField label={t('Min age')}>
<AdNumberInput
name="age"
defaultValue={ad?.age}
min={0}
max={Number.MAX_SAFE_INTEGER}
/>
<FormHelperText color="muted" fontSize="sm" mt="1">
{t('Min age to see the ad')}
</FormHelperText>
</AdFormField>
<AdFormField label={t('Min stake')}>
<AdNumberInput
name="stake"
defaultValue={ad?.stake}
min={0}
max={Number.MAX_SAFE_INTEGER}
addon={t('iDNA')}
/>
<FormHelperText color="muted" fontSize="sm" mt="1">
{t('Min stake amount to see the ad')}
</FormHelperText>
</AdFormField>
<AdFormField label="OS">
<Select
name="os"
defaultValue={ad?.os}
_hover={{
borderColor: 'gray.100',
}}
>
<option></option>
{Object.entries(OS).map(([k, v]) => (
<option key={v} value={v}>
{k}
</option>
))}
</Select>
</AdFormField>
</Stack>
</FormSection>
</Stack>
</form>
)
})
Example #6
Source File: components.js From idena-web with MIT License | 4 votes |
export function ReplenishStakeDrawer({onSuccess, onError, ...props}) {
const {t, i18n} = useTranslation()
const [{state}] = useIdentity()
const {coinbase} = useAuthState()
const {data: balanceData} = useQuery({
queryKey: ['get-balance', coinbase],
queryFn: ({queryKey: [, address]}) => callRpc('dna_getBalance', address),
enabled: Boolean(coinbase),
staleTime: (BLOCK_TIME / 2) * 1000,
notifyOnChangeProps: 'tracked',
})
const {submit} = useReplenishStake({onSuccess, onError})
const formatDna = toLocaleDna(i18n.language, {
maximumFractionDigits: 5,
})
const isRisky = [
IdentityStatus.Candidate,
IdentityStatus.Newbie,
IdentityStatus.Verified,
].includes(state)
return (
<Drawer {...props}>
<DrawerHeader>
<Stack spacing="4">
<Center bg="blue.012" h="12" w="12" rounded="xl">
<WalletIcon boxSize="6" color="blue.500" />
</Center>
<Heading
color="brandGray.500"
fontSize="lg"
fontWeight={500}
lineHeight="base"
>
{t('Add stake')}
</Heading>
</Stack>
</DrawerHeader>
<DrawerBody fontSize="md">
<Stack spacing={30}>
<Stack>
<Text>
{t(
'Get quadratic staking rewards for locking iDNA in your identity stake.'
)}
</Text>
<Text>
{t('Current stake amount: {{amount}}', {
amount: formatDna(balanceData?.stake),
nsSeparator: '!!',
})}
</Text>
</Stack>
<Stack spacing="2.5">
<form
id="replenishStake"
onSubmit={e => {
e.preventDefault()
const formData = new FormData(e.target)
const amount = formData.get('amount')
submit({amount})
}}
>
<FormControl>
<FormLabel mx={0} mb="3">
{t('Amount')}
</FormLabel>
<DnaInput name="amount" />
<FormHelperText fontSize="md">
<Flex justify="space-between">
<Box as="span" color="muted">
{t('Available')}
</Box>
<Box as="span">{formatDna(balanceData?.balance)}</Box>
</Flex>
</FormHelperText>
</FormControl>
</form>
</Stack>
{isRisky && (
<ErrorAlert>
{state === IdentityStatus.Verified
? t(
'You will lose 100% of the Stake if you fail the upcoming validation'
)
: t(
'You will lose 100% of the Stake if you fail or miss the upcoming validation'
)}
</ErrorAlert>
)}
</Stack>
</DrawerBody>
<DrawerFooter>
<HStack>
<SecondaryButton onClick={props.onClose}>
{t('Not now')}
</SecondaryButton>
<PrimaryButton form="replenishStake" type="submit">
{t('Add stake')}
</PrimaryButton>
</HStack>
</DrawerFooter>
</Drawer>
)
}
Example #7
Source File: components.js From idena-web with MIT License | 4 votes |
export function BuySharedNodeForm({
isOpen,
onClose,
providerId,
from,
to,
amount,
}) {
const {t} = useTranslation()
const [submitting, setSubmitting] = useState(false)
const {coinbase, privateKey} = useAuthState()
const failToast = useFailToast()
const [{result: balanceResult}] = useRpc('dna_getBalance', true, from)
const [{result: epochResult}] = useRpc('dna_epoch', true)
const {isPurchasing, savePurchase} = useApikeyPurchasing()
const size = useBreakpointValue(['lg', 'md'])
const variant = useBreakpointValue(['outlineMobile', 'outline'])
const labelFontSize = useBreakpointValue(['base', 'md'])
const transfer = async () => {
setSubmitting(true)
try {
const rawTx = await getRawTx(
0,
coinbase,
to,
amount,
0,
privateKeyToPublicKey(privateKey),
0,
true
)
const tx = new Transaction().fromHex(rawTx)
tx.sign(privateKey)
const result = await buyKey(coinbase, `0x${tx.toHex()}`, providerId)
savePurchase(result.id, providerId)
} catch (e) {
failToast(`Failed to send iDNA: ${e.response?.data || 'unknown error'}`)
} finally {
setSubmitting(false)
}
}
const waiting = submitting || isPurchasing
return (
<AdDrawer
isOpen={isOpen}
isMining={waiting}
onClose={onClose}
isCloseable={!waiting}
closeOnEsc={!waiting}
closeOnOverlayClick={!waiting}
>
<DrawerHeader mb={8}>
<Flex direction="column" textAlign={['center', 'start']}>
<Flex
order={[2, 1]}
align="center"
justify="center"
mt={[8, 0]}
h={12}
w={12}
rounded="xl"
bg="red.012"
>
<SendOutIcon boxSize={6} color="red.500" />
</Flex>
<Heading
order={[1, 2]}
color="brandGray.500"
fontSize={['base', 'lg']}
fontWeight={[['bold', 500]]}
lineHeight="base"
mt={[0, 4]}
>
{t('Send iDNA')}
</Heading>
</Flex>
</DrawerHeader>
<DrawerBody>
<Stack spacing={[4, 5]}>
<CustomFormControl label="From" labelFontSize={labelFontSize}>
<Input
isDisabled
value={from}
backgroundColor={['gray.50', 'gray.100']}
size={size}
variant={variant}
/>
<Flex justify="space-between">
<FormHelperText color="muted" fontSize="md">
{t('Available')}
</FormHelperText>
<FormHelperText color="black" fontSize="md">
{(balanceResult && balanceResult.balance) || 0} iDNA
</FormHelperText>
</Flex>
</CustomFormControl>
<CustomFormControl label="To" labelFontSize={labelFontSize}>
<Input
isDisabled
value={to}
backgroundColor={['gray.50', 'gray.100']}
size={size}
variant={variant}
/>
</CustomFormControl>
<CustomFormControl label="Amount, iDNA" labelFontSize={labelFontSize}>
<Input
isDisabled
value={amount}
backgroundColor={['gray.50', 'gray.100']}
size={size}
variant={variant}
/>
<FormHelperText color="muted" fontSize="md">
{t(
'Node operator provides you the shared node for the upcoming validation ceremony'
)}{' '}
{epochResult
? new Date(epochResult.nextValidation).toLocaleDateString()
: ''}
</FormHelperText>
</CustomFormControl>
</Stack>
<PrimaryButton
mt={8}
w="100%"
display={['flex', 'none']}
fontSize="mobile"
size="lg"
onClick={transfer}
isLoading={waiting}
loadingText={t('Mining...')}
>
{t('Transfer')}
</PrimaryButton>
</DrawerBody>
<DrawerFooter display={['none', 'flex']}>
<Box
alignSelf="stretch"
borderTop="1px"
borderTopColor="gray.100"
mt="auto"
pt={5}
width="100%"
>
<Stack isInline spacing={2} justify="flex-end">
<SecondaryButton
fontSize={13}
onClick={onClose}
isDisabled={waiting}
>
{t('Not now')}
</SecondaryButton>
<PrimaryButton
onClick={transfer}
isLoading={waiting}
loadingText={t('Mining...')}
>
{t('Transfer')}
</PrimaryButton>
</Stack>
</Box>
</DrawerFooter>
</AdDrawer>
)
}
Example #8
Source File: Form.js From web-client with Apache License 2.0 | 4 votes |
VulnerabilityForm = ({
isEditForm = false,
vulnerability,
vulnerabilitySetter: setVulnerability,
onFormSubmit
}) => {
const [initialised, setInitialised] = useState(false);
const [projects, setProjects] = useState(null);
const [categories, setCategories] = useState(null);
const [subCategories, setSubCategories] = useState(null);
const [targets, setTargets] = useState(null);
const [useOWASP, setMetrics] = useState(false);
useEffect(() => {
if (initialised) return;
Promise.all([
secureApiFetch(`/projects`, { method: 'GET' }),
secureApiFetch(`/vulnerabilities/categories`, { method: 'GET' }),
])
.then(resp => {
const [respA, respB] = resp;
return Promise.all([respA.json(), respB.json()]);
})
.then(([projects, categories]) => {
const defaultProjectId = projects.length ? projects[0].id : 0;
const projectId = isEditForm ? vulnerability.project_id : defaultProjectId;
setMetrics(isOwaspProject(projects, projectId))
var subcategories = null;
if (vulnerability.parent_category_id) {
secureApiFetch(`/vulnerabilities/categories/${vulnerability.parent_category_id}`, { method: 'GET' })
.then(response => response.json())
.then(json => {
subcategories = json;
})
}
secureApiFetch(`/targets?projectId=${projectId}`, { method: 'GET' })
.then(resp => resp.json())
.then(targets => {
unstable_batchedUpdates(() => {
setProjects(projects);
setCategories(categories);
setTargets(targets);
setVulnerability(prevVulnerability => {
let updatedVulnerability = prevVulnerability;
if (!idExists(projects, prevVulnerability.project_id)) {
updatedVulnerability.project_id = defaultProjectId;
}
if ((!idExists(categories, prevVulnerability.category_id)) && (!idExists(subcategories, prevVulnerability.category_id))) {
updatedVulnerability.category_id = categories[0].id;
}
if (!idExists(targets, vulnerability.target_id)) {
updatedVulnerability.target_id = null;
}
return updatedVulnerability;
})
setInitialised(true);
});
})
});
}, [initialised, isEditForm, setProjects, setCategories, setTargets, setMetrics, setVulnerability, vulnerability.target_id, vulnerability.project_id, vulnerability.parent_category_id, subCategories, setSubCategories]);
useEffect(() => {
if (!initialised) return;
if (vulnerability.parent_category_id) {
secureApiFetch(`/vulnerabilities/categories/${vulnerability.parent_category_id}`, { method: 'GET' })
.then(response => response.json())
.then(json => {
setSubCategories(json);
})
}
const projectId = vulnerability.project_id;
secureApiFetch(`/targets?projectId=${projectId}`, { method: 'GET' })
.then(resp => resp.json())
.then(targets => {
unstable_batchedUpdates(() => {
setTargets(targets);
if (isEditForm) { // Edit
if (!idExists(targets, vulnerability.target_id)) {
setVulnerability(prevVulnerability => {
return { ...prevVulnerability, target_id: 0 }
});
}
}
});
})
}, [initialised, isEditForm, setTargets, setVulnerability, vulnerability.target_id, vulnerability.project_id, vulnerability.parent_category_id]);
const idExists = (elements, id) => {
if (!elements) return false;
for (const el of elements) {
if (el.id === parseInt(id)) return true;
}
return false;
}
const isOwaspProject = (elements, id) => {
let metrics = ProjectVulnerabilityMetrics[0].id;
for (const el of elements) {
if (el.id === parseInt(id)) {
metrics = el.vulnerability_metrics;
}
}
return (ProjectVulnerabilityMetrics[1].id === metrics);
}
const onFormChange = ev => {
const target = ev.target;
const name = target.name;
let value = target.type === 'checkbox' ? target.checked : target.value;
if ('tags' === name) {
value = JSON.stringify(value.split(','));
}
if ('category_id' === name) {
if (value !== '(none)') {
secureApiFetch(`/vulnerabilities/categories/${value}`, { method: 'GET' })
.then(response => response.json())
.then(json => {
setSubCategories(json);
})
setVulnerability({ ...vulnerability, 'parent_category_id': value, [name]: value });
} else {
setVulnerability({ ...vulnerability, 'category_id': null });
}
} else if ('subcategory_id' === name) {
setVulnerability({ ...vulnerability, 'category_id': value });
} else {
setVulnerability({ ...vulnerability, [name]: value });
}
};
return <form onSubmit={onFormSubmit} className="crud">
<Accordion defaultIndex={0} allowToggle allowMultiple>
<AccordionItem index={0}>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
Basic information
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<label>Properties
<div>
<Checkbox name="is_template" onChange={onFormChange} isChecked={vulnerability.is_template}>Is template</Checkbox>
</div>
</label>
<label>External ID
<Input type="text" name="external_id" value={vulnerability.external_id || ""} onChange={onFormChange} />
</label>
<label>Summary
<Input type="text" name="summary" value={vulnerability.summary || ""} onChange={onFormChange} required autoFocus />
</label>
<label>Description
<MarkdownEditor name="description" value={vulnerability.description || ""} onChange={onFormChange} />
</label>
<label>External references
<MarkdownEditor name="external_refs" value={vulnerability.external_refs || ""} onChange={onFormChange} />
</label>
<label>Category
<Select name="category_id" value={vulnerability.parent_category_id || ""} onChange={onFormChange} required>
<option>(none)</option>
{categories && categories.map(cat =>
<option key={cat.id} value={cat.id}>{cat.name}</option>
)}
</Select>
</label>
<label>Subcategory
<Select name="subcategory_id" value={vulnerability.category_id || ""} onChange={onFormChange} required>
<option>(none)</option>
{subCategories && subCategories.map(subcat =>
<option key={subcat.id} value={subcat.id}>{subcat.name}</option>
)}
</Select>
</label>
<FormControl id="visibility" isRequired>
<FormLabel>Visibility</FormLabel>
<Select name="visibility" value={vulnerability.visibility || ""} onChange={onFormChange}>
<option value="public">Public</option>
<option value="private">Private</option>
</Select>
<FormHelperText>Private makes this vulnerability not visible to the client.</FormHelperText>
</FormControl>
<label>Risk
<Select name="risk" value={vulnerability.risk || ""} onChange={onFormChange} required>
{Risks.map(risk =>
<option key={risk.id} value={risk.id}>{risk.name}</option>
)}
</Select>
</label>
<label>Tags
<Input type="text" name="tags" onChange={onFormChange} value={vulnerability.tags ? JSON.parse(vulnerability.tags).join(',') : ''} />
</label>
<label>Proof of concept
<MarkdownEditor name="proof_of_concept" value={vulnerability.proof_of_concept || ""} onChange={onFormChange} />
</label>
<label>Impact
<MarkdownEditor name="impact" value={vulnerability.impact || ""} onChange={onFormChange} />
</label>
{
!useOWASP && <>
<label>CVSS score
<Input type="number" step="0.1" min="0" max="10" name="cvss_score" value={vulnerability.cvss_score || ""}
onChange={onFormChange} />
</label>
<label><span><CvssAbbr /> vector</span>
<Input type="text" name="cvss_vector" value={vulnerability.cvss_vector || ""} onChange={onFormChange} placeholder="eg: AV:N/AC:L/Au:S/C:P/I:P/A:N" />
</label>
</>
}
</AccordionPanel>
</AccordionItem>
{useOWASP &&
<AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
Owasp Risk Rating calculator
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<label>Owasp Risk Rating</label>
<OwaspRR vulnerability={vulnerability} vulnerabilitySetter={setVulnerability} />
</AccordionPanel>
</AccordionItem>
}
<AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
Remediation
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<label>Remediation instructions
<MarkdownEditor name="remediation" value={vulnerability.remediation || ""} onChange={onFormChange} />
</label>
<label>Remediation complexity
<Select name="remediation_complexity" value={vulnerability.remediation_complexity || ""} onChange={onFormChange} required>
{RemediationComplexity.map(complexity =>
<option key={complexity.id} value={complexity.id}>{complexity.name}</option>
)}
</Select>
</label>
<label>Remediation priority
<Select name="remediation_priority" value={vulnerability.remediation_priority || ""} onChange={onFormChange} required>
{RemediationPriority.map(priority =>
<option key={priority.id} value={priority.id}>{priority.name}</option>
)}
</Select>
</label>
</AccordionPanel>
</AccordionItem>
{
!vulnerability.is_template && <AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left">
Relations
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<label>Project
<Select name="project_id" value={vulnerability.project_id || ""} onChange={onFormChange} required>
{projects && projects.map((project, index) =>
<option key={index} value={project.id}>{project.name}</option>
)}
</Select>
</label>
<label>Affected target
<Select name="target_id" value={vulnerability.target_id || ""} onChange={onFormChange}>
<option value="0">(none)</option>
{targets && targets.map((target, index) =>
<option key={index} value={target.id}>{target.name}</option>
)}
</Select>
</label>
</AccordionPanel>
</AccordionItem>
}
</Accordion>
<Primary type="submit">{isEditForm ? "Save" : "Add"}</Primary>
</form >
}