@chakra-ui/react#ModalCloseButton TypeScript Examples

The following examples show how to use @chakra-ui/react#ModalCloseButton. 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: note-modal.tsx    From notebook with MIT License 6 votes vote down vote up
NoteModal: React.SFC<NoteFormProps> = ({
  isOpen,
  onClose,
  selectedNote
}) => {
  return (
    <AnimatePresence>
      <motion.div layoutId={selectedNote?.id}>
        <Modal
          isOpen={isOpen}
          onClose={onClose}
          scrollBehavior={"inside"}
          isCentered
          motionPreset="slideInBottom"
        >
          <ModalOverlay />
          <ModalContent>
            <motion.div>
              <ModalHeader isTruncated paddingRight="10">
                {selectedNote?.title}
              </ModalHeader>
            </motion.div>
            <ModalCloseButton />
            <motion.div>
              <ModalBody pb={6}>{selectedNote?.body}</ModalBody>
            </motion.div>
          </ModalContent>
        </Modal>
      </motion.div>
    </AnimatePresence>
  );
}
Example #2
Source File: Modal.tsx    From calories-in with MIT License 6 votes vote down vote up
function Modal({ isOpen, onClose }: Props) {
  const selectRef = useRef<HTMLSelectElement>(null)

  return (
    <ModalBase isOpen={isOpen} onClose={onClose}>
      <ModalOverlay />

      <ModalContent>
        <ModalHeader>Filters</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Content selectRef={selectRef} />
        </ModalBody>

        <ModalFooter>
          <Footer onClose={onClose} />
        </ModalFooter>
      </ModalContent>
    </ModalBase>
  )
}
Example #3
Source File: MissingFoodsModal.tsx    From calories-in with MIT License 6 votes vote down vote up
function MissingFoodsModal({ isOpen, onClose, onImport }: Props) {
  return (
    <Modal isOpen={isOpen} onClose={onClose} size="md" isCentered>
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>Missing foods</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text fontWeight="medium">
            The meal plan you contains foods that are not part of your list.
          </Text>
          <br />
          <Text>
            You can try to import the missing foods or continue without them.
          </Text>
        </ModalBody>

        <ModalFooter>
          <Button onClick={onClose} mr={3}>
            Continue
          </Button>

          <Button
            variant="solid"
            colorScheme="teal"
            onClick={() => {
              onImport()
              onClose()
            }}
          >
            Import foods
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #4
Source File: DeleteConfirmationModal.tsx    From calories-in with MIT License 6 votes vote down vote up
function DeleteConfirmationModal({
  isOpen,
  onCancel,
  onConfirm,
  text,
  confirmButtonLabel,
}: Props) {
  return (
    <Modal isOpen={isOpen} onClose={onCancel} size="xs" isCentered>
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>Delete food</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text>{text}</Text>
          <br />
          <Text fontWeight="medium">This action cannot be undone.</Text>
        </ModalBody>

        <ModalFooter>
          <Button variant="outline" onClick={onCancel}>
            Cancel
          </Button>
          <Button colorScheme="red" ml={3} onClick={onConfirm}>
            {confirmButtonLabel}
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #5
Source File: Content.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ title, onClose, initialRef, variantFormIndex }: Props) {
  const { register } = useFormContext()
  const nameRegister = register('name')
  const nameInputRef = useMergeRefs(nameRegister.ref, initialRef)

  const onSubmit = useSubmitVariantNameForm({
    variantFormIndex,
    onComplete: onClose,
  })

  const { errorMessage, isInvalid } = useFormError('name')

  useSelectInputText(initialRef)

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <ModalHeader>{title}</ModalHeader>
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Name</FormLabel>
            <Input
              autoComplete="off"
              {...nameRegister}
              ref={nameInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter name"
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Rename
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #6
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose, initialVariantForm }: Props) {
  const dietForm = useDietForm()
  const { variantsForms } = dietForm

  const getDietFormStatsTree = useGetDietFormStatsTree()
  const dietFormStatsTree = getDietFormStatsTree(dietForm)
  const initialVariantStatsTree = dietFormStatsTree.subtrees.find(
    (statsTree: StatsTree) => statsTree.id === initialVariantForm.fieldId
  )

  if (!initialVariantStatsTree) {
    throw new Error()
  }

  return (
    <ModalContent>
      <ModalHeader>Day Details</ModalHeader>
      <ModalCloseButton />

      <ModalBody>
        <VariantsDetailsFormProvider
          initialVariantForm={initialVariantForm}
          initialVariantStats={initialVariantStatsTree.stats}
        >
          <form>
            <FormFields
              initialVariantForm={initialVariantForm}
              canEdit={false}
              variantsForms={variantsForms}
              dietFormStatsTree={dietFormStatsTree}
            />
          </form>
        </VariantsDetailsFormProvider>
      </ModalBody>

      <ModalFooter>
        <Button onClick={onClose}>Close</Button>
      </ModalFooter>
    </ModalContent>
  )
}
Example #7
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Form({
  ownerName,
  notes,
  onClose,
  initialRef,
  onEditNotes,
  fieldId,
  textAreaHeight,
}: Props) {
  const { register, handleSubmit } = useFormContext()
  const notesRegister = register('notes')
  const notesInputRef = useMergeRefs(notesRegister.ref, initialRef)
  const oneTimeCheckActions = useOneTimeCheckActions()

  const onSubmit = handleSubmit((form: NotesForm) => {
    oneTimeCheckActions.set(`notes-${fieldId}`)
    onEditNotes(form.notes || undefined)
    onClose()
  })

  const { errorMessage, isInvalid } = useFormError('name')

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header ownerName={ownerName} notes={notes} />
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Notes</FormLabel>
            <Textarea
              autoComplete="off"
              {...notesRegister}
              ref={notesInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter notes"
              height={textAreaHeight}
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Save
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #8
Source File: Content.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose, title, onImport, action }: Props) {
  const { allFoods } = useFoods()

  const [blob] = useState(() => {
    const allFoodsString = JSON.stringify(allFoods)
    return new Blob([allFoodsString])
  })

  return (
    <ModalContent>
      <ModalHeader>{title}</ModalHeader>
      <ModalCloseButton />

      <ModalBody>
        <FoodsList
          allowsFiltering={false}
          height="350px"
          itemUsageType="nonInteractive"
        />
      </ModalBody>

      <ModalFooter>
        <HStack spacing={3}>
          <Button onClick={onClose}>Close</Button>

          {action === 'import' ? (
            <Button
              isDisabled={allFoods.length === 0}
              variant="solid"
              colorScheme="teal"
              onClick={onImport}
            >
              {allFoods.length > 0
                ? `Import ${allFoods.length} ${
                    allFoods.length === 1 ? 'food' : 'foods'
                  }`
                : 'Import'}
            </Button>
          ) : (
            <DownloadButton
              blob={blob}
              onClose={onClose}
              fileName={getUntitledFileName({ prefix: 'foods' })}
              label="Export"
              isDisabled={allFoods.length === 0}
            />
          )}
        </HStack>
      </ModalFooter>
    </ModalContent>
  )
}
Example #9
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose }: Props) {
  const [blob, setBlob] = useState<Blob>()
  const [url, setUrl] = useState<string>()
  const dietForm = useDietForm()

  const onUpdate = useCallback((blob: Blob, url: string) => {
    setBlob(blob)
    setUrl(url)
  }, [])

  function onViewInBrowser() {
    window.open(url, '_blank')
  }

  return (
    <ModalContent>
      <ModalHeader fontWeight="normal">
        Export{' '}
        <Text as="span" fontWeight="bold">
          {dietForm.name}
        </Text>
      </ModalHeader>
      <ModalCloseButton />
      <ModalBody px={0}>
        <Exporter onUpdate={onUpdate} />
      </ModalBody>

      <ModalFooter>
        <VStack spacing={3} width="100%">
          {blob && url && (
            <DownloadButton
              blob={blob}
              onClose={onClose}
              label="Download"
              isFullWidth={true}
              fileName={dietForm.name}
              isLoading={blob === undefined}
            />
          )}
          {blob && url && (
            <Button
              mr={3}
              variant="outline"
              colorScheme="teal"
              onClick={onViewInBrowser}
              isFullWidth={true}
            >
              View in browser
            </Button>
          )}

          <Button isFullWidth={true} variant="solid" onClick={onClose}>
            Close
          </Button>
        </VStack>
      </ModalFooter>
    </ModalContent>
  )
}
Example #10
Source File: PaymentMethodButtonModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
PaymentMethodButtonModal: FunctionComponent<Props> = ({ isOpen, onClose, paymentMethod, paymentMethodDisplayName, paymentMethodValue }) => {
    const { onCopy, hasCopied } = useClipboard(paymentMethodValue);
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const { name } = piggybankDbData;
    const Icon = paymentMethodIcons[paymentMethod];
    const addressIsUrl = isUrl(paymentMethodValue, {
        require_protocol: true,
        require_valid_protocol: true,
        protocols: ['http', 'https'],
        allow_protocol_relative_urls: false,
    });
    return (
        <Modal isOpen={isOpen} onClose={onClose}>
            <ModalOverlay />
            <ModalContent mx={6}>
                <ModalHeader
                    textAlign="center"
                    mt={3}
                    mx="auto"
                >
                    {name}
                    {"'s "}
                    {paymentMethodDisplayName}
                    {' address'}
                </ModalHeader>
                <ModalCloseButton />
                <ModalBody
                    pt={0}
                    mb={3}
                    mx="auto"
                >
                    <Flex justify="center" align="center">
                        <Icon
                            mr={2}
                            boxSize="48px"
                        />
                        <Text
                            wordBreak="break-all"
                            textAlign="center"
                        >
                            {paymentMethodValue}
                        </Text>
                    </Flex>
                    <Flex
                        my={2}
                        align="center"
                        justify="center"
                    >
                        {addressIsUrl ? (
                            <Link href={paymentMethodValue} isExternal>
                                <Button
                                    leftIcon={<ExternalLinkIcon />}
                                    href={paymentMethodValue}
                                    mr={2}
                                >
                                    Open
                                </Button>
                            </Link>
                        ) : (
                            <Button
                                leftIcon={hasCopied ? <CheckIcon /> : <CopyIcon />}
                                onClick={onCopy}
                            >
                                {hasCopied ? "Copied" : "Copy"}
                            </Button>
                        )}
                    </Flex>
                    <Text mb={2} textAlign="center">or scan QR Code:</Text>
                    <Flex justify="center">
                        <QRCode
                            id="payment-method-qr-code"
                            value={paymentMethodValue}
                            size={225}
                        />
                    </Flex>
                </ModalBody>
            </ModalContent>
        </Modal>
    );
}
Example #11
Source File: ShareButtonModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
ShareButtonModal: FunctionComponent<Props> = ({ buttonColor }) => {
    const { isOpen, onClose, onOpen } = useDisclosure();
    const { query: { piggybankName: piggybankNameQuery }} = useRouter();
    const piggybankName = Array.isArray(piggybankNameQuery) ? piggybankNameQuery[0] : piggybankNameQuery;
    const publicUrl = `coindrop.to/${piggybankName}`;
    const fullPublicUrl = `https://${publicUrl}`;
    return (
        <>
        <Button
            leftIcon={<ShareIcon />}
            onClick={onOpen}
            colorScheme={buttonColor}
            isDisabled={isOpen}
        >
            Share
        </Button>
        <Modal isOpen={isOpen} onClose={onClose}>
            <ModalOverlay />
            <ModalContent>
                <ModalCloseButton />
                <Heading
                    mt={4}
                    as="h2"
                    size="md"
                    mx={12}
                    textAlign="center"
                >
                    {publicUrl}
                </Heading>
                <ModalBody>
                    <Box mb={4}>
                        <Box>
                            <Heading as="h2" size="lg">
                                Link
                            </Heading>
                        </Box>
                        <Center mt={2}>
                            <CopyLinkShareButton textToCopy={publicUrl} />
                        </Center>
                    </Box>
                    <ShareEmbedButton
                        fullPublicUrl={fullPublicUrl}
                    />
                    <PiggybankQRCode
                        fullPublicUrl={fullPublicUrl}
                        publicUrl={publicUrl}
                    />
                    <TipCards />
                </ModalBody>
                <ModalFooter />
            </ModalContent>
        </Modal>
        </>
    );
}
Example #12
Source File: AuthModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
AuthModal: FunctionComponent<Props> = ({ isOpen }) => {
    const router = useRouter();
    const onClose = () => router.push('/', undefined, { shallow: true });
    return (
        <Modal
            id="auth-modal"
            isOpen={isOpen}
            onClose={onClose}
        >
            <ModalOverlay />
            <ModalContent>
                <ModalHeader textAlign="center" mb={-3}>Sign in to continue</ModalHeader>
                <ModalCloseButton />
                <ModalBody>
                    <FirebaseAuth />
                    <Text textAlign="center" fontSize="xs" mb={2}>
                        {'If you\'re having trouble logging in, please try again with '}
                        <Link
                            href="https://www.google.com/chrome/"
                            target="_blank"
                            rel="noreferrer"
                            textDecoration="underline"
                        >
                            Chrome
                        </Link>
                        {' or '}
                        <Link
                            href="https://www.mozilla.org/firefox"
                            target="_blank"
                            rel="noreferrer"
                            textDecoration="underline"
                        >
                            Firefox
                        </Link>
                        .
                    </Text>
                </ModalBody>
            </ModalContent>
        </Modal>
    );
}
Example #13
Source File: About.tsx    From calories-in with MIT License 4 votes vote down vote up
function About({ isOpen, onClose }: Props) {
  function onContact() {
    window.location.href = 'mailto:[email protected]'
  }

  return (
    <Modal isOpen={isOpen} onClose={onClose} size="2xl" scrollBehavior="inside">
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>About </ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text fontSize="lg">
            <Text>Hi, I'm Vladimir, the person behind this project.</Text>
            <br />
            <Text>
              <Text fontWeight="semibold" as="span" textColor="teal.600">
                Calories-In
              </Text>{' '}
              is made for people who follow meal plans that involve preparing
              everything by yourself and gives them full control to fine tune
              the nutritional values.
            </Text>
            <br />
            <Text>
              The idea was born out of my experience of trying to find a better
              alternative to Google Sheets for calculating the macros of my own
              meal plans. I wanted to be able to do this on desktop as it's more
              convenient but nothing really felt fast and simple enough.
            </Text>
            <br />
            <Text>The main differences to other apps in this space are:</Text>
            <br />
            <List ml={8}>
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Faster search
                </Text>{' '}
                : There are actually not that many foods you need when you
                prepare everything yourself. This means all of the food data can
                be downloaded beforehand which makes the search super fast. Of
                course you can add your own foods if you'd like.{' '}
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Undo/Redo
                </Text>{' '}
                : Building a plan from scratch or updating an existing one
                involves some back and forth choosing the right foods and
                adjusting their amounts. This is especially true if you want to
                be as close as possible to a specific calorie limit and have
                your macros be a certain percentages split.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Faster export
                </Text>{' '}
                : Creating the PDF file for your meal plan is done entirely
                inside the browser. It does not involve generating and
                downloading it from a server. This means I can keep the cost of
                running the website low and you get your file in just a few
                seconds.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Simpler
                </Text>{' '}
                : There are no other pages except the editor. Most of the other
                tools are bloated with additional features for professionals,
                such as managing clients, creating invoices, etc.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Fully mobile
                </Text>{' '}
                : You can use your phone or tablet to build your meal plans
                right from your browser. If you add the app to your home screen
                it will look and feel almost like a native one.
              </ListItem>
            </List>
            <Text>
              <br />
              Let me know if you found it useful or have any comments in
              general:
            </Text>
            <br />

            <Button size="lg" colorScheme="teal" onClick={onContact}>
              Contact me directly
            </Button>

            <br />
            <br />
            <Text>No email will go unanswered, I promise :)</Text>
          </Text>
        </ModalBody>

        <ModalFooter>
          <Button size="lg" onClick={onClose}>
            Close
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #14
Source File: EditPiggybankModal.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
EditPiggybankModal: FunctionComponent<Props> = ({ isOpen, onClose }) => {
    const [isSubmitting, setIsSubmitting] = useState(false);
    const { colors } = useTheme();
    const { user } = useUser();
    const { colorMode } = useColorMode();
    const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
    const accentColorLevelHover = getAccentColorLevelHover(colorMode);
    const { push: routerPush, query: { piggybankName } } = useRouter();
    const initialPiggybankId = Array.isArray(piggybankName) ? piggybankName[0] : piggybankName;
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const { avatar_storage_id: currentAvatarStorageId } = piggybankDbData;
    const initialPaymentMethodsDataFieldArray = convertPaymentMethodsDataToFieldArray(piggybankDbData.paymentMethods);
    const initialAccentColor = piggybankDbData.accentColor ?? 'orange';
    const {
        register,
        handleSubmit,
        setValue,
        watch,
        control,
        formState: { isDirty },
    } = useForm({
        defaultValues: {
            piggybankId: initialPiggybankId,
            accentColor: initialAccentColor,
            website: piggybankDbData.website ?? '',
            name: piggybankDbData.name ?? '',
            verb: piggybankDbData.verb ?? 'pay',
            paymentMethods: sortByIsPreferredThenAlphabetical(initialPaymentMethodsDataFieldArray),
        },
    });
    const paymentMethodsFieldArrayName = "paymentMethods";
    const { fields, append, remove } = useFieldArray({
        control,
        name: paymentMethodsFieldArrayName,
    });
    const {
        accentColor: watchedAccentColor,
        piggybankId: watchedPiggybankId,
    } = watch(["accentColor", "piggybankId"]);
    const isAccentColorDirty = initialAccentColor !== watchedAccentColor;
    const isUrlUnchanged = initialPiggybankId === watchedPiggybankId;
    const { isPiggybankIdAvailable, setIsAddressTouched } = useContext(AdditionalValidation);
    const onSubmit = async (formData) => {
        try {
            setIsSubmitting(true);
            const dataToSubmit = {
                ...formData,
                paymentMethods: convertPaymentMethodsFieldArrayToDbMap(formData.paymentMethods ?? []),
                owner_uid: user.id,
                avatar_storage_id: currentAvatarStorageId ?? null,
            };
            if (isUrlUnchanged) {
                await db.collection('piggybanks').doc(initialPiggybankId).set(dataToSubmit);
                mutate(['publicPiggybankData', initialPiggybankId], dataToSubmit);
            } else {
                await axios.post(
                    '/api/createPiggybank',
                    {
                        oldPiggybankName: initialPiggybankId,
                        newPiggybankName: formData.piggybankId,
                        piggybankData: dataToSubmit,
                    },
                    {
                        headers: {
                            token: user.token,
                        },
                    },
                );
                try {
                    await db.collection('piggybanks').doc(initialPiggybankId).delete();
                } catch (err) {
                    console.log('error deleting old Coindrop page');
                }
                routerPush(`/${formData.piggybankId}`);
            }
            fetch(`/${initialPiggybankId}`, { headers: { isToForceStaticRegeneration: "true" }});
            onClose();
        } catch (error) {
            setIsSubmitting(false);
            // TODO: handle errors
            throw error;
        }
    };
    const handleAccentColorChange = (e) => {
        e.preventDefault();
        setValue("accentColor", e.target.dataset.colorname);
    };
    useEffect(() => {
        register("accentColor");
    }, [register]);
    const formControlTopMargin = 2;
    return (
        <Modal
            isOpen={isOpen}
            onClose={onClose}
            size="xl"
            closeOnOverlayClick={false}
        >
            <ModalOverlay />
            <ModalContent>
                <ModalHeader>Configure</ModalHeader>
                <ModalCloseButton />
                <form id="configure-coindrop-form" onSubmit={handleSubmit(onSubmit)}>
                    <ModalBody>
                        <AvatarInput />
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel htmlFor="input-piggybankId">URL</FormLabel>
                            <EditUrlInput
                                register={register}
                                value={watchedPiggybankId}
                            />
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-accentColor"
                            >
                                Theme
                            </FormLabel>
                            <Flex wrap="wrap" justify="center">
                                {themeColorOptions.map(colorName => {
                                    const isColorSelected = watchedAccentColor === colorName;
                                    const accentColorInitial = colors[colorName][accentColorLevelInitial];
                                    const accentColorHover = colors[colorName][accentColorLevelHover];
                                    return (
                                    <Box
                                        key={colorName}
                                        as="button"
                                        bg={isColorSelected ? accentColorHover : accentColorInitial}
                                        _hover={{
                                            bg: accentColorHover,
                                        }}
                                        w="36px"
                                        h="36px"
                                        borderRadius="50%"
                                        mx={1}
                                        my={1}
                                        onClick={handleAccentColorChange}
                                        data-colorname={colorName}
                                    >
                                        {isColorSelected && (
                                            <CheckIcon color="#FFF" />
                                        )}
                                    </Box>
                                    );
                                })}
                            </Flex>
                        </FormControl>
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-name"
                            >
                                Name
                            </FormLabel>
                            <Input
                                id="input-name"
                                name="name"
                                ref={register}
                            />
                        </FormControl>
                        <FormControl
                            isRequired
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-verb"
                            >
                                Payment action name
                            </FormLabel>
                            <Select
                                id="input-verb"
                                name="verb"
                                ref={register}
                            >
                                <option value="pay">Pay</option>
                                <option value="donate to">Donate to</option>
                                <option value="support">Support</option>
                                <option value="tip">Tip</option>
                            </Select>
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-website"
                            >
                                Website
                            </FormLabel>
                            <Input
                                id="input-website"
                                name="website"
                                ref={register}
                                placeholder="http://"
                                type="url"
                            />
                        </FormControl>
                        <FormControl
                            mt={formControlTopMargin}
                        >
                            <FormLabel
                                htmlFor="input-paymentmethods"
                            >
                                Payment Methods
                            </FormLabel>
                            <PaymentMethodsInput
                                fields={fields}
                                control={control}
                                register={register}
                                remove={remove}
                                append={append}
                                fieldArrayName={paymentMethodsFieldArrayName}
                            />
                        </FormControl>
                    </ModalBody>
                    <Flex
                        id="modal-footer"
                        justify="space-between"
                        m={6}
                    >
                        <DeleteButton
                            piggybankName={initialPiggybankId}
                        />
                        <Flex>
                            <Button
                                variant="ghost"
                                onClick={onClose}
                            >
                                Cancel
                            </Button>
                            <Button
                                id="save-configuration-btn"
                                colorScheme="green"
                                mx={1}
                                type="submit"
                                isLoading={isSubmitting}
                                loadingText="Saving"
                                isDisabled={
                                    (
                                        !isDirty
                                        && !isAccentColorDirty // controlled accentColor field is not showing up in formState.dirtyFields
                                    )
                                    || !isPiggybankIdAvailable
                                    || !initialPiggybankId
                                }
                                onClick={() => setIsAddressTouched(true)}
                            >
                                Save
                            </Button>
                        </Flex>
                    </Flex>
                </form>
            </ModalContent>
        </Modal>
    );
}
Example #15
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function Form({
  onClose,
  nameInputRef,
  title,
  food,
  canEdit,
  onFoodCreatedOrUpdated,
  isEditing,
  onToggleEdit,
  onDelete,
}: Props) {
  const { onSubmit } = useSubmitFoodForm({
    onComplete: (newOrUpdatedFood: Food) => {
      onFoodCreatedOrUpdated && onFoodCreatedOrUpdated(newOrUpdatedFood, food)
      onClose()
    },
  })

  const { tabNames, onTabNameChange, selectedTabName } = useTabs({
    isEditing,
    food,
  })

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header
          title={title}
          canEdit={canEdit}
          isEditing={isEditing}
          onClose={onClose}
          onToggleEdit={onToggleEdit}
        />
        <ModalCloseButton />

        <ModalBody>
          <VStack spacing={6} align="stretch\">
            <VStack spacing={2} align="stretch">
              {isEditing ? (
                <StatFormField
                  textInputRef={nameInputRef}
                  name="name"
                  label="Name"
                  inputType="text"
                  isRequired={true}
                  isReadOnly={!isEditing}
                  hasDivider={false}
                />
              ) : (
                <Text fontSize="xl" fontWeight="bold">
                  {food?.name}
                </Text>
              )}
              <StatFormField
                name="categoryId"
                label="Category"
                inputType="foodCategory"
                isRequired={true}
                isReadOnly={!isEditing}
              />
            </VStack>

            <Tabs
              nameInputRef={nameInputRef}
              isEditing={isEditing}
              food={food}
              tabNames={tabNames}
              onTabNameChange={onTabNameChange}
              selectedTabName={selectedTabName}
            />
          </VStack>

          {isEditing && food && (
            <Box>
              <Divider />
              <Button width="100%" my={6} colorScheme="red" onClick={onDelete}>
                Delete food
              </Button>
            </Box>
          )}
        </ModalBody>

        <Footer onClose={onClose} onSubmit={onSubmit} isEditing={isEditing} />
      </ModalContent>
    </form>
  )
}
Example #16
Source File: Inspector.tsx    From openchakra with MIT License 4 votes vote down vote up
Inspector = () => {
  const dispatch = useDispatch()
  const component = useSelector(getSelectedComponent)
  const { isOpen, onOpen, onClose } = useDisclosure()
  const [componentName, onChangeComponentName] = useState('')
  const componentsNames = useSelector(getComponentNames)

  const { clearActiveProps } = useInspectorUpdate()

  const saveComponent = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    dispatch.components.setComponentName({
      componentId: component.id,
      name: componentName,
    })
    onClose()
    onChangeComponentName('')
  }
  const isValidComponentName = useMemo(() => {
    return (
      !!componentName.match(/^[A-Z]\w*$/g) &&
      !componentsNames.includes(componentName) &&
      // @ts-ignore
      !componentsList.includes(componentName)
    )
  }, [componentName, componentsNames])

  const { type, rootParentType, id, children } = component

  const isRoot = id === 'root'
  const parentIsRoot = component.parent === 'root'

  const docType = rootParentType || type
  const componentHasChildren = children.length > 0

  useEffect(() => {
    clearActiveProps()
  }, [clearActiveProps])

  return (
    <>
      <Box bg="white">
        <Box
          fontWeight="semibold"
          fontSize="md"
          color="yellow.900"
          py={2}
          px={2}
          boxShadow="sm"
          bg="yellow.100"
          display="flex"
          justifyContent="space-between"
          flexDir="column"
        >
          {isRoot ? 'Document' : type}
          {!!component.componentName && (
            <Text fontSize="xs" fontWeight="light">
              {component.componentName}
            </Text>
          )}
        </Box>
        {!isRoot && (
          <Stack
            direction="row"
            py={2}
            spacing={2}
            align="center"
            zIndex={99}
            px={2}
            flexWrap="wrap"
            justify="flex-end"
          >
            <CodeActionButton />
            {!component.componentName && (
              <ActionButton
                label="Name component"
                icon={<EditIcon path="" />}
                onClick={onOpen}
              />
            )}
            <ActionButton
              label="Duplicate"
              onClick={() => dispatch.components.duplicate()}
              icon={<CopyIcon path="" />}
            />
            <ActionButton
              label="Reset props"
              icon={<IoMdRefresh />}
              onClick={() => dispatch.components.resetProps(component.id)}
            />
            <ActionButton
              label="Chakra UI Doc"
              as={Link}
              onClick={() => {
                window.open(
                  `https://chakra-ui.com/${docType.toLowerCase()}`,
                  '_blank',
                )
              }}
              icon={<GoRepo />}
            />
            <ActionButton
              bg="red.500"
              label="Remove"
              onClick={() => dispatch.components.deleteComponent(component.id)}
              icon={<FiTrash2 />}
            />
          </Stack>
        )}
      </Box>

      <Box pb={1} bg="white" px={3}>
        <Panels component={component} isRoot={isRoot} />
      </Box>

      <StylesPanel
        isRoot={isRoot}
        showChildren={componentHasChildren}
        parentIsRoot={parentIsRoot}
      />
      <Modal onClose={onClose} isOpen={isOpen} isCentered>
        <ModalOverlay>
          <ModalContent>
            <form onSubmit={saveComponent}>
              <ModalHeader>Save this component</ModalHeader>
              <ModalCloseButton />
              <ModalBody>
                <FormControl isInvalid={!isValidComponentName}>
                  <FormLabel>Component name</FormLabel>
                  <Input
                    size="md"
                    autoFocus
                    variant="outline"
                    isFullWidth
                    focusBorderColor="blue.500"
                    errorBorderColor="red.500"
                    value={componentName}
                    onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                      onChangeComponentName(e.target.value)
                    }
                  />
                  {!isValidComponentName && (
                    <FormErrorMessage>
                      Component name must start with a capital character and
                      must not contain space or special character, and name
                      should not be already taken (including existing chakra-ui
                      components).
                    </FormErrorMessage>
                  )}
                  <FormHelperText>
                    This will name your component that you will see in the code
                    panel as a separated component.
                  </FormHelperText>
                </FormControl>
              </ModalBody>
              <ModalFooter>
                <Button
                  colorScheme="blue"
                  mr={3}
                  type="submit"
                  isDisabled={!isValidComponentName}
                >
                  Save
                </Button>
                <Button onClick={onClose}>Cancel</Button>
              </ModalFooter>
            </form>
          </ModalContent>
        </ModalOverlay>
      </Modal>
    </>
  )
}
Example #17
Source File: AccountModal.tsx    From eth-dapps-nextjs-boiletplate with MIT License 4 votes vote down vote up
export default function AccountModal({ isOpen, onClose }: Props) {
  const { globalState, dispatch } = useContext(globalContext)
  const { account, provider } = globalState
  const chainIdPaths = {
    1: '', // mainnet
    42: 'kovan.',
    3: 'ropsten.',
    4: 'rinkeby.',
    5: 'goerli.',
  }
  const chainPath = provider && chainIdPaths[parseInt(provider.chainId)]

  async function handleDeactivateAccount() {
    //deactivate();
    if (provider && !provider.isMetaMask) { // isWalletConnect then
      await provider.disconnect()
    }
    dispatch({ type: 'CLEAR_STATE'})
    onClose();
  }

  return (
    <Modal isOpen={isOpen} onClose={onClose} isCentered size="md">
      <ModalOverlay />
      <ModalContent
        background="gray.900"
        border="1px"
        borderStyle="solid"
        borderColor="gray.700"
        borderRadius="3xl"
      >
        <ModalHeader color="white" px={4} fontSize="lg" fontWeight="medium">
          Account
        </ModalHeader>
        <ModalCloseButton
          color="white"
          fontSize="sm"
          _hover={{
            color: "whiteAlpha.700",
          }}
        />
        <ModalBody pt={0} px={4}>
          <Box
            borderRadius="3xl"
            border="1px"
            borderStyle="solid"
            borderColor="gray.600"
            px={5}
            pt={4}
            pb={2}
            mb={3}
          >
            <Flex justifyContent="space-between" alignItems="center" mb={3}>
              <Text color="gray.400" fontSize="sm">
                Connected with {provider?.isMetaMask ? 'MetaMask' : 'WalletConnect'}
              </Text>
              <Button
                variant="outline"
                size="sm"
                borderColor="blue.800"
                borderRadius="3xl"
                color="blue.500"
                fontSize="13px"
                fontWeight="normal"
                px={2}
                height="26px"
                _hover={{
                  background: "none",
                  borderColor: "blue.300",
                  textDecoration: "underline",
                }}
                onClick={handleDeactivateAccount}
              >
                Disconnect
              </Button>
            </Flex>
            <Flex alignItems="center" mt={2} mb={4} lineHeight={1}>
              <Identicon />
              <Text
                color="white"
                fontSize="xl"
                fontWeight="semibold"
                ml="2"
                lineHeight="1.1"
              >
                {account &&
                  `${account.slice(0, 6)}...${account.slice(
                    account.length - 4,
                    account.length
                  )}`}
              </Text>
            </Flex>
            <Flex alignContent="center" m={3}>
              <Button
                variant="link"
                color="gray.400"
                fontWeight="normal"
                fontSize="sm"
                _hover={{
                  textDecoration: "none",
                  color: "whiteAlpha.800",
                }}
              >
                <CopyIcon mr={1} />
                Copy Address
              </Button>
              {
              chainPath?
              <Link
                fontSize="sm"
                display="flex"
                alignItems="center"
                href={`https://${chainPath}etherscan.io/address/${account}`}
                isExternal
                color="gray.400"
                ml={6}
                _hover={{
                  color: "whiteAlpha.800",
                  textDecoration: "underline",
                }}
              >  
                <ExternalLinkIcon mr={1} />
                View on Explorer
              </Link>:''
              }
            </Flex>
          </Box>
        </ModalBody>

        <ModalFooter
          justifyContent="end"
          background="gray.700"
          borderBottomLeftRadius="3xl"
          borderBottomRightRadius="3xl"
          p={6}
        >
          <Text
            color="white"
            textAlign="left"
            fontWeight="medium"
            fontSize="md"
          >
            Your transactions will appear here...
          </Text>
        </ModalFooter>
      </ModalContent>
    </Modal>
  );
}
Example #18
Source File: create-app-github.tsx    From ledokku with MIT License 4 votes vote down vote up
CreateAppGithub = () => {
  const history = useHistory();
  const toast = useToast();
  const { user } = useAuth();

  const { data: dataApps } = useAppsQuery();
  const [isNewWindowClosed, setIsNewWindowClosed] = useState(false);
  const [selectedRepo, setSelectedRepo] = useState<Repository>();
  const [selectedBranch, setSelectedBranch] = useState('');
  const [isProceedModalOpen, setIsProceedModalOpen] = useState(false);
  const {
    data: installationData,
    loading: installationLoading,
  } = useGithubInstallationIdQuery({ fetchPolicy: 'network-only' });
  const [
    getRepos,
    { data: reposData, loading: reposLoading },
  ] = useRepositoriesLazyQuery({ fetchPolicy: 'network-only' });

  const [
    getBranches,
    { data: branchesData, loading: branchesLoading },
  ] = useBranchesLazyQuery({ fetchPolicy: 'network-only' });

  const [arrayOfCreateAppLogs, setArrayOfCreateAppLogs] = useState<
    RealTimeLog[]
  >([]);
  const [isTerminalVisible, setIsTerminalVisible] = useState(false);
  const [isToastShown, setIsToastShown] = useState(false);
  const [createAppGithubMutation, { loading }] = useCreateAppGithubMutation();
  const [
    isAppCreationSuccess,
    setIsAppCreationSuccess,
  ] = useState<AppCreationStatus>();

  useAppCreateLogsSubscription({
    onSubscriptionData: (data) => {
      const logsExist = data.subscriptionData.data?.appCreateLogs;

      if (logsExist) {
        setArrayOfCreateAppLogs((currentLogs) => {
          return [...currentLogs, logsExist];
        });
        if (logsExist.type === 'end:success') {
          setIsAppCreationSuccess(AppCreationStatus.SUCCESS);
        } else if (logsExist.type === 'end:failure') {
          setIsAppCreationSuccess(AppCreationStatus.FAILURE);
        }
      }
    },
  });

  const createAppGithubSchema = yup.object().shape({
    name: yup
      .string()
      .required('App name is required')
      .matches(/^[a-z0-9-]+$/)
      .test(
        'Name exists',
        'App with this name already exists',
        (val) => !dataApps?.apps.find((app) => app.name === val)
      ),
    repo: yup.object({
      fullName: yup.string().required(),
      id: yup.string().required(),
      name: yup.string().required(),
    }),
    installationId: yup.string().required(),
    gitBranch: yup.string().optional(),
  });

  const formik = useFormik<{
    name: string;
    repo: {
      fullName: string;
      id: string;
      name: string;
    };
    installationId: string;
    gitBranch: string;
  }>({
    initialValues: {
      name: '',
      repo: {
        fullName: '',
        id: '',
        name: '',
      },
      installationId: '',
      gitBranch: '',
    },

    validateOnChange: true,
    validationSchema: createAppGithubSchema,
    onSubmit: async (values) => {
      if (installationData) {
        try {
          await createAppGithubMutation({
            variables: {
              input: {
                name: values.name,
                gitRepoFullName: values.repo.fullName,
                branchName: values.gitBranch,
                gitRepoId: values.repo.id,
                githubInstallationId: values.installationId,
              },
            },
          });
          setIsTerminalVisible(true);
        } catch (error) {
          error.message === 'Not Found'
            ? toast.error(`Repository : ${values.repo.fullName} not found`)
            : toast.error(error.message);
        }
      }
    },
  });

  const handleNext = () => {
    setIsTerminalVisible(false);
    const appId = arrayOfCreateAppLogs[arrayOfCreateAppLogs.length - 1].message;
    history.push(`app/${appId}`, 'new');
    trackGoal(trackingGoals.createAppGithub, 0);
  };

  const handleOpen = () => {
    const newWindow = window.open(
      `https://github.com/apps/${config.githubAppName}/installations/new`,
      'Install App',
      'resizable=1, scrollbars=1, fullscreen=0, height=1000, width=1020,top=' +
        window.screen.width +
        ', left=' +
        window.screen.width +
        ', toolbar=0, menubar=0, status=0'
    );
    const timer = setInterval(async () => {
      if (newWindow && newWindow.closed) {
        setIsNewWindowClosed(true);
        clearInterval(timer);
      }
    }, 100);
  };

  useEffect(() => {
    if (!installationLoading && installationData && isNewWindowClosed) {
      getRepos({
        variables: {
          installationId: installationData.githubInstallationId.id,
        },
      });

      setIsNewWindowClosed(false);
    }
  }, [
    installationData,
    installationLoading,
    isNewWindowClosed,
    setIsNewWindowClosed,
    getRepos,
  ]);

  useEffect(() => {
    if (
      !installationLoading &&
      installationData &&
      !reposLoading &&
      reposData &&
      selectedRepo
    ) {
      getBranches({
        variables: {
          installationId: installationData.githubInstallationId.id,
          repositoryName: selectedRepo.name,
        },
      });
    }
  }, [
    installationData,
    installationLoading,
    reposData,
    reposLoading,
    getBranches,
    selectedRepo?.name,
    selectedRepo,
  ]);

  const handleChangeRepo = (active: RepoOption) => {
    setSelectedRepo(active.value);
    setSelectedBranch('');
    if (installationData) {
      formik.setValues({
        name: active.value.name,
        installationId: installationData?.githubInstallationId.id,
        repo: {
          fullName: active.value.fullName,
          name: active.value.name,
          id: active.value.id,
        },
        gitBranch: '',
      });
    }
  };

  const handleChangeBranch = (active: BranchOption) => {
    setSelectedBranch(active.value.name);
    formik.setFieldValue('gitBranch', active.value.name);
  };

  const repoOptions: RepoOption[] = [];

  if (reposData && !reposLoading) {
    reposData?.repositories.map((r) =>
      repoOptions.push({ value: r, label: r.fullName })
    );
  }

  let branchOptions: BranchOption[] = [];

  if (branchesData && !branchesLoading) {
    branchesData.branches.map((b) =>
      branchOptions.push({ value: b, label: b.name })
    );
  }

  useEffect(() => {
    if (installationData && !installationLoading) {
      getRepos({
        variables: {
          installationId: installationData?.githubInstallationId.id,
        },
      });
    }
  }, [installationLoading, getRepos, installationData]);

  useEffect(() => {
    if (selectedRepo && installationData) {
      getBranches({
        variables: {
          installationId: installationData?.githubInstallationId.id,
          repositoryName: selectedRepo.name,
        },
      });
    }
  }, [selectedRepo, getBranches, installationData]);

  // Effect for app creation
  useEffect(() => {
    isAppCreationSuccess === AppCreationStatus.FAILURE && !isToastShown
      ? toast.error('Failed to create an app') && setIsToastShown(true)
      : isAppCreationSuccess === AppCreationStatus.SUCCESS &&
        !isToastShown &&
        toast.success('App created successfully') &&
        setIsToastShown(true);
  }, [isToastShown, isAppCreationSuccess, toast]);

  return (
    <>
      <HeaderContainer>
        <Header />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        {isTerminalVisible ? (
          <>
            <p className="mb-2 ">
              Creating <b>{formik.values.name}</b> app from{' '}
              <b>{formik.values.repo.name}</b>
            </p>
            <p className="text-gray-500 mb-2">
              Creating app usually takes a couple of minutes. Breathe in,
              breathe out, logs are about to appear below:
            </p>
            <Terminal className={'w-6/6'}>
              {arrayOfCreateAppLogs.map((log) => (
                <p
                  key={arrayOfCreateAppLogs.indexOf(log)}
                  className={'text-s leading-5'}
                >
                  {log.message?.replaceAll('[1G', '')}
                </p>
              ))}
            </Terminal>

            {!!isAppCreationSuccess &&
            isAppCreationSuccess === AppCreationStatus.SUCCESS ? (
              <div className="mt-12 flex justify-end">
                <Button
                  onClick={() => handleNext()}
                  color="grey"
                  iconEnd={<FiArrowRight size={20} />}
                >
                  Next
                </Button>
              </div>
            ) : !!isAppCreationSuccess &&
              isAppCreationSuccess === AppCreationStatus.FAILURE ? (
              <div className="mt-12 flex justify-start">
                <Button
                  onClick={() => {
                    setIsTerminalVisible(false);
                    formik.resetForm();
                  }}
                  color="grey"
                  iconEnd={<FiArrowLeft size={20} />}
                >
                  Back
                </Button>
              </div>
            ) : null}
          </>
        ) : (
          <>
            <Heading as="h2" size="md">
              Create a new GitHub application
            </Heading>
            {installationData &&
            !installationLoading &&
            reposData &&
            !reposLoading ? (
              <>
                <Text color="gray.400">
                  When you push to Git, your application will be redeployed
                  automatically.
                </Text>

                <Grid
                  templateColumns={{
                    sm: 'repeat(1, 1fr)',
                    md: 'repeat(3, 1fr)',
                  }}
                >
                  <GridItem colSpan={2}>
                    <Flex alignItems="center" mt="12">
                      <Avatar
                        size="sm"
                        name={user?.userName}
                        src={user?.avatarUrl}
                      />
                      <Text ml="2" fontWeight="bold">
                        {user?.userName}
                      </Text>
                    </Flex>
                    <form onSubmit={formik.handleSubmit}>
                      <Box mt="8">
                        <FormLabel>Repository</FormLabel>
                        <Select
                          placeholder="Select repository"
                          isSearchable={false}
                          onChange={handleChangeRepo}
                          options={repoOptions}
                        />
                      </Box>

                      <Text mt="1" color="gray.400" fontSize="sm">
                        Can't see your repo in the list?{' '}
                        <Link
                          onClick={() => setIsProceedModalOpen(true)}
                          textDecoration="underline"
                        >
                          Configure the GitHub app.
                        </Link>
                      </Text>

                      <Box mt="8">
                        <FormLabel>Branch to deploy</FormLabel>
                        <Select
                          placeholder="Select branch"
                          isSearchable={false}
                          disabled={
                            !branchesData ||
                            branchesLoading ||
                            reposLoading ||
                            !reposData
                          }
                          onChange={handleChangeBranch}
                          options={branchOptions}
                        />
                      </Box>

                      <Box mt="8" display="flex" justifyContent="flex-end">
                        <Button
                          type="submit"
                          color="grey"
                          disabled={!selectedBranch || !selectedRepo}
                          isLoading={loading}
                        >
                          Create
                        </Button>
                      </Box>
                    </form>
                  </GridItem>
                </Grid>
              </>
            ) : !reposLoading && !installationLoading && !reposData ? (
              <>
                <Alert mb="4" mt="4" w="65%" status="info">
                  <AlertIcon />
                  <Box flex="1">
                    <AlertTitle>Set up repository permissions</AlertTitle>
                    <AlertDescription display="block">
                      First you will need to set up permissions for repositories
                      that you would like to use with Ledokku. Once that's done,
                      it's time to choose repo and branch that you would like to
                      create app from and off we go.
                    </AlertDescription>
                  </Box>
                </Alert>

                <Button
                  color="grey"
                  onClick={() => setIsProceedModalOpen(true)}
                >
                  Set up permissions
                </Button>
              </>
            ) : (
              <Spinner />
            )}
          </>
        )}
        <Modal
          isOpen={isProceedModalOpen}
          onClose={() => setIsProceedModalOpen(false)}
          isCentered
        >
          <ModalOverlay />
          <ModalContent>
            <ModalHeader>Github setup info</ModalHeader>
            <ModalCloseButton />
            <ModalBody>
              New window is about to open. After you are done selecting github
              repos, close the window and refresh page.
            </ModalBody>

            <ModalFooter>
              <Button
                color="grey"
                variant="outline"
                className="mr-3"
                onClick={() => setIsProceedModalOpen(false)}
              >
                Cancel
              </Button>
              <Button
                color="grey"
                onClick={() => {
                  handleOpen();
                  setIsProceedModalOpen(false);
                }}
              >
                Proceed
              </Button>
            </ModalFooter>
          </ModalContent>
        </Modal>
      </Container>
    </>
  );
}
Example #19
Source File: AppProxyPorts.tsx    From ledokku with MIT License 4 votes vote down vote up
AppProxyPorts = ({ appId }: AppProxyPortsProps) => {
  const toast = useToast();
  const [isAddModalOpen, setIsAddModalOpen] = useState(false);
  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<
    false | AppProxyPort
  >(false);
  const {
    data: appProxyPortsData,
    loading: appProxyPortsLoading,
    // TODO display error
    // error: appProxyPortsError,
    refetch: appProxyPortsRefetch,
  } = useAppProxyPortsQuery({
    variables: { appId },
    notifyOnNetworkStatusChange: true,
  });
  const [
    removeAppProxyPortMutation,
    { loading: removeAppPortLoading },
  ] = useRemoveAppProxyPortMutation();

  const handleCloseModal = () => {
    setIsDeleteModalOpen(false);
  };

  const handleRemovePort = async () => {
    const proxyPort = isDeleteModalOpen;
    if (!proxyPort) return;

    try {
      await removeAppProxyPortMutation({
        variables: {
          input: {
            appId,
            scheme: proxyPort.scheme,
            host: proxyPort.host,
            container: proxyPort.container,
          },
        },
      });
      await appProxyPortsRefetch();
      setIsDeleteModalOpen(false);
      toast.success('Port mapping deleted successfully');
    } catch (error) {
      toast.error(error.message);
    }
  };

  return (
    <>
      <Box py="5">
        <Heading as="h2" size="md">
          Port Management
        </Heading>
        <Text fontSize="sm" color="gray.400">
          The following ports are assigned to your app.
        </Text>
      </Box>

      {appProxyPortsLoading ? (
        <Text fontSize="sm" color="gray.400">
          Loading...
        </Text>
      ) : null}

      {appProxyPortsData && appProxyPortsData.appProxyPorts.length > 0 ? (
        <Table>
          <Thead>
            <Tr>
              <Th>Scheme</Th>
              <Th isNumeric>Host port</Th>
              <Th isNumeric>Container port</Th>
              <Th></Th>
            </Tr>
          </Thead>
          <Tbody>
            {appProxyPortsData.appProxyPorts.map((proxyPort, index) => (
              <Tr key={index}>
                <Td>{proxyPort.scheme}</Td>
                <Td isNumeric>{proxyPort.host}</Td>
                <Td isNumeric>{proxyPort.container}</Td>
                <Td>
                  <Button
                    colorScheme="red"
                    variant="link"
                    size="sm"
                    onClick={() => setIsDeleteModalOpen(proxyPort)}
                  >
                    Delete
                  </Button>
                </Td>
              </Tr>
            ))}
          </Tbody>
        </Table>
      ) : null}

      <Modal isOpen={!!isDeleteModalOpen} onClose={handleCloseModal} isCentered>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Delete port</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            Are you sure, you want to delete this port mapping?
          </ModalBody>

          <ModalFooter>
            <Button mr={3} onClick={handleCloseModal}>
              Cancel
            </Button>
            <Button
              colorScheme="red"
              isLoading={appProxyPortsLoading || removeAppPortLoading}
              onClick={handleRemovePort}
            >
              Delete
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>

      <Button
        mt="3"
        variant="outline"
        size="sm"
        onClick={() => setIsAddModalOpen(true)}
      >
        Add port mapping
      </Button>

      <AddAppProxyPorts
        appId={appId}
        appProxyPortsRefetch={appProxyPortsRefetch}
        open={isAddModalOpen}
        onClose={() => setIsAddModalOpen(false)}
      />
    </>
  );
}
Example #20
Source File: AddAppProxyPorts.tsx    From ledokku with MIT License 4 votes vote down vote up
AddAppProxyPorts = ({
  appId,
  appProxyPortsRefetch,
  open,
  onClose,
}: AddAppProxyPortsProps) => {
  const [addAppProxyPortMutation] = useAddAppProxyPortMutation();
  const toast = useToast();
  const formik = useFormik<{ host: string; container: string }>({
    initialValues: {
      host: '',
      container: '',
    },
    validateOnChange: true,
    validationSchema: createAppProxyPortSchema,
    onSubmit: async (values) => {
      try {
        await addAppProxyPortMutation({
          variables: {
            input: {
              appId,
              host: values.host,
              container: values.container,
            },
          },
        });
        await appProxyPortsRefetch();
        toast.success('Port mapping created successfully');

        onClose();
      } catch (error) {
        toast.error(error.message);
      }
    },
  });

  if (!open) {
    return null;
  }

  return (
    <Modal isOpen={open} onClose={onClose} isCentered size="xl">
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>Add port mapping</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <SimpleGrid columns={{ sm: 1, md: 2 }} spacing={3}>
            <FormControl
              id="host"
              isInvalid={Boolean(formik.errors.host && formik.touched.host)}
            >
              <FormLabel>Host port:</FormLabel>
              <Input
                name="host"
                value={formik.values.host}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
              />
              <FormErrorMessage>{formik.errors.host}</FormErrorMessage>
            </FormControl>
            <FormControl
              id="container"
              isInvalid={Boolean(
                formik.errors.container && formik.touched.container
              )}
            >
              <FormLabel>Container port:</FormLabel>
              <Input
                name="container"
                value={formik.values.container}
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
              />
              <FormErrorMessage>{formik.errors.container}</FormErrorMessage>
            </FormControl>
          </SimpleGrid>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Cancel
          </Button>
          <Button
            colorScheme="red"
            isLoading={formik.isSubmitting}
            onClick={() => formik.handleSubmit()}
          >
            Create
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  );
}
Example #21
Source File: note-form.tsx    From notebook with MIT License 4 votes vote down vote up
NoteForm: React.SFC<NoteFormProps> = ({
  isOpen,
  onClose,
  selectedNote,
  handleNoteCreate,
  handleNoteUpdate
}) => {
  const { register, handleSubmit, formState, errors } = useForm<FormInputs>({
    mode: "onChange"
  });

  const onSubmit: SubmitHandler<FormInputs> = data => {
    let newNote: note = {
      id: "",
      title: data.title,
      body: data.body
    };
    if (handleNoteCreate) {
      newNote.id = nanoid();
      if (handleNoteCreate) handleNoteCreate(newNote);
    } else {
      newNote.id = selectedNote ? selectedNote.id : "";
      if (handleNoteUpdate) handleNoteUpdate(newNote);
    }
    onClose();
  };

  const validateTitle = (value: string) => {
    if (!value) {
      return "Title is required";
    } else return true;
  };

  const validateBody = (value: string) => {
    if (!value) {
      return "Body is required";
    } else return true;
  };

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      size="lg"
      isCentered
      motionPreset="slideInBottom"
    >
      <ModalOverlay />
      <ModalContent>
        <form onSubmit={handleSubmit(onSubmit)}>
          <ModalHeader>{selectedNote ? "Edit" : "Create"} a Note</ModalHeader>
          <ModalCloseButton />
          <ModalBody pb={6}>
            <FormControl isInvalid={!!errors?.title} isRequired>
              <FormLabel>Title</FormLabel>
              <Input
                name="title"
                placeholder="Title"
                defaultValue={selectedNote?.title}
                ref={register({ validate: validateTitle })}
              />
              <FormErrorMessage>
                {!!errors?.title && errors?.title?.message}
              </FormErrorMessage>
            </FormControl>
            <FormControl size="lg" mt={4} isInvalid={!!errors?.body} isRequired>
              <FormLabel>Body</FormLabel>
              <Textarea
                name="body"
                placeholder="Body"
                size="md"
                borderRadius="5px"
                defaultValue={selectedNote?.body}
                ref={register({ validate: validateBody })}
              />
              <FormErrorMessage>
                {!!errors?.body && errors?.body?.message}
              </FormErrorMessage>
            </FormControl>
          </ModalBody>
          <ModalFooter>
            <Button
              type="submit"
              colorScheme="blue"
              isLoading={formState.isSubmitting}
              mr={3}
            >
              Save
            </Button>
            <Button onClick={onClose}>Cancel</Button>
          </ModalFooter>
        </form>
      </ModalContent>
    </Modal>
  );
}