@chakra-ui/react#FormControl JavaScript Examples

The following examples show how to use @chakra-ui/react#FormControl. 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 6 votes vote down vote up
function CustomFormControl({label, labelFontSize = 'md', children, ...props}) {
  return (
    <FormControl {...props}>
      <FormLabel fontSize={labelFontSize} color="brandGray.500" mb={2}>
        {label}
      </FormLabel>
      {children}
    </FormControl>
  )
}
Example #2
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
VulnerabilityCategoryForm = ({ category, onFormSubmit, categorySetter: setCategory }) => {
    const [categories] = useFetch('/vulnerabilities/categories?parentsOnly=true');

    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value === "" ? null : target.value;

        setCategory({ ...category, [name]: value });
    };

    return <form id="vulnerability_category_form" onSubmit={onFormSubmit}>
        <FormControl id="parent_id" isRequired>
            <FormLabel>Parent category</FormLabel>
            {categories && <Select name="parent_id" value={category.parent_id} onChange={onFormInputChange}>
                <option>(none)</option>
                {categories.filter(category => category.parent_id === null).map(category => <option key={category.id} value={category.id}>{category.name}</option>)}
            </Select>}
        </FormControl>
        <FormControl id="name" isRequired>
            <FormLabel>Name</FormLabel>
            <Input name="name" autoFocus value={category.name} onChange={onFormInputChange} />
        </FormControl>
        <FormControl id="description">
            <FormLabel>Description</FormLabel>
            <Input name="description" value={category.description} onChange={onFormInputChange} />
        </FormControl>
    </form>
}
Example #3
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
TargetForm = ({ newTarget, onFormSubmit, targetSetter: setTarget }) => {
    const onFormChange = ev => {
        const target = ev.target;
        const name = target.name;
        let value = target.value;

        if ('tags' === name) {
            value = JSON.stringify(value.split(','));
        }

        setTarget({ ...newTarget, [name]: value });
    };

    return <div>
        <form onSubmit={onFormSubmit}>
            <FormControl id="name" isRequired>
                <FormLabel>Name</FormLabel>
                <Input name="name" placeholder="e.g. 127.0.0.1" onChange={onFormChange} isRequired autoFocus />
            </FormControl>
            <FormControl id="tags">
                <FormLabel>Tags</FormLabel>
                <Input name="tags" placeholder="e.g. linux,production" onChange={onFormChange} />
            </FormControl>
            <FormControl id="kind" isRequired>
                <FormLabel>Kind</FormLabel>
                <Select name="kind" onChange={onFormChange}>
                    {TargetKinds.map((targetKind, index) =>
                        <option key={index} value={targetKind.value}>{targetKind.description}</option>
                    )}
                </Select>
            </FormControl>
        </form>
    </div>
}
Example #4
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
NotesForm = ({ note, onFormSubmit, noteSetter: setNote }) => {
    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;

        setNote({ ...note, [name]: value });
    };

    return <form onSubmit={onFormSubmit}>
        <FormControl id="content" isRequired>
            <FormLabel>Content</FormLabel>
            <Textarea name="content" style={{ width: '100%' }} value={note.content}
                onChange={onFormInputChange} autoFocus /><br />
        </FormControl>
        <FormControl id="visibility" isRequired>
            <FormLabel>Visibility</FormLabel>
            <Select name="visibility" value={note.visibility} onChange={onFormInputChange}>
                <option value="private">Private</option>
                <option value="public">Public</option>
            </Select>
        </FormControl>
    </form>
}
Example #5
Source File: Form.js    From web-client with Apache License 2.0 6 votes vote down vote up
DocumentForm = ({ document, onFormSubmit, documentSetter: setNote, isEditForm = false }) => {
    const onFormInputChange = ev => {
        const target = ev.target;
        const name = target.name;
        const value = target.value;

        setNote({
            ...document, [name]: value
        });
    };

    return <form onSubmit={onFormSubmit}>
        <FormControl isRequired>
            <FormLabel htmlFor="title">Title</FormLabel>
            <Input type="text" name="title" id="title" value={document.title || ""} onChange={onFormInputChange} autoFocus />
        </FormControl>

        <FormControl isRequired>
            <FormLabel htmlFor="content">Content (markdown supported)</FormLabel>
            <MarkdownEditor name="content" style={{ width: '100%' }} required value={document.content || ""}
                onChange={onFormInputChange} /><br />
        </FormControl>

        <FormControl>
            <FormLabel htmlFor="visibility">Visibility</FormLabel>
            <Select name="visibility" id="visibility" value={document.visibility} onChange={onFormInputChange} required>
                <option value="private">Private</option>
                <option value="public">Public</option>
            </Select>
        </FormControl>

        <PrimaryButton type="submit">{isEditForm ? "Update" : "Create"}</PrimaryButton>
    </form>
}
Example #6
Source File: podcasts.js    From grandcast.fm with Apache License 2.0 6 votes vote down vote up
Podcasts = () => {
  const [getPodcasts, { data }] = useLazyQuery(PodcastSearchQuery)
  const { isSignedIn } = useAuth()
  const [searchString, setSearchString] = useState('')
  return (
    <Container>
      {!isSignedIn() && <SignIn />}
      {isSignedIn() && (
        <div>
          <FormControl id="podcastsearch">
            <FormLabel>Search podcasts</FormLabel>
            <Flex>
              <Input onChange={(e) => setSearchString(e.target.value)} />
              <Button
                ml={4}
                onClick={() =>
                  getPodcasts({ variables: { searchTerm: searchString } })
                }
              >
                Search
              </Button>
            </Flex>
          </FormControl>
          <VStack>
            {data?.podcastSearch.map((p) => {
              return <Podcast podcast={p} />
            })}
          </VStack>
        </div>
      )}
    </Container>
  )
}
Example #7
Source File: SignIn.js    From grandcast.fm with Apache License 2.0 6 votes vote down vote up
SignIn = () => {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const { signIn } = useAuth()

  function onSubmit(e) {
    e.preventDefault()
    signIn({ username, password })
  }

  return (
    <div>
      <FormControl b="1px" id="signin">
        <FormLabel m={4}>Sign In</FormLabel>
        <Input
          m={4}
          type="text"
          placeholder="username"
          onChange={(e) => setUsername(e.target.value)}
        ></Input>
        <Input
          m={4}
          type="password"
          placeholder="password"
          onChange={(e) => setPassword(e.target.value)}
        ></Input>
        <Button m={4} w="100%" onClick={onSubmit} type="submit">
          Log In
        </Button>
      </FormControl>
    </div>
  )
}
Example #8
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function FormControlWithLabel({
  label,
  labelFontSize = 'md',
  children,
  ...props
}) {
  return (
    <FormControl {...props}>
      <FormLabel fontSize={labelFontSize} color="brandGray.500" mb={2}>
        {label}
      </FormLabel>
      {children}
    </FormControl>
  )
}
Example #9
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function VotingInlineFormControl({
  htmlFor,
  label,
  tooltip,
  children,
  ...props
}) {
  return (
    <FormControl display="inline-flex" {...props}>
      {tooltip ? (
        <FormLabel htmlFor={htmlFor} color="muted" py={2} minW={32} w={32}>
          <Tooltip label={tooltip} placement="top" zIndex="tooltip">
            <Text
              as="span"
              borderBottomStyle="dotted"
              borderBottomWidth="1px"
              borderBottomColor="muted"
              cursor="help"
            >
              {label}
            </Text>
          </Tooltip>
        </FormLabel>
      ) : (
        <FormLabel htmlFor={htmlFor} color="muted" py={2} minW={32} w={32}>
          {label}
        </FormLabel>
      )}
      <Box w="md">{children}</Box>
    </FormControl>
  )
}
Example #10
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function OracleFormControl({label, children, ...props}) {
  return (
    <FormControl {...props}>
      <FormLabel color="brandGray.500" mb={2}>
        {label}
      </FormLabel>
      {children}
    </FormControl>
  )
}
Example #11
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function AdFormField({label, children, maybeError, ...props}) {
  return (
    <FormControl isInvalid={Boolean(maybeError)} {...props}>
      <Flex>
        <FormLabel color="muted" w="32" pt={2}>
          {label}
        </FormLabel>
        <Box w="sm">
          {children}
          <AdFormError>{maybeError}</AdFormError>
        </Box>
      </Flex>
    </FormControl>
  )
}
Example #12
Source File: Send.js    From web-client with Apache License 2.0 5 votes vote down vote up
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 #13
Source File: containers.js    From idena-web with MIT License 5 votes vote down vote up
export function AdMediaInput({
  name,
  value,
  label,
  description,
  fallbackSrc,
  maybeError,
  onChange,
  ...props
}) {
  const src = React.useMemo(
    () =>
      isValidImage(value) ? URL.createObjectURL(value) : value ?? adFallbackSrc,
    [value]
  )

  return (
    <FormControl isInvalid={Boolean(maybeError)}>
      <FormLabel htmlFor={name} m={0} p={0}>
        <VisuallyHiddenInput
          id={name}
          name={name}
          type="file"
          accept="image/png,image/jpg,image/jpeg"
          onChange={async e => {
            if (onChange) {
              const {files} = e.target
              if (files.length) {
                const [file] = files
                if (hasImageType(file)) {
                  onChange(file)
                }
              }
            }
          }}
          {...props}
        />
        <HStack spacing={4} align="center" cursor="pointer">
          <Box flexShrink={0}>
            {src !== adFallbackSrc ? (
              <AdImage src={src} width={70} />
            ) : (
              <Center
                bg="gray.50"
                borderWidth={1}
                borderColor="gray.016"
                rounded="lg"
                p="3"
              >
                <Image src={fallbackSrc} ignoreFallback boxSize="44px" />
              </Center>
            )}
          </Box>
          <Stack>
            <HStack>
              <LaptopIcon boxSize="5" color="blue.500" />
              <Text color="blue.500" fontWeight={500}>
                {label}
              </Text>
            </HStack>
            <SmallText>{description}</SmallText>
          </Stack>
        </HStack>
        <AdFormError>{maybeError}</AdFormError>
      </FormLabel>
    </FormControl>
  )
}
Example #14
Source File: containers.js    From idena-web with MIT License 5 votes vote down vote up
export function EditContactDrawer({contact, onRename, ...props}) {
  const {t} = useTranslation()

  const {updateInvite} = useInviteDispatch()

  const [isSubmitting, setIsSubmitting] = React.useState()

  const {id, firstName, lastName, receiver} = contact

  const contactName = `${firstName} ${lastName}`.trim() || receiver

  return (
    <Drawer {...props}>
      <DrawerHeader>
        <ContactDrawerHeader address={receiver} name={contactName} />
      </DrawerHeader>
      <Flex
        as="form"
        direction="column"
        flex={1}
        onSubmit={async e => {
          e.preventDefault()

          const {
            firstName: {value: firstNameValue},
            lastName: {value: lastNameValue},
          } = e.target.elements

          setIsSubmitting(true)
          await updateInvite(id, firstNameValue, lastNameValue)
          setIsSubmitting(false)

          onRename({firstName: firstNameValue, lastName: lastNameValue})
        }}
      >
        <DrawerBody>
          <Stack isInline spacing={4} mt={5}>
            <FormControl>
              <FormLabel>{t('First name')}</FormLabel>
              <Input id="firstName" defaultValue={firstName} />
            </FormControl>
            <FormControl>
              <FormLabel>{t('Last name')}</FormLabel>
              <Input id="lastName" defaultValue={lastName} />
            </FormControl>
          </Stack>
        </DrawerBody>
        <DrawerFooter>
          <PrimaryButton type="submit" ml="auto" isLoading={isSubmitting}>
            {t('Save')}
          </PrimaryButton>
        </DrawerFooter>
      </Flex>
    </Drawer>
  )
}
Example #15
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function ReceiveDrawer({isOpen, onClose, address}) {
  const {t} = useTranslation()
  const {onCopy, hasCopied} = useClipboard(address)

  const size = useBreakpointValue(['lg', 'md'])
  const qrSize = useBreakpointValue(['170px', '128px'])
  const variant = useBreakpointValue(['outlineMobile', 'outline'])

  return (
    <Drawer isOpen={isOpen} onClose={onClose}>
      <DrawerHeader mb={[12, 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="blue.012"
          >
            <ReceiveIcon boxSize={6} color="blue.500" />
          </Flex>
          <Heading
            order={[1, 2]}
            color="brandGray.500"
            fontSize={['base', 'lg']}
            fontWeight={[['bold', 500]]}
            lineHeight="base"
            mt={[0, 4]}
          >
            {t(`Receive iDNA`)}
          </Heading>
        </Flex>
      </DrawerHeader>
      <DrawerBody>
        <Stack spacing={[12, 5]}>
          <QRCode
            value={address}
            style={{height: qrSize, width: qrSize, margin: '0 auto'}}
          />
          <FormControl>
            <Flex justify="space-between">
              <FormLabel fontSize={['base', 'md']}>{t('Address')}</FormLabel>
              {hasCopied ? (
                <FormLabel fontSize={['base', 'md']}>{t('Copied!')}</FormLabel>
              ) : (
                <FlatButton onClick={onCopy} mb={2.5}>
                  {t('Copy')}
                </FlatButton>
              )}
            </Flex>
            <Input value={address} size={size} variant={variant} isDisabled />
          </FormControl>
        </Stack>
      </DrawerBody>
    </Drawer>
  )
}
Example #16
Source File: ModalDialog.js    From web-client with Apache License 2.0 5 votes vote down vote up
ReportModalDialog = ({ isOpen, onSubmit, onCancel }) => {
    const fileRef = useRef();

    const emptyReportTemplate = {
        version_name: "",
        version_description: null,
        resultFile: null,
    }
    const [reportTemplate, setReportTemplate] = useState(emptyReportTemplate)

    const onCreateReportFormSubmit = ev => {
        ev.preventDefault();

        const formData = new FormData();
        formData.append('version_name', reportTemplate.version_name);
        formData.append('version_description', reportTemplate.version_description);
        formData.append('resultFile', fileRef.current.files[0]);

        secureApiFetch(`/reports/templates`, { method: 'POST', body: formData })
            .then(() => {
                onSubmit();
                actionCompletedToast(`The report template "${reportTemplate.version_name}" has been added.`);
            })
            .catch(err => {
                errorToast(err);
            })
            .finally(() => {
                setReportTemplate(emptyReportTemplate)
            })
    }

    const onFormChange = ev => {
        setReportTemplate({ ...reportTemplate, [ev.target.name]: ev.target.value })
    }

    return <Modal size="xl" isOpen={isOpen} onClose={onCancel}>
        <ModalOverlay />
        <ModalContent>
            <ModalHeader><h4>New report template details</h4></ModalHeader>
            <ModalCloseButton />
            <ModalBody>
                <form id="reportTemplateForm" onSubmit={onCreateReportFormSubmit}>
                    <FormControl isRequired>
                        <FormLabel>Version name</FormLabel>
                        <Input type="text" name="version_name" onChange={onFormChange} autoFocus />
                    </FormControl>
                    <FormControl>
                        <FormLabel>Version description</FormLabel>
                        <Input type="text" name="version_description" onChange={onFormChange} />
                    </FormControl>
                    <FormControl isRequired>
                        <FormLabel>File</FormLabel>
                        <Input type="file" ref={fileRef} name="resultFile" onChange={onFormChange} />
                    </FormControl>
                </form>
            </ModalBody>

            <ModalFooter>
                <Button onClick={onCancel} mr={3}>Cancel</Button>
                <Button form="reportTemplateForm" type="submit" colorScheme="blue">Save</Button>
            </ModalFooter>
        </ModalContent>
    </Modal>
}
Example #17
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function SpoilInviteDrawer({onSuccess, onFail, ...props}) {
  const {t} = useTranslation()

  return (
    <Drawer {...props}>
      <DrawerHeader mb={0}>
        <Center flexDirection="column">
          <Avatar address={dummyAddress} />
          <Heading
            fontSize="lg"
            fontWeight={500}
            color="gray.500"
            mt="4"
            mb={0}
          >
            {t('Spoil invitation code')}
          </Heading>
        </Center>
      </DrawerHeader>
      <DrawerBody mt="6">
        <Text fontSize="md" mb={6}>
          {t(
            `Spoil invitations that are shared publicly. This will encourage people to share invitations privately and prevent bots from collecting invitation codes.`
          )}
        </Text>
        <Stack spacing="6">
          <form
            id="spoilInvite"
            onSubmit={async e => {
              e.preventDefault()

              const code = new FormData(e.target).get('code').trim()

              const randomPrivateKey = generatePrivateKey()

              try {
                const hash = await sendRawTx(
                  new Transaction()
                    .fromHex(
                      await getRawTx(
                        TxType.ActivationTx,
                        privateKeyToAddress(code),
                        privateKeyToAddress(randomPrivateKey),
                        0,
                        0,
                        privateKeyToPublicKey(randomPrivateKey)
                      )
                    )
                    .sign(code)
                    .toHex(true)
                )

                // eslint-disable-next-line no-unused-expressions
                onSuccess?.(hash)
              } catch (error) {
                // eslint-disable-next-line no-unused-expressions
                onFail?.(error)
              }
            }}
          >
            <FormControl>
              <FormLabel>{t('Invitation code')}</FormLabel>
              <Input name="code" placeholder={t('Invitation code to spoil')} />
            </FormControl>
          </form>
          <Text fontSize="md">
            {t(
              `When you click 'Spoil' the invitation code will be activated by a random address and wasted.`
            )}
          </Text>
        </Stack>
      </DrawerBody>
      <DrawerFooter>
        <PrimaryButton type="submit" form="spoilInvite">
          {t('Spoil invite')}
        </PrimaryButton>
      </DrawerFooter>
    </Drawer>
  )
}
Example #18
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function ActivateInvitationDialog({onClose, ...props}) {
  const {t} = useTranslation()

  const [code, setCode] = useState('')

  const [{isMining, isSuccess}, {activateInvite}] = useInviteActivation()

  const size = useBreakpointValue(['lg', 'md'])

  useEffect(() => {
    if (!isMining && isSuccess) onClose()
  }, [isMining, isSuccess, onClose])

  return (
    <Dialog title={t('Invite activation')} onClose={onClose} {...props}>
      <DialogBody mb={0}>
        <Box
          mt={4}
          as="form"
          onSubmit={async e => {
            e.preventDefault()
            await activateInvite(code)
          }}
        >
          <FormControl>
            <Stack spacing={[2, 3]}>
              <Flex justify="space-between" align="center">
                <FormLabel htmlFor="code" p={0} m={0} fontSize={['base', 'md']}>
                  {t('Enter invitation code')}
                </FormLabel>
                <PasteButton
                  isDisabled={isMining}
                  onClick={() =>
                    navigator.clipboard.readText().then(text => setCode(text))
                  }
                />
              </Flex>
              <Input
                size={size}
                value={code}
                isDisabled={isMining}
                onChange={e => setCode(e.target.value)}
              />
            </Stack>
          </FormControl>
          <Flex mt={4} align="center" justifyContent="flex-end">
            <SecondaryButton
              display={['none', 'initial']}
              onClick={onClose}
              mr={2}
            >
              {t('Cancel')}
            </SecondaryButton>
            <PrimaryButton
              size={size}
              w={['100%', 'auto']}
              type="submit"
              isLoading={isMining}
              loadingText={t('Mining...')}
            >
              {t('Activate invitation')}
            </PrimaryButton>
          </Flex>
        </Box>
      </DialogBody>
    </Dialog>
  )
}
Example #19
Source File: Preferences.js    From web-client with Apache License 2.0 5 votes vote down vote up
UserPreferences = () => {

    const user = Auth.getLoggedInUser();
    user.preferences = initialiseUserPreferences(user);

    const timezones = CountriesTimezones.getAllTimezones();
    const timezoneKeys = Object.keys(timezones).sort();

    const { setTheme } = useContext(ThemeContext);

    const { setColorMode } = useColorMode();

    const [formValues, setFormValues] = useState({
        timezone: user.timezone,
        theme: user.preferences["web-client.theme"]
    });

    const updateFormValues = ev => {
        setFormValues({ ...formValues, [ev.target.name]: ev.target.value });
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        user.timezone = formValues.timezone;
        user.preferences = { ...initialiseUserPreferences(user), "web-client.theme": formValues.theme };

        secureApiFetch(`/users/${user.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ timezone: formValues.timezone, preferences: user.preferences })
        })
            .then(() => {
                setTheme(theme => {
                    setThemeColors(formValues.theme);
                    setColorMode(formValues.theme);
                    return formValues.theme;
                });

                localStorage.setItem('user', JSON.stringify(user));

                actionCompletedToast("Your preferences have been saved.");
            })
            .catch(err => console.error(err));
    }

    return <>
        <PageTitle value="Preferences" />
        <div className='heading'>
            <Breadcrumb />
        </div>
        <Title type='User' title='Preferences' icon={<IconPreferences />} />
        <form onSubmit={onFormSubmit}>
            <FormControl>
                <FormLabel>Theme</FormLabel>
                <Select name="theme" onChange={updateFormValues} defaultValue={formValues.theme || "dark"}>
                    <option value="dark">Dark</option>
                    <option value="light">Light</option>
                </Select>
            </FormControl>
            <FormControl>
                <FormLabel>Timezone</FormLabel>
                <Select name="timezone" onChange={updateFormValues} defaultValue={user.timezone}>
                    {timezoneKeys.map((key, index) =>
                        <option key={index} value={timezones[key].name}>{timezones[key].name}</option>
                    )}
                </Select>
            </FormControl>

            <Primary type="submit">Save</Primary>
        </form>
    </>
}
Example #20
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function DeleteFlipDrawer({hash, cover, onDelete, ...props}) {
  const {t} = useTranslation()
  return (
    <Drawer {...props}>
      <DrawerHeader>
        <Flex
          align="center"
          justify="center"
          bg="red.012"
          h={12}
          w={12}
          rounded="xl"
        >
          <DeleteIcon boxSize={6} color="red.500" />
        </Flex>
        <Heading fontSize="lg" fontWeight={500} color="brandGray.500" mt={4}>
          {t('Delete flip')}
        </Heading>
      </DrawerHeader>
      <DrawerBody>
        <Text color="brandGray.500" fontSize="md">
          {t('Deleted flip will be moved to the drafts.')}
        </Text>
        <FlipImage
          src={cover}
          size={160}
          objectFit="cover"
          mx="auto"
          mt={8}
          mb={38}
          rounded="lg"
        />
        <FormControl mb={6}>
          <FormLabel htmlFor="hashInput" mb={2}>
            {t('Flip hash')}
          </FormLabel>
          <Input
            id="hashInput"
            h={8}
            borderColor="gray.100"
            lineHeight={rem(18)}
            px={3}
            pt={1.5}
            pb={2}
            mb={2}
            value={hash}
            isReadOnly
            _readOnly={{
              bg: 'gray.50',
              borderColor: 'gray.100',
              color: 'muted',
            }}
          />
        </FormControl>
        <PrimaryButton
          colorScheme="red"
          display="flex"
          ml="auto"
          _hover={{
            bg: 'rgb(227 60 60)',
          }}
          onClick={onDelete}
        >
          {t('Delete')}
        </PrimaryButton>
      </DrawerBody>
    </Drawer>
  )
}
Example #21
Source File: ProductAddEdit.js    From react-sample-projects with MIT License 4 votes vote down vote up
ProductAddEdit = () => {
  const categories = useSelector(state => state.product.categories);
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const initialValues = {
    title: '',
    price: '',
    category: '',
    description: '',
    image: '',
  };

  const validationSchema = yup.object({
    title: yup.string().required(),
    price: yup.number().required(),
    category: yup.string().required(),
    description: yup.string().required(),
    image: yup.string().url().required(),
  });

  const onFormSubmit = (values, actions) => {
    actions.setSubmitting(false);
    dispatch(addNewProduct(values));
    navigate('/');
  };

  useEffect(() => {
    dispatch(fetchCategories());
    return () => {};
  }, [dispatch]);

  return (
    <Box boxShadow="base" m={'auto'} width="clamp(300px, 60%, 100%)">
      <Formik
        initialValues={initialValues}
        onSubmit={onFormSubmit}
        validationSchema={validationSchema}
      >
        {props => (
          <Form noValidate>
            <VStack p={3} m="3">
              <Box fontWeight="semibold" mt="1" as="h2" textAlign="left">
                Add Product
              </Box>

              <Field name="title">
                {({ field, form }) => (
                  <FormControl
                    isRequired
                    isInvalid={form.errors.title && form.touched.title}
                  >
                    <FormLabel htmlFor="title">Enter Title</FormLabel>
                    <Input
                      {...field}
                      type="text"
                      id="title"
                      placeholder="Enter Title"
                    />
                    <ErrorMessage
                      name="title"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>

              <Field name="price">
                {({ field, form }) => (
                  <FormControl
                    isRequired
                    isInvalid={form.errors.price && form.touched.price}
                  >
                    <FormLabel>Enter price</FormLabel>
                    <Input type="number" placeholder="Enter price" {...field} />
                    <ErrorMessage
                      name="price"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="category">
                {({ field, form }) => (
                  <FormControl
                    name="category"
                    isRequired
                    isInvalid={form.errors.category && form.touched.category}
                  >
                    <FormLabel>Enter category</FormLabel>
                    <Select placeholder="Select category" {...field}>
                      {categories.map((category, index) => (
                        <option key={index}>{category}</option>
                      ))}
                    </Select>
                    <ErrorMessage
                      name="category"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="description">
                {({ field, form }) => (
                  <FormControl
                    name="description"
                    isRequired
                    isInvalid={
                      form.errors.description && form.touched.description
                    }
                  >
                    <FormLabel>Enter description</FormLabel>
                    <Textarea
                      {...field}
                      id="description"
                      placeholder="Enter description"
                    ></Textarea>
                    <ErrorMessage
                      name="description"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Field name="image">
                {({ field, form }) => (
                  <FormControl
                    name="image"
                    isRequired
                    isInvalid={form.errors.image && form.touched.image}
                  >
                    <FormLabel>Enter image</FormLabel>
                    <Input
                      type="url"
                      placeholder="Enter image url"
                      {...field}
                    />

                    <ErrorMessage
                      name="image"
                      component={FormErrorMessage}
                    ></ErrorMessage>
                  </FormControl>
                )}
              </Field>
              <Button
                mt={4}
                colorScheme="teal"
                type="submit"
                isLoading={props.isSubmitting}
              >
                Add Product
              </Button>
            </VStack>
          </Form>
        )}
      </Formik>
    </Box>
  );
}
Example #22
Source File: containers.js    From idena-web with MIT License 4 votes vote down vote up
export function BurnDrawer({ad, onBurn, ...props}) {
  const {t} = useTranslation()

  const [{address}] = useIdentity()

  const balance = useBalance(address)

  const failToast = useFailToast()

  const [isPending, {on: setIsPendingOn, off: setIsPendingOff}] = useBoolean()

  const {submit} = useBurnAd({
    onMined: React.useCallback(() => {
      onBurn()
      setIsPendingOff()
    }, [onBurn, setIsPendingOff]),
    onError: React.useCallback(
      error => {
        failToast(error)
        setIsPendingOff()
      },
      [failToast, setIsPendingOff]
    ),
  })

  const formatDna = useFormatDna()

  const competingAds = useCompetingAds(ad.cid, new AdTarget(ad))

  const competitorCount = competingAds?.length
  const maxCompetitor = competingAds?.sort((a, b) => b.amount - a.amount)[0]

  return (
    <AdDrawer isMining={isPending} {...props}>
      <DrawerHeader>
        <Stack spacing={4}>
          <FillCenter
            alignSelf="flex-start"
            bg="blue.012"
            w={12}
            minH={12}
            rounded="xl"
          >
            <AdsIcon boxSize={6} color="blue.500" />
          </FillCenter>
          <Heading fontSize="lg" fontWeight={500}>
            {t('Burn')}
          </Heading>
        </Stack>
      </DrawerHeader>
      <DrawerBody overflowY="auto" mx={-6} mb={10}>
        <Stack spacing="6" color="brandGray.500" fontSize="md" p={6} pt={0}>
          <Stack spacing="3">
            <Text>{t('Burn iDNA to make your ad visible.')}</Text>
          </Stack>

          <Stack spacing="6" bg="gray.50" p={6} rounded="lg">
            <Stack isInline spacing={5} align="flex-start">
              <AdImage src={adImageThumbSrc(ad)} w="10" />
              <Box>
                <Text fontWeight={500}>{ad.title}</Text>
                <ExternalLink href={ad.url} maxW="48">
                  {ad.url}
                </ExternalLink>
              </Box>
            </Stack>
            <Stack spacing={3}>
              <HDivider />
              <InlineAdStatGroup labelWidth="24">
                <InlineAdStat
                  label={t('Competitors')}
                  value={String(competitorCount)}
                />
                <InlineAdStat
                  label={t('Max bid')}
                  value={maxCompetitor ? formatDna(maxCompetitor.amount) : '--'}
                />
              </InlineAdStatGroup>
              <HDivider />
              <InlineAdStatGroup labelWidth="20">
                <SmallInlineAdStat label={t('Language')} value={ad.language} />
                <SmallInlineAdStat label={t('Min stake')} value={ad.stake} />
                <SmallInlineAdStat label={t('Min age')} value={ad.age} />
                <SmallInlineAdStat label={t('OS')} value={ad.os} />
              </InlineAdStatGroup>
            </Stack>
          </Stack>
          <form
            id="burnForm"
            onSubmit={e => {
              e.preventDefault()

              const amount = Number(new FormData(e.target).get('amount'))

              if (amount > 0 && amount < balance) {
                setIsPendingOn()
                submit({ad, amount})
              } else {
                failToast(
                  amount > 0
                    ? t('Insufficient funds to burn {{amount}}', {
                        amount: formatDna(amount),
                      })
                    : t('Invalid amount')
                )
              }
            }}
          >
            <FormControl>
              <Stack spacing={3}>
                <FormLabel htmlFor="amount" mb={0}>
                  {t('Amount, iDNA')}
                </FormLabel>
                <AdNumberInput
                  name="amount"
                  min={0}
                  max={Number.MAX_SAFE_INTEGER}
                  addon={t('iDNA')}
                />
              </Stack>
            </FormControl>
          </form>
        </Stack>
      </DrawerBody>
      <DrawerFooter>
        <PrimaryButton
          type="submit"
          form="burnForm"
          isLoading={isPending}
          loadingText={t('Mining...')}
        >
          {t('Burn')}
        </PrimaryButton>
      </DrawerFooter>
    </AdDrawer>
  )
}
Example #23
Source File: ModalDialog.js    From web-client with Apache License 2.0 4 votes vote down vote up
ReportVersionModalDialog = ({ projectId, isOpen, onSubmit, onCancel }) => {
    const defaultFormValues = { reportTemplateId: 0, name: "", description: "" };
    const [formValues, setFormValues] = useState(defaultFormValues);
    const [templates] = useFetch('/reports/templates');

    const onFormValueChange = ev => {
        ev.preventDefault();

        setFormValues({ ...formValues, [ev.target.name]: ev.target.value });
    };

    const beforeCancelCallback = ev => {
        setFormValues(defaultFormValues);
        onCancel(ev);
    }

    const onFormSubmit = ev => {
        ev.preventDefault();

        const params = {
            projectId: projectId,
            reportTemplateId: formValues.reportTemplateId,
            name: formValues.name,
            description: formValues.description
        };

        secureApiFetch(`/reports`, { method: 'POST', body: JSON.stringify(params) })
            .then(() => {
                onSubmit();
                actionCompletedToast(`The report version "${formValues.name}" has been added.`);
            })
            .catch(err => {
                console.error(err);
            })
            .finally(() => {
                setFormValues(defaultFormValues)
            })
    }

    useEffect(() => {
        if (templates !== null && templates.length > 0) {
            setFormValues((prev) => ({ ...prev, reportTemplateId: templates[0].id }))
        }
    }, [templates]);

    return <Modal size="xl" isOpen={isOpen} onClose={beforeCancelCallback}>
        <ModalOverlay />
        <ModalContent>
            <ModalHeader><HStack><TargetIcon style={{ width: '24px' }} /> <h4>New report version details</h4></HStack></ModalHeader>
            <ModalCloseButton />
            <ModalBody>
                <form id="reportVersionReportForm" onSubmit={onFormSubmit} className="crud" style={{ marginTop: '20px' }}>
                    <FormControl isRequired>
                        <FormLabel>Template</FormLabel>
                        {templates && <Select name="reportTemplateId" value={formValues.reportTemplateId} onChange={onFormValueChange}>
                            {templates.map(template => <option key={template.id} value={template.id}>{template.version_name}</option>)}
                        </Select>}
                    </FormControl>

                    <FormControl isRequired>
                        <FormLabel>Name</FormLabel>
                        <Input type="text" name="name" value={formValues.name} onChange={onFormValueChange}
                            placeholder="eg 1.0, 202103" autoFocus />
                    </FormControl>

                    <FormControl isRequired>
                        <FormLabel>Description</FormLabel>
                        <Input type="text" name="description" value={formValues.description}
                            onChange={onFormValueChange}
                            placeholder="eg Initial version, Draft"
                        />
                    </FormControl>
                </form>
            </ModalBody>

            <ModalFooter>
                <Button onClick={beforeCancelCallback} mr={3}>Cancel</Button>
                <Button form="reportVersionReportForm" type="submit" colorScheme="blue">Save</Button>
            </ModalFooter>
        </ModalContent>
    </Modal>
}
Example #24
Source File: node.js    From idena-web with MIT License 4 votes vote down vote up
function Settings() {
  const {t} = useTranslation()
  const {addNotification} = useNotificationDispatch()
  const settingsState = useSettingsState()
  const {saveConnection} = useSettingsDispatch()

  const size = useBreakpointValue(['lg', 'md'])
  const flexDirection = useBreakpointValue(['column', 'row'])
  const flexJustify = useBreakpointValue(['flex-start', 'space-between'])

  const [state, setState] = useState({
    url: settingsState.url || '',
    apiKey: settingsState.apiKey || '',
  })

  const [nodeProvider, setNodeProvider] = useState('')

  useEffect(() => {
    setState({url: settingsState.url, apiKey: settingsState.apiKey})
  }, [settingsState])

  const notify = () =>
    addNotification({
      title: 'Settings updated',
      body: `Connected to url ${state.url}`,
    })

  useEffect(() => {
    async function check() {
      try {
        const result = await checkKey(settingsState.apiKey)
        const provider = await getProvider(result.provider)
        setNodeProvider(provider.data.ownerName)
      } catch (e) {}
    }

    if (settingsState.apiKeyState === ApiKeyStates.OFFLINE) check()
  }, [settingsState.apiKeyState, settingsState.url, settingsState.apiKey])

  return (
    <SettingsLayout title={t('Node')}>
      <Stack
        spacing={5}
        mt={[3, 8]}
        width={['100%', '480px']}
        position="relative"
      >
        {settingsState.apiKeyState === ApiKeyStates.RESTRICTED && (
          <Alert
            status="error"
            bg="red.010"
            borderWidth="1px"
            borderColor="red.050"
            fontWeight={500}
            rounded="md"
            px={3}
            py={2}
          >
            <AlertIcon name="info" color="red.500" size={5} mr={3}></AlertIcon>
            {t(
              'The shared node access is restricted. You cannot use the node for the upcoming validation ceremony.'
            )}
          </Alert>
        )}
        {settingsState.apiKeyState === ApiKeyStates.OFFLINE &&
          !!settingsState.url &&
          !!settingsState.apiKey && (
            <Alert
              status="error"
              bg="red.010"
              borderWidth="1px"
              borderColor="red.050"
              fontWeight={500}
              rounded="md"
              px={3}
              py={2}
            >
              <AlertIcon name="info" color="red.500" size={5} mr={3} />
              <Text>
                {nodeProvider
                  ? t(
                      'This node is unavailable. Please contact the node owner:',
                      {
                        nsSeparator: 'null',
                      }
                    )
                  : t('Node is unavailable.')}{' '}
                {nodeProvider && (
                  <Link
                    color="#578fff"
                    href={`https://t.me/${nodeProvider}`}
                    target="_blank"
                    ml={1}
                  >
                    {nodeProvider}
                  </Link>
                )}
              </Text>
            </Alert>
          )}
        <Flex display={['none', 'flex']} justify="space-between">
          <Heading as="h1" fontSize="lg" fontWeight={500} textAlign="start">
            {t('Node settings')}
          </Heading>
          <Box mt="3px">
            <Link
              color="#578fff"
              fontSize="13px"
              fontWeight="500"
              height="17px"
              href="/node/rent"
            >
              {t('Rent a new node')}
              <ChevronDownIcon boxSize={4} transform="rotate(-90deg)" />
            </Link>
          </Box>
        </Flex>
        <FormControl
          as={Flex}
          direction={flexDirection}
          justify={flexJustify}
          mt={[0, 5]}
        >
          <Flex justify="space-between">
            <FormLabel
              fontSize={['base', 'md']}
              color={['brandGray.500', 'muted']}
              fontWeight={[500, 400]}
              mb={[2, 0]}
              lineHeight={[6, 8]}
            >
              {t('Shared node URL')}
            </FormLabel>
            <Box display={['block', 'none']}>
              <Link
                fontSize="16px"
                fontWeight="500"
                color="#578fff"
                href="/node/rent"
              >
                {t('Rent a new node')}
              </Link>
            </Box>
          </Flex>
          <Input
            id="url"
            w={['100%', '360px']}
            size={size}
            value={state.url}
            onChange={e => setState({...state, url: e.target.value})}
          />
        </FormControl>
        <FormControl as={Flex} direction={flexDirection} justify={flexJustify}>
          <FormLabel
            fontSize={['base', 'md']}
            color={['brandGray.500', 'muted']}
            fontWeight={[500, 400]}
            mb={[2, 0]}
            lineHeight={[6, 8]}
          >
            {t('Node API key')}
          </FormLabel>
          <PasswordInput
            id="key"
            w={['100%', '360px']}
            size={size}
            value={state.apiKey}
            onChange={e => setState({...state, apiKey: e.target.value})}
          />
        </FormControl>

        {settingsState.apiKeyState === ApiKeyStates.ONLINE && (
          <Alert
            status="warning"
            bg="warning.020"
            borderWidth="1px"
            borderColor="warning.100"
            fontWeight={500}
            rounded="md"
            px={3}
            py={2}
          >
            <AlertIcon size={5} mr={3} colo="warning.500"></AlertIcon>
            {t(
              'Please do not use the API key on multiple devices at the same time as this will cause the validation failure.'
            )}
          </Alert>
        )}

        <Flex justify="space-between">
          <PrimaryButton
            size={size}
            w={['100%', 'auto']}
            onClick={() => {
              saveConnection(state.url, state.apiKey, true)
              notify()
            }}
            ml="auto"
          >
            {t('Save')}
          </PrimaryButton>
        </Flex>
      </Stack>
    </SettingsLayout>
  )
}
Example #25
Source File: Form.js    From web-client with Apache License 2.0 4 votes vote down vote up
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 >
}
Example #26
Source File: ChallengeSubmission.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
// ToDo. on-line form validation
export default function ChallengeSubmission({ challenge, serverUrl, address, userProvider }) {
  const { challengeId } = useParams();
  const history = useHistory();
  const toast = useToast({ position: "top", isClosable: true });
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [deployedUrl, setDeployedUrl] = useState("");
  const [contractUrl, setContractUrl] = useState("");
  const [hasErrorField, setHasErrorField] = useState({ deployedUrl: false, contractUrl: false });

  const onFinish = async () => {
    if (!deployedUrl || !contractUrl) {
      toast({
        status: "error",
        description: "Both fields are required",
      });
      return;
    }

    if (!isValidUrl(deployedUrl) || !isValidUrl(contractUrl)) {
      toast({
        status: "error",
        title: "Please provide a valid URL",
        description: "Valid URLs start with http:// or https://",
      });

      setHasErrorField({
        deployedUrl: !isValidUrl(deployedUrl),
        contractUrl: !isValidUrl(contractUrl),
      });

      return;
    }

    setIsSubmitting(true);

    let signMessage;
    try {
      const signMessageResponse = await axios.get(serverUrl + `/sign-message`, {
        params: {
          messageId: "challengeSubmit",
          address,
          challengeId,
        },
      });

      signMessage = JSON.stringify(signMessageResponse.data);
    } catch (error) {
      toast({
        description: "Can't get the message to sign. Please try again",
        status: "error",
      });
      setIsSubmitting(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        status: "error",
        description: "The signature was cancelled",
      });
      console.error(error);
      setIsSubmitting(false);
      return;
    }

    try {
      await axios.post(
        serverUrl + serverPath,
        {
          challengeId,
          deployedUrl,
          contractUrl,
          signature,
        },
        {
          headers: {
            address,
          },
        },
      );
    } catch (error) {
      toast({
        status: "error",
        description: "Submission Error. Please try again.",
      });
      console.error(error);
      setIsSubmitting(false);

      return;
    }

    toast({
      status: "success",
      description: "Challenge submitted!",
    });
    setIsSubmitting(false);
    history.push("/portfolio");
  };

  if (!address) {
    return (
      <Text color="orange.400" className="warning" align="center">
        Connect your wallet to submit this Challenge.
      </Text>
    );
  }

  return (
    <div>
      <Heading as="h2" size="md" mb={4}>
        {challenge.label}
      </Heading>
      {challenge.isDisabled ? (
        <Text color="orange.400" className="warning">
          This challenge is disabled.
        </Text>
      ) : (
        <form name="basic" autoComplete="off">
          <FormControl id="deployedUrl" isRequired>
            <FormLabel>
              Deployed URL{" "}
              <Tooltip label="Your deployed challenge URL on surge / s3 / ipfs ">
                <QuestionOutlineIcon ml="2px" />
              </Tooltip>
            </FormLabel>
            <Input
              type="text"
              name="deployedUrl"
              value={deployedUrl}
              placeholder="https://your-site.surge.sh"
              onChange={e => {
                setDeployedUrl(e.target.value);
                if (hasErrorField.deployedUrl) {
                  setHasErrorField(prevErrorsFields => ({
                    ...prevErrorsFields,
                    deployedUrl: false,
                  }));
                }
              }}
              borderColor={hasErrorField.deployedUrl && "red.500"}
            />
          </FormControl>

          <FormControl id="contractUrl" isRequired mt={4}>
            <FormLabel>
              Etherscan Contract URL{" "}
              <Tooltip label="Your verified contract URL on Etherscan">
                <QuestionOutlineIcon ml="2px" />
              </Tooltip>
            </FormLabel>
            <Input
              type="text"
              name="contractUrl"
              value={contractUrl}
              placeholder="https://etherscan.io/address/your-contract-address"
              onChange={e => {
                setContractUrl(e.target.value);
                if (hasErrorField.contractUrl) {
                  setHasErrorField(prevErrorsFields => ({
                    ...prevErrorsFields,
                    contractUrl: false,
                  }));
                }
              }}
              borderColor={hasErrorField.contractUrl && "red.500"}
            />
          </FormControl>

          <div className="form-item">
            <Button colorScheme="blue" onClick={onFinish} isLoading={isSubmitting} mt={4} isFullWidth>
              Submit
            </Button>
          </div>
        </form>
      )}
    </div>
  );
}
Example #27
Source File: BuilderProfileCard.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
BuilderProfileCard = ({ builder, mainnetProvider, isMyProfile, userProvider, fetchBuilder, userRole }) => {
  const address = useUserAddress(userProvider);
  const ens = useDisplayAddress(mainnetProvider, builder?.id);
  const [updatedSocials, setUpdatedSocials] = useState({});
  const [isUpdatingReachedOutFlag, setIsUpdatingReachedOutFlag] = useState(false);
  const [isUpdatingSocials, setIsUpdatingSocials] = useState(false);
  const { isOpen, onOpen, onClose } = useDisclosure();
  const { hasCopied, onCopy } = useClipboard(builder?.id);
  const { borderColor, secondaryFontColor } = useCustomColorModes();
  const shortAddress = ellipsizedAddress(builder?.id);
  const hasEns = ens !== shortAddress;

  const toast = useToast({ position: "top", isClosable: true });
  const toastVariant = useColorModeValue("subtle", "solid");

  const joinedDate = new Date(builder?.creationTimestamp);
  const joinedDateDisplay = joinedDate.toLocaleString("default", { month: "long" }) + " " + joinedDate.getFullYear();

  // INFO: conditional chaining and coalescing didn't work when also checking the length
  const hasProfileLinks = builder?.socialLinks ? Object.keys(builder.socialLinks).length !== 0 : false;

  const isAdmin = userRole === USER_ROLES.admin;

  useEffect(() => {
    if (builder) {
      setUpdatedSocials(builder.socialLinks ?? {});
    }
  }, [builder]);

  const handleUpdateSocials = async () => {
    setIsUpdatingSocials(true);

    // Avoid sending socials with empty strings.
    const socialLinkCleaned = Object.fromEntries(Object.entries(updatedSocials).filter(([_, value]) => !!value));

    const invalidSocials = validateSocials(socialLinkCleaned);
    if (invalidSocials.length !== 0) {
      toast({
        description: `The usernames for the following socials are not correct: ${invalidSocials
          .map(([social]) => social)
          .join(", ")}`,
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signMessage;
    try {
      signMessage = await getUpdateSocialsSignMessage(address);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    try {
      await postUpdateSocials(address, signature, socialLinkCleaned);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingSocials(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update your socials. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    toast({
      description: "Your social links have been updated",
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingSocials(false);
    onClose();
  };

  const handleUpdateReachedOutFlag = async reachedOut => {
    setIsUpdatingReachedOutFlag(true);

    let signMessage;
    try {
      signMessage = await getUpdateReachedOutFlagSignMessage(builder.id, reachedOut);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    try {
      await postUpdateReachedOutFlag(address, builder.id, reachedOut, signature);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingReachedOutFlag(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update the reached out flag. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    toast({
      description: 'Updated "reached out" flag successfully',
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingReachedOutFlag(false);
  };

  return (
    <>
      <BuilderProfileCardSkeleton isLoaded={!!builder}>
        {() => (
          /* delay execution */
          <Flex
            borderRadius="lg"
            borderColor={borderColor}
            borderWidth={1}
            justify={{ base: "space-around", xl: "center" }}
            direction={{ base: "row", xl: "column" }}
            p={4}
            pb={6}
            maxW={{ base: "full", lg: "50%", xl: 60 }}
            margin="auto"
          >
            <Link as={RouteLink} to={`/builders/${builder.id}`}>
              <QRPunkBlockie
                withQr={false}
                address={builder.id?.toLowerCase()}
                w={52}
                borderRadius="lg"
                margin="auto"
              />
            </Link>
            <Flex alignContent="center" direction="column" mt={4}>
              {hasEns ? (
                <>
                  <Text fontSize="2xl" fontWeight="bold" textAlign="center">
                    {ens}
                  </Text>
                  <Text textAlign="center" mb={4} color={secondaryFontColor}>
                    {shortAddress}{" "}
                    <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                      <CopyIcon cursor="pointer" onClick={onCopy} />
                    </Tooltip>
                  </Text>
                </>
              ) : (
                <Text fontSize="2xl" fontWeight="bold" textAlign="center" mb={8}>
                  {shortAddress}{" "}
                  <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                    <CopyIcon cursor="pointer" onClick={onCopy} />
                  </Tooltip>
                </Text>
              )}
              {isAdmin && (
                <Center mb={4}>
                  {builder.reachedOut ? (
                    <Badge variant="outline" colorScheme="green" alignSelf="center">
                      Reached Out
                    </Badge>
                  ) : (
                    <Button
                      colorScheme="green"
                      size="xs"
                      onClick={() => handleUpdateReachedOutFlag(true)}
                      isLoading={isUpdatingReachedOutFlag}
                      alignSelf="center"
                    >
                      Mark as reached out
                    </Button>
                  )}
                </Center>
              )}
              <Divider mb={6} />
              {hasProfileLinks ? (
                <Flex mb={4} justifyContent="space-evenly" alignItems="center">
                  {Object.entries(builder.socialLinks)
                    .sort(bySocialWeight)
                    .map(([socialId, socialValue]) => (
                      <SocialLink id={socialId} value={socialValue} />
                    ))}
                </Flex>
              ) : (
                isMyProfile && (
                  <Alert mb={3} status="warning">
                    <Text style={{ fontSize: 11 }}>
                      You haven't set your socials{" "}
                      <Tooltip label="It's our way of reaching out to you. We could sponsor you an ENS, offer to be part of a build or set up an ETH stream for you.">
                        <QuestionOutlineIcon />
                      </Tooltip>
                    </Text>
                  </Alert>
                )
              )}
              {isMyProfile && (
                <Button mb={3} size="xs" variant="outline" onClick={onOpen}>
                  Update socials
                </Button>
              )}
              <Text textAlign="center" color={secondaryFontColor}>
                Joined {joinedDateDisplay}
              </Text>
            </Flex>
          </Flex>
        )}
      </BuilderProfileCardSkeleton>
      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Update your socials</ModalHeader>
          <ModalCloseButton />
          <ModalBody p={6}>
            {Object.entries(socials).map(([socialId, socialData]) => (
              <FormControl id="socialId" key={socialId} mb={3}>
                <FormLabel htmlFor={socialId} mb={0}>
                  <strong>{socialData.label}:</strong>
                </FormLabel>
                <Input
                  type="text"
                  name={socialId}
                  value={updatedSocials[socialId] ?? ""}
                  placeholder={socialData.placeholder}
                  onChange={e => {
                    const value = e.target.value;
                    setUpdatedSocials(prevSocials => ({
                      ...prevSocials,
                      [socialId]: value,
                    }));
                  }}
                />
              </FormControl>
            ))}
            <Button colorScheme="blue" onClick={handleUpdateSocials} isLoading={isUpdatingSocials} isFullWidth mt={4}>
              Update
            </Button>
          </ModalBody>
        </ModalContent>
      </Modal>
    </>
  );
}
Example #28
Source File: index.js    From UpStats with MIT License 4 votes vote down vote up
export default function Home({ status, systems, config }) {
  console.log(status);
  console.log(systems);
  const [email, setEmail] = useState("");
  const toast = useToast();
  const handleSubmit = async (email) => {
    try {
      await http.post("/subs", { email: email });
      toast({
        title: "Success",
        description: "Successfully Subscribed ",
        status: "success",
        duration: 9000,
        isClosable: true,
      });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Submit Unsuccessful",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  return (
    <>
      <Head>
        <meta charSet="UTF-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, viewport-fit=cover"
        />
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="icon" href="/favicon.ico" type="image/x-icon" />
        <title>UP Stats</title>
        <link rel="stylesheet" href="/main.css" />
      </Head>
      <main className="root">
        <header className="top-0">
          <nav>
            <div className="content-center p-4">
              {/* Nav Bar Logo */}
              <img className="nav-brand" src="assets/img/logo.jpg" />
            </div>
          </nav>
        </header>
        <section>
          <Center mt="5">
            <Box bg="blue" w="90%" p={4} color="white" borderRadius="md">
              {status.operational
                ? "All Systems Operational"
                : `${status.outageCount} Systems Outage`}
            </Box>
          </Center>
          <br />
          <VStack>
            {systems.map((system) => (
              <Flex
                id={system._id}
                borderRadius="md"
                boxShadow="lg"
                w="90%"
                p={3}
                bg="white"
              >
                <Text pl={3}>{system.name}</Text>
                <Spacer />
                {system.status === "up" && (
                  <CheckCircleIcon mr={5} mt="1" color="green" />
                )}

                {system.status === "down" && (
                  <WarningIcon mr={5} mt="1" color="red" />
                )}
              </Flex>
            ))}
          </VStack>
        </section>
        {config.mailing ? (
          <VStack p={10} m={10} borderWidth={1} borderRadius="lg">
            <h1 className="font-sans text-xl">Want to see Back in action?</h1>
            <p className="font-sans">
              Subscribe via Email and <br />
              Get notified about the System Status
            </p>
            <Center>
              <FormControl id="email" width="90%">
                <FormLabel>Email address</FormLabel>
                <Input
                  type="email"
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                />
                <Box
                  width="8em"
                  mt="3"
                  height="3em"
                  border="1px"
                  color="white"
                  bg="blue"
                  borderRadius="lg"
                  p="3"
                  onClick={() => handleSubmit(email)}
                >
                  <EmailIcon mr={3} />
                  Subscribe
                </Box>
              </FormControl>
            </Center>
          </VStack>
        ) : (
          ""
        )}
        <footer className="px-4 py-16 mx-auto max-w-7xl">
          <nav className="grid grid-cols-2 gap-12 mb-12 md:grid-cols-3 lg:grid-cols-5">
            <div>
              <p className="mb-4 text-sm font-medium text-primary">
                Handy Links
              </p>
              <a
                className="flex mb-3 text-sm font-medium text-gray-700 transition md:mb-2 hover:text-primary"
                href="https://github.com/ToolsHD/UPStats"
              >
                Opensource
              </a>
              <a
                className="flex mb-3 text-sm font-medium text-gray-700 transition md:mb-2 hover:text-primary"
                href="#"
              >
                Features
              </a>
              <a
                className="flex mb-3 text-sm font-medium text-gray-700 transition md:mb-2 hover:text-primary"
                href="#"
              >
                Pricing
              </a>
            </div>
          </nav>
          <div className="flex flex-col items-center justify-between md:flex-row">
            <a href="/" className="mb-4 md:mb-0">
              <img id="footer-img" src="assets/img/footer.jpg" />
              <span className="sr-only">UpStats</span>
            </a>
            <p className="text-sm text-center text-gray-600 md:text-left">
              © 2021 <a href="#">UP Stats</a>
            </p>
          </div>
        </footer>
        <div>
          <button onClick="topFunction()" id="scroll-to-top">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              className="h-6 w-6"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M5 10l7-7m0 0l7 7m-7-7v18"
              />
            </svg>
          </button>
        </div>
      </main>
    </>
  );
}
Example #29
Source File: playlists.js    From grandcast.fm with Apache License 2.0 4 votes vote down vote up
export default function Playlists() {
  const { isSignedIn } = useAuth()
  const [selectedPlaylist, setSelectedPlaylist] = useState('')
  const [newPlaylist, setNewPlaylist] = useState('')
  const { data } = useQuery(GET_PLAYLISTS)
  const [createPlaylist] = useMutation(CREATE_PLAYLIST)

  const filteredPlaylist = data?.playlists?.filter((p) => {
    return p.name === selectedPlaylist
  })[0]

  return (
    <Container>
      {!isSignedIn() && <SignIn />}
      {isSignedIn() && (
        <div>
          <FormControl id="playlists">
            <Flex>
              <Select
                placeholder="Select playlist"
                onChange={(e) => setSelectedPlaylist(e.target.value)}
              >
                {data?.playlists?.map((p) => {
                  return (
                    <option key={p.name} value={p.value}>
                      {p.name}
                    </option>
                  )
                })}
              </Select>
              <Popover>
                <PopoverTrigger>
                  <Button ml={4}>
                    <AddIcon />
                  </Button>
                </PopoverTrigger>
                <PopoverContent>
                  <PopoverArrow />
                  <PopoverCloseButton />
                  <PopoverHeader>Create new playlist</PopoverHeader>
                  <PopoverBody>
                    <FormControl id="newplaylist">
                      <Input
                        type="text"
                        onChange={(e) => setNewPlaylist(e.target.value)}
                      />
                      <Button
                        mt={4}
                        onClick={() =>
                          createPlaylist({
                            variables: { playlistName: newPlaylist },
                            update: (proxy) => {
                              const data = proxy.readQuery({
                                query: GET_PLAYLISTS,
                              })

                              proxy.writeQuery({
                                query: GET_PLAYLISTS,
                                data: {
                                  playlists: [
                                    ...data.playlists,
                                    {
                                      __typename: 'Playlist',
                                      name: newPlaylist,
                                    },
                                  ],
                                },
                              })
                            },
                          })
                        }
                      >
                        Create
                      </Button>
                    </FormControl>
                  </PopoverBody>
                </PopoverContent>
              </Popover>
            </Flex>
          </FormControl>
          <VStack mt={4} spacing={4}>
            {filteredPlaylist?.episodes?.map((e) => {
              return (
                <Episode key={e.id} episode={e} playlists={data.playlists} />
              )
            })}
          </VStack>
        </div>
      )}
    </Container>
  )
}